我需要将 CSV 文件写入 PHP 输出缓冲区,然后在写入完成后将该文件下载到客户端计算机。 (我想把它写在服务器上并下载它,它正在工作,但事实证明我在生产服务器上没有写访问权限)。
我有以下 PHP 脚本:
$basic_info = fopen("php://output", 'w');
$basic_header = array(HEADER_ITEMS_IN_HERE);
@fputcsv($basic_info, $basic_header);
while($user_row = $get_users_stmt->fetch(PDO::FETCH_ASSOC)) {
@fputcsv($basic_info, $user_row);
}
@fclose($basic_info);
header('Content-Description: File Transfer');
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename=test.csv');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize("php://output"));
ob_clean();
flush();
readfile("php://output");
我不知道该怎么办。 CSV 文件下载但不显示任何内容。我认为它与我的 ob_clean() 和lush() 命令的顺序有关,但我不确定订购这些东西的最佳方式是什么。
如有任何帮助,我们将不胜感激。
你做得有点太多了。 创建脚本的唯一目的是输出 CSV。 只需将其直接打印到屏幕上即可。 不要担心标头或缓冲区或 php://output 或类似的东西。
一旦确认将数据正确打印到屏幕上,只需在开头添加这些标题即可:
<?php
header("Content-disposition: attachment; filename=test.csv");
header("Content-Type: text/csv");
?>
...确认正确下载文件。 然后,如果您愿意,您可以添加其他标头(上面包含的标头是我自己使用的标头,没有任何额外的麻烦来使其工作,其他标头基本上是为了效率和缓存控制,其中一些可能已经得到适当处理由您的服务器决定,对于您的特定应用程序可能重要也可能不重要)。
如果需要,可以使用
ob_start()
和 ob_get_clean()
的输出缓冲将输出内容放入字符串中,然后可以使用该字符串来填写 Content-Length
。
正如我对埃德森答案的评论中提到的,我预计在代码的最后一行会出现“标头已发送”警告:
header('Content-Length: '.$streamSize);
因为输出是在发送此标头之前写入的,但他的示例可以正常工作。
一些调查使我得出以下结论:
当您使用输出缓冲区(无论是用户缓冲区,还是 默认 PHP 之一),您可以按照您的方式发送 HTTP 标头和内容 想。您知道任何协议都需要在正文之前发送标头 (因此术语“标头”),但是当您使用输出缓冲层时,PHP 我们会为您处理这件事。任何处理输出的 PHP 函数 标头(header()、setcookie()、session_start())实际上会使用 内部 sapi_header_op() 函数仅填充标题 缓冲。然后,当您使用 printf() 写入输出时,它会写入 输出缓冲区(假设有一个)。 当输出缓冲器是 发送后,PHP 首先开始发送标头,然后发送正文。 PHP 为您照顾一切。 如果您不喜欢这种行为,您 除了禁用任何输出缓冲层之外别无选择。
和
大多数配置下 PHP 缓冲区的默认大小是 4096 字节 (4KB) 这意味着 PHP 缓冲区最多可以容纳 4KB 的数据。 一旦这个 超出限制或PHP代码执行完成,缓冲内容 自动发送到正在使用的 PHP 后端(CGI、 mod_php、FastCGI)。 PHP-CLI 中的输出缓冲始终处于关闭状态。
Edson 的代码之所以有效,是因为输出缓冲区不会自动刷新,因为它没有超过缓冲区大小(并且在发送最后一个标头之前脚本不会明显终止)。
一旦输出缓冲区中的数据超过缓冲区大小,就会发出警告。或者在他的例子中,当
的数据$get_users_stmt->fetch(PDO::FETCH_ASSOC)
太大了。
为了防止这种情况,您应该使用 ob_start() 和 ob_end_flush() 自己管理输出缓冲;像下面这样:
// Turn on output buffering
ob_start();
// Define handle to output stream
$basic_info = fopen("php://output", 'w');
// Define and write header row to csv output
$basic_header = array('Header1', 'Header2');
fputcsv($basic_info, $basic_header);
$count = 0; // Auxiliary variable to write csv header in a different way
// Get data for remaining rows and write this rows to csv output
while($user_row = $get_users_stmt->fetch(PDO::FETCH_ASSOC)) {
if ($count == 0) {
// Write select column's names as CSV header
fputcsv($basic_info, array_keys($user_row));
} else {
//Write data row
fputcsv($basic_info, $user_row);
}
$count++;
}
// Get size of output after last output data sent
$streamSize = ob_get_length();
//Close the filepointer
fclose($basic_info);
// Send the raw HTTP headers
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename=test.csv');
header('Expires: 0');
header('Cache-Control: no-cache');
header('Content-Length: '. ob_get_length());
// Flush (send) the output buffer and turn off output buffering
ob_end_flush();
不过,你仍然受到其他限制。
我对你的代码做了一些调整。
请记住,在任何实际输出之前必须调用 header() 通过普通 HTML 标签、文件中的空行或从 PHP 发送
@
(PHP 错误控制运算符),因为它可能会导致开销,现在没有链接可以显示,但如果您搜索,您可能会找到它。在消除错误之前您应该三思而后行,大多数时间应该修复而不是消除错误。header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename=test.csv');
header('Expires: 0');
header('Cache-Control: no-cache');
$basic_info = fopen("php://output", 'w');
$basic_header = array(HEADER_ITEMS_IN_HERE);
fputcsv($basic_info, $basic_header);
$count = 0; // auxiliary variable to write csv header in a different way
while($user_row = $get_users_stmt->fetch(PDO::FETCH_ASSOC)) {
// Write select column's names as CSV header
if ($count == 0) {
fputcsv($basic_info, array_keys($user_row));
}
fputcsv($basic_info, $user_row);
$count++;
}
// get size of output after last output data sent
$streamSize = ob_get_length();
fclose($basic_info);
header('Content-Length: '.$streamSize);
如果服务器上已存在 csv 文件:
// Path to your existing CSV file on the server
$filePath = 'path/to/the/file';
// Check if the file exists
if (file_exists($filePath)) {
// Open the file in read mode
$file = fopen($filePath, 'r');
// Set appropriate headers for CSV file download
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
header('Pragma: no-cache');
header('Expires: 0');
// Output the contents of the file to php://output
fpassthru($file);
// Close the file
fclose($file);
} else {
// File not found, return an error message
echo "file not found";
}