FreeMarker生成复杂word(包含图片,表格)
Web項(xiàng)目中生成Word文檔的操作屢見不鮮,基于Java的解決方案也是很多的,包括使用Jacob、Apache POI、Java2Word、iText等各種方式,其實(shí)在從Office 2003開始,就可以將Office文檔轉(zhuǎn)換成XML文件,這樣只要將需要填入的內(nèi)容放上${}占位符,就可以使用像FreeMarker這樣的模板引擎將出現(xiàn)占位符的地方替換成真實(shí)數(shù)據(jù),這種方式較之其他的方案要更為簡(jiǎn)單。這個(gè)功能就是由XML+FreeMarker來實(shí)現(xiàn)的,Word從2003開始支持XML格式,大致的步驟:用office2003或者以上的版本編輯好word的樣式,然后另存為xml,將xml翻譯為FreeMarker模板,最后用java來解析FreeMarker模板并輸出Doc。使用Freemarker其實(shí)就只準(zhǔn)備模板和數(shù)據(jù)。
制作resume.ftl模板
首先用office【版本要2003以上,以下的不支持xml格式】編輯文檔的樣式,將需要?jiǎng)討B(tài)填充的內(nèi)容使用Freemarker標(biāo)簽替換,將Word文檔另存為XML格式(注意是另存為,不是直接改擴(kuò)展名)?,建議用Editplus、Notepad++、Sublime等工具打開查看一下,因?yàn)橛械臅r(shí)候你寫的占位符可能會(huì)被拆開,這樣Freemarker就無法處理了。
打開XML文件看看,如果剛才你寫的${qno}、${title}等被xml文件給拆散了,修改一下XML文件就OK了
修改過后另存為resume.ftl模板文件,如下所示:
接下來修改圖片,搜索w:binData 或者 png可以快速定位圖片的位置,圖片已經(jīng)是0-F的字符串了,換成一個(gè)占位符,在將要插入Word文檔的圖片對(duì)象轉(zhuǎn)換成BASE64編碼的字符串,用該字符串替換掉占位符就可以了,示意圖如下所示:
接下來就可以編碼啦:
服務(wù)的代碼:
@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();// 通過循環(huán)將表單參數(shù)放入鍵值對(duì)映射中 /*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\": \"男\(zhòng)",\"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);}// 提示:在調(diào)用工具類生成Word文檔之前應(yīng)當(dāng)檢查所有字段是否完整 // 否則Freemarker的模板殷勤在處理時(shí)可能會(huì)因?yàn)檎也坏街刀鴪?bào)錯(cuò) 這里暫時(shí)忽略這個(gè)步驟了 File file = null;InputStream fin = null;OutputStream out = null;try {// 調(diào)用工具類WordGenerator的createDoc方法生成Word文檔 Map root = new HashMap<String, Object>();root.put("questTitle", "測(cè)試word導(dǎo)出");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");// 設(shè)置瀏覽器以下載的方式處理該文件默認(rèn)名為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]; // 緩沖區(qū) int bytesToRead = -1;// 通過循環(huán)將讀入的Word文件的內(nèi)容輸出到瀏覽器中 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(); // 刪除臨時(shí)文件 }}} 工具類的代碼:
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 鉆石語(yǔ)法 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 {// 這個(gè)地方不能使用FileWriter因?yàn)樾枰付ň幋a類型否則生成的Word文檔會(huì)因?yàn)橛袩o法識(shí)別的編碼而無法打開 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;}//將圖片轉(zhuǎn)換成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="測(cè)試word導(dǎo)出"/></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中有這個(gè)類,但是卻無法直接使用,需要修改訪問權(quán)限,在Eclipse中可以這樣修改。
在項(xiàng)目上點(diǎn)右鍵選擇Properties菜單項(xiàng)進(jìn)入如下圖所示的界面:
這樣設(shè)置后就可以使用BASE64Encoder類了,在項(xiàng)目中調(diào)用getImageString 方法指定要插入的圖片的完整文件名(帶路徑的文件名),該方法返回的字符串就是將圖片處理成BASE64編碼后的字符串。
到這里就可以導(dǎo)出一個(gè)復(fù)雜的帶表格,圖片的wors文檔啦。導(dǎo)出來就是這樣的,如下圖:
總結(jié)
以上是生活随笔為你收集整理的FreeMarker生成复杂word(包含图片,表格)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ***Pregel
- 下一篇: 图片处理--羽化特效