在php中强制下载大文件并关闭输出缓冲,核心做法是调用 ob_end_clean() 清除所有缓冲区内容,配合 readfile() 或分块读取实现流式传输。这样做能避免内存溢出和文件损坏,确保浏览器直接接收原始二进制数据。

ob_end_clean() 对下载头部的影响是什么
ob_end_clean() 会丢弃当前缓冲区的所有输出,防止意外字符(如空格、BOM)混入响应体。若不清除,浏览器可能将HTML片段当作文件内容,导致下载失败或文件损坏。

<?php
$file = 'large_video.mp4';
if (!file_exists($file)) {
http_response_code(404);
exit('File not found');
}
// 关闭所有输出缓冲
while (ob_get_level() > 0) {
ob_end_clean();
}
// 设置头部
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header('Content-Length: ' . filesize($file));
header('Cache-Control: no-cache');
header('Pragma: no-cache');
// 流式输出
readfile($file);
exit;
?>
运行上述代码后,浏览器将触发下载对话框,文件名与源文件一致,大小由 filesize() 准确提供。若存在多个缓冲层,while 循环确保全部清空。未调用 ob_end_clean() 时,任何前置输出都会破坏二进制流。
立即学习“PHP免费学习笔记(深入)”;
分块读取如何降低内存占用
使用 fopen() 和 fread() 分块读取文件,可避免一次性加载整个文件到内存。每次读取固定字节数并立即输出,适合GB级大文件。

<?php
$file = 'large_archive.zip';
$chunkSize = 8192; // 8KB per chunk
while (ob_get_level() > 0) {
ob_end_clean();
}
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="archive.zip"');
header('Content-Length: ' . filesize($file));
$handle = fopen($file, 'rb');
if ($handle === false) {
exit('Cannot open file');
}
while (!feof($handle)) {
echo fread($handle, $chunkSize);
flush(); // 强制刷新输出缓冲
}
fclose($handle);
exit;
?>
该脚本以8KB为单位读取文件,通过 flush() 推送至客户端,内存峰值仅约8KB。若省略 flush(),部分服务器可能累积输出直到脚本结束,影响实时性。分块方式与 readfile() 相比,更可控但需手动管理句柄。
当文件路径含中文时下载行为如何变化
若文件路径包含中文字符,basename() 返回的中文名在 Content-Disposition 中需正确编码,否则浏览器可能显示乱码或拒绝下载。应使用 urlencode() 或遵循RFC 6266规范。
修改前节代码中的文件名处理部分:
<?php
$file = '视频资料.mp4';
$filename = basename($file);
// 正确编码中文文件名
header('Content-Disposition: attachment; filename*=UTF-8\'\''. urlencode($filename));
?>
此写法确保Chrome、Firefox等现代浏览器正确解析中文文件名。若仍用原 filename="..." 形式,非ASCII字符可能被截断或转义错误。实际测试中,filename*=UTF-8''... 兼容性优于纯ASCII方案。



















