加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

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

发布时间:2020-12-13 21:51:28 所属栏目:PHP教程 来源:网络整理
导读:我有一个脚本,使用 fputcsv 生成一个大的CSV文件,并将其发送到浏览器.它可以工作,但浏览器不显示文件下载提示(或开始下载文件),直到整个CSV文件在服务器端生成,这需要很长时间. 相反,我希望在仍然生成文件的其余部分时开始下载.我知道这是可能的,因为它是PHP
我有一个脚本,使用 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();

解决方法

根据经验,似乎在收到以Content-Disposition:附件标题为特色的回复时,不同的浏览器会在以下时刻显示文件下载对话框:

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

那么,我们的目标如下:

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

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

PHP的output_buffer

如果将output_buffering设置为Off以外的值,PHP将自动创建一个输出缓冲区,用于存储脚本尝试发送到响应主体的所有输出.您可以通过确保从php.ini文件或从apache.conf或nginx.conf等Web服务器配置文件中将output_buffering设置为Off来防止这种情况发生.或者,您可以使用ob_end_flush()ob_end_clean()在脚本开头关闭输出缓冲区(如果存在):

if (ob_get_level()) {
    ob_end_clean();
}

您的网络服务器完成缓冲

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

flush

Flushes the write buffers of PHP and whatever backend PHP is using (CGI,a web server,etc). This attempts to push current output all the way to the browser with a few caveats.

flush() may not be able to override the buffering scheme of your web server …

Several servers,especially on Win32,will still buffer the output from your script until it terminates before transmitting the results to the browser.

Server modules for Apache like mod_gzip may do buffering of their own that will cause flush() to not result in data being sent immediately to the client.

每次尝试回显任何输出时,你都可以通过在脚本开头调用ob_implicit_flush自动调用flush() – 但要注意,如果你通过一个尊重flush()调用的机制启用了gzip,比如Apache的mod_deflate模块,这种常规刷新将削弱其压缩尝试,并可能导致您的“压缩”输出比未压缩时更大.因此,对于某些适度但非微小的n,每n行输出显式调用flush()可能是更好的做法.

把它们放在一起,然后,你应该调整你的脚本看起来像这样:

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

    foreach ($array as $i => $fields) 
    {
        fputcsv($fp,'"');
        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代码尝试解决问题 – 您需要弄清楚是什么导致您的Web服务器缓冲输出并尝试使用它来解决问题您的服务器的配置文件.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读