Web项目中生成Word文档的操作屡见不鲜,基于Java的解决方案也是很多的,包括使用Jacob、Apache POI、Java2Word、iText等各种方式,其实在从Office 2003开始,就可以将Office文档转换成XML文件,这样只要将需要填入的内容放上${}占位符,就可以使用像FreeMarker这样的模板引擎将出现占位符的地方替换成真实数据,这种方式较之其他的方案要更为简单。这个功能就是由XML+FreeMarker来实现的,Word从2003开始支持XML格式,大致的步骤:用office2003或者以上的版本编辑好word的样式,然后另存为xml,将xml翻译为FreeMarker模板,最后用java来解析FreeMarker模板并输出Doc。使用Freemarker其实就只准备模板和数据。

制作resume.ftl模板

首先用office【版本要2003以上,以下的不支持xml格式】编辑文档的样式,将需要动态填充的内容使用Freemarker标签替换,将Word文档另存为XML格式(注意是另存为,不是直接改扩展名) ,建议用Editplus、Notepad++、Sublime等工具打开查看一下,因为有的时候你写的占位符可能会被拆开,这样Freemarker就无法处理了。

打开XML文件看看,如果刚才你写的${qno}、${title}等被xml文件给拆散了,修改一下XML文件就OK了

修改过后另存为resume.ftl模板文件,如下所示:

接下来修改图片,搜索w:binData 或者 png可以快速定位图片的位置,图片已经是0-F的字符串了,换成一个占位符,在将要插入Word文档的图片对象转换成BASE64编码的字符串,用该字符串替换掉占位符就可以了,示意图如下所示:

接下来就可以编码啦:

服务的代码:

@RequestMapping(value = "/demo/word", method = RequestMethod.POST)public void word(@RequestParam(value = "image", required = false) MultipartFile image, ModelMap model, HttpServletRequest req, HttpServletResponse resp) throws IOException {req.setCharacterEncoding("utf-8");Map<String, Object> map = null;Enumeration<String> paramNames = req.getParameterNames();// 通过循环将表单参数放入键值对映射中  /*while (paramNames.hasMoreElements()) {String key = paramNames.nextElement();String value = req.getParameter(key);map.put(key, value);}*/InputStream in = image.getInputStream();String reportImage = WordGenerator.getImageString(in);String jsonStr = "{\"exam\": [{\"ecnt\": 4,\"eid\": \"25\",\"options\": [{\"choice\": \"3\",\"option\": \"男\",\"rowratio\": \"75.00%\"}, {\"choice\": \"1\",\"option\": \"女\",\"rowratio\": \"25.00%\"}],\"qno\": \"3\",\"title\": \"你的性别是\",\"types\": \"单选题\"}]}";JSONObject jsonObject = JSONObject.parseObject(jsonStr);List exams = jsonObject.getJSONArray("exam");List<Map<String, Object>> arrayList = new ArrayList<Map<String, Object>>();for (int i = 0; i < exams.size(); i++) {map = new HashMap<String, Object>();JSONObject exam = (JSONObject) exams.get(i);map.put("ecnt", exam.get("ecnt"));map.put("eid", exam.get("eid"));map.put("qno", exam.get("qno"));map.put("title", exam.get("title"));map.put("types", exam.get("types"));map.put("reportImage", reportImage);List<Map<String, String>> options = (List) exam.getJSONArray("options");List<Map<String, Object>> optionList = new ArrayList<Map<String, Object>>();for (Map tempMap : options) {Map optionMap = new HashMap<String, Object>();optionMap.put("choice", tempMap.get("choice"));optionMap.put("option", tempMap.get("option"));optionMap.put("rowratio", tempMap.get("rowratio"));optionList.add(optionMap);}map.put("options", optionList);arrayList.add(map);}// 提示:在调用工具类生成Word文档之前应当检查所有字段是否完整  // 否则Freemarker的模板殷勤在处理时可能会因为找不到值而报错 这里暂时忽略这个步骤了  File file = null;InputStream fin = null;OutputStream out = null;try {// 调用工具类WordGenerator的createDoc方法生成Word文档  Map root = new HashMap<String, Object>();root.put("questTitle", "测试word导出");root.put("exams", arrayList);file = WordGenerator.createDoc(root, "resume");fin = new FileInputStream(file);resp.setCharacterEncoding("utf-8");resp.setContentType("application/ms-word;charset=utf-8");// 设置浏览器以下载的方式处理该文件默认名为resume.doc  resp.addHeader("Content-Disposition", "attachment;filename=resume.doc");out = resp.getOutputStream();// 写出流信息/*   while ((len = fs.read()) != -1) {os.write(len);}*/byte[] buffer = new byte[512]; // 缓冲区  int bytesToRead = -1;// 通过循环将读入的Word文件的内容输出到浏览器中  while ((bytesToRead = fin.read(buffer)) != -1) {out.write(buffer, 0, bytesToRead);}} finally {if (fin != null) {fin.close();}if (out != null) {out.close();}if (file != null) {file.delete(); // 删除临时文件  }}}

工具类的代码:

package cn.comm.util;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import freemarker.template.Configuration;
import freemarker.template.Template;
import sun.misc.BASE64Encoder;public class WordGenerator {private static Configuration configuration = null;private static Map<String, Template> allTemplates = null;static {configuration = new Configuration();configuration.setDefaultEncoding("utf-8");configuration.setClassForTemplateLoading(WordGenerator.class, "/cn/comm/ftl");allTemplates = new HashMap<>(); // Java 7 钻石语法  try {allTemplates.put("resume", configuration.getTemplate("resume.ftl"));} catch (IOException e) {e.printStackTrace();throw new RuntimeException(e);}}private WordGenerator() {throw new AssertionError();}public static File createDoc(Map<?, ?> dataMap, String type) {String name = "temp" + (int) (Math.random() * 100000) + ".doc";File f = new File(name);Template t = allTemplates.get(type);try {// 这个地方不能使用FileWriter因为需要指定编码类型否则生成的Word文档会因为有无法识别的编码而无法打开  Writer w = new OutputStreamWriter(new FileOutputStream(f), "utf-8");t.process(dataMap, w);w.flush();w.close();} catch (Exception ex) {ex.printStackTrace();throw new RuntimeException(ex);}return f;}//将图片转换成BASE64字符串public static String getImageString(InputStream in) throws IOException {//InputStream in = null;  byte[] data = null;try {// in = new FileInputStream(filename);  data = new byte[in.available()];in.read(data);in.close();} catch (IOException e) {throw e;} finally {if (in != null)in.close();}BASE64Encoder encoder = new BASE64Encoder();return data != null ? encoder.encode(data) : "";}}

jsp代码:

<%@ page pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Document</title>
<style type="text/css">  * { font-family: "微软雅黑"; }  .textField { border:none; border-bottom: 1px solid gray; text-align: center; }  #file { border:1px solid black; width: 80%; margin:0 auto; }  h1 input{ font-size:72px; }  td textarea { font-size: 14px; }  .key { width:125px; font-size:20px; }
</style>
</head>
<body>  <form action="/mybase/demo/word" method="post" enctype="multipart/form-data">  <div id="file" align="center">  <h1><input type="text" name="title" class="textField" value="测试word导出"/></h1>  <hr/>  <table>  <tr>  <td colspan="4">  <input type="file" name="image" /> </td>  </tr>  </table>  </div>  <div align="center" style="margin-top:15px;">  <input type="submit" value="保存Word文档" />  </div>  </form>
</body>
</html>  

注意:这里使用的BASE64Encoder类在sun.misc包下,rt.jar中有这个类,但是却无法直接使用,需要修改访问权限,在Eclipse中可以这样修改。

在项目上点右键选择Properties菜单项进入如下图所示的界面:


这样设置后就可以使用BASE64Encoder类了,在项目中调用getImageString 方法指定要插入的图片的完整文件名(带路径的文件名),该方法返回的字符串就是将图片处理成BASE64编码后的字符串。

到这里就可以导出一个复杂的带表格,图片的wors文档啦。导出来就是这样的,如下图:

FreeMarker生成复杂word(包含图片,表格)相关推荐

  1. freemarker模板导出word循环图片表格详细教程

    前言:表哥之前已经过一篇freemarker模板导出带表格word详细教程  freemarker模板导出带表格word详细教程_Java大表哥的博客-CSDN博客,为什么现在又要写一篇呢. 因为我这 ...

  2. 纯前端JS导出Word包含图片

    纯前端JS导出Word包含图片 纯前端JS导出Word文档包含图片 Word导出方式 依赖 实现代码 纯前端JS导出Word文档包含图片 在做项目中,遇到一个需求,是将后台返回的属性以及URL图片导出 ...

  3. java生成word 带表格_【java】Freemarker 动态生成word(带图片表格)

    1.添加freemarker.jar 到java项目. 2.新建word文档. 3.将文档另存为xml 格式. 4.将xml格式化后打开编辑(最好用notepad,有格式),找到需要替换的内容,将内容 ...

  4. java生成word 可变表格_【java】Freemarker 动态生成word(带图片表格)

    1.添加freemarker.jar 到java项目. 2.新建word文档. 3.将文档另存为xml 格式. 4.将xml格式化后打开编辑(最好用notepad,有格式),找到需要替换的内容,将内容 ...

  5. xwpftemplate的时间设置_java poi设置生成的word的图片为上下型环绕以及其位置

    问题描述 在使用poi-tl word模版工具时,发现生成的文档中,图片格式为嵌入型,有的图片甚至被表格遮挡一半.而自己想要的图片格式为上下型环绕,并且图片需要居中. 问题分析 poi-tl渲染图片, ...

  6. poi 拆分带图片的word_java poi设置生成的word的图片为上下型环绕以及其位置的实现...

    问题描述 在使用poi-tl word模版工具时,发现生成的文档中,图片格式为嵌入型,有的图片甚至被表格遮挡一半.而自己想要的图片格式为上下型环绕,并且图片需要居中. 问题分析 poi-tl渲染图片, ...

  7. Freemarker 生成work文档图片

    一.简介 通过freemarker生成work文档,其实原理很简单,先生成一个work的xm模板,然后替换文档中的内容为自己需要的内容就可以了. 二.生成work文档 1.生成ftl模板 把自己要修改 ...

  8. freemarker生成的word文件打不开

    用eclipse写了一个程序, 使用freemaker生成word文件,生成的word文件可以打开.但是将程序打成jar包后,运行jar包,生成的word文件就打不开了.截图: 然后我用Editplu ...

  9. 通过freemarker模板,使用jsoup将html转换为word,包含图片

    实现思路: 一.制作模板 1.在word中无非三种格式,文字.图片.表格,目前我所涉及到的业务中只有这三个,其他情况没涉及到没研究,首先需要准备一个模板文件,即xml格式的模板,通过打开一个空白的wo ...

最新文章

  1. SpringMVC调用接口一直乱码
  2. 满有趣的屏幕快捷键;)
  3. React Native 常见问题集合
  4. linq.js的用法
  5. 前端学习(1535):单一页面的优势
  6. oc 画一个圆弧_SolidWorks一步扫描特征,就可以画出一个螺母,你有思路吗
  7. Educoder 机器学习 SMO进行优化 第1关:SMO高效优化算法
  8. 适用于dev中c语言的图形库,Dev-C++中使用EGE图形库
  9. 【python】urlencode、quote、unquote
  10. CSU oj 1681 Adjoin(dfs求树上最长路径)(搜索)
  11. Spring boot与Spring cloud
  12. u8系统更改了服务器,用友u8服务器地址修改
  13. Docker 常用镜像
  14. 震撼心灵、洗礼灵魂--【经典的大师参禅的禅语】
  15. 手机备份到底备份什么
  16. 主张:男人害怕的30种女人(zt)
  17. java 线性回归_Java实现一元线性回归
  18. 李笑来——彻底戒掉抱怨(阅读总结)
  19. 苹果微软小米华为,创新四重奏?
  20. Jdbc系列八:批量处理

热门文章

  1. 逆势获投1亿,火眼金睛下的云适配BRaaS新模式引关注
  2. HDU_6078 Wavel Sequence
  3. Docker-核心篇(1)-CentOS7安装Docker社区版
  4. 【深度学习之美】LSTM长短记,长序依赖可追忆(入门系列之十四)
  5. 深大uooc学术道德与学术规范教育第十一章
  6. 深大uooc学术道德与学术规范教育第七章
  7. 为知笔记(PC端) 康奈尔模板各栏间距调整
  8. 计算 m 的 n 次幂
  9. python按字母顺序排列_如何在Python中按字母顺序排列文件?
  10. java实现“进制转换-在线工具”