产品提了个这样的需求:分别生成一个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文档
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。