PHP下载接口未返回PDF数据及对应Content-Type的问题排查
问题分析:为何PHP下载接口未返回PDF数据且Content-Type异常
一、相关代码与响应信息
1. PHP下载功能代码
<?php if(isset($_GET['path'])) { //Read the url $url = $_GET['path']; echo $url; //Clear the cache clearstatcache(); //Check the file path exists or not if(file_exists($url)) { //Define header information header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="'.basename($url).'"'); header('Content-Length: ' . filesize($url)); header('Pragma: public'); //Clear system output buffer flush(); //Read the size of the file readfile($url,true); //Terminate from the script die(); } else{ echo "File path does not exist."; } } echo "File path is not defined." ?>
2. OkHttp3请求代码
OkHttpClient client = new OkHttpClient(); String url = "http://www.hitlarweb.com/hitlar/download.php?path=fighter.pdf"; Call call = client.newCall(new Request.Builder().url(url).get().build()); try { Response response = call.execute(); if (response.code() == 200 || response.code() == 201) { Headers responseHeaders = response.headers(); for (int i = 0; i < responseHeaders.size(); i++) { Log.d(LOG_TAG, responseHeaders.name(i) + ": " + responseHeaders.value(i)); } String str = response.body().string(); } }catch(Exception e){}
3. 实际响应头
"Server" = "nginx" "Date" = "Thu, 13 Oct 2022 11:00:59 GMT" "Content-Type" = "text/html" "Transfer-Encoding" = "chunked" "Connection" = "keep-alive" "Vary" = "Accept-Encoding" "Expires" = "Thu, 01 Jan 1970 00:00:01 GMT" "Cache-Control" = "no-cache"
4. 实际响应体
<html><body><script>document.cookie="_test=3615ff5e48bd398a38d2ce932bef0629 ; expires=Thu, 31-Dec-37 23:55:55 GMT; path=" ;document.location.href="http://www.webweb.infinityfreeapp.com/lichi/download.php?path=fighter.pdf&i=1";</script></body></html>
二、问题根源
- 服务器拦截导致跳转:响应体是JS跳转代码,说明请求的
hitlarweb.com域名下的接口被主机商防盗链/流量限制机制拦截,直接返回跳转页面,你的PHP下载代码根本没被执行,自然不会输出PDF数据和正确的Content-Type。 - PHP代码本身的缺陷:就算代码正常执行,也存在问题:
- 开头的
echo $url;会提前输出内容,破坏HTTP响应头发送规则(必须在任何输出前设置头),导致后续header()调用失效。 - 硬编码
Content-Type: application/octet-stream虽能触发下载,但无法正确识别为PDF类型;同时缺少路径安全校验,存在目录遍历攻击风险。
- 开头的
三、解决指引
1. 处理服务器拦截问题
- 检查主机商后台的防盗链、流量限制设置,确认是否需要添加请求来源白名单,或该域名是否有特殊访问限制。
- 直接使用响应体中跳转后的域名地址发起请求,测试是否能正常获取PDF文件。
- 如果是Cookie验证机制,在OkHttp请求中携带响应返回的
_testCookie后再发起请求,示例代码:OkHttpClient client = new OkHttpClient.Builder() .cookieJar(new CookieJar() { private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>(); @Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) { cookieStore.put(url.host(), cookies); } @Override public List<Cookie> loadForRequest(HttpUrl url) { List<Cookie> cookies = cookieStore.get(url.host()); return cookies != null ? cookies : new ArrayList<>(); } }) .build();
2. 修复PHP代码的问题
- 移除提前输出:删除开头的
echo $url;,确保在header()调用前没有任何输出(包括PHP标签外的空格、换行)。 - 动态设置Content-Type:使用
finfo获取文件真实MIME类型,替换硬编码类型:if(file_exists($url)) { $finfo = new finfo(FILEINFO_MIME_TYPE); $mime_type = $finfo->file($url); header('Content-Description: File Transfer'); header('Content-Type: ' . $mime_type); header('Content-Disposition: attachment; filename="'.basename($url).'"'); header('Content-Length: ' . filesize($url)); header('Pragma: public'); flush(); readfile($url); die(); } - 添加路径安全校验:限制文件只能在指定目录下访问,防止恶意路径遍历:
$allowed_dir = __DIR__ . '/your_download_dir/'; // 替换为实际允许的目录 $target_file = $allowed_dir . basename($_GET['path']); $real_path = realpath($target_file); // 验证文件是否在允许的目录内 if(strpos($real_path, $allowed_dir) !== 0) { die("Invalid file request"); } $url = $real_path;
内容的提问来源于stack exchange,提问作者Hitlar India
相关产品推荐
相关产品推荐

