alibaba的easyexcel导入导出Excel处理
简介:EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。
官方文档:https://easyexcel.opensource.alibaba.com/
前提准备
依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.1</version>
</dependency>
Excel导入
最简单的读步骤:
- 创建excel对应的实体对象
- 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器(StringExcelListener)
- 直接读即可
测试实体类
@Data
@EqualsAndHashCode
public class DemoData {
private String string;
private Date date;
private Double doubleData;
}
StringExcelListener监听类
有个很重要的点,StringExcelListener不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去。
@Slf4j
public class StringExcelListener extends AnalysisEventListener
{
/**
* 自定义用于暂时存储data
* 可以通过实例获取该值
*/
private List<List<String>> datas = ListUtils.newArrayList();
/**
* 每解析一行都会回调invoke()方法
* @param o
* @param analysisContext
*/
@Override
public void invoke(Object o, AnalysisContext analysisContext)
{
@SuppressWarnings("unchecked") Map<String, String> stringMap = (HashMap<String, String>) o;
//数据存储到list,供批量处理,或后续自己业务逻辑处理。
datas.add(new ArrayList<>(stringMap.values()));
//根据自己业务做处理
}
/**
* 所有数据解析完成了 都会来调用
*
* @param analysisContext
*/
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext)
{
log.info("所有数据解析完成!");
}
/**
* 返回数据
*
* @return 返回读取的数据集合
**/
public List<List<String>> getDatas() {
return datas;
}
/**
* 设置读取的数据集合
*
* @param datas 设置读取的数据集合
**/
public void setDatas(List<List<String>> datas) {
this.datas = datas;
}
}
封装ExcelUtils工具类
@Component
public class ExcelUtils
{
/**
* 读取excel数据
*
* @param file
* @return result
* @author hecc
* @date 2022/9/27 17:43
**/
public List<List<String>> writeWithoutHead(MultipartFile file) {
List<List<String>> result = Lists.newArrayList();
try {
result = writeWithoutHead(file.getInputStream());
} catch (IOException e) {
throw new RuntimeException("APP_ADD_ERROR,文件解析异常");
}
if (CollectionUtils.isEmpty(result)) {
throw new RuntimeException("APP_ADD_ERROR,文件为空");
}
return result;
}
public List<List<String>> writeWithoutHead(InputStream inputStream) {
StringExcelListener listener = new StringExcelListener();
ExcelReader excelReader = EasyExcelFactory.read(inputStream, null, listener).headRowNumber(1).build();
excelReader.read();
List<List<String>> datas = listener.getDatas();
excelReader.finish();
return datas;
}
}
controller层
@RestController
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE,makeFinal = true)
@RequestMapping("/excel")
public class ExcelController
{
ExcelUtils excelUtils;
@PostMapping("/read")
public void read(@RequestParam("file") MultipartFile file) {
if(file.isEmpty()){
throw new RuntimeException("请上传文件!");
}
List<List<String>> lists = excelUtils.writeWithoutHead(file);
//处理,存入对象
List<DemoData> demoDataList = ListUtils.newArrayList();
lists.forEach(item->{
DemoData demoData = new DemoData();
demoData.setString(item.get(0));
try
{
demoData.setDate(DateUtils.parseDate(item.get(1)));
}
catch (ParseException e)
{
throw new RuntimeException(e);
}
demoData.setDoubleData(Double.valueOf(item.get(2)));
demoDataList.add(demoData);
});
demoDataList.forEach(System.out::println);
}
}
运行结果
修改版本
测试实体类
@Data
@EqualsAndHashCode
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
/**
* 这里用string 去接日期才能格式化。其它的也可能需要String才行
*/
@DateTimeFormat("yyyy年MM月dd日")
private String date;
@ExcelProperty(index = 2)
private Double doubleData;
}
StringExcelListener监听类
有个很重要的点,StringExcelListener不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去。
@Slf4j
public class StringExcelListener<T> extends AnalysisEventListener<T>
{
/**
* 自定义用于暂时存储data
* 可以通过实例获取该值
*/
private List<T> datas = ListUtils.newArrayList();
/**
* 每解析一行都会回调invoke()方法
* @param o
* @param analysisContext
*/
@Override
public void invoke(T o, AnalysisContext analysisContext)
{
//@SuppressWarnings("unchecked") Map<String, String> stringMap = (HashMap<String, String>) o;
//数据存储到list,供批量处理,或后续自己业务逻辑处理。
datas.add(o);
//根据自己业务做处理
}
/**
* 所有数据解析完成了 都会来调用
*
* @param analysisContext
*/
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext)
{
log.info("所有数据解析完成!");
}
/**
* 返回数据
*
* @return 返回读取的数据集合
**/
public List<T> getDatas() {
return datas;
}
/**
* 设置读取的数据集合
*
* @param datas 设置读取的数据集合
**/
public void setDatas(List<T> datas) {
this.datas = datas;
}
}
封装ExcelUtils工具类
@Component
public class ExcelUtils
{
/**
* 读取excel数据
*
* @param file
* @return result
* @date 2022/9/27 17:43
**/
public <T> List<T> writeWithoutHead(MultipartFile file, Class<T> clz ) {
List<T> result = Lists.newArrayList();
try {
result = writeWithoutHead(file.getInputStream(),clz);
} catch (IOException e) {
throw new RuntimeException("APP_ADD_ERROR,文件解析异常");
}
if (CollectionUtils.isEmpty(result)) {
throw new RuntimeException("APP_ADD_ERROR,文件为空");
}
return result;
}
public <T> List<T> writeWithoutHead(InputStream inputStream,Class<T> clz) {
StringExcelListener listener = new StringExcelListener();
ExcelReader excelReader = EasyExcelFactory.read(inputStream, clz, listener).headRowNumber(1).build();
excelReader.read();
List<T> datas = listener.getDatas();
excelReader.finish();
return datas;
}
}
controller层
@RestController
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE,makeFinal = true)
@RequestMapping("/excel")
public class ExcelController
{
ExcelUtils excelUtils;
@PostMapping("/read")
public void read(@RequestParam("file") MultipartFile file) {
if(file.isEmpty()){
throw new RuntimeException("请上传文件!");
}
List<DemoData> demoDataList = excelUtils.writeWithoutHead(file,DemoData.class);
demoDataList.forEach(System.out::println);
}
}
运行结果
Excel导出
1.简单导出
测试实体类
@Data
@EqualsAndHashCode
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
@DateTimeFormat("yyyy年MM月dd日")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
}
controller层
@PostMapping("/write")
public void write() {
//写法1
String fileName ="simpleWrite" + System.currentTimeMillis() + ".xlsx";
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);
}
}
//测试数据
private List<DemoData> data() {
List<DemoData> list = ListUtils.newArrayList();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
运行结果:
项目目录下:
2.web导出
实体类同上
controller层
/**
* 文件下载(失败了会返回一个有部分数据的Excel)
* <p>
* 1. 创建excel对应的实体对象 参照{@link DemoData}
* <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(), DemoData.class).sheet("模板").doWrite(data());
}
//测试数据
private List<DemoData> data() {
List<DemoData> list = ListUtils.newArrayList();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
参考:https://blog.csdn.net/liangwenmail/article/details/119916114