天天看點

SpringBoot內建檔案 - 如何基于POI-tl和word模闆導出龐大的Word檔案?

前文我們介紹了通過Apache POI通過來導出word的例子;那如果是word模闆方式,有沒有開源庫通過模闆方式導出word呢?poi-tl是一個基于Apache POI的Word模闆引擎,也是一個免費開源的Java類庫,你可以非常友善的加入到你的項目中,并且擁有着讓人喜悅的特性。本文主要介紹通過SpringBoot內建poi-tl實作模闆方式的Word導出功能。

知識準備

需要了解檔案上傳和下載下傳的常見場景和技術手段。@pdai

什麼是poi-tl

如下内容來源于,​​poi-tl官網​​。

poi-tl(poi template language)是Word模闆引擎,使用Word模闆和資料建立很棒的Word文檔。

優勢:

SpringBoot內建檔案 - 如何基于POI-tl和word模闆導出龐大的Word檔案?

它還支援自定義插件,如下是​​官網代碼倉庫​​支援的特性

poi-tl supports custom functions (plug-ins), functions can be executed anywhere in the Word template, do anything anywhere in the document is the goal of poi-tl.
Feature Description
:white_check_mark: Text Render the tag as text
:white_check_mark: Picture Render the tag as a picture
:white_check_mark: Table Render the tag as a table
:white_check_mark: Numbering Render the tag as a numbering
:white_check_mark: Chart Bar chart (3D bar chart), column chart (3D column chart), area chart (3D area chart), line chart (3D line chart), radar chart, pie chart (3D pie Figure) and other chart rendering
:white_check_mark: If Condition Hide or display certain document content (including text, paragraphs, pictures, tables, lists, charts, etc.) according to conditions
:white_check_mark: Foreach Loop Loop through certain document content (including text, paragraphs, pictures, tables, lists, charts, etc.) according to the collection
:white_check_mark: Loop table row Loop to copy a row of the rendered table
:white_check_mark: Loop table column Loop copy and render a column of the table
:white_check_mark: Loop ordered list Support the loop of ordered list, and support multi-level list at the same time
:white_check_mark: Highlight code Word highlighting of code blocks, supporting 26 languages and hundreds of coloring styles
:white_check_mark: Markdown Convert Markdown to a word document
:white_check_mark: Word attachment Insert attachment in Word
:white_check_mark: Word Comments Complete support comment, create comment, modify comment, etc.
:white_check_mark: Word SDT Complete support structured document tag
:white_check_mark: Textbox Tag support in text box
:white_check_mark: Picture replacement Replace the original picture with another picture
:white_check_mark: bookmarks, anchors, hyperlinks Support setting bookmarks, anchors and hyperlinks in documents
:white_check_mark: Expression Language Fully supports SpringEL expressions and can extend more expressions: OGNL, MVEL...
:white_check_mark: Style The template is the style, and the code can also set the style
:white_check_mark: Template nesting The template contains sub-templates, and the sub-templates then contain sub-templates
:white_check_mark: Merge Word merge Merge, you can also merge in the specified position
:white_check_mark: custom functions (plug-ins) Plug-in design, execute function anywhere in the document

poi-tl的TDO模式

TDO模式:Template + data-model = output

以官網的例子為例:

XWPFTemplate template = XWPFTemplate.compile("template.docx").render(
  new HashMap<String, Object>(){{
    put("title", "Hi, poi-tl Word模闆引擎");
}});  
template.writeAndClose(new FileOutputStream("output.docx"));       
  • compile 編譯模闆 - Template
  • render 渲染資料 - data-model
  • write 輸出到流 - output

Template:模闆

模闆是Docx格式的Word文檔,你可以使用Microsoft office、WPS Office、Pages等任何你喜歡的軟體制作模闆,也可以使用Apache POI代碼來生成模闆。

所有的标簽都是以​

​{{​

​開頭,以​

​}}​

​結尾,标簽可以出現在任何位置,包括頁眉,頁腳,表格内部,文本框等,表格布局可以設計出很多優秀專業的文檔,推薦使用表格布局。

poi-tl模闆遵循“所見即所得”的設計,模闆和标簽的樣式會被完全保留。

Data-model:資料

資料類似于哈希或者字典,可以是Map結構(key是标簽名稱):

Map<String, Object> data = new HashMap<>();
data.put("name", "Sayi");
data.put("start_time", "2019-08-04");      

可以是對象(屬性名是标簽名稱):

public class Data {
  private String name;
  private String startTime;
  private Author author;
}      

資料可以是樹結構,每級之間用點來分隔開,比如​

​{ {author.name} }​

​标簽對應的資料是author對象的name屬性值。

Word模闆不是由簡單的文本表示,是以在渲染圖檔、表格等元素時提供了資料模型,它們都實作了接口RenderData,比如圖檔資料模型PictureRenderData包含圖檔路徑、寬、高三個屬性。

Output:輸出

以流的方式進行輸出:

template.write(OutputStream stream);      

可以寫到任意輸出流中,比如檔案流:

template.write(new FileOutputStream("output.docx"));      

比如網絡流:

response.setContentType("application/octet-stream");
response.setHeader("Content-disposition","attachment;filename=\""+"out_template.docx"+"\"");

// HttpServletResponse response
OutputStream out = response.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(out);
template.write(bos);
bos.flush();
out.flush();
PoitlIOUtils.closeQuietlyMulti(template, bos, out); // 最後不要忘記關閉這些流。      

實作案例

這裡展示SpringBoot內建poi-tl基于word模闆導出Word, 以及導出markdown為word的例子。

Pom依賴

引入poi的依賴包

基礎的包:

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

插件的包如下,比如highlight,markdown包

<dependency>
    <groupId>com.deepoove</groupId>
    <artifactId>poi-tl-plugin-highlight</artifactId>
    <version>1.0.0</version>
</dependency>
<dependency>
    <groupId>com.deepoove</groupId>
    <artifactId>poi-tl-plugin-markdown</artifactId>
    <version>1.0.3</version>
</dependency>      

導出基于template的word

controller中的方法

@ApiOperation("Download Word")
@GetMapping("/word/download")
public void download(HttpServletResponse response){
    try {
        XWPFTemplate document = userService.generateWordXWPFTemplate();
        response.reset();
        response.setContentType("application/octet-stream");
        response.setHeader("Content-disposition",
                "attachment;filename=user_word_" + System.currentTimeMillis() + ".docx");
        OutputStream os = response.getOutputStream();
        document.write(os);
        os.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}      

Service中的實際方法

@Override
public XWPFTemplate generateWordXWPFTemplate() throws IOException {
    Map<String, Object> content = new HashMap<>();
    content.put("title", "Java 全棧知識體系");
    content.put("author", "pdai");
    content.put("site", new HyperlinkTextRenderData("https://pdai.tech", "https://pdai.tech"));

    content.put("poiText", "Apache POI 是建立和維護操作各種符合Office Open XML(OOXML)标準和微軟的OLE 2複合文檔格式(OLE2)的Java API。用它可以使用Java讀取和建立,修改MS Excel檔案.而且,還可以使用Java讀取和建立MS Word和MSPowerPoint檔案。更多請參考[官方文檔](https://poi.apache.org/index.html)");

    content.put("poiText2", "生成xls和xlsx有什麼差別?POI對Excel中的對象的封裝對應關系?");
    content.put("poiList", Numberings.create("excel03隻能打開xls格式,無法直接打開xlsx格式",
            "xls隻有65536行、256列; xlsx可以有1048576行、16384列",
            "xls占用空間大, xlsx占用空間小,運算速度也會快一點"));

    RowRenderData headRow = Rows.of("ID", "Name", "Email", "TEL", "Description").textColor("FFFFFF")
            .bgColor("4472C4").center().create();
    TableRenderData table = Tables.create(headRow);
    getUserList()
            .forEach(a -> table.addRow(Rows.create(a.getId() + "", a.getUserName(), a.getEmail(), a.getPhoneNumber() + "", a.getDescription())));
    content.put("poiTable", table);

    Resource resource = new ClassPathResource("pdai-guli.png");
    content.put("poiImage", Pictures.ofStream(new FileInputStream(resource.getFile())).create());

    return XWPFTemplate.compile(new ClassPathResource("poi-tl-template.docx").getFile()).render(content);
}

private List<User> getUserList(){
    List<User> userList = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        userList.add(User.builder()
                .id(Long.parseLong(i + "")).userName("pdai" + i).email("[email protected]" + i).phoneNumber(121231231231L)
                .description("hello world" + i)
                .build());
    }
    return userList;
}      

準備模闆

SpringBoot內建檔案 - 如何基于POI-tl和word模闆導出龐大的Word檔案?

導出word

SpringBoot內建檔案 - 如何基于POI-tl和word模闆導出龐大的Word檔案?

導出markdown為word

controller中的方法

@ApiOperation("Download Word based on markdown")
@GetMapping("/word/downloadMD")
public void downloadMD(HttpServletResponse response){
    try {
        XWPFTemplate document = userService.generateWordXWPFTemplateMD();
        response.reset();
        response.setContentType("application/octet-stream");
        response.setHeader("Content-disposition",
                "attachment;filename=user_word_" + System.currentTimeMillis() + ".docx");
        OutputStream os = response.getOutputStream();
        document.write(os);
        os.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}      

Service中實作的方法

@Override
public XWPFTemplate generateWordXWPFTemplateMD() throws IOException {
    MarkdownRenderData code = new MarkdownRenderData();

    Resource resource = new ClassPathResource("test.md");
    code.setMarkdown(new String(Files.readAllBytes(resource.getFile().toPath())));
    code.setStyle(MarkdownStyle.newStyle());

    Map<String, Object> data = new HashMap<>();
    data.put("md", code);

    Configure config = Configure.builder().bind("md", new MarkdownRenderPolicy()).build();

    return XWPFTemplate.compile(new ClassPathResource("markdown_template.docx").getFile(), config).render(data);
}      

準備模闆

SpringBoot內建檔案 - 如何基于POI-tl和word模闆導出龐大的Word檔案?

導出word

SpringBoot內建檔案 - 如何基于POI-tl和word模闆導出龐大的Word檔案?

示例源碼

參考文章

更多内容