浏览器正确触发下载的关键是设置Content-Type为application/octet-stream和Content-Disposition为attachment; filename="xxx",并确保无输出前发送响应头、路径安全校验及Web服务器正确路由请求。

PHP 输出文件流时 Content-Type 和 Content-Disposition 怎么设
浏览器能否正确触发下载,关键不在 PHP 是否读取了文件,而在于响应头是否明确告诉浏览器“这不是要渲染的页面,而是要保存的文件”。Content-Type 设为 application/octet-stream 是最稳妥的选择——它不依赖文件扩展名,避免因 MIME 类型识别错误导致浏览器尝试打开而非下载。同时必须设置 Content-Disposition 为 attachment; filename="xxx",其中 filename 值需用 rawurlencode() 处理中文名,否则 Safari 和部分安卓浏览器会乱码或截断。
-
Content-Type: application/octet-stream比application/pdf或image/png更通用,尤其适合用户上传后又下载的场景 -
Content-Disposition: attachment; filename*=UTF-8''xxx.pdf是 RFC 5987 标准写法,兼容性优于仅用filename - 务必在
header()之前确保无任何输出(包括空格、BOM、echo),否则 headers already sent 错误直接中断流程
用 readfile() 还是 fpassthru()?什么时候该用 fopen() + fread()
三者都能输出文件内容,但适用场景和资源消耗差异明显。readfile() 最简单,适合中小文件(一般 ≤ 10MB),它自动处理缓冲并返回字节数;fpassthru() 需要先 fopen(),优势在于可配合 fseek() 实现断点续传或分片下载;而手动 fread() 分块读取只在需要加密、动态拼接或限速时才值得引入——多数下载功能没必要自己管理缓冲区。
- 优先用
readfile($filepath):代码短、不易出错、PHP 内部优化好 - 需要支持 HTTP Range 请求(如视频拖拽、大文件续传)时,必须用
fopen()+fpassthru(),并手动解析$_SERVER['HTTP_RANGE'] - 绝对不要用
file_get_contents()加echo下载大文件——内存会爆,且无法控制传输过程
如何安全地提供用户上传目录下的文件下载
直接拼接 $_GET['file'] 到路径里是典型路径遍历漏洞。比如传入 ../../etc/passwd 就可能泄露系统文件。核心原则是:不信任任何用户输入,路径必须白名单校验或严格限制根目录。
html5动态显示媒体视频播放器代码,这个我们在企业网站或者教学网站会用到,教学网站,有一些视频要播放,那么就会用到播放器,可以参考源码,看看播放器的效果是如何实现的,php中文网推荐下载!
- 用
basename($_GET['file'])只取文件名,再拼到预设的下载目录(如/var/www/uploads/),彻底切断上级路径可能 - 若需支持子目录,可用
realpath()+strpos()检查结果是否仍位于允许目录内,例如:if (0 !== strpos(realpath($user_path), '/var/www/uploads')) die('Access denied'); - 禁止通过参数指定完整路径或扩展名,文件存在性验证(
is_file()、is_readable())必须在路径规范化之后执行
下载链接被右键另存为失败或提示 404 的常见原因
这类问题往往不是 PHP 逻辑错,而是 Web 服务器配置或 URL 路由干扰所致。Nginx/Apache 若把下载请求当成普通页面路由,就可能转发给 index.php,导致 PHP 脚本没运行或返回 HTML 内容;或者 .htaccess 中的重写规则意外截断了带参数的 URL。
立即学习“PHP免费学习笔记(深入)”;
- 检查下载 URL 是否被伪静态规则捕获,例如
download.php?file=test.pdf被重写成/download/test.pdf后,实际未映射回 PHP 脚本 - 确认 Web 服务器对
.php后缀的请求确实交由 PHP 解释器执行,某些共享主机禁用了 query string 传递 - 用 curl -I 查看真实响应头,如果返回的是
text/html或 404,说明请求根本没进到你的 PHP 文件里










