Принудительная загрузка файлов в PHP - внутри фреймворка Joomla - PullRequest
6 голосов
/ 10 ноября 2010

У меня есть некоторый PHP-код, который выполняет запрос к базе данных, сохраняет результаты в CSV-файл, а затем позволяет пользователю загрузить файл.Проблема в том, что файл csv содержит страницу HTML вокруг фактического содержимого csv.

Я уже прочитал все связанные с этим вопросы, включая этот .К сожалению, мой код существует в 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

БОЛЬШЕ РЕДАКТИРУЕТСЯ Вот снимок страницы, на которой находится код;сгенерированный файл все еще загружает HTML-файл для подменю.Код для выбранной ссылки (Экспорт в CSV) теперь

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

alt text

Теперь вот скриншот сгенерированного, сохраненного файла:

alt text

Он сократился во время загрузки здесь, но текст, выделенный желтым цветом, является html-кодом для subnav (список клиентов, добавление нового клиента, экспорт в формате csv).Вот как теперь выглядит мой полный код;если бы я мог просто избавиться от этого последнего бита html, это было бы идеально.

  $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

Вот код (я думаю), который работалдля меня.Используйте параметр RAW в ссылке, которая вызывает действие:

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 ]

3 голосов
/ 12 ноября 2010

Добавьте этот метод к вашему контроллеру:

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

Затем добавьте новую модель с именем export.php, код ниже.Вам нужно будет изменить или расширить код в соответствии с вашей ситуацией.

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


}
?>

Затем перейдите в файл 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 голосов
/ 11 ноября 2010

Это пример кода, который я только что приготовил, чтобы помочь вам. Используйте его как метод действия в вашем контроллере.

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

EDIT

Во избежание использования промежуточного файла, замените

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

0 голосов
/ 06 апреля 2015

Другим способом вывода данных CSV в приложении Joomla является создание представления с использованием CSV, а не формата HTML.То есть создайте файл следующим образом:

компоненты / com_mycomp / views / что-то / 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 = что-то & format = csv

Теперь вы можете задать вопрос «встроенной» части вContent-диспозиция.Если я правильно помню, когда писал этот код несколько лет назад, у меня были проблемы с опцией «attachment».Эта ссылка, которую я только что гуглил, теперь казалась знакомой для него: https://dotanything.wordpress.com/2008/05/30/content-disposition-attachment-vs-inline/.С тех пор я использую «inline», и мне все еще предлагают сохранить файл соответствующим образом из любых браузеров, с которыми я тестирую.В последнее время я не пробовал использовать 'attachment', поэтому теперь он может нормально работать (ссылка существует 7 лет!)

0 голосов
/ 12 ноября 2010

Вы можете использовать Apache mod_cern_meta для добавления заголовков HTTP к статическим файлам.Content-Disposition: attachment.Необходимые файлы .htaccess и .meta могут быть созданы PHP.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...