easypoi,根据模板生成word工具类
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <version>2.6.3</version> </dependency> //XWPFDocument 转 MultipartFile(MockMultipartFile),则需要导入 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.6.RELEASE</version> <scope>compile</scope> </dependency>
import cn.afterturn.easypoi.entity.ImageEntity; import cn.afterturn.easypoi.word.WordExportUtil; import com.xxiang.digital_cloud.entity.vo.ProjectDailyVo; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.io.FilenameUtils; import org.apache.poi.xwpf.usermodel.Document; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFPictureData; import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody; import org.springframework.mock.web.MockMultipartFile; import org.springframework.web.multipart.MultipartFile; import java.io.*; import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @Slf4j public class WordUtils { /** * 图片大小路径设置 * 默认长宽是200 * @return */ public static ImageEntity pictureSize(String s){ ImageEntity imageEntity = new ImageEntity(); imageEntity.setHeight(200); imageEntity.setWidth(200); imageEntity.setUrl(s); imageEntity.setType(ImageEntity.URL); return imageEntity; } /** * word提交 * @return */ public static XWPFDocument wordSubmit(String wordUrl, String wordTemplateUrl, Map<String, Object> map){ XWPFDocument doc = null; try { doc = WordExportUtil.exportWord07(wordUrl, map); FileOutputStream fos = new FileOutputStream(wordTemplateUrl); doc.write(fos); fos.close(); } catch (Exception e) { e.printStackTrace(); } return doc; } /** * 多个word数据合并到一个word中提交 * @param wordUrl * @param wordTemplateUrl * @param list * @return */ public static XWPFDocument moreWordSubmit(String wordUrl, String wordTemplateUrl, List<Map<String, Object>> list){ XWPFDocument doc = null; try { doc = WordExportUtil.exportWord07(wordUrl, list); FileOutputStream fos = new FileOutputStream(wordTemplateUrl); doc.write(fos); fos.close(); } catch (Exception e) { e.printStackTrace(); } return doc; } /** * 将对象转换成map * @param obj * @return * @throws Exception */ public static Map<String, Object> objectToMap(Object obj) throws Exception { if (obj == null) { return null; } Map<String, Object> map = new HashMap<String, Object>(); Field[] declaredFields = obj.getClass().getDeclaredFields(); for (Field field : declaredFields) { field.setAccessible(true); map.put(field.getName(), field.get(obj)); } return map; } /** * 删除临时文件 * */ public static void delFileWord(String filePathUrl, String FileName){ File file = new File(filePathUrl+FileName); File file1 = new File(filePathUrl); file.delete(); file1.delete(); } /** * XWPFDocument 转 MultipartFile(MockMultipartFile) * * @param document 文档对象 * @param fileName 文件名 * @return */ public static MultipartFile xwpfDocumentToMockMultipartFile(XWPFDocument document, String fileName) { try { String contentType = "text/plain"; ByteArrayOutputStream bos = new ByteArrayOutputStream(); document.write(bos); //XWPFDocument 转 byte[] byte[] barray = bos.toByteArray(); //byte[] 转 InputStream InputStream is = new ByteArrayInputStream(barray); //InputStream 转 MultipartFile MultipartFile multipartFile = new MockMultipartFile(fileName, fileName, contentType, is); return multipartFile; } catch (Exception e) { e.printStackTrace(); return null; } } /** * word文件合并 * @param wordList * @return * @throws Exception */ public static XWPFDocument mergeWord(List<XWPFDocument> wordList) throws Exception{ if (CollectionUtils.isEmpty(wordList)) { throw new RuntimeException("待合并的word文档list为空"); } XWPFDocument doc = wordList.get(0); int size = wordList.size(); if (size > 1) { doc.createParagraph().setPageBreak(true); for (int i = 1; i < size; i++) { // 从第二个word开始合并 XWPFDocument nextPageDoc = wordList.get(i); // 最后一页不需要设置分页符 if (i != (size-1)) { nextPageDoc.createParagraph().setPageBreak(true); } appendBody(doc, nextPageDoc); } } return doc; } private static void appendBody(XWPFDocument src, XWPFDocument append) throws Exception { CTBody src1Body = src.getDocument().getBody(); CTBody src2Body = append.getDocument().getBody(); List<XWPFPictureData> allPictures = append.getAllPictures(); // 记录图片合并前及合并后的ID Map<String,String> map = new HashMap<>(); for (XWPFPictureData picture : allPictures) { String before = append.getRelationId(picture); //将原文档中的图片加入到目标文档中 String after = src.addPictureData(picture.getData(), Document.PICTURE_TYPE_PNG); map.put(before, after); } appendBody(src1Body, src2Body,map); } private static void appendBody(CTBody src, CTBody append, Map<String,String> map) throws Exception { XmlOptions optionsOuter = new XmlOptions(); optionsOuter.setSaveOuter(); String appendString = append.xmlText(optionsOuter); String srcString = src.xmlText(); String prefix = srcString.substring(0,srcString.indexOf(">")+1); String mainPart = srcString.substring(srcString.indexOf(">")+1,srcString.lastIndexOf("<")); String sufix = srcString.substring( srcString.lastIndexOf("<") ); String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<")); if (map != null && !map.isEmpty()) { //对xml字符串中图片ID进行替换 for (Map.Entry<String, String> set : map.entrySet()) { addPart = addPart.replace(set.getKey(), set.getValue()); } } //将两个文档的xml内容进行拼接 CTBody makeBody = CTBody.Factory.parse(prefix+mainPart+addPart+sufix); src.set(makeBody); } /** * 流式分组 * @param dailyVoList * @return */ public static List<List<ProjectDailyVo>> objectMap(List<ProjectDailyVo> dailyVoList){ Map<String, List<ProjectDailyVo>> collect = dailyVoList.stream().collect(Collectors.groupingBy(ProjectDailyVo::getPhaseCombinationId)); List<List<ProjectDailyVo>> list = new ArrayList<>(); for (String key:collect.keySet() ) { List<ProjectDailyVo> projectDailyVos = collect.get(key); list.add(projectDailyVos); } return list; } public static boolean deleteDir(String path) { File file = new File(path); if (!file.exists()) {//判断是否待删除目录是否存在 return false; } String[] content = file.list();//取得当前目录下所有文件和文件夹 for (String name : content) { File temp = new File(path, name); if (temp.isDirectory()) {//判断是否是目录 deleteDir(temp.getAbsolutePath());//递归调用,删除目录里的内容 temp.delete();//删除空目录 } else { if (!temp.delete()) {//直接删除文件 System.err.println("Failed to delete " + name); } } } return true; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具