AngularJS处理PDF Buffer响应实现浏览器端预览
技术栈与需求
- 运行环境:NodeJS 16.4、AngularJS 1.5.8
- 后端能力:已集成JSReport实现报表生成
- 业务目标:通过JSReport模板生成PDF文件,后端接口返回PDF内容后,前端在浏览器中优先以新标签页形式预览PDF
初始实现(存在问题)
后端初始代码
初始逻辑直接返回设置了PDF响应头的Buffer内容:
router.post('/queryreport/certificationAdvancement', async function(req, res) { try { const data = req.body.data; var inspectionType = ''; if(data.inspection == 12) inspectionType = 'Ispezioni Visive'; else inspectionType = `Ispezioni ${data.supervision_number}^ Sorveglianza`; const title = 'Stato Avanzamento delle Certificazioni'; const subtitle = `${inspectionType} - Anno ${data.year}`; // 生成报表头 const header = await get_queryReport_header(data.dealer, title, subtitle); // 生成报表页脚 const footer = await get_footer(title); // 生成报表数据集 const dataset = await get_certificationAdvancement_dataset(data); // 调用JSReport渲染报表 const report = await jsreport.render({ template: {name: 'certificationAdvancement'}, data: {header: header, dataset: dataset, footer: footer} }) const buffer_report = await report.body(); console.log(buffer_report); res.contentType('application/pdf'); res.send(buffer_report); } catch (error) { global.loggerWinston.error(commons.sios_loggerWinstonError(error, 'SIOS')); return commons.showerror(error, res); } });
控制台打印的生成结果为标准PDF Buffer格式:
<Buffer 25 50 44 46 2d 31 2e 36 0a 25 ff ff ff ff 0a 0a 34 20 30 20 6f 62 6a 0a 3c 3c 0a 09 2f 63 61 20 31 0a 09 2f 42 4d 20 2f 4e 6f 72 6d 61 6c 0a 3e 3e 0a ... 38478 more bytes>
前端初始尝试代码
尝试了直接打开接口、拼接data URL触发下载等多种方案,均无法正常预览/下载PDF,核心代码如下:
$scope.download_jsreport = function () { var data = {year: $scope.Year, dealer: $scope.Dealer, inspection: parseInt($scope.Inspection)}; if($scope.numSorv) data.supervision_number = $scope.numSorv; /* jsreportService.certificationAdvancement.post 为封装的接口调用服务 也可直接使用$http.post发起请求: $http.post('/api/jsreport/queryreport/certificationAdvancement', {data: data}) .then(function(result) { }); */ //$window.open(jsreportService.certificationAdvancement.post({data: data}), '_blank'); jsreportService.certificationAdvancement.post({data: data}, function (result) { /* var a = document.createElement("a"); a.href = "data:application/octet-stream;base64," + encodeURIComponent(result.report); a.target = "_blank"; a.download = result.filename; document.body.appendChild(a); a.click(); var a_link = document.createElement('a'); a_link.href = 'data: application/octet-stream; base64, ' + result.buffer_report.data; a_link.download = 'certificationAdvancement.pdf'; a_link.click(); */ }); }
最终修复方案
问题原因是AngularJS默认将响应解析为文本/JSON格式,直接传输二进制Buffer会出现内容转码损坏,因此调整前后端传输与解析逻辑:
后端调整
将生成的PDF Buffer转为JSON格式的字节数组返回:
// 调用JSReport渲染报表 const report = await jsreport.render({ template: {name: 'certificationAdvancement'}, data: {header: header, dataset: dataset, footer: footer} }); const resultReport = await report.body(); return res.send(Buffer.from(resultReport).toJSON());
前端调整
接收数据后通过Uint8Array构造PDF类型的Blob对象,生成对象URL后即可实现新标签页预览或文件下载:
const reportBuffer = new Uint8Array(result.data); const file = new Blob([reportBuffer], {type: 'application/pdf'}); const fileURL = window.URL.createObjectURL(file); // 优先方案:新标签页预览PDF window.open(fileURL, '_blank'); // 若需要触发下载,替换为以下逻辑 // const fileName = 'certificationAdvancement.pdf'; // var a_link = document.createElement('a'); // document.body.appendChild(a_link); // a_link.style.display = 'none'; // a_link.href = fileURL; // a_link.download = fileName; // a_link.click(); // 预览/下载完成后释放对象URL,避免内存泄漏 // window.URL.revokeObjectURL(fileURL);
提示:若需要强制下载而非预览,除了使用a标签加download属性的方案,也可以在后端设置
Content-Disposition: attachment; filename="certificationAdvancement.pdf"响应头实现。
内容的提问来源于stack exchange,提问作者alesssz
相关产品推荐
相关产品推荐

