Symfony3+Filesaver.js通过Ajax下载.gz文件损坏问题求助
解决Symfony 3中Ajax+FileSaver.js下载.gz文件损坏的问题
你的问题核心在于二进制数据的传输和处理方式错误,导致下载后的文件不是原始的gzip二进制内容,而是经过错误编码的文本数据,所以无法正常解压。下面分步骤给出修复方案:
问题根源分析
- 后端将.gz文件的二进制内容转成了base64字符串返回,但前端直接把这个字符串当作文件内容生成Blob,没有解码回原始二进制,相当于保存的是base64文本而非gzip文件。
- Ajax请求默认以
text类型接收响应,会对二进制数据做文本编码处理,可能破坏原始字节。
最优解决方案:直接传输二进制数据
这种方式更高效,也避免编码解码的麻烦,推荐使用Symfony自带的BinaryFileResponse来处理文件输出,同时前端设置Ajax接收Blob类型的响应。
后端(Symfony 3)代码修改
用BinaryFileResponse可以自动处理文件的MIME类型、内容长度等响应头,比手动读取文件更可靠:
use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\HttpFoundation\Request; // 你的控制器方法内 public function ajaxAction(Request $request) { if ($request->request->get('type') === 'download_file') { $myfile = '/opt/myfile.gz'; // 创建二进制文件响应 $response = new BinaryFileResponse($myfile); // 设置下载文件名,即使前端处理出错,浏览器也能直接触发下载 $response->setContentDisposition( ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'filename.gz' ); return $response; } // 其他业务逻辑... }
如果不想用BinaryFileResponse,手动处理二进制输出的话:
$myfile = '/opt/myfile.gz'; $fileContent = file_get_contents($myfile); $response = new Response($fileContent); $response->headers->set('Content-Type', 'application/gzip'); $response->headers->set('Content-Length', filesize($myfile)); return $response;
前端Ajax代码修改
关键是设置responseType: 'blob',让Ajax直接接收二进制Blob对象:
function requestAjax(json_data) { var request = $.ajax({ url: '/ajax', method: 'post', data: json_data, // 告诉Ajax接收二进制Blob类型的响应 xhrFields: { responseType: 'blob' } }); return request; } var json_text = {'type': 'download_file'}; var request = requestAjax(json_text); request.done(function (response) { // response已经是Blob对象,直接传给saveAs即可 saveAs(response, 'filename.gz'); });
备选方案:使用base64编码(不推荐)
如果因为某些原因必须用base64传输,需要在前端解码后再生成Blob:
后端代码(保持原来的base64编码逻辑)
$myfile = '/opt/myfile.gz'; $fp = fopen($myfile, "rb"); $response = base64_encode(fread($fp, filesize($myfile))); fclose($fp); return new Response($response);
前端代码修改(解码base64为二进制)
request.done(function (response) { // 将base64字符串解码为二进制数组 var byteStr = atob(response); var byteArray = new Uint8Array(byteStr.length); for (var i = 0; i < byteStr.length; i++) { byteArray[i] = byteStr.charCodeAt(i); } // 生成正确的gzip类型Blob var blob = new Blob([byteArray], { type: 'application/gzip' }); saveAs(blob, 'filename.gz'); });
这种方案会增加数据传输量(base64会比原始二进制大30%左右),所以优先推荐第一种直接传输二进制的方式。
内容的提问来源于stack exchange,提问作者Masha
相关产品推荐
相关产品推荐

