教您如何3分钟快速搞定EasyExcel导入与导出功能
作者:陈序员->陈
对于EasyExcel库,我们可以使用它来实现数据的导入和导出,下面这篇文章主要给大家介绍了关于如何3分钟快速搞定EasyExcel导入与导出功能的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
前言
该文章主要是介绍如何快速实现导入与导出功能
一、EasyExcel是什么?
引用官方的说明:EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。
他能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。
二、使用步骤
1.引入库
pom.xml引入jar依赖包:
<!-- EasyExcel --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.6</version> </dependency>
2.导入
2.1创建对应excel表格的实体类
@ExcelProperty该注解的value是对应excle表格的列名称,index对应的是excel列的下标
@Data
public class ImportModel {
/**
* 树根
*/
@ExcelProperty(value = "名称", index = 0)
private String ;
/**
* 树根标题
*/
@ExcelProperty(value = "编码", index = 1)
private String code;
}2.2后端接收请求入口
/**
* 导入
*/
@ApiOperation("导入")
@PostMapping("/importTree")
public R importTree(@RequestParam("file") MultipartFile file) {
/** 根据自己的业务自行选择在哪处理 */
/** 直接在controllerr处理 */
// EasyExcel.read(file.getInputStream(), BigTreeImportCommand.class, new ExcelListener(urbanFacilityManageService)).sheet().doRead();
/** 在service实现类处理 */
Boolean flag = treeService.importTree(file, tenantId);
return R.ok("导入成功");
}2.3业务处理
@Override
public Boolean importTree(MultipartFile file) {
try {
/** 处理数据的监听器,有参构造,根据自己的业务决定 */
TreeExcelListener excelListener = new TreeExcelListener(importService,tenantId);
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
//EasyExcel.read(文件流,对应的Model实体类,监听器).sheet(那张sheet表,可以使用下标或者使用sheet名字).head(表头对应的实体类).headRowNumber(表头占几行).registerReadListener(处理数据的监听器类).doRead()
EasyExcel.read(file.getInputStream(), ImportModel.class, excelListener).sheet(0).head(ImportModel.class).headRowNumber(1).doRead();
return true;
} catch (Exception e) {
throw new RuntimeException(e);
}
}2.4监听器处理数据
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.ieds.back.interfaces.sys.web.command.BigTreeImportModel;
/**
* @Author: ChenFB
* @CreateTime: 2023-04-23 17:18
* @Description: 导入监听器
*/
public class ImportListener extends AnalysisEventListener<BigTreeImportModel> {
private ImportService importService;
private String tenantId;
/** 有参构造,建议将serverjie接口以参数的方式参进来,方便数据入库处理,这个参数自行界定 */
public ImportListener(ImportService importService, String tenantId) {
this.importService = importService;
this.tenantId = tenantId;
}
/** 读取每一行数据都会进入该方法,数据处理与校验可以在该方法进行处理 */
@Override
public void invoke(BigTreeImportModel data, AnalysisContext context) {
}
/** 所有数据读取完毕后,最终会执行这个方法,所以建议数据处理完后,批量在该方法进行入库处理 */
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
}
3.导出
3.1创建导出格式生成对应的excel表格的实体类,这个可以看2.1
3.2后端接收请求入口
/**
* 导出
*/
@ApiOperation("导出")
@GetMapping("/exportTree")
public void exportTree(HttpServletResponse response, @RequestParam("tenantId") String tenantId) {
treeService.exportTree(response,tenantId);
}3.3业务处理
@Override
public void exportTree(HttpServletResponse response, String tenantId) {
ExcelWriter writer = null;
try {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); //设置响应内容类型
response.setCharacterEncoding("utf-8");//编码
// 设置文件名, ps:把字符串中所有的'+'替换成'%20',在URL中%20代表空格
String fileName = URLEncoder.encode("文件名,根据业务决定", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");//设置响应头
response.setHeader("fileName", fileName + ".xlsx");//设置响应头
/** 导出的数据集合 */
List<ExportModel> modelList = new ArrayList<>();
/** registerWriteHandler(合并行数,那些列需要合并) */
writer = EasyExcel.write(response.getOutputStream(), BigTreeImportModel.class).registerWriteHandler(new MergeStrategy(modelList.size(), 0, 1, 2, 7, 8, 10, 11)).build();//获取写出流
WriteSheet sheet = EasyExcel.writerSheet("sheet名称,自行决定").build();//创建表格,设置表格页名称
writer.write(modelList, sheet);//读出
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (null != writer) {
writer.finish();//关闭流
}
}
}4.合并工具类
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import org.apache.commons.collections.map.HashedMap;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import java.util.*;
/**
* @Author: ChenFB
* @CreateTime: 2023-04-20 10:56
* @Description: Excel单元格合并工具类
*/
public class MergeStrategy extends AbstractMergeStrategy {
// 合并的列编号,从0开始,指定的index或自己按字段顺序数
private Set<Integer> mergeCellIndex = new HashSet<>();
// 数据集大小,用于区别结束行位置
private Integer maxRow = 0;
// 禁止无参声明
private MergeStrategy() {
}
public MergeStrategy(Integer maxRow, int... mergeCellIndex) {
Arrays.stream(mergeCellIndex).forEach(item -> {
this.mergeCellIndex.add(item);
});
this.maxRow = maxRow;
}
// 记录上一次合并的信息
private Map<Integer, MergeRange> lastRow = new HashedMap();
// 每行每列都会进入,绝对不要在这写循环
@Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
int currentCellIndex = cell.getColumnIndex();
// 判断该行是否需要合并
if (mergeCellIndex.contains(currentCellIndex)) {
String currentCellValue = cell.getStringCellValue();
int currentRowIndex = cell.getRowIndex();
if (!lastRow.containsKey(currentCellIndex)) {
// 记录首行起始位置
lastRow.put(currentCellIndex, new MergeRange(currentCellValue, currentRowIndex, currentRowIndex, currentCellIndex, currentCellIndex));
return;
}
//有上行这列的值了,拿来对比.
MergeRange mergeRange = lastRow.get(currentCellIndex);
if (!(mergeRange.lastValue != null && mergeRange.lastValue.equals(currentCellValue))) {
// 结束的位置触发下合并.
// 同行同列不能合并,会抛异常
if (mergeRange.startRow != mergeRange.endRow || mergeRange.startCell != mergeRange.endCell) {
sheet.addMergedRegionUnsafe(new CellRangeAddress(mergeRange.startRow, mergeRange.endRow, mergeRange.startCell, mergeRange.endCell));
}
// 更新当前列起始位置
lastRow.put(currentCellIndex, new MergeRange(currentCellValue, currentRowIndex, currentRowIndex, currentCellIndex, currentCellIndex));
}
// 合并行 + 1
mergeRange.endRow += 1;
// 结束的位置触发下最后一次没完成的合并
if (relativeRowIndex.equals(maxRow - 1)) {
MergeRange lastMergeRange = lastRow.get(currentCellIndex);
// 同行同列不能合并,会抛异常
if (lastMergeRange.startRow != lastMergeRange.endRow || lastMergeRange.startCell != lastMergeRange.endCell) {
sheet.addMergedRegionUnsafe(new CellRangeAddress(lastMergeRange.startRow-1, lastMergeRange.endRow, lastMergeRange.startCell, lastMergeRange.endCell));
}
}
}
}
}
class MergeRange {
public int startRow;
public int endRow;
public int startCell;
public int endCell;
public String lastValue;
public MergeRange(String lastValue, int startRow, int endRow, int startCell, int endCell) {
this.startRow = startRow;
this.endRow = endRow;
this.startCell = startCell;
this.endCell = endCell;
this.lastValue = lastValue;
}
}附:EasyExcel结合Mybatis plus的分页功能进行分页导出
工具类
import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Builder;
import lombok.Data;
import org.springframework.http.HttpHeaders;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
public class EasyExcelUtil {
@Data
@Builder
public static class ExcelParam {
/**
* 查询 Mapper
*/
private BaseMapper baseMapper;
/**
* Lambda查詢方式
*/
private LambdaQueryWrapper lambdaQueryWrapper;
/**
* 页码,默认从1开始
*/
private Integer pageNo = 1;
/**
* 分页条数,,默认每个sheet 1000 条数据
*/
private Integer pageSize = 1000;
/**
* 用于存放查询到的結果,让Excel生成
*/
private Class<?> respClazz;
/**
* 生成的Excel 名称,不加后缀
*/
private String fileName;
/**
* Excel sheet名称
*/
private String sheetName;
}
public static void exportExcel(ExcelParam excelParam, HttpServletResponse response) {
response.setContentType("application/vnd.ms-excel;charset=utf-8");
response.setCharacterEncoding("utf-8");
response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + excelParam.getFileName() + ".xlsx");
try (
ServletOutputStream outputStream = response.getOutputStream();
ExcelWriter excelWriter = EasyExcel.write(outputStream, excelParam.getRespClazz()).build();
) {
Page page = new Page(excelParam.getPageNo(), excelParam.getPageSize());
page = (Page) excelParam.getBaseMapper().selectPage(page, excelParam.getLambdaQueryWrapper());
/** 构建 */
WriteSheet writeSheet1 = EasyExcel.writerSheet(1, excelParam.getSheetName() + "第" + excelParam.getPageNo() + "页").build();
/** 获取总数 */
Long totalPage = page.getPages();
List records = page.getRecords();
/** 写入内容 */
excelWriter.write(records, writeSheet1);
writeSheet1 = null; // GC
// 若为空表
if (CollUtil.isEmpty(page.getRecords())) {
/** 生成完毕 */
excelWriter.finish();
/** 立即刷回 */
outputStream.flush();
return;
}
for (int i = excelParam.pageNo + 1, index = 2; i <= totalPage; i++, index++) {
/** 清空*/
records.clear();
WriteSheet writeSheet = EasyExcel.writerSheet(index, excelParam.getSheetName() + "第" + i + "页").build();
page.setCurrent(i);
/** 新的查询 */
page = (Page) excelParam.getBaseMapper().selectPage(page, excelParam.getLambdaQueryWrapper());
records = page.getRecords();
/** 输入内容內容 */
excelWriter.write(records, writeSheet);
}
/** 生成完毕 */
excelWriter.finish();
/** 立即刷回 */
outputStream.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}使用
EasyExcelUtil.ExcelParam p = EasyExcelUtil.ExcelParam.builder()
.baseMapper(workRollPlanService.getBaseMapper()) // 传Service类的BaseMapper进去
.lambdaQueryWrapper(new LambdaQueryWrapper<WorkRollPlan>() // 构建查询条件
.eq(true, WorkRollPlan::getIsDeleted, 0)
)
.pageNo(1) // 数据从第一行开始,如果表头只有一行就是1,表头有两行的话就写2
.respClazz(WorkRollPlan.class) // 这里写实体类
.pageSize(1000) // Excel每页数据大小
.fileName("test") // 文件名
.sheetName("测试sheet") // 页名
.build();
EasyExcelUtil.exportExcel(p, response);总结
到此这篇关于如何3分钟快速搞定EasyExcel导入与导出功能的文章就介绍到这了,更多相关EasyExcel导入与导出内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
