IO系列-- JAVA PDF合并

前言

本章主要讲解JAVA中怎么进行PDF合并 并响应给前端 前端进行预览操作

IO实战代码 主要是记录一些常用 但是很容易忘记的IO流操作

欢迎查看👉🏻👉🏻👉🏻JAVA IO 专栏 查漏补缺 指教一二

虽然你现在用不到 但是未来你一定用得到 🥸

POM依赖

引入下方依赖 后续代码都是在这个版本进行开发 不同版本可能会有些依赖报错

pom 复制代码
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext7-core</artifactId>
    <version>7.1.16</version>
    <type>pom</type>
</dependency>

java代码

文档合并

java 复制代码
/**
 * 将pdf文档转换成字节数组
 *
 * @return 返回对应PDF文档的字节数组
 */
private static byte[] getPdfBytes(InputStream inputStream) throws Exception {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    byte[] data = new byte[2048];
    int len;
    while ((len = inputStream.read(data)) != -1) {
        out.write(data, 0, len);
    }
    return out.toByteArray();
}

/**
 * 基于内存中的字节数组进行PDF文档的合并
 *
 * @param firstPdf  第一个PDF文档
 * @param secondPdf 第二个PDF文档
 */
private static byte[] mergePdfBytes(byte[] firstPdf, byte[] secondPdf) {
    try {
        if (firstPdf != null && secondPdf != null) {
            // 创建字节数组,基于内存进行合并
            ByteArrayOutputStream bass = new ByteArrayOutputStream();
            PdfDocument destDoc = new PdfDocument(new PdfWriter(bass));
            // 合并的pdf文件对象
            PdfDocument firstDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(firstPdf)));
            PdfDocument secondDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(secondPdf)));
            // 合并对象
            PdfMerger merger = new PdfMerger(destDoc);
            merger.merge(firstDoc, 1, firstDoc.getNumberOfPages());
            merger.merge(secondDoc, 1, secondDoc.getNumberOfPages());
            // 关闭文档流
            merger.close();
            firstDoc.close();
            secondDoc.close();
            destDoc.close();
            return bass.toByteArray();
        }
    } catch (IOException e) {
        e.printStackTrace();
        log.error("合并PDF文件失败 {}", e.getMessage());
    }
    return null;
}

调用

参数 List<InputStream> inputStreamList 自行将pdf转换 是需要合并的pdf流

例如: // xxx.pdf 填入pdf 路径路径 InputStream fileInputStream = new FileInputStream(new File("xxx.pdf"));

java 复制代码
/**
 * @param response        响应
 * @param inputStreamList 文件流列表
 * @throws Exception 异常处理
 */
private void handleResponse(HttpServletResponse response,
                            List<InputStream> inputStreamList) throws Exception {
    if (CollUtil.isNotEmpty(inputStreamList)) {
        // 处理响应
        int size = inputStreamList.size();
        byte[] pdfData = getPdfBytes(inputStreamList.get(0));
        for (int i = 1; i < size; i++) {
            pdfData = mergePdfBytes(pdfData, getPdfBytes(inputStreamList.get(i)));
        }
        if (pdfData != null) {
            response.setContentType("application/pdf");
            response.setHeader("Content-Disposition", "attachment; filename=merged.pdf");

            try (OutputStream outputStream = response.getOutputStream()) {
                outputStream.write(pdfData);
                outputStream.flush();
            }
        }
    }
}

代码直接复制后段合并逻辑就处理完成了

前端接收

因为请求方式不同所有自行修改 只是给一个例子

请求一定要带上 responseType: "blob"

js 复制代码
export function expressService(waybillNoList) {
  return request({
    url: 'xxx' ,
    method: 'get',
    responseType: "blob"
  })
}

预览打印

js 复制代码
   async expressServiceData() {
            try {
                this.buttonLoading = true;
                const response = await expressService(this.expressServicelist);
                this.buttonLoading = false;

                const pdfBlob = new Blob([response.data], {
                    type: "application/pdf",
                });
                const blobUrl = URL.createObjectURL(pdfBlob);

                const printWindow = window.open(blobUrl, "_blank");

                // 等待新窗口加载完成后触发打印
                printWindow.onload = function () {
                    printWindow.print();
                };
            } catch (error) {
                this.buttonLoading = false;
                console.error("Error calling expressService:", error);
            }
        },

效果

相关推荐
codeGoogle14 小时前
自研 IM 还是选择第三方 SDK?企业开发者应该如何权衡?
前端·后端·程序员
用户9385156350715 小时前
React Context 与自定义 Hook 从底层到实践:「跨层级通信 + 副作用封装」全解析
前端·javascript·react.js
滴滴答答哒15 小时前
VUE3+element-plus MultiSelect 多选下拉组件
前端·javascript·vue.js
其美杰布-富贵-李16 小时前
04 watch 与 Vue 响应式数据流
前端·javascript·vue.js
DLYSB_16 小时前
API 网关流量洪峰与突发 CC 攻击:我用 Go 写了个“现场物理防御哨兵”,把故障响应压缩到秒级
开发语言·后端·golang·报警灯
markinmarkin17 小时前
Spring 中Bean 的作用域有哪些?
java·后端·spring
赵大仁17 小时前
生成式 UI 实战:用 JSON Schema + React 动态渲染 AI 界面
前端·ai·react·next.js·前端架构·生成式ui
橙子家18 小时前
使用方法 ToDictionary() 来优化查询时间复杂度:O(N*M) -> O(1*M)【C# 基础】
后端
kyriewen18 小时前
我排查了一个React内存泄漏——罪魁祸首是这3个被忽略的清理函数
前端·javascript·面试
IT_陈寒18 小时前
我又被JavaScript的隐式类型转换坑了
前端·人工智能·后端