快速学习阿里巴巴EasyExcel
程序员文章站
2022-06-13 19:10:14
...
1.Excel导入导出的应用场景
- 数据导入:减轻录入的工作量
- 数据导出:统计信息归档
- 数据传输:异构系统之间数据传输
2.EasyExcel简介
2.1.EasyExcel特点
Java领域解析,生成Excel比较有名的框架有Apache poi,jxl等,但他们都存在一个严重的问题就是非常的耗内存,如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc.
EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单,节省内存著称,EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。
2.2.2 代码演示
2.2.3开发环境IDEA+SringBoot
2.2.4.Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.6</version>
</dependency>
<!--xls-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
</project>
2.2.5.实体entity
@Data
@ContentRowHeight(20)
@HeadRowHeight(20)
@ColumnWidth(15)
public class DemoData {
/**
* 设置excel表头名称
*/
@ExcelProperty(value = "学生编号",index = 0)
private Integer sno;
@ExcelProperty(value = "学生姓名",index = 1)
private String sname;
@DateTimeFormat("yyyy-MM-dd")
@ExcelProperty(value = "日期",index = 2 )
private Date loanBeginDate;
}
2.2.6.service类
public interface DemoService {
//导入
boolean save(List<DemoData> list);
//导出
List<DemoData> data(String typeIndex);
}
@Service
public class DemoServiceImpl implements DemoService{
@Override
public boolean save(List<DemoData> list) {
return false;
}
@Override
public List<DemoData> data(String typeIndex) {
List<DemoData> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
DemoData data = new DemoData();
data.setSno(i);
data.setSname("宝宝"+i);
data.setLoanBeginDate(new Date());
list.add(data);
}
return list;
}
}
2.2.7.controller类
@RestController
@RequestMapping("test/")
public class EasyExcelRead {
@Resource
private DemoService demoService;
/*
* 导入
*/
@PostMapping("/upload")
public String upload(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), DemoData.class, new DemoDataListener(demoService)).sheet().doRead();
return "success";
}
/*
* 导出
*/
@GetMapping("download")
public void download(HttpServletResponse response, String typeIndex) throws IOException {
try {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里需要设置不关闭流
String fileName = URLEncoder.encode("导出模板", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), DemoData.class).autoCloseStream(Boolean.FALSE).sheet("模板").doWrite(demoService.data(typeIndex));
} catch (Exception e) {
// 重置response
response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
Map<String, String> map = new HashMap<String, String>();
map.put("status", "failure");
map.put("message", "下载文件失败" + e.getMessage());
response.getWriter().println(JSON.toJSONString(map));
}
}
}
2.2.8.listener监听类
public class DemoDataListener extends AnalysisEventListener<DemoData> {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 3000;
List<DemoData> list = new ArrayList<DemoData>();
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private DemoService demoService;
public DemoDataListener(DemoService demoService) {
this.demoService = demoService;
}
@Override
public void invoke(DemoData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
if (list.size() >= BATCH_COUNT) {
saveData();
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
saveData();
LOGGER.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
//demoService.save(list);
list.stream().forEach(demoData -> System.out.println(demoData.getSname()));
LOGGER.info("存储数据库成功!");
}
}
easyexcel 读excel 的时候可以用 AnalysisEventListener 监听器 invoke() 对读文件的数据监听处理,每处理一行会调用invoke方法
2.3.启动项目,访问导入接口 127.0.0.1:8080/test/upload
表格数据
访问接口
后台打印
2.4 导出数据
本应该从DB拿数据在此省略步骤直接从代码中生成数据
public List<DemoData> data(String typeIndex) {
List<DemoData> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
DemoData data = new DemoData();
data.setSno(i);
data.setSname("张三"+i);
data.setLoanBeginDate(new Date());
list.add(data);
}
return list;
}
访问接口 直接使用浏览器访问127.0.0.1:8080/test/download