前言
项目中操作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 failure
Caused by: org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException: No valid entries or contents found, this is not a valid OOXML (Office Open XML) file
at 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包里面的模板,并且输出文件流