SpringBoot整合EasyExcel
summo 人气:0栗子
在后端管理系统的开发中,经常有需要导出当前表格数据的功能
,有些前端表格组件可以直接做到,但是不够灵活。因为前端拿到的数据始终是经过处理的,如果想拿到原版数据,必须后端处理。如下图:
除了使用Apache POI包,还有没有其他的选择?当然有! 这里我给大家推荐一款非常简单且容易上手的开源组件:Alibaba EasyExcel
1.组件介绍
首先放出官网地址,欢迎大家star(目前已经24K): https://alibaba-easyexcel.github.io/docs/current/
EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。
64M内存20秒读取75M(46W行25列)的Excel(3.0.2+版本)
Alibaba EasyExcel的核心类是EasyExcel
类
/** * 最简单的读 * <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(); }
/** * 最简单的写 * <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()); }
2.配置文件
SpringBoot项目pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>SpringBoot-easyexcel</artifactId> <version>0.0.1-SNAPSHOT</version> <name>SpringBoot-easyexcel</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 一般easyexcel都会和lombok搭配使用 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> <!-- 最新版 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.1.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3.项目代码
项目结构
ExportController.java
package com.example.springbooteasyexcel.controller; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.write.metadata.WriteSheet; import com.example.springbooteasyexcel.data.Mock; import com.example.springbooteasyexcel.sheet.CitySheet; import com.example.springbooteasyexcel.sheet.CompanySheet; import com.example.springbooteasyexcel.sheet.UserSheet; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URLEncoder; @RestController @RequestMapping("/export") public class ExportController { /** * @param response * @url <a>http://localhost:8080/export/test1</a> * 在Excel中写入单个sheet */ @RequestMapping("/test1") public void test1(HttpServletResponse response) { //从HttpServletResponse中获取OutputStream输出流 try { // 设置响应类型 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); // 设置编码格式 response.setCharacterEncoding("utf-8"); // 设置URLEncoder.encode 防止中文乱码 String fileName = URLEncoder.encode("用户信息表", "UTF-8").replaceAll("\\+", "%20"); // 设置响应头 response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); // 写出Excel EasyExcel.write(response.getOutputStream(), UserSheet.class).inMemory(true).sheet("用户信息表").doWrite(Mock.userList()); } catch (IOException e) { throw new RuntimeException("数据或文件损坏,无法下载"); } } /** * 在Excel中写入多个sheet * * @url <a>http://localhost:8080/export/test2</a> */ @RequestMapping("/test2") public void test2(HttpServletResponse response) throws Exception { // 设置响应类型 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); // 设置编码格式 response.setCharacterEncoding("utf-8"); // 设置URLEncoder.encode 防止中文乱码 String fileName = URLEncoder.encode("信息表", "UTF-8").replaceAll("\\+", "%20"); // 设置响应头 response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); // 多个sheet的输出需要使用ExcelWriter类,这里想要下载成功,需要输出到OutputStream中 try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).inMemory(true).build()) { // 创建用户信息表的sheet,写入用户信息数据,1代表sheet的位置是第一个 WriteSheet userInfoSheet = EasyExcel.writerSheet(0, "用户信息表").head(UserSheet.class).build(); excelWriter.write(Mock.userList(), userInfoSheet); // 创建城市信息表的sheet,写入城市信息数据,2代表sheet的位置是第二个 WriteSheet cityInfoSheet = EasyExcel.writerSheet(1, "城市信息表").head(CitySheet.class).build(); excelWriter.write(Mock.cityList(), cityInfoSheet); // 创建公司信息表的sheet,写入公司信息数据,3代表sheet的位置是第三个 WriteSheet companyInfoSheet = EasyExcel.writerSheet(2, "公司信息表").head(CompanySheet.class).build(); excelWriter.write(Mock.companyList(), companyInfoSheet); } } }
Mock.java
以下数据均来自于网络,如有侵权,请联系删除
package com.example.springbooteasyexcel.data; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.data.RichTextStringData; import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.write.metadata.style.WriteFont; import com.example.springbooteasyexcel.sheet.CitySheet; import com.example.springbooteasyexcel.sheet.CompanySheet; import com.example.springbooteasyexcel.sheet.UserSheet; import org.apache.poi.ss.usermodel.IndexedColors; import java.util.ArrayList; import java.util.Calendar; import java.util.List; public class Mock { public static List<UserSheet> userList() { List<UserSheet> list = new ArrayList<>(10); list.add(UserSheet.builder().userId(001L).userName("张三").userPhone("11112223123").userEmail("zhansan@163.com").userAddress("北京朝阳区").gender(buildCellData("男")).registerTime(Calendar.getInstance().getTime()).build()); list.add(UserSheet.builder().userId(002L).userName("李四").userPhone("11112223123").userEmail("lisi@qq.com").userAddress("南京玄武门").gender(buildCellData("女")).registerTime(Calendar.getInstance().getTime()).build()); list.add(UserSheet.builder().userId(003L).userName("王五").userPhone("11112223123").userEmail("wangwu@google.com").userAddress("杭州未来科技城").gender(buildCellData("男")).registerTime(Calendar.getInstance().getTime()).build()); list.add(UserSheet.builder().userId(004L).userName("赵六").userPhone("11112223123").userEmail("zhaoliu@baidu.com").userAddress("上海徐家汇").gender(buildCellData("女")).registerTime(Calendar.getInstance().getTime()).build()); return list; } private static WriteCellData<String> buildCellData(String gender) { // 设置单个单元格多种样式 WriteCellData<String> cellData = new WriteCellData<>(); // 设置单个单元格的填充类型 cellData.setType(CellDataTypeEnum.RICH_TEXT_STRING); RichTextStringData richTextStringData = new RichTextStringData(); cellData.setRichTextStringDataValue(richTextStringData); richTextStringData.setTextString(gender); WriteFont writeFont = new WriteFont(); if ("男".equalsIgnoreCase(gender)) { //设置颜色为红色 writeFont.setColor(IndexedColors.RED.getIndex()); } else if ("女".equalsIgnoreCase(gender)) { //设置颜色为绿色 writeFont.setColor(IndexedColors.GREEN.getIndex()); } //应用颜色字体 richTextStringData.applyFont(writeFont); return cellData; } public static List<CitySheet> cityList() { List<CitySheet> list = new ArrayList<>(10); list.add(CitySheet.builder().cityName("杭州市").cityDesc("杭州市一般指杭州。 杭州,简称“杭”,古称临安、钱塘,浙江省辖地级市、省会、副省级市、特大城市、国务院批复确定的浙江省经济、文化、科教中心,长江三角洲中心城市之一,环杭州湾大湾区核心城市、G60科创走廊中心城市。").build()); list.add(CitySheet.builder().cityName("合肥市").cityDesc("合肥市一般指合肥。 合肥,简称“庐”或“合”,古称庐州、庐阳、合淝,安徽省辖地级市、省会,是合肥都市圈中心城市,国务院批复确定的中国长三角城市群副中心城市,全国四大科教基地、现代制造业基地和综合交通枢纽。").build()); list.add(CitySheet.builder().cityName("武汉市").cityDesc("武汉市一般指武汉。 武汉,简称“汉”,别称江城,是湖北省省会,中部六省唯一的副省级市,超大城市,中国中部地区的中心城市,全国重要的工业基地、科教基地和综合交通枢纽,联勤保障部队机关驻地。").build()); list.add(CitySheet.builder().cityName("深圳市").cityDesc("深圳市一般指深圳。 深圳,简称“深”,别称鹏城,广东省辖地级市,是广东省副省级市,国家计划单列市,超大城市,国务院批复确定的中国经济特区、全国性经济中心城市、国际化城市、科技创新中心、区域金融中心、商贸物流中心。").build()); return list; } public static List<CompanySheet> companyList() { List<CompanySheet> list = new ArrayList<>(10); list.add(CompanySheet.builder().companyName("阿里巴巴").companyBoss("马云").companyBase("杭州市").companyDesc("阿里巴巴集团经营多项业务,另外也从关联公司的业务和服务中取得经营商业生态系统上的支援。业务和关联公司的业务包括:淘宝网、天猫、聚划算、全球速卖通、阿里巴巴国际交易市场、1688、阿里妈妈、阿里云、蚂蚁集团 [408] 、菜鸟网络等。").build()); list.add(CompanySheet.builder().companyName("字节跳动").companyBoss("张一鸣").companyBase("北京市").companyDesc("字节跳动的全球化布局始于2015年 [3] ,“技术出海”是字节跳动全球化发展的核心战略 [4] ,其旗下产品有今日头条、西瓜视频、抖音、头条百科、皮皮虾、懂车帝、悟空问答等。").build()); list.add(CompanySheet.builder().companyName("腾讯").companyBoss("马化腾").companyBase("深圳市").companyDesc("社交和通信服务QQ及微信/WeChat、社交网络平台QQ空间、腾讯游戏旗下QQ游戏平台、门户网站腾讯网、腾讯新闻客户端和网络视频服务腾讯视频等。").build()); list.add(CompanySheet.builder().companyName("百度").companyBoss("李彦宏").companyBase("北京市").companyDesc("百度(Baidu)是拥有强大互联网基础的领先AI公司。百度愿景是:成为最懂用户,并能帮助人们成长的全球顶级高科技公司。").build()); return list; } }
CitySheet.java
package com.example.springbooteasyexcel.sheet; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.write.style.ColumnWidth; import lombok.Builder; import lombok.Data; @Data @Builder public class CitySheet { @ExcelProperty(value = "城市名称", index = 0) @ColumnWidth(10) private String cityName; @ExcelProperty(value = "城市介绍", index = 1) @ColumnWidth(60) private String cityDesc; }
CompanySheet.java
package com.example.springbooteasyexcel.sheet; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.write.style.ColumnWidth; import lombok.Builder; import lombok.Data; @Data @Builder public class CompanySheet { @ExcelProperty(value = "公司名称", index = 0) @ColumnWidth(10) private String companyName; @ExcelProperty(value = "公司创始人", index = 1) @ColumnWidth(10) private String companyBoss; @ExcelProperty(value = "公司总基地", index = 2) @ColumnWidth(10) private String companyBase; @ExcelProperty(value = "公司简介", index = 3) @ColumnWidth(50) private String companyDesc; }
UserSheet.java
package com.example.springbooteasyexcel.sheet; import com.alibaba.excel.annotation.ExcelIgnore; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.metadata.data.WriteCellData; import lombok.Builder; import lombok.Data; import java.util.Date; @Data @Builder public class UserSheet { @ExcelProperty(value = "用户ID", index = 0) @ColumnWidth(10) private Long userId; @ExcelProperty(value = "用户名称", index = 1) @ColumnWidth(10) private String userName; @ExcelProperty(value = {"基本信息", "手机号码"}, index = 2) @ColumnWidth(20) private String userPhone; @ExcelProperty(value = {"基本信息", "电子邮箱"}, index = 3) @ColumnWidth(20) private String userEmail; @ExcelProperty(value = {"基本信息", "地址"}, index = 4) @ColumnWidth(20) private String userAddress; @ExcelProperty(value = "注册时间", index = 5) @ColumnWidth(20) private Date registerTime; @ExcelProperty(value = "性别,男:红色/女:绿色") @ColumnWidth(30) private WriteCellData<String> gender; /** * 忽略这个字段 */ @ExcelIgnore private Integer age; }
SpringBootEasyexcelApplication.java
package com.example.springbooteasyexcel; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringBootEasyexcelApplication { public static void main(String[] args) { SpringApplication.run(SpringBootEasyexcelApplication.class, args); } }
4.效果展示
单个sheet导出
多个sheet导出
5.总结
- 1、Alibaba EasyExcel不仅支持写Excel,还支持读Excel和填充Excel,有兴趣的话可以自己去研究,官网地址已经贴在上面了,我这里只做一个引路的。
- 2、常用注解有三个
@ExcelProperty
、@ColumnWidth
、@ExcelIgnore
。 (1)@ExcelProperty
不仅确定表头,还可以合并行,用法如下:
@ExcelProperty(value = {"基本信息", "手机号码"}, index = 2) @ColumnWidth(20) private String userPhone; @ExcelProperty(value = {"基本信息", "电子邮箱"}, index = 3) @ColumnWidth(20) private String userEmail; @ExcelProperty(value = {"基本信息", "地址"}, index = 4) @ColumnWidth(20) private String userAddress;
效果如下:
- (2)
@ColumnWidth
主要是控制列宽 - (3)
@ExcelIgnore
忽略不需要输出的字段 3、写有两种形式 (1)写到文件
/** * 最简单的写 * <p> * 1. 创建excel对应的实体对象 参照{@link DemoData} * <p> * 2. 直接写即可 */ @Test public void simpleWrite() { // 注意 simpleWrite在数据量不大的情况下可以使用(5000以内,具体也要看实际情况),数据量大参照 重复多次写入 // 写法1 JDK8+ // since: 3.0.0-beta1 String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 // 如果这里想使用03 则 传入excelType参数即可 EasyExcel.write(fileName, DemoData.class) .sheet("模板") .doWrite(() -> { // 分页查询数据 return data(); }); // 写法2 fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 // 如果这里想使用03 则 传入excelType参数即可 EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data()); // 写法3 fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去写 try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) { WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); excelWriter.write(data(), writeSheet); } }
(2)写到Web流,这里的ContentType和CharacterEncoding不要乱码,否则很容易乱码或者文件损坏
/** * 文件下载(失败了会返回一个有部分数据的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.openxmlformats-officedocument.spreadsheetml.sheet"); 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()); }
加载全部内容