通过Freemark渲染数据到Word里并生成压缩包
通过Freemark渲染数据到Word里并生成压缩包
功能描述:现有一功能,需要把数据值渲染到Word里,并把生成的Word文件放到压缩包里,最后把压缩包下载下来,完成功能。
- 效果图:(带图片)
- 模板制作图:(图片需要base64位,先在Word模板里放一张图片占位,制作好模板另存为XML格式,不要打开,再把.xml格式的后缀改为.ftl格式,放到tempates下,最后把模板中的图片base64位编码改为${image},模板就制作完成了。)
.ftl文件中图片占位符要修改的地方,原本是base64编码,删掉改为自己在map里定义一的字段。
代码实现
- 接口
@PostMapping("/genTestReport")@ApiOperation(value = "生成测试报告")public void genTestReport(TestReport testReport, HttpServletResponse response) {//查询验证数据列表List<TestReportVO> testReportVOList = testService.selectTestReportList(testReport);if (CollectionUtil.isEmpty(testReportVOList)) {throw new BaseException("查询数据为空!");}//response设置response.reset();response.setCharacterEncoding(Constants.UTF8);response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);try {FileUtils.setAttachmentResponseHeader(response, "测试报告附件.zip");// 获取输出流ServletOutputStream servletOutputStream = response.getOutputStream();ZipOutputStream zos = new ZipOutputStream(servletOutputStream);//循环处理,把每条需求生成一个申请单,最后打包成压缩包下载for (TestReportVO testReportVO: testReportVOList) {ByteArrayOutputStream outputStream = new ByteArrayOutputStream();try {//处理截图String screenshot = getImgBySignature(testReportVO.getFileUrl());//生成wordWordUtil wordUtil = new WordUtil();Map<String, Object> dataMap = new HashMap<>();buildReqReviewMeetData(testReportVO, dataMap, screenshot);outputStream.reset();wordUtil.generateFromTemplates(dataMap, "testReport-template.ftl", outputStream);//文件名要唯一,所以拼接了一个testIdString entryName = "测试报告(" + testReportVO.testId() + ").docx";writeZip(zos, entryName, new ByteArrayInputStream(outputStream.toByteArray()));} catch (Exception e) {logger.error("处理压缩包信息错误:", e);}}// 完成 ZIP 文件的写入zos.finish();servletOutputStream.flush();} catch (IOException e) {logger.error("批量导出测试报告单错误:", e);}}//替换占位符里的数据private void buildReqReviewMeetData(TestReportVO testReportVO, Map<String, Object> dataMap, String screenshot) {dataMap.put("testContent", testReportVO.getTestContent() == null ? "" : testReportVO.getTestContent());//测试内容dataMap.put("testResult", testReportVO.getTestResult() == null ? "" : testReportVO.getTestResult());//测试结果dataMap.put("image", screenshot);//图片}/*** zip 写入一条数据** @param zos 压缩包流* @param entryName 文件名(唯一)* @param bais 文件流* @throws IOException*/private void writeZip(ZipOutputStream zos, String entryName, InputStream bais) throws IOException {try {zos.putNextEntry(new ZipEntry(entryName));byte[] buffer = new byte[1024]; // 1KB 缓冲区int bytesRead;// 逐块读取和写入数据while ((bytesRead = bais.read(buffer)) != -1) {zos.write(buffer, 0, bytesRead);}zos.closeEntry();} catch (Exception e) {logger.error("写入压缩包数据错误:", e);} finally {if (bais != null) {IOUtils.close(bais);}}}//处理图片为base64位private String getImgBySignature(String screenshot) {//获取内容//TODO:换成自己的根据地址获取文件流的方法,转为base64位即可try (InputStream inputStream = minio.download(screenshot)) {byte[] bytes = IOUtils.toByteArray(inputStream);String base64 = Base64.getEncoder().encodeToString(bytes);return base64;} catch (Exception e) {logger.error("处理 URL {} 时出错: {}", path, e.getMessage(), e);}return "";}
2、FileUtils类
public class FileUtils {public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {String percentEncodedFileName = percentEncode(realFileName);StringBuilder contentDispositionValue = new StringBuilder();contentDispositionValue.append("attachment; filename=").append(percentEncodedFileName).append(";").append("filename*=").append("utf-8''").append(percentEncodedFileName);response.setHeader("Content-disposition", contentDispositionValue.toString());response.setHeader("download-filename", percentEncodedFileName);}/*** 百分号编码工具方法** @param s 需要百分号编码的字符串* @return 百分号编码后的字符串*/public static String percentEncode(String s) throws UnsupportedEncodingException {String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());return encode.replaceAll("\\+", "%20");}
}
3、WordUtil 类
public class WordUtil {/*** 模板方式*/private final Configuration configuration;/*** 根据模板生成文件流* @param date 模板数据* @param templateName 模板名称* @param outputStream 输出流* @throws Exception*/public void generateFromTemplates(Map date, String templateName, OutputStream outputStream) throws Exception {Template template = configuration.getTemplate(templateName);Writer w = new OutputStreamWriter(outputStream, "utf-8");template.process(date, w);w.close();}
}