java实现根据Velocity批量生成pdf并合成zip压缩包

Velocity 模版操作

用的之前写好的: 传送门

其中需要新加一个转成输入流的方法

java 复制代码
    public static InputStream convertToPdf(StringWriter stringWriter) throws IOException {
        //将 HTML 转为字节流
        byte[] htmlBytes = stringWriter.toString().getBytes(StandardCharsets.UTF_8);
        //创建 PDF 输出流
        ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
        try (PdfWriter writer = new PdfWriter(pdfOutputStream);
             PdfDocument pdfDocument = new PdfDocument(writer)) {
            // 设置 A4 纸张和边距
            pdfDocument.setDefaultPageSize(PageSize.A4);
            pdfDocument.setTagged(); // 支持无障碍阅读
            //配置中文字体
            ConverterProperties properties = new ConverterProperties();
            FontProvider fontProvider = new FontProvider();
            fontProvider.addFont("STSongStd-Light", "UniGB-UCS2-H"); // 添加中文字体
            properties.setFontProvider(fontProvider);
            //转换 HTML 到 PDF
            try (InputStream htmlStream = new ByteArrayInputStream(htmlBytes)) {
                HtmlConverter.convertToPdf(htmlStream, pdfDocument, properties);
            }
        }
        // 返回 PDF 的输入流
        return new ByteArrayInputStream(pdfOutputStream.toByteArray());
    }

具体使用

java 复制代码
public void reportBatchDownload(ReportBatchDownload params, HttpServletResponse response) {
        List<SpeExamineInfo> infos = speExamineInfoMapper.selectListByReportBatchDownload(params);
        if (infos.isEmpty()) {
            throw new ServiceException("当前没有已完成的预约记录");
        }
        String filName = "体检报告.zip";
        // 设置请求流
        try {
            FileUtils.setAttachmentResponseHeader(response, filName);
            response.setContentType("application/zip");
            try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream(), StandardCharsets.UTF_8)) {
                for (SpeExamineInfo info : infos) {
                    VelocityContext velocityContext = getVelocityContext(info.getId(), info, null);
                    StringWriter stringWriter = VelocityUtils.genHtml(velocityContext, "vm/report.vm");
                    try (InputStream inputStream = VelocityUtils.convertToPdf(stringWriter)) {
                        // 添加ZIP条目
                        zipOut.putNextEntry(new ZipEntry(buildFilePath(info)));
                        // 写入文件内容
                        byte[] cache = new byte[8192];
                        int nRead;
                        while ((nRead = inputStream.read(cache)) != -1) {
                            zipOut.write(cache, 0, nRead);
                        }
                        zipOut.closeEntry();
                    }
                    zipOut.flush();
                }
                // 最后刷新缓冲区
                zipOut.finish();
            }
        } catch (IOException e) {
            throw new RuntimeException("生成压缩包失败", e);
        }

    }

    private String buildFilePath(SpeExamineInfo info){
        return "/" + info.getSchName() + "/" + info.getGradeName() + "/" + info.getClassName() + "/" + info.getStuName() + "_体检报告.pdf";
    }
    
// 处理vm模版变量
private VelocityContext getVelocityContext(String infoId, SpeExamineInfo speExamineInfo, String imgPrefix) {
        SpeExamineResQuery speExamineResQuery = new SpeExamineResQuery();
        speExamineResQuery.setInfoId(infoId);
        List<SpeExamineResDto> speExamineResDtos = speExamineResMapper.selectList(speExamineResQuery);
        SpeExamineType speExamineType = new SpeExamineType();
        speExamineType.setInfoId(infoId);
        List<SpeExamineType> speExamineTypes = speExamineTypeMapper.selectList(speExamineType);
        Map<String, Object> res = new HashMap<>();
        speExamineResDtos.forEach(item -> {
            if (StringUtils.isNotBlank(item.getItemResLabel())) {
                res.put(item.getItemCode() + "_label", item.getItemResLabel());
            }
            res.put(item.getItemCode(), item.getItemRes());
            res.put(item.getItemCode() + "_conclusion", item.getConclusion());
            res.put(item.getItemCode() + "_hasException", item.getHasException());
        });
        Map<String, Object> type = new HashMap<>();
        speExamineTypes.forEach(item -> {
            type.put(item.getItemType() + "_advice", item.getDocAdvice());
            type.put(item.getItemType() + "_signature", (StringUtils.isNotBlank(imgPrefix) ? imgPrefix : "" ) + item.getDocSign());
        });
        Map<String, Object> param = new HashMap<>(3);
        param.put("info", speExamineInfo);
        param.put("res", res);
        param.put("type", type);
        VelocityContext velocityContext = new VelocityContext();
        velocityContext.put("pdf", param);
        return velocityContext;
    }

效果图


相关推荐
AM越.1 小时前
Java设计模式详解--装饰器设计模式(含uml图)
java·设计模式·uml
5980354151 小时前
【java工具类】小数、整数转中文大写
android·java·开发语言
JIngJaneIL1 小时前
基于java + vue个人博客系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
吃喝不愁霸王餐APP开发者2 小时前
Java后端服务在对接全国性霸王餐API时的多数据中心部署与就近调用策略
java·开发语言
从心归零2 小时前
springboot-jpa的批量更新方法
java·spring boot·spring
这周也會开心2 小时前
128陷阱,==与equals区别
java·开发语言
沙漠豪2 小时前
提取PDF发票信息的Python脚本
开发语言·python·pdf
TAEHENGV3 小时前
回收站模块 Cordova 与 OpenHarmony 混合开发实战
android·java·harmonyos
a努力。3 小时前
宇树Java面试被问:方法区、元空间的区别和演进
java·后端·面试·宇树科技
2501_916766543 小时前
【面试题1】128陷阱、==和equals的区别
java·开发语言