java基於feemarker 生成word文檔(超級簡單)


問題由來:

開發個新需求,需要按規定導出word文檔,文檔截圖如下

 

 

因為之前沒做過這個,一臉懵B啊,導出excel和txt倒是經常接觸到,對於這個word這種格式不嚴謹的文件怎么處理呢?

 

技術選型:可協助實現的技術很多,但是本人極力推薦feemarker,簡直太好用了。

 

具體實施:

步驟一:maven項目 先添加如下依賴:

<!--freemarker 協助word 導出-->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.20</version>
        </dependency>

不是maven的,去下載feemarker.jar 導入就好了

步驟二原理就是先做一個word模板, 該模板中變量數據用${xxx}這種方式填寫, 然后再導出時只需讀取模板然后用相應的數據替換其中的${xxx}即可. 

我們這里設置了三個變量;

步驟三把該word文檔 另存為xml文件(是另存為,不是改擴展名為xml),然后再改擴展名為ftl,步驟不能錯。隨便放在一個地方,我們這里先放在D盤根目錄 名為 測試.ftl;

步驟四:上代碼

try {
                Map<String,String> dataMap = new HashMap<String,String>();
                dataMap.put("account", "123456");
                dataMap.put("name", "張三");
                dataMap.put("idNumber", "123123");
                Configuration configuration = new Configuration();
                configuration.setDefaultEncoding("utf-8");
                //指定模板路徑的第二種方式,我的路徑是D:/      還有其他方式
                configuration.setDirectoryForTemplateLoading(new File("D:/"));

                // 輸出文檔路徑及名稱
                File outFile = new File("D:/test.doc");
                //以utf-8的編碼讀取ftl文件
                Template t =  configuration.getTemplate("測試.ftl","utf-8");
                Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "utf-8"),10240);
                t.process(dataMap, out);
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }

跑起來,大兄弟。

步驟四:跑完,發現在我的D盤  有個test.doc,點開看看

完美啊,發現數據進去了。

 

技術擴展(循環列表):

其實到這里,已經可以滿足很多的場景需要了。

但是我們這里還欠缺了點,因為我們的數據列表是循環的,因為可能不止一條數據。

那怎么辦呢?

其實feemarker已經為我們考慮了這一點了, 先上代碼

Map<String,String> map1 = new HashMap<String,String>();
                map1.put("account", "123456");
                map1.put("name", "張三");
                map1.put("idNumber", "123123");
                Map<String,String> map2 = new HashMap<String,String>();
                map2.put("account", "456789");
                map2.put("name", "李四");
                map2.put("idNumber", "123");

                List<Map<String,String>> newlist = new ArrayList<>();
                newlist.add(map1);
                newlist.add(map2);
                Map<String, Object> dataMap = new HashMap<>();
                dataMap.put("userList", newlist);

                Configuration configuration = new Configuration();
                configuration.setDefaultEncoding("utf-8");
                //指定模板路徑的第二種方式,我的路徑是D:/      還有其他方式
                configuration.setDirectoryForTemplateLoading(new File("D:/"));

                // 輸出文檔路徑及名稱
                File outFile = new File("D:/test.doc");
                //以utf-8的編碼讀取ftl文件
                Template t =  configuration.getTemplate("測試.ftl","utf-8");
                Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "utf-8"),10240);
                t.process(dataMap, out);
                out.close();

 

代碼里我們對數據的封裝做了改動,我們傳了list進去,list里面都是key相同的map;

還得對我們的 ftl 文件內容改動一下

搜索  w:tr 可以找到行的起點與結束點(注意第一對w:tr 是表頭,應找第二對 w:tr), 如圖(網上摘錄的圖,因為我自己的ftl文件內容太大,截不了): 

 用<#list userList as user> </#list>標簽將第二對 w:tr 標簽包圍起來(userList是集合的key, user是集合中的每個元素, 類似<c:forEach items='userList' var='user'>), 如圖: 

當然,這里的user.a   要換  user隨便寫,和上面的as user對應起來就好,這里需要改成user.account, user.name,user.idNumber;

 

現在看看文件變成什么樣了

 

 完美解決list循環讀取的問題;

擴展二(直接導出):

顯然我們這里處理文件的方式是直接生成文件放在了D盤里,更多的時候,我們需要直接導出;

                Map<String,String> dataMap = getData();  //獲取數據
                //Configuration用於讀取ftl文件
                Configuration configuration = new Configuration();
                configuration.setDefaultEncoding("utf-8");

                //獲取模板所在的路徑
                configuration.setDirectoryForTemplateLoading(new File(pathHelper.getWebClassesPath()+"/template"));

                //以utf-8的編碼讀取ftl文件
                Template t =  configuration.getTemplate("測試.ftl","utf-8");

                String fileName = URLEncoder.encode("客戶信息-","UTF-8") + dataMap.get("formCode")+".doc";
                //1.設置文件ContentType類型,這樣設置,會自動判斷下載文件類型
                response.setContentType("multipart/form-data");
                //2.設置文件頭:最后一個參數是設置下載文件名(假如我們叫a.pdf)
                response.setHeader("Content-Disposition", "attachment;fileName=" + fileName);
                Writer out = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), "utf-8"),10240);
                t.process(dataMap, out);
                out.close();

這樣就可以直接導出了。

可能遇到的bug:

就是獲取模板的那步可能會出問題,提示找不到模板。feemarker為我們提供了三種方法

第一種:基於類路徑,HttpWeb包下的framemaker.ftl文件
  configuration.setClassForTemplateLoading(this.getClass(), "/HttpWeb");


第二種:基於文件系統

configuration.setDirectoryForTemplateLoading(new File("/template"))

 

第三種:基於Servlet Context,指的是基於WebRoot下的template下的framemaker.ftl文件

HttpServletRequest request = ServletActionContext.getRequest();

configuration.setServletContextForTemplateLoading(request.getSession().getServletContext(), "/template");

因為放在項目中,肯定想設置個相對路徑,我就選擇了第一種,

項目存放路徑

注意:configuration.setClassForTemplateLoading(this.getClass(),"/jasperreports");   
這樣就可以了,千萬別寫什么 /resources/jasperreports,因為maven項目打包的時候,resources文件夾就不存在了。



好了到這里,介紹完畢,是不是很簡單又美觀呢。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM