为什么当我导出数据时,下载窗口不会出现在浏览器中很长时间


why doesn't download window appear in browser for long when I export data

我尝试将数据从数据库导出到.csv。当我单击导出链接时,如果有相当多的数据,我在浏览器中很长时间都看不到保存窗口。如果脚本看起来像挂起了一段时间,并且在相当长的时间后可以在浏览器中看到保存窗口,这可能会非常混乱。控制器中的代码如下所示:

$this->_helper->layout->disableLayout();
        $this->_helper->viewRenderer->setNoRender();
        $fileName = $list->list_name . '.csv';
        $this->getResponse()->setHeader('Content-Type', 'text/csv; charset=utf-8')
                            ->setHeader('Content-Disposition', 'attachment; filename="'. $fileName . '"');      
        $contacts = new Contact();
        $contacts->export($listId);

导出方法逐个读取记录并打印如下内容:

    $fp = fopen('php://output', 'w');        
    foreach ($mongodbCursor as $subscriber) {
           $row = formRow($subscriber);
       fputcsv($fp, $row);
        }       

我看到在某些应用程序上保存winow几乎立即出现,当您单击保存时,您会看到下载进度。

我试图替换:

    $this->getResponse()->setHeader('Content-Type', 'text/csv; charset=utf-8')
                        ->setHeader('Content-Disposition', 'attachment; filename="'. $fileName . '"');

有了这个:

    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename="'. $fileName . '"');

到目前为止,它没有帮助。我想知道是否可以在从数据库中一一读取所有数据之前发送标头?感谢您的帮助。

嗯,我不熟悉 php://output,我的应用程序使用 fopen,fwrite,fclose 将我的信息写入一个临时文件,之后我用类似的 header(); 选项给出它。

            $filesize = filesize("tmp/export.csv");
            header("Content-Type: text/csv");
            header("Content-Disposition: attachment; filename='"export.csv'"");
            header("Conent-Length: $filesize");
            readfile("tmp/export.csv");
            unlink("tmp/export.csv");
            exit;

这个会立即提供浏览器的下载窗口。

您可以尝试这样做:

  • 调用 header 函数而不是 $this->getResponse()->setHeader()(响应内容可能保存在缓冲区中,仅在完成时输出 - 导出完成的时间)
  • 尝试直接回显内容而不是写入 php://output(如果您在此之前设置了标头,则回显的所有内容都将放置在生成的 CSV 文件中)

编辑

fputcsv替换为如下所示print_row函数

function print_row($row) {
        echo '"' . implode('","', $row) . '"' . "'r'n"; 
}

该函数将第一个参数作为数组获取,添加",,并对内容进行回显。

// $file = path of file
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;

来源: http://php.net/manual/en/function.readfile.php

尝试在Content-Type值中使用application/force-download而不是text/csv

header("Content-Type: application/force-download; name='"" . $fileName . "'""); 

这将强制使用实际的下载框,而不仅仅是浏览器中显示内容。

以下是来自 MIME 应用程序的一些文档:

Mime application/force-download,通常

表示为 mime application/x-force-download,通常与 PHP 代码结合使用以触发下载框,而不是用于在 Web 浏览器中显示内容。

例如,如果您的网站上有一个文件,您希望用户下载而不是在 Web 浏览器中查看,则可以在文件的标头内容类型字段中输入相应的 MIME 应用程序/强制下载代码。请注意,MIME 应用程序/强制下载不是已注册的 MIME 类型。此外,当在PHP代码中使用时,mime application/force-download的首选拼写包含"x-"前缀。

我不知道

这是否是一个很好的解决方案,我没有过多地探索它,但它似乎正在起作用。在导出之前,我在标题之后添加一些数据到缓冲区后,它会加速。

    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename="'. $fileName . '"');
    echo str_pad('', ini_get('output_buffering'));      
    ob_flush();
    flush();
    Model_Subscriber1::exportList($listId);

为了使它在控制器中工作,我在Zend Bootstrap中添加了.php:

/**
 * hint to the dispatcher that it should not use output buffering to capture output generated by action controllers.
 *  By default, the dispatcher captures any output and appends it to the response object body content. 
 */
protected function _initFront()
{
    $frontController = Zend_Controller_Front::getInstance();
    $frontController->setParam('disableOutputBuffering', 1);        
}
看起来在这种情况下,

我快速在浏览器中获得下载窗口,然后导出数据,这可能需要相当长的时间。我不知道这个解决方案现在是否可以接受。我很乐意在这里发表你对此的看法。它会导致将空数据添加到导出文件时出现问题。因此,我将其更改为控制器。

    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename="'. $fileName . '"');
    Model_Subscriber1::exportList($listId);

和 chang 函数导出到这样的东西:

  foreach ($mongodbCursor as $subscriber) {
           $row = formRow($subscriber);
    echo '"' . implode('","', $row) . '"' . "'r'n";
    ob_flush();
    flush();                    
  }       

您的函数可能希望在长时间操作之前调用 flush/ob_flush,并让 HTTP 标头在长进程之前发送到客户端。