在生成时将大型CSV文件下载到浏览器

Spi*_*erQ 3 php csv download

我有一个生成大型CSV文件的脚本,fputcsv并将其发送到浏览器.它可以工作,但浏览器不显示文件下载提示(或开始下载文件),直到整个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();
Run Code Online (Sandbox Code Playgroud)

Mar*_*ery 10

根据经验,似乎在接收具有Content-Disposition: attachment标题的响应时,不同的浏览器将在以下时刻显示文件下载对话框:

  • Firefox会在收到标题后立即显示该对话框
  • 一旦收到标题加上响应正文的255个字节,Internet Explorer就会显示该对话框.
  • 一旦收到标题加上响应正文的1023个字节,Chromium就会显示该对话框.

那么,我们的目标如下:

  1. 尽快将响应正文的第一个千字节刷新到浏览器,以便Chrome用户尽早看到文件下载对话框.
  2. 此后,定期向浏览器发送更多内容.

阻碍这些目标的可能是多级缓冲,您可以尝试以不同的方式进行战斗.

PHP的output_buffer

如果您output_buffering设置了其他值Off,PHP将自动创建一个输出缓冲区,用于存储脚本尝试发送给响应主体的所有输出.您可以通过确保从文件或Web服务器配置文件(例如或)output_buffering设置为来阻止此操作.或者,您可以在脚本开头使用或关闭输出缓冲区(如果存在):Offphp.iniapache.confnginx.confob_end_flush()ob_end_clean()

if (ob_get_level()) {
    ob_end_clean();
}
Run Code Online (Sandbox Code Playgroud)

您的网络服务器完成缓冲

一旦输出超过PHP输出缓冲区,它可能会被您的网络服务器缓冲.您可以尝试通过flush()定期调用(例如每100行)来解决这个问题,尽管PHP手册对提供任何保证犹豫不决,列出了一些可能失败的特殊情况:

红晕

...

刷新PHP的写缓冲区以及PHP正在使用的任何后端(CGI,Web服务器等).这会尝试将当前输出一直推送到浏览器,但需要注意几点.

flush()可能无法覆盖Web服务器的缓冲方案......

多个服务器,尤其是Win32上的服务器,仍将缓冲脚本的输出,直到它终止,然后再将结果传输到浏览器.

像mod_gzip这样的Apache服务器模块可能会自行缓冲,这会导致flush()不会导致数据立即发送到客户端.

你可以在flush()每次尝试回显任何输出时自动调用PHP ,通过ob_implicit_flush在脚本的开头调用- 但要注意,如果你通过一种尊重flush()调用的机制启用了gzip ,比如Apache的mod_deflate模块,那么这种常规的刷新将会瘫痪它的压缩尝试可能会导致您的"压缩"输出大于未压缩的输出.因此,对于一些适度但非微小的n,明确地调用flush()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);

?>
Run Code Online (Sandbox Code Playgroud)

如果这不起作用,那么我认为您无法通过PHP代码尝试解决问题 - 您需要弄清楚是什么导致您的Web服务器缓冲输出并尝试使用它来解决问题您的服务器的配置文件.