maven导入包
<!-- poi-tl --> <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.12.0</version> </dependency> <!-- poi-ooxml --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.2</version> </dependency> <!-- poi --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.2</version> </dependency> <!-- poi-ooxml-schemas --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>4.1.2</version> </dependency> <!-- poi-scratchpad --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>5.2.2</version> <scope>compile</scope> </dependency>
controller
@PostMapping("/expDoc") @ApiOperation(value = "导出记录台账(word)", notes = "导出记录台账") public void expDoc(HttpServletResponse response,@RequestBody PoiWordQuery query) throws IOException { List<ExportWordVo> records = service.expZdryGkjlDocListTwo(query); if(records!=null){ XWPFDocument document = new XWPFDocument(); for (int i = 0; i < records.size(); i++) { ExportWordVo record = records.get(i); // 创建表格 XWPFTable table = document.createTable(); // 设置表格布局为固定宽度 CTTblLayoutType tblLayout = table.getCTTbl().getTblPr().addNewTblLayout(); tblLayout.setType(STTblLayoutType.FIXED); // 设置表格总宽度 table.setWidth("8400"); // 设置表格内容 PoiWordUtil.addTableRowThree(table, "登记时间", record.getDjsj(), "登记姓名", record.getDjmjxm()); PoiWordUtil.addTableRowThree(table, "登记身份证号", record.getDjmjsfzh(), "登记联系电话", record.getDjmjlxdh()); PoiWordUtil.addTableRowThree(table, "核查时间", record.getHssj(), "核查地点", record.getHsddqhmc()); PoiWordUtil.addTableRowThree(table, "在原籍市内干嘛", record.getZyjsnzt(), "是否离开本市/社区", "1".equals(record.getSflkbsmc()) ? "是" : "否"); PoiWordUtil.addTableRowThree(table, "居住地地址", record.getQwdxxdz(), "离开本市", record.getLkbssy()); PoiWordUtil.addTableRowThree(table, "是否在本社区居住", record.getSfzbsqjzmc(), "来本社区", record.getLbsqsy()); PoiWordUtil.addTableRowThree(table, "来本社区时间", record.getLbsqsj(), "在本社区详细居住地址", record.getZbsqxxjzdz()); PoiWordUtil.addTableRowThree(table, "租房/自有房屋", record.getFwqkmc(), "联系电话", record.getLxdh()); PoiWordUtil.addTableRow(table, "现实情况描述", record.getXsqkms()); // 在表格中添加所有照片到单元格的方法调用 if (record.getHsdjzp() != null && !record.getHsdjzp().isEmpty()) { String[] images = record.getHsdjzp().split(","); List<File> imageFiles = new ArrayList<>(); try { for (String imageUrl : images) { File imageFile = PoiWordUtil.downloadImageToLocal(imageUrl.trim()); if (imageFile != null && imageFile.exists()) { imageFiles.add(imageFile); } } // 将多张图片添加到同一个表格单元格中 if (!imageFiles.isEmpty()) { PoiWordUtil.addImageRowWithMultipleImages(table, "核实照片", imageFiles); } } finally { // 删除临时文件 for (File imageFile : imageFiles) { if (imageFile.exists()) { imageFile.delete(); } } } } PoiWordUtil.addTableRowThree(table, "从事职业类型", record.getShsf(), "工作单位", record.getGzdw()); PoiWordUtil.addTableRowThree(table, "收入来源", record.getSrly(), "经济情况", record.getJjqk()); table.removeRow(0); // 分页 if(i != records.size()-1){ document.createParagraph().setPageBreak(true); } } PoiWordUtil.setResponseHeader(response); OutputStream os = response.getOutputStream(); document.write(os); os.close(); } }
工具类,有一个东西一直搞不懂,所以有测试方法,根据controller调用到的方法来看就好。
我之前是使用的poi-tl,直接通过word模板去渲染,之前的文章有写,但是发现渲染图片的时候有问题,poi-tl的占位符是这样子的,单张照片{{@val}},多张图片占位符是{{?pictures}}{{@picture}}{{/pictures}}正常文字的是{{val}},但是我发现返回多个对象
的时候,去渲染图片,每一个对象都是一个XWPFTemplate,多个XWPFTemplate放进XWPFDocument,最后的导出来图片只有一个框框,但是没有图片内容,所以是XWPFTemplate放在XWPFDocument的时候,文字的内容已经装进去了,但是图片没有正确装进去,因为我去渲染单个对象,渲染完毕XWPFTemplate,直接导出,不装Document,无论是单张图片,还是多张图片的占位符,图片展示都是正确的,然后我这个问题一直又解决不了,再不耽误进度的情况下,就采取别的实现方式了,有大佬知道的希望指点一下
后面对象里面有多张图片,又是List集合对象的话,我就没用poi-tl的模板渲染了,直接使用XWPFTable去拼接单元格。
我这个代码导出来,默认是一行4列,也有一行两列的,第二列合并单元格,也有单元格是放多张图片的。
public class PoiWordUtil { private static final Logger log = org.slf4j.LoggerFactory.getLogger(PoiWordUtil.class); /** * 导出word台账 将键值对的recod渲染到模板 导出 * @param response * @param records * @param templateURL */ public static void expPoiWord(HttpServletResponse response, List<Map<String, Object>> records,String templateURL) { RestTemplate restTemplate = new RestTemplate(); byte[] templateData = restTemplate.getForObject(templateURL, byte[].class); // 创建主文档对象 try (XWPFDocument document = new XWPFDocument()) { if(StringUtils.isNull(records)){ return; } // 遍历每条记录 for (Map<String, Object> record : records) { InputStream templateInputStream = new ByteArrayInputStream(templateData); // 使用每条记录渲染模板 templateInputStream.reset(); // 重置输入流以重新读取 XWPFTemplate template = XWPFTemplate.compile(templateInputStream); template.render(record); // 将渲染后的表格复制到主文档 for (XWPFTable table : template.getXWPFDocument().getTables()) { document.createTable(); document.setTable(document.getTables().size() - 1, table); } // 为每个记录插入分页符,除最后一个 if (record != records.get(records.size() - 1)) { document.createParagraph().setPageBreak(true); } template.close(); } setResponseHeader(response); OutputStream os = response.getOutputStream(); document.write(os); os.close(); } catch (IOException e) { e.printStackTrace(); } } public static void expPoiWordRecord(HttpServletResponse response, Map<String, Object> record,String templateURL) { RestTemplate restTemplate = new RestTemplate(); byte[] templateData = restTemplate.getForObject(templateURL, byte[].class); try { if(StringUtils.isNull(record)){ return; } InputStream inputStream = new ByteArrayInputStream(templateData); XWPFTemplate template = XWPFTemplate.compile(inputStream); template.render(record); setResponseHeader(response); OutputStream os = response.getOutputStream(); template.write(os); os.close(); } catch (IOException e) { e.printStackTrace(); } } public static XWPFTemplate expPoiWordRecordUpload(Map<String, Object> record,String templateURL) { RestTemplate restTemplate = new RestTemplate(); byte[] templateData = restTemplate.getForObject(templateURL, byte[].class); if(StringUtils.isNull(record)){ return null; } InputStream inputStream = new ByteArrayInputStream(templateData); XWPFTemplate template = XWPFTemplate.compile(inputStream); template.render(record); return template; } public static void setResponseHeader(HttpServletResponse response) { response.reset(); response.setContentType("application/octet-stream"); response.setHeader("Content-disposition", "attachment;filename=user_word_" + System.currentTimeMillis() + ".docx"); } // 辅助方法,用于将输入流转换为字节数组(可提取到工具类中方便复用) private static byte[] readInputStreamToByteArray(InputStream inputStream) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = inputStream.read(buffer)) > 0) { bos.write(buffer, 0, len); } return bos.toByteArray(); } public static void expPoiWordBf(HttpServletResponse response, List<Map<String, Object>> records,String templateURL) { RestTemplate restTemplate = new RestTemplate(); byte[] templateData = restTemplate.getForObject(templateURL, byte[].class); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // 创建主文档对象 try (XWPFDocument document = new XWPFDocument()) { if(StringUtils.isNull(records)){ return; } // 遍历每条记录 for (int i = 0; i < records.size(); i++) { Map<String, Object> record = records.get(i); InputStream templateInputStream = new ByteArrayInputStream(templateData); templateInputStream.reset(); XWPFTemplate template = XWPFTemplate.compile(templateInputStream); template.render(record); // 将渲染后的表格复制到主文档 for (XWPFTable table : template.getXWPFDocument().getTables()) { document.createTable(); XWPFTableCell cell = table.getRows().get(13).getCell(1); List<String> imageUrls = parseImageUrls(record.get("hsdjzp").toString()); cell.removeParagraph(0); for (String imageUrl : imageUrls) { System.out.println("打印照片:"+imageUrl); try (InputStream imageStream = new URL(imageUrl).openStream()) { XWPFParagraph paragraph = cell.addParagraph(); XWPFRun run = paragraph.createRun(); run.addPicture( imageStream, Document.PICTURE_TYPE_JPEG, imageUrl, Units.toEMU(100), Units.toEMU(150) ); } catch (Exception e) { System.err.println("无法插入图片: " + imageUrl); e.printStackTrace(); } } document.setTable(document.getTables().size() - 1, table); int h = table.getRows().size(); for (int i1 = 0; i1 < h; i1++) { int l = table.getRow(i1).getTableCells().size(); for (int i2 = 0; i2 < l; i2++) { if(StringUtils.isNotEmpty(table.getRow(i1).getCell(i2).getText())){ System.out.println("第"+i1+"行,第"+i2+"列:"+table.getRow(i1).getCell(i2).getText()); }else{ System.out.println("第"+i1+"行,第"+i2+"列:meiyou"); } } } for (String imageUrl2 : imageUrls) { try (InputStream imageStream = new URL(imageUrl2).openStream()) { // 创建图片段落 XWPFParagraph paragraph = document.createParagraph(); XWPFRun run = paragraph.createRun(); // 插入图片 run.addPicture( imageStream, Document.PICTURE_TYPE_JPEG, imageUrl2, Units.toEMU(200), // 图片宽度 Units.toEMU(150) // 图片高度 ); } catch (Exception e) { System.err.println("无法插入图片: " + imageUrl2); e.printStackTrace(); } } } // 为每个记录插入分页符,除最后一个 if (record != records.get(records.size() - 1)) { document.createParagraph().setPageBreak(true); } template.close(); } setResponseHeader(response); OutputStream os = response.getOutputStream(); document.write(os); os.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 将集合转成键值对 用来渲染word模板 * @param objects * @return */ public static List<Map<String, Object>> convertToRecords(List<?> objects) { List<Map<String, Object>> records = new ArrayList<>(); for (Object obj : objects) { Map<String, Object> map = new HashMap<>(); // 获取所有字段 Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { // 确保可以访问私有字段 field.setAccessible(true); try { // 处理图片字段(假设数据库中图片字段存储的是图片 URL,以逗号分隔多个 URL,根据实际调整){{?pictures}}{{@picture}}{{/pictures}} if("fhfs".equals(field.getName()) && field.get(obj)!=null){ String imageUrls = field.get(obj).toString(); List<Map<String, PictureRenderData>> imageList = new ArrayList<>(); String[] urls = imageUrls.split(","); for (String url : urls) { Map<String, PictureRenderData> imageMap = new HashMap<>(); imageMap.put("picture", Pictures.ofUrl(url.trim()).size(100, 100).create()); imageList.add(imageMap); } }else{ map.put(field.getName(), field.get(obj)); } map.put(field.getName(), field.get(obj)); } catch (IllegalAccessException e) { e.printStackTrace(); } } records.add(map); } return records; } /** * 将集合转成键值对 用来渲染word模板 * @param object * @return */ public static Map<String, Object> convertToRecord(Object object) { //List<Map<String, Object>> records = new ArrayList<>(); Map<String, Object> map = new HashMap<>(); // 获取所有字段 Field[] fields = object.getClass().getDeclaredFields(); for (Field field : fields) { // 确保可以访问私有字段 field.setAccessible(true); try { map.put(field.getName(), field.get(object)); } catch (IllegalAccessException e) { e.printStackTrace(); } } return map; } // 处理图片字段(假设数据库中图片字段存储的是图片 URL,以逗号分隔多个 URL,根据实际调整) /* if("hsdjzp".equals(field.getName()) && field.get(obj)!=null){ String imageUrls = field.get(obj).toString(); List<Map<String, PictureRenderData>> imageList = new ArrayList<>(); String[] urls = imageUrls.split(","); for (String url : urls) { System.out.println("打印url地址:"+url); Map<String, PictureRenderData> imageMap = new HashMap<>(); imageMap.put("picture", Pictures.ofUrl(url.trim()).size(100, 100).create()); imageList.add(imageMap); } System.out.println("打印url地址:"+urls[0]); map.put("picture", urls[0]); }else{ map.put(field.getName(), field.get(obj)); }*/ /** * 解析图片地址为列表 * @param imageUrls * @return */ private static List<String> parseImageUrls(String imageUrls) { return Arrays.asList(imageUrls.split(",")); } /** * 查询相对应的字典,对字典进行转化 * @param wordList * @param fieldToDictMap * @param systemClient */ public static void getWordGetSet(List<?> wordList, Map<String, String> fieldToDictMap,SystemClient systemClient,String type) { Map<String, Map<String, String>> allDictionaries = new HashMap<>(); for (Map.Entry<String, String> entry : fieldToDictMap.entrySet()) { String fieldName = entry.getValue(); SysDictItem dictItemQuery = new SysDictItem(); dictItemQuery.setNbbsf(fieldName); Result<List<SysDictItem>> result = systemClient.getDictItem(dictItemQuery); // 防止空结果 if (result != null && result.getData() != null) { Map<String, String> dictMap = result.getData().stream() .filter(Objects::nonNull) .collect(Collectors.toMap(SysDictItem::getXxdm, SysDictItem::getXxdmmc)); allDictionaries.put(fieldName, dictMap); } } for (Object word : wordList) { for (Map.Entry<String, String> entry : fieldToDictMap.entrySet()) { String fieldName = entry.getKey(); String dictType = entry.getValue(); Map<String, String> dictMap = allDictionaries.get(dictType); // 动态调用 getter 和 setter 方法 if (dictMap != null) { try { // 获取字段值 Method getter = word.getClass().getMethod("get" + capitalize(fieldName)); Object fieldValue = getter.invoke(word); if (fieldValue != null) { // 设置转换后的值 Method setter = word.getClass().getMethod("set" + capitalize(fieldName), String.class); setter.invoke(word, dictMap.getOrDefault(fieldValue.toString(), "未知")); } } catch (Exception e) { // 捕获异常并记录日志 log.error("getter或setter方法调用失败,请检查字段名是否正确,例如:get" + capitalize(fieldName)); e.printStackTrace(); } } } switch (type) { case "zdrygkjl": getWordGetSetZdryGkjl(word); break; case "zdry": getWordGetSetZdry(word); break; } } } /** * 重点人员台账特殊字段处理 */ public static <T> void getWordGetSetZdry(T word){ // 通过反射机制来判断是否存在类似jzdpcsmc的属性,并进行处理(更通用但性能有一定损耗的方式) try { // 获取名为jzdpcsmc的get方法 java.lang.reflect.Method getMethod = word.getClass().getMethod("getJzdpcsmc"); if (getMethod.getReturnType() == String.class) { String value = (String) getMethod.invoke(word); if (value!= null) { // 获取对应的set方法 System.out.println("打印value:"+value); java.lang.reflect.Method setMethod = word.getClass().getMethod("setJzdpcsmc", String.class); // 调用CacheUtils.getGajgmc处理值并设置回去 System.out.println("转化=====:"+CacheUtils.getGajgmc(value)); setMethod.invoke(word, CacheUtils.getGajgmc(value)); } } java.lang.reflect.Method getMethodGxdw = word.getClass().getMethod("getGxdwmc"); if (getMethodGxdw.getReturnType() == String.class) { String value = (String) getMethodGxdw.invoke(word); if (value!= null) { // 获取对应的set方法 System.out.println("打印value:"+value); java.lang.reflect.Method setMethod = word.getClass().getMethod("setGxdwmc", String.class); // 调用CacheUtils.getGajgmc处理值并设置回去 System.out.println("转化=====:"+CacheUtils.getGajgmc(value)); setMethod.invoke(word, CacheUtils.getGajgmc(value)); } } } catch (Exception e) { // 处理反射调用可能出现的异常,比如方法不存在等情况,可以根据实际需求更精细地处理异常 e.printStackTrace(); } } /** * 重点人员跟踪记录特殊字段处理 * @param word */ public static <T> void getWordGetSetZdryGkjl(T word){ // 通过反射机制来判断是否存在类似jzdpcsmc的属性,并进行处理(更通用但性能有一定损耗的方式) try { // 获取名为jzdpcsmc的get方法 java.lang.reflect.Method getMethod = word.getClass().getMethod("getGxdwmc"); if (getMethod.getReturnType() == String.class) { String value = (String) getMethod.invoke(word); if (value!= null) { // 获取对应的set方法 java.lang.reflect.Method setMethod = word.getClass().getMethod("setGxdwmc", String.class); // 调用CacheUtils.getGajgmc处理值并设置回去 setMethod.invoke(word, CacheUtils.getGajgmc(value)); } } java.lang.reflect.Method getMethodZkzt = word.getClass().getMethod("getZkztmc"); if (getMethodZkzt.getReturnType() == String.class) { String value = (String) getMethodZkzt.invoke(word); if (value!= null) { // 获取对应的set方法 java.lang.reflect.Method setMethod = word.getClass().getMethod("setZkztmc", String.class); // 调用CacheUtils.getGajgmc处理值并设置回去 if("2".equals(value)){ setMethod.invoke(word, "在控"); }else{ setMethod.invoke(word, "未在控"); } } } } catch (Exception e) { // 处理反射调用可能出现的异常,比如方法不存在等情况,可以根据实际需求更精细地处理异常 e.printStackTrace(); } } /** * 首字母大写辅助方法 */ private static String capitalize(String str) { if (str == null || str.isEmpty()) { return str; } return str.substring(0, 1).toUpperCase() + str.substring(1); } /** * 将多张图片渲染到word的单元格内 * @param table * @param label * @param imageFiles */ public static void addImageRowWithMultipleImages(XWPFTable table, String label, List<File> imageFiles) { try { XWPFTableRow row = table.createRow(); // 设置左侧单元格 XWPFTableCell cell1 = row.getCell(0); cell1.setText(label); cell1.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); cell1.setWidth("2000"); // 设置右侧单元格用于放置多张图片 XWPFTableCell cell2 = row.addNewTableCell(); XWPFParagraph paragraph = cell2.addParagraph(); XWPFRun run = paragraph.createRun(); cell2.setWidth("6400"); // 合并右侧三列(第2列到第4列) table.getRow(table.getNumberOfRows() - 1).getCell(1).getCTTc().getTcPr().addNewGridSpan().setVal(BigInteger.valueOf(3)); // 遍历所有图片文件并逐一添加到同一个段落中 for (File imageFile : imageFiles) { try (InputStream inputStream = Files.newInputStream(imageFile.toPath())) { run.addPicture( inputStream, XWPFDocument.PICTURE_TYPE_JPEG, imageFile.getName(), Units.toEMU(150), // 宽度 Units.toEMU(150) // 高度 ); // 每张图片后添加一个换行符(如果需要) run.addBreak(BreakType.TEXT_WRAPPING); } catch (Exception e) { log.error("图片处理失败: " + imageFile.getName() + ". " + e.getMessage()); } } paragraph.setAlignment(ParagraphAlignment.CENTER); } catch (Exception e) { log.error("图片行处理失败" + e.getMessage()); } } /** * 一行4列,每次放入2组对象 * @param table * @param label * @param value * @param labeltwo * @param valuetwo */ public static void addTableRowThree(XWPFTable table, String label, String value,String labeltwo, String valuetwo) { XWPFTableRow row = table.createRow(); // 设置左侧单元格样式 XWPFTableCell cell1 = row.getCell(0); cell1.setText(label); cell1.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); // 设置右侧单元格样式 XWPFTableCell cell2 = row.addNewTableCell(); cell2.setText(value != null ? value : ""); cell2.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); // 设置单元格宽度 cell1.setWidth("2000"); cell2.setWidth("2200"); // 设置右侧单元格样式 XWPFTableCell cell3 = row.addNewTableCell(); cell3.setText(labeltwo); cell3.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); // 设置单元格宽度 cell1.setWidth("2000"); cell2.setWidth("2200"); // 设置右侧单元格样式 XWPFTableCell cell4 = row.addNewTableCell(); cell4.setText(valuetwo != null ? valuetwo : ""); cell4.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); // 设置单元格宽度 cell3.setWidth("2000"); cell4.setWidth("2200"); row.setHeight(600); } /** * 一行4列,第二列合并后面3列 * @param table * @param label * @param value * @return */ public static XWPFTableRow addTableRow(XWPFTable table, String label, String value) { XWPFTableRow row = table.createRow(); // 设置左侧单元格样式 XWPFTableCell cell1 = row.getCell(0); cell1.setText(label); cell1.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); // 设置右侧单元格样式 XWPFTableCell cell2 = row.addNewTableCell(); cell2.setText(value != null ? value : ""); cell2.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); // 设置单元格宽度 cell1.setWidth("2000"); cell2.setWidth("6400"); table.getRow(table.getNumberOfRows() - 1).getCell(1).getCTTc().getTcPr().addNewGridSpan().setVal(BigInteger.valueOf(3)); row.setHeight(600); return row; } /** * 通过地址下载图片到本地,为渲染文档做准备 * @param imageUrl * @return * @throws IOException */ public static File downloadImageToLocal(String imageUrl) throws IOException { URL url = new URL(imageUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.connect(); if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { // 创建临时文件 File tempFile = File.createTempFile("image", ".jpg"); try (InputStream inputStream = connection.getInputStream(); FileOutputStream outputStream = new FileOutputStream(tempFile)) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); // 修复偏移量 } } return tempFile; } else { return null; } }
Hello!希望可以共同进步!一起讨论吧~
如果有域的区分,记得图片处理的时候,注意域的IP处理哦!
标签:word,String,渲染,new,record,value,poi,table From: https://www.cnblogs.com/shendidi/p/18599068