我有一些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;
EDITS 完整网址如下所示:
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
现在,这是生成的已保存文件的屏幕截图:
在此处上传期间收缩,但以黄色突出显示的文本是subnav的html代码(列出客户,添加新客户,导出为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);
答案 0 :(得分:3)
这是我刚刚编写的一段示例代码,可以帮助您解决问题。将它用作控制器中的操作方法。
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
函数。
答案 1 :(得分:3)
将此方法添加到您的控制器:
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' );
祝你好运!
答案 2 :(得分:0)
您可以使用Apache的mod_cern_meta
将HTTP标头添加到静态文件中。 Content-Disposition: attachment
。 PHP可以创建所需的.htaccess
和.meta
文件。
答案 3 :(得分:0)
在Joomla应用程序中输出CSV数据的另一种方法是使用CSV而不是HTML格式创建视图。也就是说,按如下方式创建文件:
组件/ com_mycomp /视图/东西/ 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
现在,您可以质疑&#39;内联&#39;参与Content-disposition。如果我几年前在编写这段代码时回忆得正确的话,那么“附件”就会出现问题。选项。我刚刚用Google搜索的这个链接似乎很熟悉它的驱动程序:https://dotanything.wordpress.com/2008/05/30/content-disposition-attachment-vs-inline/。我一直在使用&#39; inline&#39;从那时起,我仍然被提示从我测试的任何浏览器中适当地保存文件。我还没有尝试过使用附件&#39;最近的任何时候,所以它当然可以正常工作(现在有7年的链接!)