FreeMarker生成带图片的word文档(浏览器输出)
所需依赖
<!--生成word文档所需-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
制作模板的步骤参见上篇博客,有一点需要注意的是document.xml中图片路径保存成本地路径格式以支持各种图片格式写入至文档。
这里的路径搞了好长时间,简单记录一下:
这里的文件名要使用这种格式才能在xml文件中保存为全路径名称,不知道是不是我wps的问题,最后是使用了这种方法完成的。
下面记录一下使用的主要代码:
FreeMarker工具类:
public class FreeMarkerUtil { public static Configuration getConfiguration() throws IOException{ //创建配置实例 Configuration configuration = new Configuration(Configuration.VERSION_2_3_28); //设置编码 configuration.setDefaultEncoding("utf-8"); //设置模板加载文件夹(模板路径) String pathName = ConstantFactory.me().getFilePath()+"templates"+File.separator; configuration.setDirectoryForTemplateLoading(new File(pathName)); return configuration; } /** * 获取模板字符串 * @param dataMap 参数 * @param templateName 模板名称 * @return */ public static String getFreemarkerContent(Map dataMap, String templateName) { String result = ""; try { Configuration configuration = getConfiguration(); //获取模板 Template template = configuration.getTemplate(templateName,"utf-8"); StringWriter swriter = new StringWriter(); //生成文件 template.process(dataMap, swriter); result = swriter.toString(); } catch (Exception e) { e.printStackTrace(); } return result; } /** * 获取模板字符串输入流 * @param dataMap 参数 * @param templateName 模板名称 * @return */ public static ByteArrayInputStream getFreemarkerContentInputStream(Map dataMap, String templateName) { ByteArrayInputStream in = null; try { Configuration configuration = getConfiguration(); //获取模板 Template template = configuration.getTemplate(templateName,"utf-8"); StringWriter swriter = new StringWriter(); //生成文件 template.process(dataMap, swriter); in = new ByteArrayInputStream(swriter.toString().getBytes("utf-8")); } catch (Exception e) { e.printStackTrace(); } return in; } }
word工具类:
生成本地文件时更改outputStream 输出流形式即可
public class WordUtil { private final static String separator = File.separator; private final static String xmlDocument = "document.xml"; private final static String xmlDocumentXmlRels = "document.xml.rels"; private final static String xmlContentTypes = "[Content_Types].xml"; /** * 生成评价数据word文档 * @param outputStream 浏览器输出流 * @param dataMap 填充数据 * @param templateDocxPathName docx模板全路径名称 * @return * @author xWang * @Date 2020-06-17 */ public void createDocx(OutputStream outputStream,Map dataMap, String templateDocxPathName) { try { //获取 document.xml 输入流 ByteArrayInputStream documentInput = FreeMarkerUtil.getFreemarkerContentInputStream(dataMap, xmlDocument); //获取 document.xml.rels 输入流 String xmlDocumentXmlRelsComment = FreeMarkerUtil.getFreemarkerContent(dataMap, xmlDocumentXmlRels); ByteArrayInputStream documentXmlRelsInput = new ByteArrayInputStream(xmlDocumentXmlRelsComment.getBytes("utf-8")); //获取 header1.xml 输入流 //ByteArrayInputStream headerInput = FreeMarkUtils.getFreemarkerContentInputStream(dataMap, xmlHeader, templatePath); //获取 [Content_Types].xml 输入流 ByteArrayInputStream contentTypesInput = FreeMarkerUtil.getFreemarkerContentInputStream(dataMap, xmlContentTypes); //读取 document.xml.rels 文件 并获取rId 与 图片的关系 (如果没有图片 此文件不用编辑直接读取就行了) Document document = DocumentHelper.parseText(xmlDocumentXmlRelsComment); Element rootElt = document.getRootElement(); // 获取根节点 Iterator iter = rootElt.elementIterator();// 获取根节点下的子节点head List<Map> titleList = JSON.parseArray(JSON.toJSONString(dataMap.get("titleList")), Map.class); for (Map<String,Object> map:titleList ) { List<Map> list = JSON.parseArray(JSON.toJSONString(map.get("optionsList")), Map.class); // 遍历Relationships节点 while (iter.hasNext()) { Element recordEle = (Element) iter.next(); String id = recordEle.attribute("Id").getData().toString(); String target = recordEle.attribute("Target").getData().toString(); if (target.indexOf("media") == 0) { for (Map<String, String> picMap : list) { if (target.endsWith(picMap.get("name"))) { picMap.put("rId", id); } } } } } File docxFile = new File(templateDocxPathName); if (!docxFile.exists()) { docxFile.createNewFile(); } ZipFile zipFile = new ZipFile(docxFile); Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries(); ZipOutputStream 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 (next.getName().equals("[Content_Types].xml")) { if (contentTypesInput != null) { while ((len = contentTypesInput.read(buffer)) != -1) { zipout.write(buffer, 0, len); } contentTypesInput.close(); } } else if (next.getName().indexOf("document.xml.rels") > 0) { //写入主数据配置信息 if (documentXmlRelsInput != null) { while ((len = documentXmlRelsInput.read(buffer)) != -1) { zipout.write(buffer, 0, len); } documentXmlRelsInput.close(); } } else if ("word/document.xml".equals(next.getName())) { //写入主数据信息 if (documentInput != null) { while ((len = documentInput.read(buffer)) != -1) { zipout.write(buffer, 0, len); } documentInput.close(); } } else if ("word/header1.xml".equals(next.getName())) { //写入页眉信息 // if (headerInput != null) { // while ((len = headerInput.read(buffer)) != -1) { // zipout.write(buffer, 0, len); // } // headerInput.close(); // } } else { while ((len = is.read(buffer)) != -1) { zipout.write(buffer, 0, len); } is.close(); } } } //写入新图片 for (Map<String,Object> map:titleList ) { List<Map> picList = JSON.parseArray(JSON.toJSONString(map.get("optionsList")), Map.class); len = -1; if (picList != null && !picList.isEmpty()) { for (Map<String, String> pic : picList) { ZipEntry next = new ZipEntry("word" + separator + "media" + separator + pic.get("name")); zipout.putNextEntry(new ZipEntry(next.toString())); InputStream in = new FileInputStream(pic.get("path")); while ((len = in.read(buffer)) != -1) { zipout.write(buffer, 0, len); } in.close(); } } } zipout.close(); } catch (Exception e) { // System.err.println(e.getMessage()); e.getStackTrace(); } }