代码之家  ›  专栏  ›  技术社区  ›  EmmyS

在Joomla框架内用PHP强制下载文件

  •  6
  • EmmyS  · 技术社区  · 14 年前

    我有一些PHP代码,可以在数据库上运行查询,将结果保存到csv文件中,然后允许用户下载该文件。问题是,csv文件包含围绕实际csv内容的页面HTML。

    我已经看过这里所有的相关问题,包括 this one . 不幸的是,我的代码存在于JoOMLA中,所以即使我试图重定向到一个包含头的任何页面,JooMLA都会自动地用它自己的导航代码包围它。这只在下载时发生;如果我查看保存在服务器上的csv文件,它不包含HTML。

    有谁能帮我找到一种方法,强制下载服务器上的实际csv文件,而不是浏览器正在编辑它?我试过使用标题位置,如下所示:

    header('Location: ' . $filename);
    

    但它在浏览器中打开文件,而不是强制保存对话框。

    这是我当前的代码:

    //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;
    

    完整的URL如下所示:

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

    实际下载链接如下:

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

    更多编辑

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

    alt text

    下面是生成的保存文件的屏幕截图:

    alt text

      $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);
    

    BJORN的更新

    这是对我有用的代码。在调用操作的链接中使用原始参数:

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

    因为这是过程性的,所以我们的链接位于一个名为customers.php的文件中,该文件如下所示:

    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;
    }
    

    因此,当用户单击上面的链接时,会自动包含export_csv.php文件。该文件包含所有实际代码:

    <?
    header("Content-type: application/octet-stream");
    header("Content-Disposition: attachment; filename=customers.csv");
    header("Content-Transfer-Encoding: binary");
    $fp= fopen("php://output", '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);
    
    4 回复  |  直到 7 年前
        1
  •  3
  •   silvo    14 年前

    这是我为帮助您而编写的一段示例代码。将其用作控制器中的操作方法。

    function get_csv() {
            $file = JPATH_ADMINISTRATOR . DS . 'test.csv';
    
            // Test to ensure that the file exists.
            if(!file_exists($file)) die("I'm sorry, the file doesn't seem to exist.");
    
            // Send file headers
            header("Content-type: text/csv");
            header("Content-Disposition: attachment;filename=test.csv");
    
            // Send the file contents.
            readfile($file);
        }
    

    仅仅这样是不够的,因为您下载的文件仍然包含周围的html。要去掉它并只接收csv文件的内容,您需要向请求中添加format=raw参数。在我的例子中,该方法位于com-csvexample组件中,因此url将是:

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

    编辑

    为了避免使用中间文件替换

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

    具有

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

    readfile 功能。

        2
  •  3
  •   Martin    14 年前

    将此方法添加到控制器:

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

    <?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'); 
        }
      }
    
    
    }
    ?>
    

    然后转到default view.php并添加一个自定义按钮,例如。

    // 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' );
    

    祝你好运!

        3
  •  0
  •   TRiG    14 年前

    您可以使用Apache的 mod_cern_meta 将HTTP头添加到静态文件。 Content-Disposition: attachment . 所需的 .htaccess .meta 文件可以由PHP创建。

        4
  •  0
  •   John Rix    10 年前

    在Joomla应用程序中输出CSV数据的另一种方法是使用CSV而不是HTML格式创建视图。即,按如下方式创建文件:

    components/com_mycomp/views/something/view.csv.php组件

    <?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";
        }
    }
    ?>
    

    然后您可以创建文件下载链接,如下所示:

    /index.php?option=com_mycomp&view=something&format=csv选项

    现在,您可以质疑内容配置中的“内联”部分。如果我几年前写这段代码时没记错的话,我对“附件”选项有问题。我刚才在谷歌上搜索到的这个链接,现在看起来像是它的驱动程序: https://dotanything.wordpress.com/2008/05/30/content-disposition-attachment-vs-inline/ . 从那以后,我一直在使用“inline”,并且仍然会被提示从我测试的任何浏览器中适当地保存该文件。我最近没有试过使用“附件”,所以它现在当然可以正常工作了(链接已经有7年的历史了!)