
php 下载 pdf 文件后损坏,通常因响应头中 content-type 设置错误或输出缓冲干扰导致;正确设置 mime 类型、禁用额外输出并确保文件流完整传输是关键解决方案。
php 下载 pdf 文件后损坏,通常因响应头中 content-type 设置错误或输出缓冲干扰导致;正确设置 mime 类型、禁用额外输出并确保文件流完整传输是关键解决方案。
在 PHP 中实现 PDF 文件的安全下载,不能仅依赖通用的 application/octet-stream 类型——它虽能触发浏览器下载,但会绕过 PDF 阅读器的预览逻辑,且极易因输出污染(如意外空格、BOM、echo 输出)导致二进制流损坏,最终使 Adobe Acrobat 等工具报错“文件已损坏”或“未正确编码”。
核心问题定位
您当前的下载逻辑存在三处关键缺陷:
- MIME 类型不准确:使用 application/octet-stream 无法向客户端明确声明文件为 PDF,部分浏览器/阅读器可能拒绝解析或误判编码;
- readfile() 前存在潜在输出:若 uploads/ 目录路径不存在、文件权限不足或 file_exists() 判断后文件被移动,readfile() 失败时后续代码仍执行,可能输出错误信息(如 echo "Failed..."),污染二进制流;
- 数组索引错误与变量混淆:$data[0]['name'] 正确,但后续却使用 $data['name'](未定义索引),导致 filesize() 和 readfile() 路径拼接失败,实际读取空文件或报错。
正确下载实现(含防御性处理):
if (isset($_GET['file_id'])) {
$id = (int)$_GET['file_id']; // 强制整型,防止 SQL 注入
// 使用预处理语句防止注入(强烈推荐)
$stmt = $mysqli->prepare("SELECT id, name, size FROM files WHERE id = ?");
$stmt->bind_param('i', $id);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 0) {
die('File not found.');
}
$file = $result->fetch_assoc();
$filepath = 'uploads/' . $file['name'];
if (!file_exists($filepath) || !is_readable($filepath)) {
die('File not accessible.');
}
// ✅ 关键:精确指定 PDF MIME 类型
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . basename($filepath) . '"'); // 或 'attachment;' 强制下载
header('Content-Length: ' . filesize($filepath));
header('Cache-Control: private, must-revalidate, max-age=0');
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
header('Pragma: public');
// ✅ 清除所有可能的输出缓冲
if (ob_get_level()) {
ob_end_clean();
}
// ✅ 安全输出文件流
readfile($filepath);
// ✅ 更新下载计数(独立事务,不影响文件流)
$updateStmt = $mysqli->prepare("UPDATE files SET downloads = downloads + 1 WHERE id = ?");
$updateStmt->bind_param('i', $id);
$updateStmt->execute();
exit;
}上传端同步优化建议:
- 使用 move_uploaded_file() 替代 copy(),更安全可靠;
- 对文件名进行白名单过滤(如 preg_replace('/[^a-zA-Z0-9._-]/', '', $filename)),避免路径遍历;
- 存储时记录原始文件名与唯一哈希名(如 sha1_file($file) . '.pdf'),提升安全性。
注意事项总结:
✅ 始终使用 application/pdf 作为 PDF 的 Content-Type;
✅ 下载前调用 ob_end_clean() 消除任何缓冲区残留;
✅ 严格校验文件存在性与可读性,失败时 die() 而非 echo;
✅ 数据库操作务必使用预处理语句,杜绝 SQL 注入;
✅ 避免在 readfile() 后执行任何输出(包括 header() 或 echo)。
遵循以上规范,PDF 文件将保持二进制完整性,可被 Adobe Reader、Chrome 内置查看器等正常打开与渲染。
立即学习“PHP免费学习笔记(深入)”;



















