Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便。
本文只介绍最简单的读取,写入和其他复杂操作可参考官方文档;
官方文档地址:**https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
</properties>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.2.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
引入swagger依赖用作测试。
@SpringBootApplication
@EnableOpenApi
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
server:
port: 8081
spring:
application:
name: netty-test-01
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&characterEncoding=utf-8
username: root
password: root
package com.test.netty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.util.ListUtils;
import com.test.netty.pojo.DataDto;
import com.test.netty.service.DataService;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class ExcelListener extends AnalysisEventListener<DataDto> {
private DataService dataService;
private static final int BATCH_COUNT = 5;
private List<DataDto> dataDtos = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
public ExcelListener() {
this.dataDtos = new ArrayList<>();
}
public ExcelListener(DataService dataService) {
this.dataService = dataService;
}
@Override
public void invoke(DataDto dto, AnalysisContext analysisContext) {
log.info("read data:{},invoke size:{}",dto.toString(),dataDtos.size());
dataDtos.add(dto);
if (dataDtos.size() >= BATCH_COUNT){
saveData();
log.info("客户端用户id:{},用户名:{},用户真实姓名:{},手机号:{}",dto.getClientId(),dto.getUsername(),dto.getRealName(),dto.getPhone());
dataDtos = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
int size = dataDtos.size();
saveData();
log.info("full size:{}",size);
}
private void saveData(){
dataService.insertBatch(dataDtos);
log.info("{}条数据,开始存储数据库",dataDtos.size());
}
}
补充一下实体类
@Data
public class DataDto {
private int clientId;
private String username;
private String realName;
private String phone;
}
建表sql:
CREATE TABLE `data_dto`(
`id` int(10) not null auto_increment comment 'id',
`client_id` VARCHAR(50) DEFAULT null COMMENT '客户端用户id',
`username` VARCHAR(20) DEFAULT null COMMENT '用户名',
`real_name` VARCHAR(50) DEFAULT null COMMENT '用户名',
`phone` VARCHAR(50) DEFAULT null COMMENT '手机号',
PRIMARY KEY (`id`)
)
写接口测试一下
package com.test.netty.controller;
import com.alibaba.excel.EasyExcel;
import com.test.netty.ExcelListener;
import com.test.netty.pojo.DataDto;
import com.test.netty.service.DataService;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@RestController
@RequestMapping("excel")
@Api(tags = "excel操作")
public class ExcelController {
@Autowired
private DataService dataService;
@PostMapping("upload")
public void upload(@RequestPart("file") MultipartFile file)throws IOException {
EasyExcel.read(file.getInputStream(), DataDto.class,new ExcelListener(dataService)).autoCloseStream(true).sheet().doRead();
}
}
package com.test.netty.service;
import com.baomidou.mybatisplus.service.IService;
import com.test.netty.pojo.DataDto;
public interface DataService extends IService<DataDto> {
}
package com.test.netty.service.impl;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.test.netty.mapper.DataMapper;
import com.test.netty.pojo.DataDto;
import com.test.netty.service.DataService;
import org.springframework.stereotype.Service;
@Service
public class DataServiceImpl extends ServiceImpl<DataMapper, DataDto> implements DataService {
}
这里直接使用了mybatisPlus的通用方法,不用mybatisPlus的可以写一个批量插入方法
启动项目后,浏览器打开,输入ocalhost:8081/swagger-ui/,进入swagger测试页面
选择文件后执行,成功后查看数据库数据都有。