Velocity 模版操作
用的之前寫好的: 傳送門
其中需要新加一個轉成輸入流的方法
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 到 PDFtry (InputStream htmlStream = new ByteArrayInputStream(htmlBytes)) {HtmlConverter.convertToPdf(htmlStream, pdfDocument, properties);}}// 返回 PDF 的輸入流return new ByteArrayInputStream(pdfOutputStream.toByteArray());}
具體使用
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;}
效果圖