我有一个脚本,使用fputcsv
生成一个大型CSV文件并将其发送到浏览器。它可以工作,但浏览器不显示文件下载提示(或开始下载文件),直到整个CSV文件在服务器端生成,这需要很长时间。
相反,我希望在仍然生成文件的其余部分时开始下载。我知道这是可能的,因为它是PHPMyAdmin中的'导出数据库'选项的工作原理 - 即使您的数据库很大,单击“导出”按钮也会立即开始下载。
如何调整下面的现有代码,让下载立即开始?
$csv = 'title.csv';
header( "Content-Type: text/csv;charset=utf-8" );
header( "Content-Disposition: attachment;filename=\"$csv\"" );
header( "Pragma: no-cache" );
header( "Expires: 0" );
$fp = fopen('php://output', 'w');
fputcsv($fp, array_keys($array), ';', '"');
foreach ($array as $fields)
{
fputcsv($fp, $fields, ';', '"');
}
fclose($fp);
exit();
答案 0 :(得分:9)
根据经验,似乎在收到带有Content-Disposition: attachment
标题的回复时,不同的浏览器会在以下时刻显示文件下载对话框:
我们的目标如下:
阻碍这些目标的可能是多级缓冲,你可以尝试以不同的方式进行战斗。
如果您将output_buffering
设置为Off
以外的值,PHP将自动创建一个输出缓冲区,用于存储脚本尝试发送到响应正文的所有输出。您可以通过确保output_buffering
文件中Off
设置为php.ini
,或者apache.conf
或nginx.conf
等网络服务器配置文件来阻止此操作。或者,您可以使用ob_end_flush()
或ob_end_clean()
在脚本开头关闭输出缓冲区(如果存在):
if (ob_get_level()) {
ob_end_clean();
}
一旦输出超过PHP输出缓冲区,它可能会被您的网络服务器缓冲。您可以通过定期调用flush()
来尝试解决此问题(例如,每100行),尽管PHP手册对提供任何保证犹豫不决,列出了可能失败的特定情况:
冲洗
...
刷新PHP的写缓冲区以及PHP正在使用的任何后端(CGI,Web服务器等)。这会尝试将当前输出一直推送到浏览器,但需要注意几点。
flush()可能无法覆盖Web服务器的缓冲方案......
多个服务器,尤其是Win32上的服务器,仍会缓冲脚本的输出,直到它终止,然后才将结果传输到浏览器。
像mod_gzip这样的Apache服务器模块可能会自行缓冲,导致 flush()不会导致数据立即发送到客户端。
您可以在每次尝试回显任何输出时自动调用PHP flush()
,方法是在脚本开头调用ob_implicit_flush
- 但要注意,如果您通过一种机制启用了gzip尊重flush()
调用,例如Apache的mod_deflate
模块,这种定期刷新会削弱其压缩尝试,并可能导致您的压缩'输出大于未压缩的输出。明确地调用flush()
每个 n 输出行,对于一些适度但非微小的 n ,因此可能是更好的做法。
总而言之,你应该调整你的脚本看起来像这样:
<?php
if (ob_get_level()) {
ob_end_clean();
}
$csv = 'title.csv';
header( "Content-Type: text/csv;charset=utf-8" );
header( "Content-Disposition: attachment;filename=\"$csv\"" );
header( "Pragma: no-cache" );
header( "Expires: 0" );
flush(); // Get the headers out immediately to show the download dialog
// in Firefox
$array = get_your_csv_data(); // This needs to be fast, of course
$fp = fopen('php://output', 'w');
fputcsv($fp, array_keys($array), ';', '"');
foreach ($array as $i => $fields)
{
fputcsv($fp, $fields, ';', '"');
if ($i % 100 == 0) {
flush(); // Attempt to flush output to the browser every 100 lines.
// You may want to tweak this number based upon the size of
// your CSV rows.
}
}
fclose($fp);
?>
如果这不起作用,那么我不认为您可以从PHP代码中做更多的事情来尝试解决问题 - 您需要弄清楚什么是&#39;导致您的Web服务器缓冲输出并尝试使用服务器的配置文件解决该问题。
答案 1 :(得分:0)
尚未对此进行测试。尝试在n个数据行之后刷新脚本。
flush();
答案 2 :(得分:0)
尝试 Mark Amery 的答案,但只强调以下内容:
$array = get_your_csv_data(); // This needs to be fast, of course
如果要获取大量记录,请按块(例如,每1000条记录)进行获取。
所以:
答案 3 :(得分:-2)
我认为你正在寻找octet-stream标题。
$csv = 'title.csv';
header('Content-Type: application/octet-stream');
header("Content-Disposition: attachment;filename=\"$csv\"" );
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate');
header('Expires: 0');
$fp = fopen('php://output', 'w');
fputcsv($fp, array_keys($array), ';', '"');
foreach ($array as $fields)
{
fputcsv($fp, $fields, ';', '"');
}
fclose($fp);
exit();