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;
}
效果图