源码地址:
github:https://github.com/wesley5201314/java-export-word-plugin
gitosc:https://git.oschina.net/zhengweishan/export_word_plugin
背景
最近用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存放数据
HashMap<String, Object> data = new HashMap<String, Object>();
//创建富文本
StringBuilder sb = new StringBuilder();
sb.append("<div>");
sb.append("<img style='height:100px;width:200px;display:block;' src='w:\\2.jpg' />");
sb.append("</br><span>wesley 演示 导出富文本!@@#######¥¥%%%%………………&&&**~~~~~~&&&&&&&&、、、、、、、、</span>");
sb.append("</br><span>----多图分割线---</span>");
sb.append("</br><img style='height:100px;width:200px;display:block;' src='w:\\1.jpg' />");
sb.append("</br><span>中国梦,幸福梦!</span>");
sb.append("</div>");
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);
List<RichHtmlHandler> richHtmlHandlerList = new ArrayList<RichHtmlHandler>();
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, Map<String, Object> dataMap, 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<String, Object> 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);
}
}
效果展示
到此富文本导出就完成了。