You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自动下载PDF提示“Failed to load PDF document”问题咨询(附PHP代码)

排查PDF下载报错「Failed to load PDF document」的方案

咱们一步步来拆解你遇到的问题,从代码逻辑到环境配置逐一排查,帮你搞定这个PDF下载失败的问题~

可能的报错原因及解决方法

1. 数据库存储的是URL而非本地文件路径

如果你的download_pdf字段存的是完整的HTTP/HTTPS URL(比如http://yourdomain.com/uploads/pdf/file.pdf),那file_exists()和readfile()根本没法处理——这两个函数只认服务器本地的文件路径(绝对路径或相对路径),不认网络地址。

解决办法:

  • 先查数据库里download_pdf的实际值,确认是本地路径(比如uploads/pdf/file.pdf)。如果确实存的是URL,要么改成存储本地路径,要么从URL里提取出本地路径部分(比如去掉域名前缀)。

2. 手动拼接路径的逻辑太脆弱

你用explode("/")拆分路径再手动拼接的方式很容易翻车:如果存储的路径不是恰好三段(比如uploads/file.pdf这种两段结构),$file_arr[2]会是未定义的,直接导致$path无效,file_exists()返回false,最终没有输出任何PDF内容,浏览器自然提示加载失败。

解决办法:
直接用数据库取出的$file作为路径就行,不需要手动拆分。为了避免相对路径的歧义,建议转为服务器绝对路径:

$file = $row["download_pdf"];
// 转换为服务器绝对路径
$absolute_path = $_SERVER['DOCUMENT_ROOT'] . '/' . $file;

3. 响应头里的文件名参数错误

你现在把完整路径$path作为下载文件名,浏览器会把整个路径当成文件名,这会导致解析混乱,甚至无法正确识别文件格式。

解决办法:
filename只需要保留文件名本身,用basename()函数就能直接提取:

header("Content-disposition: attachment; filename=" . basename($absolute_path));

4. 多余输出污染了PDF内容

如果你的PHP文件开头有BOM(字节顺序标记)、空格,或者在header()之前有任何echo、print甚至注释外的空行,都会导致header()发送失败,同时这些多余内容会被加到PDF输出的开头,让浏览器无法识别有效的PDF文件。

解决办法:

  • 检查PHP文件开头,确保<?php是文件的第一个字符,前面没有任何空格或BOM。
  • 在发送header()之前,清空输出缓冲:
ob_clean();
flush();

5. 文件权限不足

就算file_exists()返回true,PHP运行的用户(比如www-data、apache)可能没有读取该PDF文件的权限,导致readfile()无法输出文件内容,浏览器拿到空的或损坏的内容就会报错。

解决办法:

  • 检查PDF文件和所在目录的权限,一般文件设为644,目录设为755,确保PHP进程有读取权限。
  • 用is_readable()替代file_exists(),同时检查文件是否可读:
if(is_readable($absolute_path)) {
    // 执行下载逻辑
} else {
    die("错误:文件不存在或无法读取");
}

修正后的完整代码

我把所有问题点都修复了,还加上了SQL注入防护和路径安全校验,你可以直接用:

<?php
// 假设$conn已经正确连接数据库
$id = $_GET['id']; 

// 重要:必须做SQL注入防护,用预处理语句
$select = "SELECT * FROM `success-stories` where id=?";
$stmt = $conn->prepare($select);
$stmt->bind_param("i", $id); // 假设id是整数类型,非整数请调整参数类型
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();

if(!$row) {
    die("未找到对应的记录");
}

$file = $row["download_pdf"];
// 转换为服务器绝对路径
$absolute_path = $_SERVER['DOCUMENT_ROOT'] . '/' . $file;

// 路径安全校验:确保文件在允许的目录内(比如uploads目录)
$allowed_dir = realpath($_SERVER['DOCUMENT_ROOT'] . '/uploads');
$real_file_path = realpath($absolute_path);
if(strpos($real_file_path, $allowed_dir) !== 0) {
    die("非法路径请求");
}

// 检查文件是否可读
if(is_readable($real_file_path)) {
    // 清空输出缓冲,避免多余内容干扰
    ob_clean();
    flush();
    
    // 设置完整的响应头
    header("Content-Description: File Transfer");
    header("Content-Type: application/pdf");
    header("Content-Disposition: attachment; filename=\"" . basename($real_file_path) . "\"");
    header("Content-Transfer-Encoding: binary");
    header("Expires: 0");
    header("Cache-Control: must-revalidate");
    header("Pragma: public");
    header("Content-Length: " . filesize($real_file_path)); // 告诉浏览器文件大小,提升体验
    
    // 输出文件内容
    readfile($real_file_path);
    exit; // 终止脚本,避免后续输出
} else {
    die("错误:文件不存在或无法读取");
}
?>

额外提醒

  • SQL注入风险:你的原始代码直接把$id拼接到SQL语句里,这是非常危险的,一定要用预处理语句(上面的代码已经修复了这个问题)。
  • 路径遍历攻击:如果用户能控制id对应的download_pdf值,可能会通过../来遍历服务器上的其他文件,所以加上路径安全校验很有必要。

内容的提问来源于stack exchange,提问作者shiva kant tiwari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 20:57:35