2010-11-10 4 views
6

Ich habe einige PHP-Code, der eine Abfrage auf eine Datenbank ausführt, speichert die Ergebnisse in eine CSV-Datei und ermöglicht dann dem Benutzer, die Datei herunterzuladen. Das Problem ist, die CSV-Datei enthält HTML-Seite um den tatsächlichen CSV-Inhalt.Forcing-Datei-Download in PHP - innerhalb Joomla-Framework

Ich habe alle verwandten Fragen hier bereits gelesen, einschließlich this one. Leider existiert mein Code innerhalb von Joomla, und selbst wenn ich versuche, auf eine Seite umzuleiten, die nur Header enthält, umgibt Joomla sie automatisch mit einem eigenen Navigationscode. Dies geschieht nur zum Zeitpunkt des Downloads; Wenn ich mir die CSV-Datei ansehe, die auf dem Server gespeichert ist, enthält sie nicht den HTML-Code.

Kann mir jemand mit einem Weg helfen, einen Download der tatsächlichen CSV-Datei zu erzwingen, wie es auf dem Server ist, anstatt als der Browser es bearbeitet wird? Ich habe versucht, den Header Standort verwendet wird, wie folgt aus:

header('Location: ' . $filename); 

aber es öffnet die Datei im Browser, anstatt zwingt den Dialog zum Speichern.

Hier ist meine aktuellen Code:

//set dynamic filename 
$filename = "customers.csv"; 
//open file to write csv 
$fp = fopen($filename, 'w'); 

//get all data 
$query = "select 
    c.firstname,c.lastname,c.email as customer_email, 
    a.email as address_email,c.phone as customer_phone, 
    a.phone as address_phone, 
    a.company,a.address1,a.address2,a.city,a.state,a.zip, c.last_signin 
    from {$dbpre}customers c 
    left join {$dbpre}customers_addresses a on c.id = a.customer_id order by c.last_signin desc"; 

$votes = mysql_query($query) or die ("File: " . __FILE__ . "<br />Line: " . __LINE__ . "<p>{$query}<p>" . mysql_error()); 
$counter = 1; 
while ($row = mysql_fetch_array($votes,1)) { 
    //put header row 
    if ($counter == 1){ 
     $headerRow = array(); 
     foreach ($row as $key => $val) 
      $headerRow[] = $key; 
     fputcsv($fp, $headerRow); 
    } 
    //put data row 
    fputcsv($fp, $row); 
    $counter++; 
} 

//close file 
fclose($fp); 

//redirect to file 
header("Content-type: application/octet-stream"); 
header("Content-Disposition: attachment; filename=".$filename); 
header("Content-Transfer-Encoding: binary"); 
readfile($filename); 
exit; 

EDITS Volle URL sieht wie folgt aus:

http://mysite.com/administrator/index.php?option=com_eimcart&task=customers 

mit dem eigentlichen Download-Link wie folgt aussehen:

http://mysite.com/administrator/index.php?option=com_eimcart&task=customers&subtask=export 

MOR E EDITS Hier ist eine Einstellung der Seite, auf der sich der Code befindet. Die generierte Datei zieht immer noch den HTML-Code für das Untermenü ein. Der Code für den ausgewählten Link (Export als CSV) ist jetzt

index.php?option=com_eimcart&task=customers&subtask=export&format=raw 

alt text

Hier ist ein Screenshot der erzeugten, gespeicherten Datei:

alt text

Es während der schrumpften Laden Sie hier hoch, aber der gelb hervorgehobene Text ist der HTML-Code für die Subnav (Kunden auflisten, neuen Kunden hinzufügen, als CSV exportieren). So sieht mein vollständiger Code jetzt aus; wenn ich nur das letzte Stück HTML loswerden könnte, wäre es perfekt.

$fp= fopen("php://output", 'w'); 

      $query = "select c.firstname,c.lastname,c.email as customer_email, 
         a.email as address_email,c.phone as customer_phone, 
         a.phone as address_phone, a.company, a.address1, 
         a.address2,a.city,a.state,a.zip,c.last_signin 

         from {$dbpre}customers c 
         left join {$dbpre}customers_addresses a on c.id = a.customer_id 
         order by c.last_signin desc"; 

      $votes = mysql_query($query) or die ("File: " . __FILE__ . "<br />Line: " . __LINE__ . "<p>{$query}<p>" . mysql_error()); 
      $counter = 1; 

      //redirect to file 
      header("Content-type: application/octet-stream"); 
      header("Content-Disposition: attachment; filename=customers.csv"); 
      header("Content-Transfer-Encoding: binary"); 

      while ($row = mysql_fetch_array($votes,1)) { 

        //put header row 
        if ($counter == 1){ 
          $headerRow = array(); 
          foreach ($row as $key => $val) 
            $headerRow[] = $key; 

          fputcsv($fp, $headerRow); 
        } 

        //put data row 
        fputcsv($fp, $row); 
       $counter++; 
      } 

      //close file 
      fclose($fp); 

UPDATE Bjorn

Hier ist der Code (glaube ich), die für mich gearbeitet. Verwenden Sie den RAW-param in dem Link, der die Aktion aufruft:

index.php?option=com_eimcart&task=customers&subtask=export&format=raw 

Da dies verfahren war unser Link in einer Datei namens customers.php, die wie folgt aussieht:

switch ($r['subtask']){ 
    case 'add': 
    case 'edit': 
     //if the form is submitted then go to validation 
       include("subnav.php"); 
     if ($r['custFormSubmitted'] == "true") 
      include("validate.php"); 
     else 
      include("showForm.php"); 
     break; 

    case 'delete': 
       include("subnav.php"); 
     include("process.php"); 
      break; 

    case 'resetpass': 
       include("subnav.php"); 
     include("resetpassword"); 
      break; 

    case 'export': 
     include("export_csv.php"); 
      break; 


    default: 
       include("subnav.php"); 
     include("list.php"); 
     break; 
} 

Also, wenn ein Benutzer hat auf den obigen Link geklickt, die Datei export_csv.php ist automatisch enthalten.Diese Datei enthält den gesamten tatsächlichen Code:

+0

Sie müssen wahrscheinlich Joomla's URL-Rewriting für diesen speziellen Fall ausschalten. Können Sie einige vollständige URLs und Ihre .htaccess-Datei anzeigen? –

+0

OP bearbeiten, um angeforderte Informationen aufzunehmen. – EmmyS

+0

Ah, okay, das ist anders: Joomla schlägt wahrscheinlich die Kopf-/Fußzeile auf die Ausgabe in der Indexdatei, das kann nicht durch Hinzufügen einer Regel in .htaccess behoben werden ... Benötigt einen Joomla-Experten zum Aussortieren –

Antwort

3

Dies ist ein Stück Beispielcode, den ich gerade gekocht habe, um Ihnen zu helfen. Verwenden Sie es als eine Aktionsmethode in Ihrem Controller.

Dies allein wird nicht ausreichen, da die heruntergeladene Datei immer noch den umgebenden HTML-Code enthält. Um es loszuwerden und nur den Inhalt der CSV-Datei zu erhalten, müssen Sie Ihrer Anfrage format = row-Parameter hinzufügen. In meinem Fall ist das Verfahren innerhalb der com_csvexample Komponente, so würde die URL sein:

/index.php?option=com_csvexample&task=get_csv&format=raw 

EDIT

Um eine Zwischendatei ersetzen

//set dynamic filename 
$filename = "customers.csv"; 
//open file to write csv 
$fp = fopen($filename, 'w'); 

mit

zu vermeiden, mit
//open the output stream for writing 
//this will allow using fputcsv later in the code 
$fp= fopen("php://output", 'w'); 

Verwenden Sie diese Methode y Sie müssen den Code verschieben, der Header sendet, bevor alles in die Ausgabe geschrieben wird. Sie benötigen auch nicht den Anruf an die readfile Funktion.

+0

Ich analysierte nur Ihren Code ein wenig mehr in die Tiefe. Gibt es einen Grund, dass Sie möchten, dass die CSV-Datei auf dem Server gespeichert wird oder nur erstellt wird, damit Sie sie später an den Browser senden können? Wenn Sie es nicht brauchen, um auf dem Server zu bleiben, können Sie den csv direkt auf den Ausgangsstrom mit Echo ausgeben, sich viel Mühe ersparend ... Denken Sie daran, die Kopfzeilen zuerst zu schicken :) – silvo

+0

Das ist Code, den ich so geerbt habe Ich habe mit dem gearbeitet, was da war. Es gibt wirklich keinen Grund, die Datei auf dem Server zu behalten. Können Sie das Senden per Echo näher erläutern? Wird dies noch eine Datei erstellen, die der Benutzer auf seinen Computer herunterladen wird? Oder wird es nur die CSV im Browser anzeigen? – EmmyS

+0

Bitte werfen Sie einen Blick auf meine verbesserte Antwort. Das direkte Drucken des csv auf die Ausgabe unterscheidet sich nicht funktional von dem, was Ihr Code gerade macht. Das Verhalten des Browsers hängt von den Kopfzeilen ab, die Sie senden. Wenn Sie einen Header senden, der den Browser darüber informiert, dass der Inhalt tatsächlich ein Anhang ist, wird ein Dateidownload ausgelöst. – silvo

3

diese Methode, um Ihren Controller hinzufügen:

function exportcsv() { 
    $model = & $this->getModel('export'); 
    $model->exportToCSV(); 
} 

Dann ein neues Modell hinzufügen export.php, Code unten genannt. Sie müssen den Code für Ihre Situation ändern oder erweitern.

<?php 
/** 
* @package TTVideo 
* @author Martin Rose 
* @website www.toughtomato.com 
* @version 2.0 
* @copyright Copyright (C) 2010 Open Source Matters. All rights reserved. 
* @license http://www.gnu.org/copyleft/gpl.html GNU/GPL 
*/ 

//No direct acesss 
defined('_JEXEC') or die(); 
jimport('joomla.application.component.model'); 
jimport('joomla.filesystem.file'); 
jimport('joomla.filesystem.archive'); 
jimport('joomla.environment.response'); 

class TTVideoModelExport extends JModel 
{ 

    function exportToCSV() { 
    $files = array(); 
    $file = $this->__createCSVFile('#__ttvideo'); 
    if ($file != '') $files[] .= $file; 
    $file = $this->__createCSVFile('#__ttvideo_ratings'); 
    if ($file != '') $files[] .= $file; 
    $file = $this->__createCSVFile('#__ttvideo_settings'); 
    if ($file != '') $files[] .= $file; 
    // zip up csv files to be delivered 
    $random = rand(1, 99999); 
    $archive_filename = JPATH_SITE.DS.'tmp'.DS.'ttvideo_'. strval($random) .'_'.date('Y-m-d').'.zip'; 
    $this->__zip($files, $archive_filename); 
    // deliver file 
    $this->__deliverFile($archive_filename); 
    // clean up 
    JFile::delete($archive_filename); 
    foreach($files as $file) JFile::delete(JPATH_SITE.DS.'tmp'.DS.$file); 
    } 

    private function __createCSVFile($table_name) { 
    $db = $this->getDBO(); 
    $csv_output = ''; 

    // get table column names 
    $db->setQuery("SHOW COLUMNS FROM `$table_name`"); 
    $columns = $db->loadObjectList(); 

    foreach ($columns as $column) { 
     $csv_output .= $column->Field.'; '; 
    } 
    $csv_output .= "\n"; 

    // get table data 
    $db->setQuery("SELECT * FROM `$table_name`"); 
    $rows = $db->loadObjectList(); 
    $num_rows = count($rows); 
    if ($num_rows > 0) { 
     foreach($rows as $row) { 
     foreach($row as $col_name => $value) { 
      $csv_output .= $value.'; '; 
     } 
     $csv_output .= "\n"; 
     } 
    } 
    $filename = substr($table_name, 3).'.csv'; 
    $file = JPATH_SITE.DS.'tmp'.DS.$filename; 
    // write file to temp directory 
    if (JFile::write($file, $csv_output)) return $filename; 
    else return ''; 
    } 

    private function __deliverFile($archive_filename) { 
    $filesize = filesize($archive_filename); 
    JResponse::setHeader('Content-Type', 'application/zip'); 
    JResponse::setHeader('Content-Transfer-Encoding', 'Binary'); 
    JResponse::setHeader('Content-Disposition', 'attachment; filename=ttvideo_'.date('Y-m-d').'.zip'); 
    JResponse::setHeader('Content-Length', $filesize); 
    echo JFile::read($archive_filename); 
    } 

    /* creates a compressed zip file */ 
    private function __zip($files, $destination = '') { 
    $zip_adapter = & JArchive::getAdapter('zip'); // compression type 
    $filesToZip[] = array(); 
    foreach ($files as $file) { 
     $data = JFile::read(JPATH_SITE.DS.'tmp'.DS.$file); 
     $filesToZip[] = array('name' => $file, 'data' => $data); 
    } 
    if (!$zip_adapter->create($destination, $filesToZip, array())) { 
     global $mainframe; 
     $mainframe->enqueueMessage('Error creating zip file.', 'message'); 
    } 
    } 


} 
?> 

Dann gehen Sie zu Ihrer Standard-view.php und fügen Sie eine benutzerdefinierte Schaltfläche, z.

// custom export to set raw format for download 
$bar = & JToolBar::getInstance('toolbar'); 
$bar->appendButton('Link', 'export', 'Export CSV', 'index.php?option=com_ttvideo&task=export&format=raw'); 

Viel Glück!

+0

Danke, Martin, aber dieser Code (den ich geerbt habe) wird prozedural gemacht, nicht in mvc. – EmmyS

0

Sie können Apache mod_cern_meta verwenden, um HTTP-Header zu statischen Dateien hinzuzufügen. Content-Disposition: attachment. Die benötigten .htaccess und .meta Dateien können von PHP erstellt werden.

0

Eine weitere Möglichkeit, CSV-Daten in einer Joomla-Anwendung auszugeben, besteht darin, eine Ansicht im CSV- statt im HTML-Format zu erstellen. Das heißt, eine Datei erstellen, wie folgt:

Komponenten/com_mycomp/views/etwas/view.csv.php

und fügen Sie Inhalt ähnlich dem folgenden:

<?php 
// No direct access 
defined('_JEXEC') or die; 

jimport('joomla.application.component.view'); 

class MyCompViewSomething extends JViewLegacy // Assuming a recent version of Joomla! 
{   
    function display($tpl = null) 
    { 
     // Set document properties 
     $document = &JFactory::getDocument(); 
     $document->setMimeEncoding('text/csv'); 

     JResponse::setHeader('Content-disposition', 'inline; filename="something.csv"', true); 

     // Output UTF-8 BOM 
     echo "\xEF\xBB\xBF"; 

     // Output some data 
     echo "field1, field2, 'abc 123', foo, bar\r\n"; 
    } 
} 
?> 

Dann können Sie Datei erstellen Download-Links wie folgt:

/index.php?option=com_mycomp & view = etwas & format = csv

Jetzt, Sie hätten Recht, den Inline-Teil in der Content-Disposition zu hinterfragen. Wenn ich mich beim Schreiben dieses Codes vor einigen Jahren richtig erinnere, hatte ich Probleme mit der Option "Anhang". Dieser Link, den ich gerade googelte, kam mir als Treiber dafür bekannt vor: https://dotanything.wordpress.com/2008/05/30/content-disposition-attachment-vs-inline/. Seitdem verwende ich 'Inline' und werde immer noch aufgefordert, die Datei in allen Browsern zu speichern, in denen ich getestet habe.Ich habe in letzter Zeit nicht versucht, 'attachment' zu verwenden, so dass es jetzt natürlich funktionieren kann (der Link dort ist jetzt 7 Jahre alt!)