Java實現(xiàn)用Freemarker完美導(dǎo)出word文檔(帶圖片)
前言
最近在項目中,因客戶要求,將頁面內(nèi)容(如合同協(xié)議)導(dǎo)出成word,在網(wǎng)上翻了好多,感覺太亂了,不過最后還是較好解決了這個問題。
準(zhǔn)備材料
1.word原件 2.編輯器(推薦Firstobject free XML editor)
實現(xiàn)步驟
1.用Microsoft Office Word打開word原件;
2.把需要動態(tài)修改的內(nèi)容替換成***,如果有圖片,盡量選擇較小的圖片幾十K左右,并調(diào)整好位置;
3.另存為,選擇保存類型Word 2003 XML 文檔(*.xml)【這里說一下為什么用Microsoft Office Word打開且要保存為Word 2003XML,本人親測,用WPS找不到Word 2003XML選項,如果保存為Word XML,會有兼容問題,避免出現(xiàn)導(dǎo)出的word文檔不能用Word 2003打開的問題】;
4.用Firstobject free XML editor打開文件,選擇Tools下的Indent【或者按快捷鍵F8】格式化文件內(nèi)容。左邊是文檔結(jié)構(gòu),右邊是文檔內(nèi)容;
5. 將文檔內(nèi)容中需要動態(tài)修改內(nèi)容的地方,換成freemarker的標(biāo)識。其實就是Map<String, Object>中key,如${landName};
6.在加入了圖片占位的地方,會看到一片base64編碼后的代碼,把base64替換成${image},也就是Map<String, Object>中key,值必須要處理成base64;
代碼如:<w:binData w:name="wordml://自定義.png" xml:space="preserve">${image}</w:binData>
注意:“>${image}<”這尖括號中間不能加任何其他的諸如空格,tab,換行等符號。
如果需要循環(huán),則使用:<#list maps as map></#list> maps是Map<String, Object>中key,值為數(shù)組,map為自定義;
7. 標(biāo)識替換完之后,模板就弄完了,另存為.ftl后綴文件即可。注意:一定不要用word打開ftl模板文件,否則xml內(nèi)容會發(fā)生變化,導(dǎo)致前面的工作白做了。
代碼實現(xiàn)
工具類WordUtils.Java
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.URLEncoder; import java.util.Date; import java.util.Map; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import freemarker.template.Configuration; import freemarker.template.Template; public class WordUtils { //配置信息,代碼本身寫的還是很可讀的,就不過多注解了 private static Configuration configuration = null; //這里注意的是利用WordUtils的類加載器動態(tài)獲得模板文件的位置 // private static final String templateFolder = WordUtils.class.getClassLoader().getResource("../../").getPath() + "WEB-INF/templetes/"; private static final String templateFolder = "H:/我的項目/lm/lm/web/src/main/webapp/WEB-INF/templates"; static { configuration = new Configuration(); configuration.setDefaultEncoding("utf-8"); try { configuration.setDirectoryForTemplateLoading(new File(templateFolder)); } catch (IOException e) { e.printStackTrace(); } } private WordUtils() { throw new AssertionError(); } public static void exportMillCertificateWord(HttpServletRequest request, HttpServletResponse response, Map map,String title,String ftlFile) throws IOException { Template freemarkerTemplate = configuration.getTemplate(ftlFile); File file = null; InputStream fin = null; ServletOutputStream out = null; try { // 調(diào)用工具類的createDoc方法生成Word文檔 file = createDoc(map,freemarkerTemplate); fin = new FileInputStream(file); response.setCharacterEncoding("utf-8"); response.setContentType("application/msword"); // 設(shè)置瀏覽器以下載的方式處理該文件名 String fileName = title+DateUtil.formatDateDetailTime(new Date()) + ".doc"; response.setHeader("Content-Disposition", "attachment;filename=" .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8")))); out = response.getOutputStream(); 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(); // 刪除臨時文件 } } private static File createDoc(Map<?, ?> dataMap, Template template) { String name = "sellPlan.doc"; File f = new File(name); Template t = template; try { // 這個地方不能使用FileWriter因為需要指定編碼類型否則生成的Word文檔會因為有無法識別的編碼而無法打開 Writer w = new OutputStreamWriter(new FileOutputStream(f), "utf-8"); t.process(dataMap, w); w.close(); } catch (Exception ex) { ex.printStackTrace(); throw new RuntimeException(ex); } return f; } }
Action
@RequestMapping("/exportSellPlan") public @ResponseBody void exportSellPlan(Long id){ Calendar calendar = Calendar.getInstance();// 取當(dāng)前日期。 if(id!=null){ SellPlan plan=sellService.getSellPlanInfo(id); //獲得數(shù)據(jù) Map<String, Object> map = new HashMap<String, Object>(); map.put("bYear", plan.getBusinessYear()!=null?plan.getBusinessYear():""); map.put("lYear", plan.getLiveYear()!=null?plan.getLiveYear():""); map.put("leader",plan.getLeader()!=null?plan.getLeader():""); map.put("phone", plan.getPhone()!=null?plan.getPhone():""); map.put("curYear", calendar.get(Calendar.YEAR)+""); map.put("image", getImageBase(plan.getPositionImage())); try { WordUtils.exportMillCertificateWord(getRequest(),getResponse(),map,"方案","sellPlan.ftl"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Base64處理
//獲得圖片的base64碼 @SuppressWarnings("deprecation") public String getImageBase(String src) { if(src==null||src==""){ return ""; } File file = new File(getRequest().getRealPath("/")+src.replace(getRequest().getContextPath(), "")); if(!file.exists()) { return ""; } InputStream in = null; byte[] data = null; try { in = new FileInputStream(file); } catch (FileNotFoundException e1) { e1.printStackTrace(); } try { data = new byte[in.available()]; in.read(data); in.close(); } catch (IOException e) { e.printStackTrace(); } BASE64Encoder encoder = new BASE64Encoder(); return encoder.encode(data); }
Javascript
window.location.href="<%=path%>/exportSellPlan?id=" rel="external nofollow" + id;
結(jié)束語
如果對Freemarker標(biāo)簽不熟的,可以在網(wǎng)上先學(xué)習(xí)下,了解文檔結(jié)構(gòu)。
相關(guān)鏈接
Firstobject free XML editor下載地址:http://www.firstobject.com/dn_editor.htm
freemarker 官網(wǎng):http://freemarker.org/
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- java Apache poi 對word doc文件進(jìn)行讀寫操作
- java導(dǎo)出生成word的簡單方法
- java實現(xiàn)word文件轉(zhuǎn)html文件
- java實現(xiàn)在線預(yù)覽--poi實現(xiàn)word、excel、ppt轉(zhuǎn)html的方法
- java使用poi讀取ppt文件和poi讀取excel、word示例
- 實例講解Java讀取一般文本文件和word文檔的方法
- 使用Java讀取Word文件的簡單例子分享
- java使用POI實現(xiàn)html和word相互轉(zhuǎn)換
- Java使用poi將word轉(zhuǎn)換為html
- Java實現(xiàn)向Word文檔添加文檔屬性
相關(guān)文章
SpringBoot 關(guān)于Feign的超時時間配置操作
這篇文章主要介紹了SpringBoot 關(guān)于Feign的超時時間配置操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09SpringMVC使用@ExceptionHandler注解在Controller中處理異常
這篇文章主要為大家介紹了SpringMVC使用@ExceptionHandler注解在Controller中處理異常示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10springboot的類加載器(org.springframework.boot.loader)過程詳解
這篇文章主要介紹了springboot的類加載器(org.springframework.boot.loader),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11如何用java程序(JSch)運行遠(yuǎn)程linux主機(jī)上的shell腳本
這篇文章主要介紹了如何用java程序(JSch)運行遠(yuǎn)程linux主機(jī)上的shell腳本,幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-08-08SpringBoot?Starter自定義全局加解密組件的詳細(xì)流程
SpringBoot?Starter作用將一組相關(guān)的依賴打包,簡化項目的配置和初始化過程,通過特定的Starter開發(fā)者可以快速的實現(xiàn)特定功能模塊的開發(fā)和擴(kuò)展,本文給大家介紹了SpringBoot?Starter自定義全局加解密組件的詳細(xì)流程,需要的朋友可以參考下2024-02-02spring-boot-maven-plugin引入出現(xiàn)爆紅(已解決)
這篇文章主要介紹了spring-boot-maven-plugin引入出現(xiàn)爆紅(已解決),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03