EasyExcel
EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。
1. 大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,
而是从磁盘上一行行读取数据,逐个解析;
2. 采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)
详细操作见官网:EasyExcel 超详细!!!
Web上传
- 上传的excel:
- 创建excel对应实体对象和监听器对象(每解析一行,边将解析结果通知EventListener,执行invoke方法),调用EasyExcel.read读文件:
@Data
public class UploadData {
@ExcelProperty("用户ID")
private Long userId;
@ExcelProperty("内容")
private String content;
@ExcelProperty("提交时间")
private Date submitTime;
}
@Slf4j
@Data
public class UploadExcelListener extends AnalysisEventListener<UploadData> {
/**
* Excel定义的表头
*/
private static final List<String> COMMON_HEADS = new ArrayList(16);
/**
* Excel解析出的行数据
*/
private List<UploadData> datas = new ArrayList<>();
static {
COMMON_HEADS.add("用户ID");
COMMON_HEADS.add("内容");
COMMON_HEADS.add("提交时间");
}
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
log.info("开始解析Excel数据,headMap={}", JsonUtils.serialize(headMap));
// Excel表头数据校验
int headSize = headMap.size();
if (headSize != COMMON_HEADS.size()) {
throw new RuntimeException("上传的Excel中表头与模板不一致,核实后重新上传!");
}
for (int i = 0; i < headSize; i++) {
if (!COMMON_HEADS.get(i).equals(headMap.get(i))) {
throw new RuntimeException("上传的Excel中表头与模板不一致,核实后重新上传!");
}
}
}
@Override
public void invoke(UploadData data, AnalysisContext context) {
Integer rowIndex = context.readRowHolder().getRowIndex();
log.info("开始解析Excel数据,rowIndex={}, data={}", rowIndex, JsonUtils.serialize(data));
// Excel行数据有效性校验
Assert.isTrue(data.getUserId() != null && data.getUserId() > 0L,
String.format("Excel中第%d行:用户ID不能为空且>0!", rowIndex));
Assert.notBlank(data.getContent(), String.format("Excel中第%d行:内容不能为空!", rowIndex));
datas.add(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
log.info("Excel解析完成,xxx");
}
}
// 上传excel文件url
@PostMapping("/upload/url")
public void uploadExcel(@RequestParam String excelFileUrl) {
File tmpFile = null;
try {
// 1.创建临时文件,并下载excel
tmpFile = FileUtil.createTempFile().toFile();
boolean downloadSuccess = HttpUtil.downloadFile(excelFileUrl, tmpFile);
if (!downloadSuccess) {
log.error("无效的Excel文件地址,excelFileUrl={}", excelFileUrl);
return;
}
// 2.读Excel,并解析
UploadExcelListener uploadExcelListener = new UploadExcelListener();
EasyExcel.read(tmpFile, UploadData.class, uploadExcelListener).sheet().doRead();
List<UploadData> datas = uploadExcelListener.getDatas();
// 3.进行业务操作 todo
} catch (IOException e) {
log.error("Excel上传异常,xxx");
}
}
// 上传excel文件
@PostMapping("/upload/file")
public void upload(@RequestParam MultipartFile file) {
try {
// 1.读Excel,并解析
UploadExcelListener uploadExcelListener = new UploadExcelListener();
EasyExcel.read(file.getInputStream(), UploadData.class, uploadExcelListener).sheet().doRead();
List<UploadData> datas = uploadExcelListener.getDatas();
// 2.进行业务操作 todo
} catch (IOException e) {
log.error("Excel上传异常,xxx");
}
}
- 解析出excel中数据:
Web下载
- 创建excel对应的实体对象,调用EasyExcel.write写文件:
@Data
public class DownloadData {
@ExcelProperty("用户ID")
private Long userId;
@ExcelProperty("内容")
private String content;
// 导出excel时忽略此字段
@ExcelIgnore
@ExcelProperty("评分")
private Double score;
// 导出excel时按照此格式导出日期
@DateTimeFormat("yyyy-MM-dd")
@ExcelProperty("评分时间")
private Date createTime;
}
@PostMapping("/download")
public void download(HttpServletResponse response) {
// 1.查询业务数据 todo
List<DownloadData> datas = getBusinessData();
try {
// 2.导出Excel
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("下载Excel", "UTF-8");
response.addHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet().doWrite(datas);
} catch (IOException e) {
log.error("Excel下载异常");
}
}
- 导出的excel: