产品提了个这样的需求:分别生成一个word文档和pdf文档,里面需要包含数据,这些数据需要有对应的样式比如字体大小,表格,段落等。开始的想法是用Apache POI,但看了那一大段填充值操作的代码发现太过冗长,决定采用Freemarker模版的方案。
百度了一圈看上去 Spring Boot + FreeMarker制作word模板导出Word表格 这种做法像那么一回事,尝试了一波后发现生成的是只能生成doc格式的文件!只能生成doc格式的文件!只能生成doc格式的文件!我要的是docx格式的呀哥???
迫不得已我又只能经过漫长的搜索尝试失败再搜索尝试失败.....好吧,终于找到生成docx的方案:
1.将docx后缀修改为zip,取出其中的document.xml文档以及_rels文件夹下的document.xml.rels文档。
document.xml是填充文本类内容需要用用到的文件,比如你可以这样修改
意思是在生成新的docx时,将这个${}内容替换成我们传进来的参数,至于模版怎么整理,还有freemarker的循环,判断这些语法就自行百度吧,easy!
document.xml.rels是你的docx中需要替换其中的图片时要用到的文件,比如我将原来的
<Relationship Id="rId8" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image1.jpeg"/>
替换成了
<Relationship Id="rId8" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/${img.imgTargetName}"/>
意思是我将image1.jpeg替换成了${img.imgTargetName}这个变量对应的图片,当然zip包里的media文件夹也需要将新的图片文件写进去,需要注意的是,新文件的后缀名最好与原来文件的后缀名一致,否则生成的docx可能打不开。
2.读取zip文件的压缩流,将原来的document.xml、document.xml.rel文件替换成填充过内容的的 document.xml、document.xml.rels文件,把图片写入zip文件下word/media文件夹中
输出docx文档。
上代码
public static String exportImgWordDocx(Map map,String wordXmlName,String wordImgXmlResName) throws Exception { Template freemarkerTemplate = configuration.getTemplate(wordXmlName); Template imgFreemarkerTemplate = configuration.getTemplate(wordImgXmlResName); InputStream fin = null; ServletOutputStream out = null; Writer writerOut = null; Writer writerOutImg = null; File outdocxXmlFile =null; File outdocxImgXmlFile = null; File docxFile = null; String docxPath = null; try { //指定输出word docx的数据文件的路径 String outdocxXmlFilePath = exportResourcePath+UUID.randomUUID().toString()+".xml"; outdocxXmlFile = new File(outdocxXmlFilePath); FileOutputStream fos = new FileOutputStream(outdocxXmlFile); writerOut = new BufferedWriter(new OutputStreamWriter(fos),10240); //将map数据写入到xml模板中,获取docx数据文件 freemarkerTemplate.process(map,writerOut); //指定输出word docx的图片数据文件的路径 String outdocxImgXmlFilePath = exportResourcePath+UUID.randomUUID().toString()+".xml.rels"; outdocxImgXmlFile = new File(outdocxImgXmlFilePath); FileOutputStream fosImg = new FileOutputStream(outdocxImgXmlFile); writerOutImg = new BufferedWriter(new OutputStreamWriter(fosImg),10240); //将map数据写入到图片xml模板中,获取docx图片数据文件 imgFreemarkerTemplate.process(map,writerOutImg); //将docx数据xml文件、图片数据xml文件替换到对应的docx实例zip压缩包中去 ClassPathResource resource = new ClassPathResource("templates/SummaryTemplate.zip"); InputStream zipFileInputStream = resource.getInputStream(); docxPath = exportResourcePath +UUID.randomUUID().toString()+".docx"; docxFile = new File(docxPath); ZipInputStream zipInputStream = ZipUtils.wrapZipInputStream(zipFileInputStream); ZipOutputStream zipOutputStream = ZipUtils.wrapZipOutputStream(new FileOutputStream(docxFile)); String itemNameOne = "word/document.xml"; String itemNameTwo = "word/_rels/document.xml.rels"; List<Map<String,String>> imgList = (List<Map<String,String>>)map.get("imgList"); ZipUtils.replaceTwoItem(zipInputStream, zipOutputStream, itemNameOne, new FileInputStream(outdocxXmlFile),itemNameTwo,new FileInputStream(outdocxImgXmlFile),imgList); } catch (TemplateException e) { StackTraceElement[] stackTraceElements = e.getStackTrace(); logger.error(e.toString() + "---" + stackTraceElements[0].toString()); throw new SystemRunException(e.toString() + "---" + stackTraceElements[0].toString()); }finally { if (out != null) { out.close(); } if (fin != null) { fin.close(); } if(writerOut != null){ writerOut.close(); } if(writerOutImg != null){ writerOutImg.close(); } if(outdocxXmlFile!=null){ outdocxXmlFile.delete();//删除docx临时数据文件 } if(outdocxImgXmlFile!=null){ //outdocxImgXmlFile.delete();//删除docx临时图片数据文件 } if(docxFile!=null){ //docxFile.delete();//删除docx临时文件 } } return docxPath; }
如果需要生成pdf格式文件,也是需要先生成docx格式文件再转换成pdf格式文件,至于这个转换的过程,我找的到方案里最简单靠谱的还是用asponse-words,但这是个收费软件,csdn里很多破解版的jar下载资源,不使用破解版的话生成的pdf文件是会有水印的。
上代码:
public static void asposeWord2Pdf(String docxPath,String pdfPath){ logger.info("docxPath" + docxPath + "pdfPath" + pdfPath); if (!getLicense()) { return; } FileOutputStream os =null; try { File file = new File(pdfPath); // 新建一个空白pdf文档 os = new FileOutputStream(file); Document doc = new Document(docxPath); // Address是将要被转化的word文档 doc.save(os, SaveFormat.PDF); } catch (Exception e) { StackTraceElement[] stackTraceElements = e.getStackTrace(); logger.error(e.toString() + "---" + stackTraceElements[0].toString()); throw new SystemRunException(e.toString() + "---" + stackTraceElements[0].toString()); }finally{ if(os!=null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } }
后面可能还会遇到SpringBoot引用外部jar的问题,生成的Pdf文件里都是小方块这样的问题,生成文件慢各种各样的问题,都需要一一耐心解决,别问我怎么知道的....哭!
以下都是大佬:
java利用Freemarker模板生成docx格式的word文档
java利用Freemarker模板生成格式友好的doc或者docx文档
freemarker基于docx格式创建模板导出带图片pdf文件