目錄
Excel模板的設計" >Excel模板的設計
一個簡單的Excel報表範本 " >一個簡單的Excel報表範本
一個複雜的Excel報表範本 " >一個複雜的Excel報表範本
複雜範本設計剖析 " >複雜範本設計剖析
准备模板数据 " >准备模板数据
图片数据导出 " >图片数据导出
将数据导出至模板 " >将数据导出至模板
首頁 專題 excel 使用 EasyPOI 優雅匯出Excel模板資料(含圖片)

使用 EasyPOI 優雅匯出Excel模板資料(含圖片)

Jul 26, 2023 pm 04:32 PM
easypoi

前言

最近有讀者在問easypoi的問題,抽空整理了一篇文章。

正文

EasyPOI功能如同名字Easy,主打的功能就是容易,讓一個沒接觸過POI的人員可以方便的寫出Excel導出,Excel模板導出,Excel導入,Word範本匯出。透過簡單的註解和模板語言(熟悉的表達式語法),完成先前複雜的寫法。

本文主要透過簡單的分析讓讀者知道Excel模板該如何編寫,EasyPOI要如何使用才能導出滿足自己需要的Excel數據,從而簡化編碼。同時本文也會對一些不常見的功能如圖片匯出功能進行說明,讓讀者少踩坑。

版本及相依說明

EasyPOI4.0.0及以後的版本依賴Apache POI的4.0.0及以後版本。所以在maven的配置中,兩者的版本號碼一定要匹配。

要注意的是,Apache POI的4.0.0相對之前的版本有很大的變更,如果之前程式碼中Excel操作部分依賴舊的版本,那麼不建議使用4.0.0及之後的版本。當然,如果之前程式碼不涉及或很少涉及WorkBook的創建細節,使用新版也沒有問題。

筆者需要改寫的項目是基於JEECG 3.7版本,依賴的是3.9版本的Apache POI,而JEECG維護的jeasypoi版本最高只有2.2.0,而該版本並不支援模板匯出圖片功能。說到這裡又要吐槽以下JEECG團隊,既然自己不打算維護jeasypoi,那專案中直接使用官方的EasyPOI不就好了,2.2.0版本的jeasypoi給開發者挖了多少坑啊!

為了和舊版相容,又想使用EasyPOI帶來的圖片匯出功能,所以筆者最終採用的EasyPOI版本是3.3.0,對應的Apache POI依賴是3.15。

Maven配置如下所示:

<properties>
    <poi.version>3.15</poi.version>
    <easypoi.version>3.3.0</easypoi.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>${poi.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>${poi.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml-schemas</artifactId>
        <version>${poi.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-scratchpad</artifactId>
        <version>${poi.version}</version>
    </dependency>
    <dependency>
        <groupId>cn.afterturn</groupId>
        <artifactId>easypoi-web</artifactId>
        <version>${easypoi.version}</version>
    </dependency>
</dependencies>
登入後複製

Excel模板的設計

我們使用EasyPOI的模板匯出功能就是不想透過編碼的方式來設計Excel報表的樣式,所以工作的第一步就是設計Excel模板,分清楚哪些部分是固定的,哪些是需要循環填入的。 EasyPOI有自己的表達式語言,每種表達式的詳細介紹請參考後文的參考連結。

一個簡單的Excel報表範本

#一些簡單的範本就不在這裡詳細解釋了,只放一下效果圖和模板配置內容。等讀者明白了複雜的模板如何製作並如何填值的時候,簡單的很快就能明白了。

先看看報表效果圖:

使用 EasyPOI 優雅匯出Excel模板資料(含圖片)

再看看實際的範本:

使用 EasyPOI 優雅匯出Excel模板資料(含圖片)

看了上述兩張圖,是不是已經感受到模板導出功能的強大了呢?

一個複雜的Excel報表範本

下面要介紹的這個範本比較複雜,不像是常見的那種一行是一筆記錄的情況,所以將詳細介紹該範本的配置,順帶對EasyPOI的部分錶達式進行簡單介紹。

還是先看效果圖:

使用 EasyPOI 優雅匯出Excel模板資料(含圖片)

再看看範本:

使用 EasyPOI 優雅匯出Excel模板資料(含圖片)

##這兩張圖一對比,是不是有種知識改變命運的感覺?

複雜範本設計剖析

#從貨品資訊的範本圖及效果圖中我們發現,整個範本實際上分為上下兩部分。其中上部分為不變的抬頭訊息,下部分為循環插入的貨品明細訊息。所以我們關注的重點是下半部的文法。

下半部的第一列圖中沒有顯示完整,實際上是{{!fe: list t.id。

注意,這裡 沒有 }}符號!根據EasyPOI的官方文檔,{{}}代表的是表達式,根據表達式取裡邊的值。仔細看圖可以發現,表達式的閉合符號{{}}出現在圖中的右下角。也就是說,從第一列{{開始到右下角}}結束,這中間的所有內容都是表達式的一部分。

因為整個範本資訊都是表達式的一部分,所以即使是普通字串也需要專門標明。下面將表達式中的子表達式逐一說明。

!fe: 遍歷資料不建立row。

官方文件中的這句話大家理解起來可能有點費解,什麼叫不創建row?實際上,不建立row是相對於建立row而言的,建立row的表達式是fe:。

就像是資料庫中每筆記錄對應著一個實體對象,創建row表示每行就是一個實體對象Entity,這個實體對象的屬性用{{}}表達式包裹起來。

不建立row表示整個表達式中只有一個實體物件Object,只不過這個Object比較特別,它是由list中N個Entity拼接起來的。每一個Entity不只是指模型本身,也包含了Excel的樣式,例如佔用了幾個單元格,單元格的座標、排布順序等。

list 自訂的名稱,表示表達式中的資料集合,由程式碼以list為鍵,從Map中取得值的集合。

list這個名字很容易理解,就是一個佔位符,可以隨便取。 EasyPOI解析到list就知道Map中存在該鍵的值的集合,後邊解析到資料就從該集合取即可。

搜尋Java知音公眾號,回覆“後端面試”,送你一份Java面試題寶典.pdf

t 預定義值,表示集合中的任意物件。

從模板中我們大致可以感覺到,list中每個物件叫做t,t.name就代表t的name屬性,所以t這個名字就可以隨便叫,反正它和list一樣,作用是佔位符。

但其實這是一個大坑!如果你把t換成了其他值例如g,模板中其他地方寫g.name g.code等等,最後是解析不到的!官方文件對這一點並沒有強調,而是作者實際踩了坑之後才發現的!

]] 換行符號 多行遍歷匯出。

對於這個符號的官方解釋也是莫名其妙,什麼叫換行符,多行遍歷導出?實際上它的意思就是,當解析到表達式中含有這個符號,該行後邊的內容就不解析了,管你後邊有沒有其他內容或樣式。

該符號一定要寫在每行的最後一列,不然會出現每行列數不一樣的情況,EasyPOI內部做賦值的時候就會報空指標異常了。

‘’ 單引號表示常數值 ‘’ 例如’1’ 那麼輸出的就是 1

##官方文件中這裡的介紹也有坑。 ''是表示常數值,但實際上Excel中只是這麼些是不對的,因為Excel的單元格中遇到'後會認為後面都是字符串,所以得在單元格中寫''庫別:' ,這樣顯示出來的才是'庫別:',而不是字串庫別:'。

經過上述分析,圖中的範本其實就類似以下內容:

{{!fe: list t.id ‘库别:’ t.bin 换行 ‘商品名称:’ t.name 换行 ‘商品编号:’ t.code t.barcode 换行 ‘生产日期:’ t.proDate 换行 ‘进货日期:’ t.recvDate}}

如果list中有多条记录,上述字符串就再循环拼接一些内容,最终都在一个{{}}表达式中。

至此,模板的设计已剖析完毕,读者可根据自己的需求结合官方文档自行设计模板。下面将对模板赋值进行介绍。

准备模板数据

从上节的描述中可知,只需要准备一个Map的对象即可,其中键为list,值为一个List数组,数组中元素类型为Map。代码如下:

Map<String, Object> total = new HashMap<>();
List<Map<String, Object>> mapList = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
    Map<String, Object> map = new HashMap<>();
    map.put("id", i + "");
    map.put("bin", "001 1000千克");
    map.put("name", "商品" + i);
    map.put("code", "goods" + i);
    map.put("proDate", "2019-05-30");
    map.put("recvDate", "2019-07-07");

    // 插入图片
    ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
    BufferedImage bufferImg = ImageIO.read(BarcodeUtil.generateToStream("001"));
    ImageIO.write(bufferImg, "jpg", byteArrayOut);
    ImageEntity imageEntity = new ImageEntity(byteArrayOut.toByteArray(), 200, 1000);
    map.put("barcode", imageEntity);

    mapList.add(map);
}
total.put("list", mapList);
登入後複製

图片数据导出

上述代码中需要特殊关注的是图片导出部分。EasyPOI导出图片有两种方式,一种是通过图片的Url,还有一种是获取图片的byte[],毕竟图片的本质就是byte[]。因为笔者的项目中图片不是存放在数据库之中,而是需要根据查询结果动态生成条码,所以通过byte[]导出图片。

ImageEntity是EasyPOI内置的一个JavaBean,用于设定图片的宽度和高度、导出方式、RowSpan和ColumnSpan等。调试EasyPOI的源码可知,当设置了RowSpan或者ColumnSpan之后,图片的高度设置就失效了,图片大小会自动填充图片所在的单元格。

图片导出的坑点在于导出图片的大小。假设我们将四个单元格合成为一个,希望导出的图片能填充合并之后的单元格,但是对不起,EasyPOI暂时做不到,它只会填充合并之前左上角的单元格,具体原因如下源码所示:

//BaseExportService.java
ClientAnchor anchor;
if (type.equals(ExcelType.HSSF)) {
    anchor = new HSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1),
            cell.getRow().getRowNum() + 1);
} else {
    anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1),
            cell.getRow().getRowNum() + 1);
}
登入後複製

可以看到,在创建图片插入位置的时候已经指定了图片的跨度为1行1列,即左上角的单元格。如果之前又设置了RowSpan或者ColumnSpan,那么图片高度的设置也会失效,最终导致导出的图片非常小。

搜索Java知音公众号,回复“后端面试”,送你一份Java面试题宝典.pdf

个人认为ImageEntity提供的RowSpan或者ColumnSpan的set方法并没有什么用,因为我们动态创建的合并单元格并不能被赋值。所以,导出图片的最好方式就是直接指定它的高度,因为宽度会自动填充单元格,模板中单元格的宽度要合适。

//ExcelExportOfTemplateUtil.java
if (img.getRowspan()>1 || img.getColspan() > 1){
    img.setHeight(0);
    PoiMergeCellUtil.addMergedRegion(cell.getSheet(),cell.getRowIndex(),
            cell.getRowIndex() + img.getRowspan() - 1, cell.getColumnIndex(), cell.getColumnIndex() + img.getColspan() -1);
}
登入後複製

将数据导出至模板

以上准备工作全部完成后就可以将模板和数据进行组装了,或者说是渲染,代码如下所示:

public static void exportByTemplate(String templateName, Map<String, Object> data, OutputStream fileOut) {
    TemplateExportParams params = new TemplateExportParams("export/template/" + templateName, true);
    try {
        Workbook workbook = ExcelExportUtil.exportExcel(params, data);
        workbook.write(fileOut);
    } catch (Exception e) {
        LogUtil.error("", e);
    }
}
登入後複製

总结

网上针对EasyPOI的介绍多限于最基本的行插入功能,但实际上Excel模板的需求可能各式各样。本文只是抛砖引玉,对EasyPOI中的部分概念做了详细介绍,希望帮助大家少踩坑。

如果想详细了解EasyPOI的各种功能,参考链接中的文档说明及测试项目源码就是最好的学习资料。希望大家都能得心应手地使用EasyPOI,大大提升开发效率!

参考链接

EasyPOI官方文档

  • https://opensource.afterturn.cn/doc/easypoi.html

EasyPOI测试项目

  • https://gitee.com/lemur/easypoi-test

一些坑

近日有网友求助我解决EasyPOI的复杂模板配置问题,通过解决该网友的问题发现了EasyPOI中的几个坑点,补充说明几个问题。

在複雜模板設計剖析一節中已經描述了EasyPOI支援的複雜的模板該如何配置。此模板的配置絕對是正確的,但有3個點沒有說清楚,大家在照葫蘆畫瓢時容易出錯:

  1. {!fe: list需要在一個單獨的列中。 EasyPOI原始碼中是根據該儲存格的行、列跨距來決定list中的每個元素需要多少行的。例如上述圖片中,該儲存格的跨距是5行1列,也就是說,以後list中的每個元素都會佔用5行。如果覺得該列不符合自訂模板的風格,可以把該列的列寬設為0,但一定需要有{{!fe: list。
  2. 在物件的起始和結束符號{{}}之間不能有任何空的單元格!程式碼中在解析到該單元格為空時會直接拋異常,如果就希望該單元格為空,則得顯示寫入空字串:’’’。
  3. 換行符]]必須佔用每行的最後一個儲存格!比如說第一行有10個單元格,第二行只用了前5個,那麼不能直接在第5個結束直接寫換行符]],而是需要把6-10個單元格合併,然後寫入]]。參考上述圖片中生產日期所在行的最後一列。這麼設定的原因是EasyPOI要求每行的單元格數目完全一致,因為源碼中判斷了每個單元格的列跨度,如果提前使用了]]換行符,那麼該列的數目就和其他行不同,那麼賦值的時候就亂掉了,會出現索引異常。

以上是使用 EasyPOI 優雅匯出Excel模板資料(含圖片)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何在Excel中創建時間軸以濾波樞軸表和圖表 如何在Excel中創建時間軸以濾波樞軸表和圖表 Mar 22, 2025 am 11:20 AM

本文將指導您完成為Excel Pivot表和圖表創建時間表的過程,並演示如何使用它以動態和引人入勝的方式與數據進行交互。 您的數據在Pivo中組織了

Excel公式在列或行中找到前3、5、10個值 Excel公式在列或行中找到前3、5、10個值 Apr 01, 2025 am 05:09 AM

本教程演示瞭如何在數據集中有效地定位頂部N值並使用Excel公式檢索關聯的數據。 無論您需要最高,最低還是符合特定標準的人,本指南都提供解決方案。 Findi

您需要知道的所有要對Google表中的所有數據進行排序 您需要知道的所有要對Google表中的所有數據進行排序 Mar 22, 2025 am 10:47 AM

掌握Google表格分類:綜合指南 在Google表中對數據進行排序不需要復雜。本指南涵蓋了各種技術,從整個床單到特定範圍,按顏色,日期和多個列。 無論你是諾維

正則提取在Excel中提取字符串(一個或所有匹配) 正則提取在Excel中提取字符串(一個或所有匹配) Mar 28, 2025 pm 12:19 PM

在本教程中,您將學習如何在Excel中使用正則表達式來查找和提取與給定模式相匹配的子字符串。 Microsoft Excel提供了許多功能,可以從單元格中提取文本。這些功能可以與大多數

將下拉列表添加到Outlook電子郵件模板 將下拉列表添加到Outlook電子郵件模板 Apr 01, 2025 am 05:13 AM

本教程向您展示瞭如何將下拉列表添加到Outlook電子郵件模板中,包括多個選擇和數據庫總體。 雖然Outlook並未直接支持下拉列表,但本指南提供了創造性的解決方法。 電子郵件模板SAV

如何安排發送前景 如何安排發送前景 Mar 22, 2025 am 09:57 AM

如果您現在可以撰寫一封電子郵件並在以後的更合適的時間發送它,是否會很方便?有了Outlook的調度功能,您就可以做到這一點! 想像一下您在深夜工作,靈感來自輝煌的IDE

如何在Gmail中啟用模板 - 快速設置指南 如何在Gmail中啟用模板 - 快速設置指南 Mar 21, 2025 pm 12:03 PM

本指南向您顯示了啟用Gmail中的電子郵件模板的兩種簡單方法:使用Gmail的內置設置或安裝Gmail Chrome擴展名的共享電子郵件模板。 Gmail模板是經常發送電子郵件的大量節省時間,消除了

如何在Excel單元中刪除 /拆分文本和數字 如何在Excel單元中刪除 /拆分文本和數字 Apr 01, 2025 am 05:07 AM

本教程展示了使用內置功能和自定義VBA函數在Excel單元格中分離文本和數字的幾種方法。 您將在刪除文本時學習如何提取數字,隔離文本時丟棄數字

See all articles