使用 fread 配合 fpassthru 或循环读取是 php 处理大文件下载的标准做法,核心在于设置正确的 http 响应头并分块输出内容以避免内存溢出。具体做法是先发送 content-type、content-disposition 和 content-length 等头部信息,然后以固定大小(如 8192 字节)循环读取文件句柄并立即输出,直到文件末尾。这种方法能确保即使文件达到 gb 级别,php 脚本的内存占用也保持在极低水平。

如何正确设置 HTTP 响应头以触发浏览器下载
要强制浏览器下载而非预览文件,必须发送特定的 HTTP 头部。关键参数包括 Content-Type 指定 MIME 类型,Content-Disposition 设置为 attachment 并附带文件名,以及 Content-Length 告知客户端文件大小以便显示进度条。在输出任何内容前调用 header() 函数即可生效。

<?php $file = '/path/to/large/file.zip';
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
// 清除缓冲区以防止干扰二进制数据
ob_clean();
flush();
readfile($file);
exit;
}
如何实现分块读取以支持断点续传和大文件流式传输
对于超大文件,直接使用 readfile 可能不够灵活,手动分块读取允许我们实现更精细的控制,例如支持断点续传的 Range 请求。通过检查 $_SERVER['HTTP_RANGE'] 解析起始字节,使用 fseek 定位文件指针,并以循环方式读取小块数据输出,可以显著降低服务器内存压力并提升稳定性。

<?php $file = '/path/to/large/file.zip';
$chunkSize = 8192; // 8KB chunks
if (!file_exists($file)) {
http_response_code(404);
exit;
}
// 获取 Range 请求头,支持断点续传
$start = 0;
$end = filesize($file) - 1;
if (isset($_SERVER['HTTP_RANGE'])) {
preg_match('/bytes=(\d+)-(\d*)/', $_SERVER['HTTP_RANGE'], $matches);
if ($matches) {
$start = intval($matches[1]);
$end = $matches[2] ? intval($matches[2]) : $end;
}
}
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header('Accept-Ranges: bytes');
header("Content-Length: " . ($end - $start + 1));
header("Content-Range: bytes $start-$end/" . filesize($file));
ob_clean();
flush();
$handle = fopen($file, 'rb');
fseek($handle, $start);
while (!feof($handle)) {
echo fread($handle, $chunkSize);
flush();
}
fclose($handle);
exit;
当文件路径包含中文或特殊字符时下载会失败吗
如果文件路径或文件名中包含非 ASCII 字符(如中文),直接传递给 basename 或嵌入 HTTP 头可能导致乱码或下载失败。这是因为 HTTP 头默认使用 ISO-8859-1 编码,而现代浏览器通常期望 UTF-8 编码的文件名。解决方案是使用 rawurlencode 对文件名进行 URL 编码,或者遵循 RFC 6266 标准使用 filename*=UTF-8''... 格式。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











