阿里開源項目-[Word文檔生成神器poi.tl],[easyexcel解決poi的oom問題]


Word文檔生成神器 poi-tl(poi-template-language)

github地址:https://github.com/Sayi/poi-tl

Build Status jdk1.6+ jdk1.8 poi3.16%2B poi4.0.0 Gitter

Word 模板引擎,基於Apache POI - the Java API for Microsoft Documents。

What is poi-tl

FreeMarker、Velocity基於文本模板和數據生成新的HTML頁面、配置文件等,poi-tl是Word模板引擎,基於Microsoft Word模板和數據生成新的文檔。

Word模板擁有豐富的樣式,poi-tl在生成的文檔中會完美保留模板中的樣式,還可以為標簽設置樣式,標簽的樣式會被應用到替換后的文本上,因此你可以專注於模板設計。

poi-tl是一種 "logic-less" 模板引擎,沒有復雜的控制結構和變量賦值,只有標簽,一些標簽可以被替換為文本、圖片、表格等,一些標簽會隱藏某些文檔內容,而另一些標簽則會將一系列文檔內容循環渲染。

"Powerful" constructs like variable assignment or conditional statements make it easy to modify the look of an application within the template system exclusively... however, at the cost of separation, turning the templates themselves into part of the application logic.

《Google CTemplate》

poi-tl支持自定義函數(插件),函數可以在Word模板的任何位置執行,在文檔的任何地方做任何事情(Do Anything Anywhere)是poi-tl的星辰大海。

Maven

<dependency>
  <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.9.1</version> </dependency>

2分鍾快速入門

從一個超級簡單的例子開始:把{{title}}替換成"poi-tl 模板引擎"。

  1. 新建文檔模板template.docx,包含標簽{{title}}
  2. TDO模式:Template + data-model = output
//核心API采用了極簡設計,只需要一行代碼 XWPFTemplate.compile("template.docx").render(new HashMap<String, Object>(){{ put("title", "poi-tl 模板引擎"); }}).writeToFile("out_template.docx");

打開out_template.docx文檔吧,一切如你所願。

標簽

標簽由前后兩個大括號組成,{{title}}是標簽,{{?title}}也是標簽,title是這個標簽的名稱,?標識了標簽類型,接下來我們來看看有哪些標簽類型。

文本

文本標簽是Word模板中最基本的標簽類型,{{name}}會被數據模型中key為name的值替換,如果找不到默認會清空標簽,可以配置是保留還是拋出異常。

文本標簽的樣式會應用到替換后的文本上,正如下面的例子所示。

數據:

{
  "name": "Mama", "thing": "chocolates" }

Word模板:

{{name}} always said life was like a box of {{thing}}.
{{name}} always said life was like a box of {{thing}}.

輸出:

Mama always said life was like a box of chocolates.
Mama always said life was like a box of chocolates.

圖片

圖片標簽以@開始,如{{@logo}}會在數據中尋找key為logo的值,然后將標簽替換成圖片。由於Word文檔中圖片不是由字符串表示(在文本型模板中,比如HTML網頁圖片是由字符串<img src="" />表示),所以圖片標簽對應的數據有一定的結構要求,這些結構都會有相應的Java類對應。

數據:

{
  "watermelon": { "image": "assets/watermelon.png", "pictureType" : "PNG" }, "lemon": { "image": "http://xxx/lemon.png", "pictureType" : "PNG" }, "banana": { "image": "sob.png", "pictureType" : "PNG", "width": 24, "height": 24 } }

Word模板:

Fruit Logo:
watermelon {{@watermelon}}
lemon {{@lemon}}
banana {{@banana}}

輸出:

Fruit Logo:
watermelon 🍉
lemon 🍋
banana 🍌

表格

表格標簽以#開始,如{{#table}},它會被渲染成N行N列的Word表格,N的值取決於table標簽的值。

數據:

{
  "rows": [ { "cells": [ { "paragraphs": [ { "contents": [ { "text": "Song name" } ] } ] }, { "paragraphs": [ { "contents": [ { "text": "Artist" } ] } ] } ] } ] }

Word模板:

{{#song}}

輸出:

Song name Artist

列表

列表標簽對應Word的符號列表或者編號列表,以*開始,如{{*number}}

數據:

{
  "format" : { "lvlText" : "●" }, "items" : [ { "contents" : [ { "text" : "Plug-in grammar, add new grammar by yourself" } ] }, { "contents" : [ { "text" : "Supports word text, local pictures, web pictures, table, list, header, footer..." } ] }, { "contents" : [ { "text" : "Templates, not just templates, but also style templates" } ] } ] }

Word模板:

{{*feature}}

輸出:

● Plug-in function, define your own function
● Supports text, pictures, table, list, if, foreach...
● Templates, not just templates, but also style templates

區塊對

區塊對由前后兩個標簽組成,開始標簽以?標識,結束標簽以/標識,如{{?sections}}作為sections區塊的起始標簽,{{/sections}}為結束標簽,sections是這個區塊對的名稱。

區塊對在處理一系列文檔元素的時候非常有用,位於區塊對中的文檔元素(文本、圖片、表格等)可以被渲染零次,一次或N次,這取決於區塊對的取值。

False或空集合

如果區塊對的值是nullfalse或者空的集合,位於區塊中的所有文檔元素將不會顯示,類似於if語句的條件為false

數據:

{
  "announce": false }

Word模板:

Made it,Ma!{{?announce}}Top of the world!{{/announce}}
Made it,Ma!
{{?announce}}
Top of the world!🎋
{{/announce}}

輸出:

Made it,Ma!
Made it,Ma!

非False且不是集合

如果區塊對的值不為nullfalse,且不是集合,位於區塊中的所有文檔元素會被渲染一次,if語句的條件為true

數據:

{
  "person": { "name": "Sayi" } }

Word模板:

{{?person}}
  Hi {{name}}!
{{/person}}

輸出:

  Hi Sayi!

非空集合

如果區塊對的值是一個非空集合,區塊中的文檔元素會被迭代渲染一次或者N次,這取決於集合的大小,類似於foreach語法。

數據:

{
  "songs": [ { "name": "Memories" }, { "name": "Sugar" }, { "name": "Last Dance(伍佰)" } ] }

Word模板:

{{?songs}}
{{name}}
{{/songs}}

輸出:

Memories
Sugar
Last Dance(伍佰)

在循環中可以通過一個特殊的標簽{{=#this}}直接引用當前迭代的對象。

數據:

{
  "produces": [ "application/json", "application/xml" ] }

Word模板:

{{?produces}}
{{=#this}}
{{/produces}}

輸出:

application/json
application/xml

嵌套

嵌套是在Word模板中引入另一個Word模板,可以理解為import、include或者word文檔合並,以+標識,如{{+nested}}

數據:

{
  "nested": { "file": "template/sub.docx", "dataModels": [ { "addr": "Hangzhou,China" }, { "addr": "Shanghai,China" } ] } }

給定兩個WordWord模板:

main.docx:
Hello, World
{{+nested}}

template/sub.docx:
Address: {{addr}}

輸出:

Hello, World
Address: Hangzhou,China
Address: Shanghai,China

詳細文檔與示例

中文文檔Documentation

更多的示例以及所有示例的源碼參見JUnit單元測試。

 

Contributing貢獻

你可以有很多途徑加入這個項目,不限於以下方式:

  • 反饋使用中遇到的問題
  • 分享成功的喜悅
  • 更新和完善文檔
  • 解決和討論Issue

建議和完善

參見常見問題,歡迎在GitHub Issue中提問和交流。

社區交流討論群:Gitter頻道

 

 

--------------------------------------------------

 

 

EasyExcel

github地址:https://github.com/alibaba/easyexcel

Build Status Maven central License

官方網站: https://yuque.com/easyexcel

常見問題

因為公司不方便用QQ,所以建議加釘釘群

JAVA解析Excel工具EasyExcel

Java解析、生成Excel比較有名的框架有Apache poi、jxl。但他們都存在一個嚴重的問題就是非常的耗內存,poi有一套SAX模式的API可以一定程度的解決一些內存溢出的問題,但POI還是有一些缺陷,比如07版Excel解壓縮以及解壓后存儲都是在內存中完成的,內存消耗依然很大。easyexcel重寫了poi對07版Excel的解析,能夠原本一個3M的excel用POI sax依然需要100M左右內存降低到幾M,並且再大的excel不會出現內存溢出,03版依賴POI的sax模式。在上層做了模型轉換的封裝,讓使用者更加簡單方便

64M內存1分鍾內讀取75M(46W行25列)的Excel

當然還有急速模式能更快,但是內存占用會在100M多一點 img

相關文檔

維護者

玉霄、庄家鉅、懷宇

快速開始

讀Excel

DEMO代碼地址:https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/demo/read/ReadTest.java

    /**
 * 最簡單的讀  * <p>1. 創建excel對應的實體對象 參照{@link DemoData}  * <p>2. 由於默認一行行的讀取excel,所以需要創建excel一行一行的回調監聽器,參照{@link DemoDataListener}  * <p>3. 直接讀即可  */ @Test public void simpleRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 這里 需要指定讀用哪個class去讀,然后讀取第一個sheet 文件流會自動關閉 EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead(); }

寫Excel

DEMO代碼地址:https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java

    /**
 * 最簡單的寫  * <p>1. 創建excel對應的實體對象 參照{@link com.alibaba.easyexcel.test.demo.write.DemoData}  * <p>2. 直接寫即可  */ @Test public void simpleWrite() { String fileName = TestFileUtil.getPath() + "write" + System.currentTimeMillis() + ".xlsx"; // 這里 需要指定寫用哪個class去讀,然后寫到第一個sheet,名字為模板 然后文件流會自動關閉 // 如果這里想使用03 則 傳入excelType參數即可 EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data()); }

web上傳、下載

DEMO代碼地址:https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java

   /**
 * 文件下載(失敗了會返回一個有部分數據的Excel)  * <p>  * 1. 創建excel對應的實體對象 參照{@link DownloadData}  * <p>  * 2. 設置返回的 參數  * <p>  * 3. 直接寫,這里注意,finish的時候會自動關閉OutputStream,當然你外面再關閉流問題不大  */ @GetMapping("download") public void download(HttpServletResponse response) throws IOException { // 這里注意 有同學反應使用swagger 會導致各種問題,請直接用瀏覽器或者用postman response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); // 這里URLEncoder.encode可以防止中文亂碼 當然和easyexcel沒有關系 String fileName = URLEncoder.encode("測試", "UTF-8").replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data()); } /**  * 文件上傳  * <p>1. 創建excel對應的實體對象 參照{@link UploadData}  * <p>2. 由於默認一行行的讀取excel,所以需要創建excel一行一行的回調監聽器,參照{@link UploadDataListener}  * <p>3. 直接讀即可  */ @PostMapping("upload") @ResponseBody public String upload(MultipartFile file) throws IOException { EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead(); return "success"; }

 


免責聲明!

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



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