为什么 PHP 在 fclose() 之后继续输出到 CSV 文件


Why PHP continue to output to CSV file after fclose()?

在网页上,我使用以下代码将一些数据写入CSV文件,最后用fclose((关闭;

header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename='.$filename);
$out = fopen('php://output', 'w');
fputcsv($out, $cvs_cols);
fclose($out);
echo "HELLO WORLD"; // sneaks into CSV!?

为什么"HELLO WORLD"在已经fclose((的情况下进入CSV下载文件?我想输出要在浏览器中显示的页面的其余 HTML。我该怎么做?

在 1 个 HTTP 请求之后是 1 个响应。您不能同时发送内容类型text/csv和内容类型text/html(SPDY 可能是,但纯 HTTP 不是(。

fclose关闭文件描述符,但不关闭浏览器的输出。

您还应该设置一个Content-Length标题并输入文件大小。

马克贝克已经在评论中给出了最重要的一点:

echo和写入php://output会将内容放入同一流中:STDOUT 。其他选择是将CSV写入内存(但不使用它,则毫无意义(或文件。阅读有关这些流的更多信息:http://www.php.net/manual/en/features.commandline.io-streams.php

可能的解决方案:

您需要 2 个 HTTP 请求。 1 用于下载,另一个用于您的 HTML。最流行的方法是首先使用 HTML 响应并放入类似的东西

<meta http-equiv="refresh"
      content="3; URL=http://yourserver.com/download.php?id=pdf&amp;id=123" />

这将在 3 秒后开始下载。

目前还没有"CSV 文件"。

您正在做的是向客户端发送数据流,并告诉客户端此流具有text/csv Content-Typefilename $filename。 然后,客户端可以选择将其另存为 CSV 文件或仅在浏览器中显示。

此代码:

$out = fopen('php://output', 'w');
fputcsv($out, $cvs_cols);
fclose($out);

正在有效地做与echo $cvs_cols相同的事情(用一些额外的东西来格式化 csv 输出(。

因此,当调用echo "HELLO WORLD";时,它会以与$cvs_cols变量内容相同的数据流发送。

当您调用fopen('php://output', 'w')时,您将创建第二个文件句柄来php://output因为默认情况下会创建一个句柄,以输出对echo等的调用。因此,当您调用fclose($out)时,您只会关闭第二个文件句柄。

这里有一个非常古老的线程,但为了解决这个问题,我只是添加了一个简单的 exit((; 命令。因此,一个按钮使用查询字符串"action=export_csv"调用同一页面,然后使用 exit(( 运行该操作;在最后一行,希望有帮助。

<a href="?action=export_csv">Export CSV</a>

那么页面上的"操作"是:

if(isset($_GET['action']) && $_GET['action']=='export_csv'){
    // output headers so that the file is downloaded rather than displayed
    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename=email-responses.csv');
    // create a file pointer connected to the output stream
    $output = fopen('php://output', 'w');
    // output the column headings
    fputcsv($output, array('Email address'));
    $db = new PDO('mysql:host=hostname_mysql;dbname=database_mysql;charset=UTF8', username_mysql, password_mysql);
    $query = "SELECT XXX FROM XXXX";
    $result = $db->query($query);
    $data = $result->fetchAll(PDO::FETCH_ASSOC);
    // loop over the rows, outputting them
    foreach($data as $row){
        fputcsv($output, $row);
    }
    fclose($output);
    exit();
}

使用

ob_clean(( : ob_clean — 清理(擦除(输出缓冲区

flush(( : flush — 刷新输出缓冲区(flush(

ob_start();
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename='.$filename);
$out = fopen('php://output', 'w');
fputcsv($out, $cvs_cols);
fclose($out);
ob_end_clean();   //    the buffer and never prints or returns anything.

echo "HELLO WORLD"; // sneaks into CSV!?