前言
項目中操作excel是一種很常用的功能,比如下載一份excel的報價單。這篇文章會介紹一款excel的處理工具以及導出遇到的三個常見異常(重要)。
之前遇到一個這樣的需求:后臺管理頁面,點擊下載按鈕,下載一份excel格式的報價清單
是不是讓人頭疼? 別怕,往下看,很簡單~
easyexcel
EasyExcel
是一個基于Java的、快速、簡潔、解決大文件內存溢出的Excel處理工具。他能讓你在不用考慮性能、內存的等因素的情況下,快速完成Excel的讀、寫等功能,這是阿里開源項目,官方文檔:https://easyexcel.opensource.alibaba.com/docs/current/
文檔里面有"讀Excel",“寫Excel”,"填充Excel"這三個欄目,從上面的需求來看,我們導出的表格不是規則的表格,并且里面還有特定的樣式,因此我們選擇填充Excel。
文檔寫得非常詳細,又是中文,大家可以自己看文檔。我這里給出一個簡單的例子
引入依賴:
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version>
</dependency>
文檔好像沒有直接給出依賴
瀏覽器下載示例:
public void down(){ExcelWriter excelWriter = null; // Ⅰtry {//boards 數據省略//access 數據省略ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletResponse response = servletRequestAttributes.getResponse();// ⅡString fileName = URLEncoder.encode(LocalDate.now() + " 測試.xlsx", StandardCharsets.UTF_8.toString()).replace("+", "%20"); // Ⅲresponse.setContentType("application/force-download");response.setCharacterEncoding("utf-8");response.setHeader("Content-Disposition", "attachment;filename=" + fileName);InputStream inputStream = this.getClass().getResourceAsStream("/static/price-template.xlsx");// ⅣexcelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(inputStream).excelType(ExcelTypeEnum.XLSX).build();WriteSheet writeSheet = EasyExcel.writerSheet().build();//ⅤFillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();excelWriter.fill(new FillWrapper("list1", boards), fillConfig, writeSheet);excelWriter.fill(new FillWrapper("list2", access), fillConfig, writeSheet);excelWriter.fill(orderEntity, writeSheet);Map<String, BigDecimal> map = new HashMap<>();map.put("boardsTotalPrice", boardsTotalPrice);map.put("accessTotalPrice", accessTotalPrice);map.put("totalPrice", boardsTotalPrice.add(accessTotalPrice));excelWriter.fill(map, writeSheet);} catch (Exception e){e.printStackTrace();} finally {if (!Objects.isNull(excelWriter)){excelWriter.close();}}
}
**Ⅰ:**try catch之前定義一個ExcelWriter,最后在finally里面關閉,釋放資源
**Ⅱ:**HttpServletResponse response可以通過參數傳入,這里直接通過對象獲取
**Ⅲ:**輸出格式如下的文件”2023-12-11 測試.xlsx“,因為是中文,所以使用URLEncoder,因為有空格,所以有這樣的代.replace(“+”, “%20”)
**Ⅳ:**以流的形式讀取模板,注意模板是放在resource下的static目錄下
**Ⅴ:**這里就是填充數據了,因為有兩個列表,因此定義list1和list2,下面看看填充的模板
總得來說大部分代碼是固定的,只有填充數據那里是動態的
遇到的問題
這里講講開發過程中遇到的問題
1.依賴沖突
先看看會報什么錯誤
com.alibaba.excel.exception.ExcelGenerateException: java.lang.NoSuchMethodError: 'void org.apache.poi.ss.usermodel.Cell.setBlank()'
或
com.alibaba.excel.exception.ExcelGenerateException: java.lang.NoSuchMethodError: 'javax.xml.parsers.DocumentBuilder org.apache.poi.util.XMLHelper.newDocumentBuilder()'
出現這個問題大概率就是引入了
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.0.0</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.0.0</version>
</dependency>
因為
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version>
</dependency>
已經包含了上面的兩個依賴。
2.模板文件編譯后損壞
報錯:
com.alibaba.excel.exception.ExcelGenerateException: Create workbook failureCaused by: org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException: No valid entries or contents found, this is not a valid OOXML (Office Open XML) fileat org.apache.poi.openxml4j.util.ZipArchiveThresholdInputStream.getNextEntry(ZipArchiveThresholdInputStream.java:145)
Caused by: java.util.zip.ZipException: Unexpected record signature: 0X9
因為文件損壞,無法創建workbook,如遇到這個問題在pom文件添加以下插件即可
<!-- 避免font文件的二進制文件格式壓縮破壞 -->
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><version>3.2.0</version><configuration><nonFilteredFileExtensions><nonFilteredFileExtension>xlsx</nonFilteredFileExtension></nonFilteredFileExtensions></configuration>
</plugin>
3.線上無法讀取模板
先看看報錯
java.io.FileNotFoundException: file:/data2/xxx0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/static/template/price-template.xlsx (No such file or directory)
你本地是沒有問題的,但是部署到線上卻有問題
首先出現這樣的問題大概率是你在讀取模板的時候使用了以下的方式
ResourceUtils.getFile("classpath:static\xxx");
或
ResourceUtils.getURL("/static/price-template.xlsx").getPath();
通過這種正常路徑訪問
比如說我上面的代碼剛開始是這樣寫的
String path = ResourceUtils.getURL("/static/price-template.xlsx").getPath();
FileInputStream inputStream = new FileInputStream(path);
本地是沒有問題的,因為price-template.xlsx文件就是真實的存儲在磁盤里面的static目錄下,但到線上就不一樣了,因為當打成jar包,該文件是存在jar包文件資源里,而不是真實存在于磁盤路徑上。
所以后面我把代碼改成
InputStream inputStream = this.getClass().getResourceAsStream("/static/price-template.xlsx");
直接獲取jar包里面的模板,并且輸出文件流