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;
    }

效果图


相关推荐
BD_Marathon5 小时前
【Flink】部署模式
java·数据库·flink
鼠鼠我捏,要死了捏8 小时前
深入解析Java NIO多路复用原理与性能优化实践指南
java·性能优化·nio
ningqw8 小时前
SpringBoot 常用跨域处理方案
java·后端·springboot
superlls8 小时前
(Redis)主从哨兵模式与集群模式
java·开发语言·redis
叫我阿柒啊10 小时前
Java全栈工程师面试实战:从基础到微服务的深度解析
java·redis·微服务·node.js·vue3·全栈开发·电商平台
hqxstudying11 小时前
mybatis过渡到mybatis-plus过程中需要注意的地方
java·tomcat·mybatis
lichkingyang11 小时前
最近遇到的几个JVM问题
java·jvm·算法
ZeroKoop11 小时前
多线程文件下载 - 数组切分,截取文件名称
java
Monly2112 小时前
IDEA:控制台中文乱码
java·ide·intellij-idea
叫我阿柒啊12 小时前
从全栈开发到微服务架构:一次真实的Java面试实录
java·redis·ci/cd·微服务·vue3·springboot·jwt