使用 header() 设置响应头并配合 readfile() 实现强制下载,核心在于发送正确的 mime 类型和 content-disposition 头。具体做法是先调用 header('content-type: application/octet-stream') 声明二进制流,再调用 header('content-disposition: attachment; filename="example.pdf"') 指定文件名,最后用 readfile('path/to/file') 读取并输出文件内容。执行后浏览器会触发下载对话框而非直接打开文件。

Content-Disposition 头中文件名包含中文或特殊字符时如何处理
当文件名包含非 ASCII 字符(如中文)时,直接使用双引号包裹会导致部分浏览器解析错误或乱码。标准做法是使用 RFC 5987 编码格式,即在 filename*=UTF-8'' 前缀后跟上 URL 编码后的文件名。同时保留一个纯 ASCII 的 filename 作为兼容旧浏览器的降级方案。这样能确保现代浏览器正确显示中文文件名,而旧浏览器至少能下载一个无中文名或简化的版本。

header('Content-Type: application/octet-stream');
$filename = '报告.pdf';
// 生成 RFC 5987 编码的文件名
$encoded_filename = rawurlencode($filename);
header("Content-Disposition: attachment; filename=\"{$filename}\"; filename*=UTF-8''{$encoded_filename}");
readfile('/path/to/' . $filename);
readfile() 在文件不存在或权限不足时的行为表现
当指定的文件路径不存在或当前 PHP 进程没有读取权限时,readfile() 不会抛出异常,而是返回 false 并在错误级别触发警告。此时之前设置的 header 依然有效,但输出内容为空。开发者需要在代码中显式检查返回值,以便在文件不可用时返回 HTTP 404 状态码或自定义错误信息,避免用户看到空白页面。
立即学习“PHP免费学习笔记(深入)”;

$file_path = '/path/to/missing.pdf';
if (!file_exists($file_path)) {
http_response_code(404);
die('File not found');
}
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="missing.pdf"');
if (readfile($file_path) === false) {
die('Failed to read file');
}基于程序中某个输入、条件或结果的具体问题
在大型文件下载场景中,如果文件体积超过服务器内存限制或网络超时时间,直接使用 readfile() 可能导致请求中断或内存溢出。虽然 readfile() 内部是流式读取,但 PHP 脚本的执行时间受 max_execution_time 限制。对于超大文件,建议结合 fpassthru() 或手动分块读取,并设置 set_time_limit(0) 以允许长时间运行,确保下载过程稳定完成。



















