AJAX强制下载PDF文件损坏:如何修复Blob创建环节的编码问题?
问题
通过服务器端cURL调用API获取包含base64编码PDF的XML文件,提取PDF后用PHP临时保存并输出,前端通过AJAX接收并创建Blob下载,但生成的PDF文件损坏无法正常打开。
后端PHP代码
<?php $pdfContents = $content["DocumentHeader"]["DocumentPdf"]; // base64 string PDF $endDocument = base64_decode($pdfContents); $realName = 'some_random_string.pdf'; $filename = 'adjusted_name.pdf'; file_put_contents($realName,$endDocument); header('Content-Description: File Transfer'); header('Content-type: application/octetstream'); header('Content-Disposition: attachment; filename='.$filename); header('Connection: Keep-Alive'); header('Expires: 0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Pragma: public'); header('Content-Transfer-Encoding: binary'); readfile($realName); unlink($realName); die(); ?>
前端AJAX代码
$.ajax({ url: 'get-file.php', type: 'GET', data: {}, success:function(data,status,xhr) { var blob = new Blob([data], { type:'application/pdf' }); var downloadUrl = URL.createObjectURL(blob); window.location.href = downloadUrl; } });
已尝试的调整均无效:
- 修改后端
Content-Type为application/pdf - 移除
Content-Transfer-Encoding头 - 使用
TextEncoder/Decoder处理数据 - 将
data转换为Uint8Array创建Blob
约束条件:
- 不直接提供PDF链接:需隐藏真实文件名、避免服务器长期留存文件,支持数百用户同时使用
- 不设置AJAX的
responseType为blob:响应可能是包含错误信息的JSON - 使用AJAX:避免浏览器拦截新标签,保持用户在当前页面
解决方案
问题核心是AJAX默认以字符串格式接收二进制数据时会出现编码错乱,导致Blob损坏。以下是针对性的修复方案:
1. 后端优化(可选但推荐)
去掉不必要的临时文件写入,直接输出解码后的二进制内容,同时修正响应头确保数据完整性:
<?php $pdfContents = $content["DocumentHeader"]["DocumentPdf"]; $endDocument = base64_decode($pdfContents); $filename = 'adjusted_name.pdf'; // 清理可能存在的多余输出,避免混入二进制数据 ob_clean(); flush(); header('Content-Description: File Transfer'); header('Content-Type: application/pdf'); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); // 增加内容长度头,帮助前端准确处理数据 header('Content-Length: ' . strlen($endDocument)); echo $endDocument; die(); ?>
2. 前端正确处理二进制数据
使用responseType: 'arraybuffer'接收原始二进制数据,再通过响应头区分PDF和错误JSON:
$.ajax({ url: 'get-file.php', type: 'GET', data: {}, responseType: 'arraybuffer', // 以ArrayBuffer格式接收,完整保留原始字节 success:function(data, status, xhr) { var contentType = xhr.getResponseHeader('Content-Type'); // 区分PDF和错误JSON响应 if (contentType === 'application/pdf') { // 直接用ArrayBuffer创建Blob var blob = new Blob([data], { type: 'application/pdf' }); var downloadUrl = URL.createObjectURL(blob); // 创建a标签触发下载,比直接跳转更稳定 var a = document.createElement('a'); a.href = downloadUrl; // 可从响应头提取文件名:xhr.getResponseHeader('Content-Disposition').split('filename=')[1] a.download = 'adjusted_name.pdf'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(downloadUrl); // 释放浏览器资源 } else { // 将ArrayBuffer转为字符串解析错误JSON var decoder = new TextDecoder('utf-8'); var errorJson = decoder.decode(data); var errorObj = JSON.parse(errorJson); // 这里添加错误提示逻辑 console.error('请求出错:', errorObj); } }, error: function() { console.error('请求失败'); } });
关键说明
responseType: 'arraybuffer'是核心:它会跳过字符串编码转换,完整保留PDF的原始二进制字节,避免编码丢失- 通过
Content-Type头区分响应类型,既满足二进制PDF的处理需求,又能兼容错误JSON的解析 - 使用a标签触发下载比直接
window.location.href更稳定,还能指定自定义文件名
内容的提问来源于stack exchange,提问作者FiddlingAway
相关产品推荐
相关产品推荐

