基于iReport、Ajax与Servlet实现服务端PDF无预览直接打印的问题
我来帮你把整个PDF无预览打印的实现逻辑理清楚,顺便解决你现在遇到的Ajax配置错误问题~
一、先修正客户端Ajax的核心错误
你提到客户端Ajax设置了datetype: text,这是导致PDF无法正常处理的关键问题!因为服务端返回的是二进制PDF流,如果用text格式接收,会把二进制字节转换成文本编码,直接破坏PDF的结构,自然无法正常加载或打印。
正确的做法是把请求的响应类型设置为blob,这样才能完整接收二进制的PDF数据。
二、服务端(iReport+Servlet)生成PDF并输出的正确逻辑
关于PdfWriter、ServletOutputStream和HttpServletResponse的配合,其实不需要手动用PdfWriter去写流——iReport的JasperReports API已经封装了直接输出PDF到OutputStream的方法,我们只需要把响应的输出流传给它就行。具体步骤如下:
- 加载iReport编译好的
.jasper模板文件; - 准备报表所需的参数和数据源;
- 填充报表生成
JasperPrint对象; - 配置
HttpServletResponse的响应头,告诉浏览器这是PDF文件; - 把
JasperPrint导出到response.getOutputStream()中,直接发送给客户端。
给你一段服务端Servlet的示例代码:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { // 1. 加载报表模板(路径根据你的项目结构调整) InputStream reportTemplate = getServletContext().getResourceAsStream("/WEB-INF/reports/your_report.jasper"); // 2. 准备报表参数和数据源 Map<String, Object> reportParams = new HashMap<>(); reportParams.put("reportTitle", "销售报表"); // 示例参数 // 这里替换成你的实际数据源(比如从数据库查询的列表) JRDataSource dataSource = new JRBeanCollectionDataSource(yourBusinessDataList); // 3. 填充报表 JasperPrint jasperPrint = JasperFillManager.fillReport(reportTemplate, reportParams, dataSource); // 4. 配置响应头 response.setContentType("application/pdf"); // 设置为inline让浏览器直接处理PDF,而不是触发下载 response.setHeader("Content-Disposition", "inline; filename=print_report.pdf"); response.setContentLengthLong(jasperPrint.getBytes().length); // 可选,设置内容长度 // 5. 导出PDF到响应输出流 ServletOutputStream out = response.getOutputStream(); JasperExportManager.exportReportToPdfStream(jasperPrint, out); // 确保流输出完成 out.flush(); out.close(); } catch (JRException e) { e.printStackTrace(); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "报表生成失败,请稍后重试"); } }
三、客户端JavaScript处理PDF流并实现无预览打印
修正Ajax配置后,我们需要把接收到的Blob对象转换成可访问的URL,再通过iframe或新窗口加载,最后触发打印。这里推荐用iframe的方式,避免浏览器拦截弹窗。
示例代码:
function triggerPdfPrint() { const xhr = new XMLHttpRequest(); xhr.open('POST', '/your-report-servlet-url', true); // 关键:设置响应类型为blob,接收二进制PDF数据 xhr.responseType = 'blob'; xhr.onload = function() { if (xhr.status === 200) { const pdfBlob = xhr.response; // 创建Blob的临时URL const pdfObjectUrl = URL.createObjectURL(pdfBlob); // 创建隐藏的iframe加载PDF const printIframe = document.createElement('iframe'); printIframe.style.display = 'none'; printIframe.src = pdfObjectUrl; document.body.appendChild(printIframe); // 等待PDF加载完成后触发打印 printIframe.onload = function() { printIframe.contentWindow.print(); // 打印完成后释放临时URL资源,避免内存泄漏 setTimeout(() => { URL.revokeObjectURL(pdfObjectUrl); document.body.removeChild(printIframe); }, 1000); }; } else { alert(`获取PDF失败:${xhr.statusText}`); } }; xhr.onerror = function() { alert('请求出错,请检查网络或服务端状态'); }; // 如果需要传递参数,用FormData封装 const formData = new FormData(); formData.append('param1', 'your-value'); // 示例参数 xhr.send(formData); }
四、几个关键注意点
- 不要用jQuery的
$.ajax直接处理Blob:jQuery的datatype选项没有原生的blob支持,用原生XMLHttpRequest更靠谱; - 服务端不要提前关闭输出流:确保
JasperExportManager完成导出后再flush并close; - 打印时机要等PDF加载完成:必须监听iframe的
onload事件,否则可能打印空白内容。
内容的提问来源于stack exchange,提问作者김선호
相关产品推荐
相关产品推荐

