源码地址:
github:
gitosc:
背景
最近用java开发一个中车项目管理系统,里面有一个维修单word导出功能。
可用方案
在网上查找资料,总结出两种比较可行的方案。
(1) 制作word模板,导出成mht文件(单页面网页格式),然后往模板里渲染数据,最终生成word文档。
(2) 制作word模板,导出成xml文件,然后往模板里渲染数据,最终生成word文档。
两种都是采用模板的思想,比用poi去组织word格式简单的很多很多。不同点在于导出文件的格式不同,一个是mht文件,一个是xml文件。考虑到本项目中的维修单个别字段采用了富文本编辑器实现,保存进数据库中是html格式的字符串,所以采用第一种方案,即通过mht文件来实现。
实现思路
由于我们是要用word来解析带图片的富文本(说白了就是解析一段html,当然这段html代码是包含img标签:图片),so...传统的word模板导出(word另存为xml,在修改后缀为ftl)是行不通的,因为他解析不了html代码(至少我目前没有找到这方便的解决方案,大神勿喷~),这样的话我就要换用一种模板来处理这个模板:word模板另存为mht格式,再修改后缀为ftl。剩下的就是后台操作了,找到你存富文本的字段(html代码)获取里面的img标签,找到图片,并把图片解析为base64字符串,填充到我们只做的模板上就ok了。
实现步骤及注意事项
创建mht模板
根据模板引擎的语法规则填入占位符制作word模板,保存为mht文件。eg:
然后打开mht文件,在mht文件中插入图片资源的base64及xml 的href引用的占位。如下图:
${imagesBase64String} 和 ${imagesXmlHrefString}这两个是我们手动加进去的,这也是解析富文本的关键所在。
由于mht文件是采用的是“us-ascii”编码,属性后面都必须带有3D前缀。所以包含html内容的需进行一下替换操作。
全文检索gb2312把他改成utf-8,同时需要加上3D前缀,对应着格式来改 一般就这两种:
<meta http-equiv=3DContent-Type content=3D"text/html; charset=3Dutf-8">
Content-Type: text/html; charset=3D"utf-8"
处理数据
一般的属性数据组织起来简单,无非就是从数据库中获取,处理也简单。现在模拟一个富文本的数据
//用map存放数据 HashMapdata = new HashMap (); //创建富文本 StringBuilder sb = new StringBuilder(); sb.append(" "); sb.append(" "); sb.append(" wesley 演示 导出富文本!@@#######¥¥%%%%………………&&&**~~~~~~&&&&&&&&、、、、、、、、"); sb.append(" ----多图分割线---"); sb.append(" "); sb.append(" 中国梦,幸福梦!"); sb.append(""); RichObject richObject = new RichObject(); richObject.setHtml(sb.toString()); //--------------------此处可以spring配置文件配置,也可以直接读取属性文件获取------------------ //从mht文件中找 richObject.setDocSrcLocationPrex("file:///C:/268D4AA4"); //这里是从mht中获取的资源文件所在的文件夹 richObject.setDocSrcParent("word.files"); //资源文件夹名字 richObject.setNextPartId("01D2C8DD.BC13AF60"); //下一部分的ID //以下三个属性字段我也不是很懂 查询网上是这么用的 不过根据字段应该大致能猜到是做什么用的。 richObject.setShapeidPrex("_x56fe__x7247__x0020"); richObject.setTypeid("#_x0000_t75"); richObject.setSpidPrex("_x0000_i"); richObject.setWebAppliction(false); //这里封装了一个Hnadler处理对象,来处理数据。 RichHtmlHandler richHtmlHandler = WordGeneratorWithFreemarker.createRichHtmlHandler(richObject); ListrichHtmlHandlerList = new ArrayList (); richHtmlHandlerList.add(richHtmlHandler); //这里就是我们刚才加的两个字段,也是我们富文本文件处理的关键两个字段 data.put("imagesXmlHrefString", WordGeneratorWithFreemarker.getXmlImgHref(richHtmlHandlerList));// logger.debug("------imagesXmlHrefString-------"+WordGeneratorWithFreemarker.getXmlImgHref(richHtmlHandlerList)); data.put("imagesBase64String", WordGeneratorWithFreemarker.getImagesBase64String(richHtmlHandlerList)); logger.debug("------imagesBase64String-------"+WordGeneratorWithFreemarker.getImagesBase64String(richHtmlHandlerList)); data.put("name", "wesley"); data.put("datetime","2017-05-10"); data.put("title","演示demo"); data.put("context1", richHtmlHandler.getHandledDocBodyBlock()); data.put("context2", richHtmlHandler.getHandledDocBodyBlock()); data.put("context3", richHtmlHandler.getHandledDocBodyBlock()); data.put("context4", richHtmlHandler.getHandledDocBodyBlock()); data.put("context5", richHtmlHandler.getHandledDocBodyBlock()); data.put("context6", richHtmlHandler.getHandledDocBodyBlock());
渲染模板
String docFilePath = "w:\\temp_by_wesley.doc";//目标文件 String templatePath = Class.class.getResource("/ftl").getPath(); templatePath = java.net.URLDecoder.decode(templatePath,"utf-8");//这里我的路径有空格添加此处理 logger.debug("------templatePath-------"+templatePath); WordGeneratorWithFreemarker.createDoc(templatePath,"word.ftl",data,docFilePath); /** * 创建doc文件 * [@param](https://my.oschina.net/u/2303379) templatePath 模板所在路径 xxx/xxx/template * [@param](https://my.oschina.net/u/2303379) templateName 模板名字 xxx.ftl * [@param](https://my.oschina.net/u/2303379) dataMap 数据集合 * [@param](https://my.oschina.net/u/2303379) outPath 输出文件路径 xxx/xxx/xxx.doc */ public static void createDoc(String templatePath, String templateName, MapdataMap, String outPath) throws Exception{ logger.debug("WordGeneratorWithFreemarker createDoc()"); Freemarker.fprint(templatePath,templateName,dataMap,outPath); } /** * 基于文件的输出 * * @param templatePath 模板所在路径 xxx/xxx/template * @param templateName 模板名字 xxx.ftl * @param dataMap 数据集合 * @param outPath 输出文件路径 xxx/xxx/xxx.doc */ public static void fprint(String templatePath, String templateName, Map root, String outPath) { logger.debug("Freemarker fprint file"); try { getInstance(templatePath); Template template = getTemplate(templateName); Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(outPath)), "UTF-8")); template.process(root, out); out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (TemplateException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } }
效果展示
到此富文本导出就完成了。