java利用Freemarker模板生成docx格式的word文档(全过程)
参考:https://my.oschina.net/u/3737136/blog/2958421?tdsourcetag=s_pcqq_aiomsg
具体思路
1.创建一个docx文档模板,其中的英文是根据自己需要填充的内容。
2.把docx文档修改为ZIP格式(修改.docx后缀名为.zip),然后把zip解压到当前目录
3.修改word目录下document.xml文档,把如下原来是time改为${time},改好后放入项目中,用于后边内容填充。【有时候一个单词可能会被拆分,自己要做相应的调整】
<#list minuteList as minute></#list> 可用于循环填充,相当于 for (Minute minte:minuteList),示列如下:
<#list minuteList as minute> <w:p w14:paraId="3BB7AF6B" w14:textId="2AD2E958" w:rsidR="00D80192" w:rsidRDefault="00C4552D" w:rsidP="00E47882"> <w:pPr> <w:pStyle w:val="a6"/> <w:numPr> <w:ilvl w:val="0"/> <w:numId w:val="2"/> </w:numPr> <w:ind w:firstLineChars="0"/> </w:pPr> <w:proofErr w:type="spellStart"/> <w:r w:rsidRPr="00C4552D"> <w:t>${minute.meeting_decision_content}</w:t> </w:r> <w:proofErr w:type="spellEnd"/> <w:r> <w:t xml:space="preserve"> </w:t> </w:r> <w:bookmarkStart w:id="0" w:name="_GoBack"/> <w:bookmarkEnd w:id="0"/> <w:r> <w:rPr> <w:rFonts w:hint="eastAsia"/> </w:rPr> <w:t>交由</w:t> </w:r> <w:proofErr w:type="spellStart"/> <w:r w:rsidRPr="00C4552D"> <w:t>${minute.meeting_decision_executor} </w:t> </w:r> <w:proofErr w:type="spellEnd"/> <w:r> <w:rPr> <w:rFonts w:hint="eastAsia"/> </w:rPr> <w:t>负责,在</w:t> </w:r> <w:proofErr w:type="spellStart"/> <w:r w:rsidRPr="00C4552D"> <w:t>${minute.meeting_decision_deadline} </w:t> </w:r> <w:proofErr w:type="spellEnd"/> <w:r> <w:rPr> <w:rFonts w:hint="eastAsia"/> </w:rPr> <w:t>前完成。</w:t> </w:r> </w:p> </#list>
---------下边思路在代码中实现------------
4..把内容填充到document.xml里
5.在输入docx文档的时候把填充过内容的的 document.xml用流的方式写入zip(详见下面代码)。
6.输出docx文档
docx模板修改成zip格式后的信息如下(因为word文档本身就是ZIP格式实现的)
注:【在文档里边加入图片须进行rels文档的相关操作,我这里没用到,如有需要可以查看上边的参考链接】
---------代码部分------------
pom中导入依赖:
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> </dependency>
项目结构:【箭头指向才是用到的,其他部分的测试目前没有删】
FreeMarkUtils类
import freemarker.template.Configuration; import freemarker.template.Template; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.StringWriter; import java.util.Map; public class FreeMarkUtils { private static Logger logger = LoggerFactory.getLogger(FreeMarkUtils.class); public static Configuration getConfiguration(){ //创建配置实例 Configuration configuration = new Configuration(Configuration.VERSION_2_3_28); //设置编码 configuration.setDefaultEncoding("utf-8"); configuration.setClassForTemplateLoading(FreeMarkUtils.class, "/templates/wordTemplates");//换成自己对应的目录 return configuration; } /** * 获取模板字符串输入流 * @param dataMap 参数 * @param templateName 模板名称 * @return */ public static ByteArrayInputStream getFreemarkerContentInputStream(Map dataMap, String templateName) { ByteArrayInputStream in = null; try { //获取模板 Template template = getConfiguration().getTemplate(templateName); StringWriter swriter = new StringWriter(); //生成文件 template.process(dataMap, swriter); in = new ByteArrayInputStream(swriter.toString().getBytes("utf-8"));//这里一定要设置utf-8编码 否则导出的word中中文会是乱码 } catch (Exception e) { logger.error("模板生成错误!"); } return in; } }
FreemarkerTest类
import com.example.meeting.util.FreeMarkUtils; import java.io.*; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; public class FreemarkerTest { private static String document="document.xml"; //outputStream 输出流可以自己定义 浏览器或者文件输出流 public static void createDocx(Map dataMap, OutputStream outputStream) { ZipOutputStream zipout = null; try { /*//图片配置文件模板 ByteArrayInputStream documentXmlRelsInput = FreeMarkUtils.getFreemarkerContentInputStream(dataMap, documentXmlRels);*/ //内容模板 ByteArrayInputStream documentInput = FreeMarkUtils.getFreemarkerContentInputStream(dataMap, document); //最初设计的模板 //File docxFile = new File(WordUtils.class.getClassLoader().getResource(template).getPath()); File docxFile = new File("xxxx\\会议纪要.zip");//换成自己的zip路径 if (!docxFile.exists()) { docxFile.createNewFile(); } ZipFile zipFile = new ZipFile(docxFile); Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries(); zipout = new ZipOutputStream(outputStream); //开始覆盖文档------------------ int len = -1; byte[] buffer = new byte[1024]; while (zipEntrys.hasMoreElements()) { ZipEntry next = zipEntrys.nextElement(); InputStream is = zipFile.getInputStream(next); if (next.toString().indexOf("media") < 0) { zipout.putNextEntry(new ZipEntry(next.getName())); if ("word/document.xml".equals(next.getName())) {//如果是word/document.xml由我们输入 if (documentInput != null) { while ((len = documentInput.read(buffer)) != -1) { zipout.write(buffer, 0, len); } documentInput.close(); } } else { while ((len = is.read(buffer)) != -1) { zipout.write(buffer, 0, len); } is.close(); } } } } catch (Exception e) { System.out.println("word导出失败:"+e.getStackTrace()); //logger.error(); }finally { if(zipout!=null){ try { zipout.close(); } catch (IOException e) { System.out.println("io异常"); } } if(outputStream!=null){ try { outputStream.close(); } catch (IOException e) { System.out.println("io异常"); } } } } public static void main(String arg[]){ Map dataMap=new HashMap(); ArrayList minuteList=new ArrayList(); MinuteTest minuteTest1=new MinuteTest(); minuteTest1.setMeeting_decision_content("决策1"); minuteTest1.setMeeting_decision_executor("执行者1"); minuteTest1.setMeeting_decision_deadline("截至日期1"); minuteList.add(minuteTest1); MinuteTest minuteTest2=new MinuteTest(); minuteTest2.setMeeting_decision_content("决策2"); minuteTest2.setMeeting_decision_executor("执行者2"); minuteTest2.setMeeting_decision_deadline("截至日期2"); minuteList.add(minuteTest2); dataMap.put("meeting_name", "如何使象牙山发展得更加美好"); dataMap.put("time", "2019-09-15 15:30"); dataMap.put("site", "会议室212"); dataMap.put("organizer", "张三"); dataMap.put("department", "策划部"); dataMap.put("attendee", "谢大脚、谢广坤、刘能"); dataMap.put("meeting_content", "关于象牙山发展中的每个人的义务"); dataMap.put("recorder", "王五"); dataMap.put("checker", "大老板"); dataMap.put("minuteList",minuteList); //指定输出docx路径 File outFile = new File("xxx\\test.docx") ; try { createDocx(dataMap,new FileOutputStream(outFile)); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
MinuteTest实体类
public class MinuteTest { private String meeting_decision_content; private String meeting_decision_executor; private String meeting_decision_deadline; public String getMeeting_decision_content() { return meeting_decision_content; } public void setMeeting_decision_content(String meeting_decision_content) { this.meeting_decision_content = meeting_decision_content; } public String getMeeting_decision_executor() { return meeting_decision_executor; } public void setMeeting_decision_executor(String meeting_decision_executor) { this.meeting_decision_executor = meeting_decision_executor; } public String getMeeting_decision_deadline() { return meeting_decision_deadline; } public void setMeeting_decision_deadline(String meeting_decision_deadline) { this.meeting_decision_deadline = meeting_decision_deadline; } }
在FreemarkerTest中运行后结果: