1 环境配置
<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency>
2 doc文档或docx文档
需要在指定需要填充数据的位置,添加标识,这只是初步的标识,
3 另存为xml格式
会发现,这些标识,可能会发生转义(因为字体,颜色,加粗,下划线等等因素)。如图下:
然后在xml文件中,找到对应标识的位置,改成如下格式。
${zpmc?if_exists}
修改后的为:
因为,可能由于数据的问题,某些字段数据并不存在,所以,要在后面加上 ?if_exists
4 核心代码
package com.oldlu; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.HashMap; import java.util.Map; import freemarker.cache.FileTemplateLoader; import freemarker.cache.TemplateLoader; import freemarker.template.Configuration; import freemarker.template.Template; public class CreateWordT { public static void main(String[] args) { Map<String, Object> cont = new HashMap<String, Object>();// 存储数据 cont.put("xmh", "111"); cont.put("hth", "222"); cont.put("zpmc", ""); cont.put("zpsm", "444"); cont.put("zpzs", "555"); try { //模板的路径 File fir = new File("W:/test/wswhr/"); //生成文件的路径及文件名。 File outFile = new File("W:/test/wswhr/委托创作合同.doc"); Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8")); // 使用FileTemplateLoader //指定模板路径 TemplateLoader templateLoader = null; templateLoader = new FileTemplateLoader(fir); String tempname = "委托创作合同.xml"; Configuration cfg = new Configuration(); cfg.setTemplateLoader(templateLoader); Template t = cfg.getTemplate(tempname, "UTF-8"); t.process(cont, out); out.flush(); out.close(); } catch (Exception e) { e.printStackTrace(); } } }
代码中,t.process(cont,out);
会在模板t中,填充cont的数据,输出到out文件中。
成功后的结果如下:
因为作品名称,我没有放数据,所以为空。
5.填充生成到docx
测试中docx使用office是无法打开但是WPS是可以打开这就有了问题所以解决方法如下
大致流程:
5.1.创建模板docx并取出document.xml
新建一个docx文档,放在D盘命名test_template.docx
5.2.用winrar打开test_template.docx,取出word/document.xml
把xml文件格式化一下看起来更清晰,并且把要替换的内容用freemarker的指令代替,最后将文件重命名为test.xml放在D盘下面
5.3.准备工作完毕,上代码了,这个类是把内容填充到xml
import java.io.File; import java.io.IOException; import java.io.Writer; import java.util.Map; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; public class XmlToExcel { private static XmlToExcel tplm = null; private Configuration cfg = null; private XmlToExcel() { cfg = new Configuration(); try { // 注册tmlplate的load路径 // cfg.setClassForTemplateLoading(this.getClass(), "/template/"); cfg.setDirectoryForTemplateLoading(new File("D:/")); } catch (Exception e) { } } private static Template getTemplate(String name) throws IOException { if (tplm == null) { tplm = new XmlToExcel(); } return tplm.cfg.getTemplate(name); } /** * * @param templatefile 模板文件 * @param param 需要填充的内容 * @param out 填充完成输出的文件 * @throws IOException * @throws TemplateException */ public static void process(String templatefile, Map param, Writer out) throws IOException, TemplateException { // 获取模板 Template template = XmlToExcel.getTemplate(templatefile); template.setOutputEncoding("UTF-8"); // 合并数据 template.process(param, out); if (out != null) { out.close(); } } }
5.4.这个类是把填充完毕的xml转成docx
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; /** * 其实docx属于zip的一种,这里只需要操作word/document.xml中的数据,其他的数据不用动 * * @author yigehui * */ public class XmlToDocx { /** * * @param documentFile 动态生成数据的docunment.xml文件 * @param docxTemplate docx的模板 * @param toFileName 需要导出的文件路径 * @throws ZipException * @throws IOException */ public void outDocx(File documentFile, String docxTemplate, String toFilePath) throws ZipException, IOException { try { File docxFile = new File(docxTemplate); ZipFile zipFile = new ZipFile(docxFile); Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries(); ZipOutputStream zipout = new ZipOutputStream(new FileOutputStream(toFilePath)); int len = -1; byte[] buffer = new byte[1024]; while (zipEntrys.hasMoreElements()) { ZipEntry next = zipEntrys.nextElement(); InputStream is = zipFile.getInputStream(next); // 把输入流的文件传到输出流中 如果是word/document.xml由我们输入 zipout.putNextEntry(new ZipEntry(next.toString())); if ("word/document.xml".equals(next.toString())) { InputStream in = new FileInputStream(documentFile); while ((len = in.read(buffer)) != -1) { zipout.write(buffer, 0, len); } in.close(); } else { while ((len = is.read(buffer)) != -1) { zipout.write(buffer, 0, len); } is.close(); } } zipout.close(); } catch (Exception e) { e.printStackTrace(); } } }
5.5.main方法调用
public static void main(String[] args) throws IOException, TemplateException { try { // xml的文件名 String xmlTemplate = "test.xml"; // docx的路径和文件名 String docxTemplate = "d:\\test_template.docx"; // 填充完数据的临时xml String xmlTemp = "d:\\temp.xml"; // 目标文件名 String toFilePath = "d:\\test.docx"; Writer w = new FileWriter(new File(xmlTemp)); // 1.需要动态传入的数据 Map<String, Object> p = new HashMap<String, Object>(); List<String> students = new ArrayList<String>(); students.add("张三"); students.add("李四"); students.add("王二"); p.put("ddeptdept", "研发部门"); p.put("ddatedate", "2016-12-15"); p.put("dnamename", students); // 2.把map中的数据动态由freemarker传给xml XmlToExcel.process(xmlTemplate, p, w); // 3.把填充完成的xml写入到docx中 XmlToDocx xtd = new XmlToDocx(); xtd.outDocx(new File(xmlTemp), docxTemplate, toFilePath); } catch (Exception e) { e.printStackTrace(); } }
6 推荐一个封装好的工具
我看很多人关注这篇文章,所以更新一下采用一个写好的代码只需几行即可完成上述操作
import com.deepoove.poi.XWPFTemplate; import java.io.FileOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * @author oldlu * @version 1.0 * @date 2022/1/27 0027 16:16 */ public class PoiTlUtil { public static void createWord(String docxTemplatePath,Map<String, Object> map,String docxOutTemplatePath) throws IOException { XWPFTemplate template = XWPFTemplate.compile(docxTemplatePath) .render(map); FileOutputStream out; out = new FileOutputStream(docxOutTemplatePath); template.write(out); out.flush(); out.close(); template.close(); } public static void main(String[] args) { Map map=new HashMap(); map.put("title","oldlu"); try { PoiTlUtil.createWord("E:/template.docx",map,"E:/template1.docx"); } catch (IOException e) { e.printStackTrace(); } } }