在工作中,经常需要把excel中的数据导入系统,亦或是把系统中符合筛选条件的数据通过excel的方式导出。
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后的存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到KB级别,并且再大的excel不会出现内存溢出;03版excel的处理采用POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便。
引入jar包。由于easyExcel中已经引入poi-ooxml、poi等jar。如果开发的老项目中也有poi-ooxml、poi的引入,注意jar包版本问题。
com.alibaba
easyexcel
1.1.2-beta5
学习easyExcel最好的方式,就是下载源码,参考项目中的测试用例。本篇教程为实践的总结,解决了实际开发中遇到的问题。两者结合学习事半功倍。
easyExcel GitHub链接
easyExcel源码的测试用例中,提供了多种读取excel的方法。比如:
建议采用第4种方式,因为满足的场景更多,03版excel处于淘汰阶段,不予介绍;上述提到4种excel解析方法具体该如何使用,可以参考easyExcel源码中的测试用例。
easyExcel源码的测试用例中,仅给出mavan项目resource路径下静态excel文件的解析的demo程序,实际项目中,文件都来自ajax发送的请求体。下面给出实际开发中controller层的代码。
@ResponseBody
@RequestMapping(value = "/importExcel", method = RequestMethod.POST,
produces = "application/json;charset=utf-8")
public Result batchAddByExcel(
@RequestParam(value = "excel", required = false) MultipartFile excel,
@RequestParam(value = "name", required = false) String currentUser,
@RequestParam(value = "userId", required = false) Integer userId,
BatchAddPromotionVO request) {
// 打印入参日志
log.info("request = {},name = {}", request, currentUser);
// 进行参数校验,检查入参的合理性
Result result = checkParam(excel, request);
if (!result.isSuccess()) {
return result;
}
Map> sheetRowModelMap = null;
try {
// 解析excel文档。具体如何解析,省略。请参考easyExcel的源码测试用例
sheetRowModelMap = parseExcelData(excel);
} catch (Exception e) {
log.error("parseExcelData error = ", e);
return Result.wrapError(1, "解析excel失败" + e.getMessage());
}
// 遍历excel数据转化为业务需要的VO数据
List list = parseExcelDataToVO(request, sheetRowModelMap);
// 进行业务代码编写
return Result.wrapSuccess(true);;
}
注意点:在校验excel文件的合理性时,用下面的方式可能是有问题的。
private Result checkParam(MultipartFile excel, BatchAddPromotionVO request) {
if (excel == null || request == null) {
return Result.wrapError(1, "参数异常");
}
// excel文件的扩展名是可以是大写的 XLSX,所以这么判断不完整
if (!excel.getOriginalFilename().contains(".xlsx")) {
return Result.wrapError(1, "文件格式错误,请重新下载模板");
}
return Result.wrapSuccess(true);
}
上述代码中BatchAddPromotionVO,是一个复杂的java对象,定义如下:
package com.missfresh.pmp.admin.vo.productPromotion;
import com.alibaba.fastjson.JSONArray;
import com.missfresh.domain.Result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BatchAddPromotionVO {
private String operateGroup;
private Integer promoteLevel;
private Integer businessType;
/**
* 位置信息,用于接收一个大json字符串
*/
private String positionString;
/**
* 上面的 positionString接收到参数后,需要编写代码将其 转成 positionList
*/
private List positionList;
}
这个controller方法既需要上传excel文件,又需要上传一堆业务参数。代码编写完成后,使用postman调用接口,是一个问题,该如何调接口?如下图所示:
首先需要了解什么是 MIME TYPE?链接:https://www.cnblogs.com/jsean/articles/1610265.html
easyExcel中的测试用例中也给出了多种写静态excel文件的方法,可以供开发者学习。在实际的业务中,需要把符合筛选条件的数据通过excel的方式导出、提供下载excel的功能。
@ResponseBody
@RequestMapping(value = "/exportSelectedPromotion.do",
method = RequestMethod.POST, produces = "application/json;charset=utf-8")
public void exportPromotionByExcel07(@RequestBody PromotionListQueryVO queryVO,
HttpServletResponse response) {
log.info("queryVO = {}", queryVO);
try {
// 查符合筛选条件的数据
List list = getPromotionDTOList(queryVO);
// 把符合条件的数据,转化成写excel需要的Model实体
List modelList = converseListToWriteModel(list);
String time = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
// 指定导出的excel文件的名字
String fileName = "promotion_export_" + time;
// 指定工作簿的名字
String sheetName = "sheet1";
// easyexcel工具类实现Excel文件导出(下文给出ExcelUtil的代码)
ExcelUtil.writeExcel(response, modelList, fileName, sheetName, new PromotionWriteModel());
} catch (Exception e) {
e.printStackTrace();
log.error("exportPromotionByExcel07 error:", e);
}
}
ExcelUtil代码如下:
package com.missfresh.pmp.admin.util;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;
public class ExcelUtil {
/**
* 导出 Excel :一个 sheet,带表头.
*
* @param response HttpServletResponse
* @param list 数据 list,每个元素为一个 BaseRowModel
* @param fileName 导出的文件名
* @param sheetName 导入文件的 sheet 名
* @param model 映射实体类,Excel 模型
* @throws Exception 异常
*/
public static void writeExcel(
HttpServletResponse response, List extends BaseRowModel> list,
String fileName, String sheetName, BaseRowModel model) throws Exception {
ExcelWriter writer =
new ExcelWriter(getOutputStream(fileName, response), ExcelTypeEnum.XLSX);
Sheet sheet = new Sheet(1, 0, model.getClass());
sheet.setSheetName(sheetName);
writer.write(list, sheet);
writer.finish();
}
/**
* 导出文件时为Writer生成OutputStream.
*
* @param fileName 文件名
* @param response response
* @return ""
*/
private static OutputStream getOutputStream(String fileName,
HttpServletResponse response) throws Exception {
try {
fileName = URLEncoder.encode(fileName, "UTF-8");
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf8");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");
response.setHeader("Pragma", "public");
response.setHeader("Cache-Control", "no-store");
response.addHeader("Cache-Control", "max-age=0");
return response.getOutputStream();
} catch (IOException e) {
throw new Exception("导出excel表格失败!", e);
}
}
}
填入excel中的数据,按键盘del键删不干净,easyExcel在解析时依然可以读到del键删过的单元格,bean里每个元素都解析成了null,过滤掉这些bean就行。临时解决办法时,别操作del键。选中要删除的 行 – 右键 —删除,这样能删干净。如下图:
end,转载请注明出处。