You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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的方法,我们只需要把响应的输出流传给它就行。具体步骤如下:

  1. 加载iReport编译好的.jasper模板文件;
  2. 准备报表所需的参数和数据源;
  3. 填充报表生成JasperPrint对象;
  4. 配置HttpServletResponse的响应头,告诉浏览器这是PDF文件;
  5. 把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,提问作者김선호

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:39:52