<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          我是如何使用freemarker生成Word文件的?

          共 4959字,需瀏覽 10分鐘

           ·

          2020-09-15 02:23

          背景

          一天,產(chǎn)品經(jīng)理遞給我了一份word報(bào)告,我定睛一看

          這個(gè)文檔有大大小小的標(biāo)題層級(jí),還有排版好的段落、各種一目了然的餅圖、走勢(shì)圖,當(dāng)然還少不了顏色循環(huán)交替的報(bào)表。精致程度不亞于小明同學(xué)的學(xué)習(xí)報(bào)告。

          準(zhǔn)備

          魯迅:身為一名Java程序員,任何時(shí)候都不要忘記站在巨人的肩膀上。

          通過(guò)某歌搜索關(guān)鍵詞:java+word+導(dǎo)出,我立馬得出了很多成熟的方案,通過(guò)橫向、縱向比較,再結(jié)合本次報(bào)告樣式比較多、用戶可靈活選擇不同模塊導(dǎo)出的特點(diǎn),最終,我決定使用Freemarker 動(dòng)態(tài)替換模版數(shù)據(jù)來(lái)導(dǎo)出word文檔。至于導(dǎo)出文檔的最終格式,有兩種選擇:



          那到底使用doc還是docx格式的文檔?

          每當(dāng)人生當(dāng)中每次面臨選擇我都很慎重。最終我選擇使用docx格式(原因文末會(huì)講),但是為了讓大家有更多的選擇,滿足更多的業(yè)務(wù)場(chǎng)景,借此機(jī)會(huì),小明會(huì)分別給大家介紹使用freemarker導(dǎo)出word文檔種格式的方式。

          思路

          FreeMarker是一個(gè)基于Java的模板引擎,最初專注于使用MVC軟件架構(gòu)生成動(dòng)態(tài)網(wǎng)頁(yè)。但是,它是一個(gè)通用的模板引擎,不依賴于servlets或HTTP或HTML,因此它通常還用于生成源代碼,配置文件或電子郵件。

          此時(shí),我們用它動(dòng)態(tài)生成xml文件,進(jìn)而導(dǎo)出word文檔。


          整體流程如下:

          準(zhǔn)備

          • WPS

          由金山軟件股份有限公司發(fā)布,用于辦公軟件最常用的文字編輯、表格、演示稿等功能。

          對(duì),就是這個(gè)國(guó)產(chǎn)的辦公軟件。除了它強(qiáng)制登錄以外,我也是第一次發(fā)現(xiàn)在導(dǎo)出文檔這件事上,它如多年好友般友好。(word解析后的xml文件閱讀性很強(qiáng),一般人我不告訴他)

          • 開(kāi)發(fā)工具(IDEA、Visual Studio Code等) 你喜歡的,順手的,就是最好的。

          實(shí)現(xiàn)

          集成Freemarker模版引擎

          本次項(xiàng)目使用的框架依舊是Springboot,這個(gè)框架在集成各個(gè)組件表現(xiàn)都很便捷,不再贅述,這次集成Freemarker也不例外。

          • 首先我們?cè)陧?xiàng)目中增添依賴spring-boot-starter-freemarkerpom.xml文件如下所示:
          <dependency>
          ????<groupId>org.springframework.bootgroupId>
          ????<artifactId>spring-boot-starter-freemarkerartifactId>
          dependency>
          • 按照默認(rèn)約定,我們可以在resources下創(chuàng)建一個(gè)templates文件夾(查看FreeMarkerProperties源碼可以發(fā)現(xiàn)默認(rèn)目錄就是這個(gè)),用于存放模版文檔。
          • application.yml增加配置
          spring:
          ??freemarker:
          ????template-loader-path:?classpath:/templates
          ????cache:?false?#?開(kāi)發(fā)環(huán)境緩存關(guān)閉
          ????suffix:?xml
          ????charset:?UTF-8

          生成doc格式的文檔

          這里先拿使用freemarker導(dǎo)出doc格式的word文檔舉例。

          • 首先將docxTemplate.docx(調(diào)整好樣式的模版文檔)另存為WORD 2003 XML文檔(*.xml)






            此處命名為docTemplete.xml,使用編輯工具首次打開(kāi)時(shí),會(huì)發(fā)現(xiàn)這個(gè)文檔里面是壓縮的xml,因此我們首先需要格式化一下。

          注意:如果你使用的是Visual Studio Code開(kāi)發(fā)工具,一定要檢查你所使用的xml格式化插件,是否會(huì)優(yōu)化你的xml標(biāo)簽 。比如:會(huì)變成。使用Visual Studio Code的同學(xué),oh my god ! 小明在這里推薦大家使用這個(gè)插件:XML Language Support by Red Hat

          • 現(xiàn)在,我們就使用freemarker語(yǔ)法編輯docTemplete.xml,比如使用占位符${}替換當(dāng)前文檔中的文本,以達(dá)到動(dòng)態(tài)生成文本的目的,直接上代碼。
          public?static?Configuration?getConfiguration(){
          ????????//創(chuàng)建配置實(shí)例
          ????????Configuration?configuration?=?new?Configuration(Configuration.VERSION_2_3_28);
          ????????//設(shè)置編碼
          ????????configuration.setDefaultEncoding("utf-8");
          ????????configuration.setClassForTemplateLoading(WordUtil.class,?"/templates");
          ????????return?configuration;
          }

          ????/**
          ?????*?生成doc文件
          ?????*
          ?????*?@param?ftlFileName?模板ftl文件的名稱
          ?????*?@param?params??????動(dòng)態(tài)傳入的數(shù)據(jù)參數(shù)
          ?????*?@param?outFilePath?生成的最終doc文件的保存完整路徑
          ?????*/
          ????public?void?ftlToDoc(String?ftlFileName,?Map?params,?String?outFilePath)?{
          ????????try?{
          ????????????/**?加載模板文件?**/
          ????????????Template?template?=?configuration.getTemplate(ftlFileName);
          ????????????/**?指定輸出word文件的路徑?**/
          ????????????File?docFile?=?new?File(outFilePath);
          ????????????FileOutputStream?fos?=?new?FileOutputStream(docFile);
          ????????????Writer?bufferedWriter?=?new?BufferedWriter(new?OutputStreamWriter(fos,?"utf-8"),?10240);
          ????????????template.process(params,?bufferedWriter);
          ????????????if?(bufferedWriter?!=?null)?{
          ????????????????bufferedWriter.close();
          ????????????}
          ????????}?catch?(TemplateException?e)?{
          ????????????e.printStackTrace();
          ????????}?catch?(IOException?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????}

          生成docx格式的文檔

          高能預(yù)警! 在成功使用Freemarker動(dòng)態(tài)導(dǎo)出doc格式的文檔之后,相信大家和我的心情一樣非常激動(dòng)。但以上操作只是一個(gè)小鋪墊,接下來(lái)我們來(lái)看看如何實(shí)現(xiàn)docx格式的文檔導(dǎo)出,小明相信一定會(huì)讓各位看官大跌眼鏡!不,大開(kāi)眼界!首先,告訴大家一個(gè)秘密:docx格式的文檔其實(shí)是一個(gè)ZIP格式的壓縮文件哦! 什么?你不信?驗(yàn)證如下:

          • windows的小伙伴 將docx文檔修改為ZIP格式(修改.docx后綴名為.zip),然后通過(guò)解壓工具解壓。
          • MacOS的小伙伴 直接使用unzip命令解壓word文檔,解壓過(guò)后我們會(huì)發(fā)現(xiàn)該文檔其實(shí)還有自己的目錄結(jié)構(gòu)!




            當(dāng)然,這么多文件我們不必一一知悉,只需關(guān)注小明紅線標(biāo)注的文件和目錄即可:
          • document.xml文件用于存放核心數(shù)據(jù),文字,表格,圖片引用等
          • media目錄用于存放所有文檔的圖片
          • _rels目錄下的document.xml.rels里存放的是配置信息,比如圖片引用關(guān)系,即在document.xml中引用id對(duì)應(yīng)media中的哪個(gè)圖片。
          • 獲取zip里的document.xml文檔以及_rels文件夾下的document.xml.rels文檔
          • 顯而易見(jiàn),如果我們要想根據(jù)數(shù)據(jù)動(dòng)態(tài)導(dǎo)出不同的word文檔,只需要:通過(guò)freemarker將本次數(shù)據(jù)填充到document.xml中,并將圖片配置信息填充至document.xml.rels文檔里,再用文件流把本次圖片寫入到media目錄下替換已經(jīng)存在的圖片,最后把填充過(guò)內(nèi)容的document.xml、document.xml.rels以及media用流的方式寫入zip即可輸出docx文檔!上代碼。

          好吧,限于篇幅,代碼見(jiàn)文末 Github地址

          問(wèn)題及解決方案

          當(dāng)然,大家在第一次嘗試去干某一件事時(shí),都不一定是一蹴而就的。就比如在導(dǎo)出word時(shí),就可能會(huì)遇到以下問(wèn)題。

          特殊字符

          問(wèn)題:有些文本數(shù)據(jù)中難免含有特殊字符,如:< > @ ! $ & 等等。

          解決方案:這些特殊字符如果不進(jìn)行轉(zhuǎn)義,就會(huì)引起word打不開(kāi)的現(xiàn)象,比如表格中的超鏈接的&符號(hào),就需要替換為&,如果你的文檔用office打開(kāi)時(shí)提示文件損壞,九成是因?yàn)樘厥夥?hào)引起的,我們可以打開(kāi)documet.xml定位報(bào)錯(cuò)位置;當(dāng)然還有終極方案,我們可以利用Freemarker的語(yǔ)法直接在模板中使用 處理。比如:

          ?${article.title}?]]>

          圖片變形

          問(wèn)題:因?yàn)閑charts生成的圖表是響應(yīng)式的,不同的屏幕大小、分辨率,會(huì)造成每次前端傳過(guò)來(lái)的圖片寬高比例不一致,如果還直接將圖片按照之前的比例放進(jìn)文檔,會(huì)造成生成后文檔中的圖片變形。

          思路:首先將文檔中的圖片設(shè)置為原圖,然后鎖定寬高比,將圖片調(diào)整到合適大小,解壓文檔從document.xml,得到此時(shí)word中該圖片寬高對(duì)應(yīng)的值,如下所示:



          要想保證不同像素比例的寬高在文檔中不變形,我們需要固定cy的值,然后根據(jù)固定比例動(dòng)態(tài)求得當(dāng)前像素比例圖片在word中代表的寬cx的值。計(jì)算方法如下所示:公式:

          a/b?=?x/y

          其中,a表示圖片在word中寬的數(shù)值,b代表圖片在word中高的數(shù)值,x表示前端傳過(guò)來(lái)圖片的寬(單位:像素),y表示前端傳過(guò)來(lái)圖片的高(單位:像素)。因此,已知b、x、y,根據(jù)公式,我們即可求出a;

          我就是文末

          當(dāng)然,還有用一些其他注意事項(xiàng):

          • 如果word中的模塊比較多的話,使用Freemarker語(yǔ)法要仔細(xì)一點(diǎn);
          • 為什么小明最終選擇導(dǎo)出docx格式的文檔呢?(還不是因?yàn)楫a(chǎn)品經(jīng)理提的需求嘛)因?yàn)閐oc格式的文檔,小明嘗試導(dǎo)出后,發(fā)現(xiàn)該文檔并不是一個(gè)合法的doc文檔,體現(xiàn)在:不能在手機(jī)上(微信、釘釘)正常預(yù)覽,office提示以xml形式打開(kāi)等。因此在導(dǎo)出doc文檔時(shí),通過(guò)Freemaker填充document.xml后得到的并不是一個(gè)合法的word文檔,查了相關(guān)資料,還需要借助第三方工具進(jìn)行簽名,而簽名還需要在windows系統(tǒng)下才能完成,但是我們平時(shí)用的生產(chǎn)環(huán)境都是Linux……因此,考慮再三,再三權(quán)衡,最終選擇導(dǎo)出docx格式的文檔。這種方式再適合不過(guò),而且還能保證在當(dāng)前主流APP上都能正常預(yù)覽。
          • 敲黑板!導(dǎo)出docx文檔最重要的一個(gè)思想是將本次數(shù)據(jù)寫入并覆蓋模版文件(在商業(yè)中,相當(dāng)于借殼上市),重新輸出一個(gè)zip格式壓縮的文件,這個(gè)文件就是我們最終想要的文檔。

          以上,就是小明本次word導(dǎo)出的前前后后,現(xiàn)在已經(jīng)在線上平穩(wěn)運(yùn)行數(shù)十個(gè)日夜。如果你也曾經(jīng)遇到過(guò)或者現(xiàn)在正好遇到word文檔導(dǎo)出開(kāi)發(fā)的問(wèn)題,歡迎一起討論交流。

          相關(guān)鏈接

          我上傳了工具類,包含doc、docx 的導(dǎo)出,以及導(dǎo)出word文檔時(shí)特殊符號(hào)轉(zhuǎn)義,還有圖片Base64轉(zhuǎn)換成文件輸出的方法。

          GitHub地址:https://github.com/WhenCoding/coder-xiaoming/blob/master/src/main/java/com/xm/coder/util/WordUtil.java

          < END >
          程序員小明
          一個(gè)很少加班的程序員
          微信掃描二維碼,關(guān)注我的公眾號(hào)
          瀏覽 85
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品成人免费精品自在线观看 | 国家产在线观看一二 | 欧洲三级在线 | 国产福利一区二区 | 丁香乱伦 |