EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。
增加相关依赖包
com.alibaba
easyexcel
2.1.4
在doSomething方法中实现新增逻辑,可实现异步导入
/**
* 监听类,可以自定义
*
* @author liuyi
* @Created 2019-7-18 18:01:53
**/
public class ExcelListener extends AnalysisEventListener {
/**
* 自定义用于暂时存储data。
* 可以通过实例获取该值
*/
private List
/**
* Excel工具类
*
* @author liuyi
* @Created 2019-7-18 18:01:53
**/
@Slf4j
public class EasyExcelUtil {
/**
* 读取单个sheet的excel文件
* @param excel 文件
* @param t 实体类型
* @param headRowNumber 头行数
* @return
* @throws Exception
*/
public static List readSingleExcel(MultipartFile excel, T t,int headRowNumber) throws IOException {
return EasyExcel.read(excel.getInputStream(), t.getClass(), new ExcelListener())
.sheet().headRowNumber(headRowNumber).doReadSync();
}
/**
* 导出文件
* 导出模板时,tList传一个空list即可
* @param tList 数据集
* @param tClass 数据类型
* @param
* @throws IOException
*/
public static void writeSingleExcel(String fileName,String sheetName, List tList, Class tClass) throws IOException{
HttpServletResponse response = RequestHolder.getResponse();
try (ServletOutputStream outputStream = response.getOutputStream()){
setResponse(fileName, response);
EasyExcel.write(outputStream, tClass).autoCloseStream(Boolean.FALSE)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.registerWriteHandler(new CustomCellWriteHandler())
.sheet(sheetName)
.doWrite(tList);
} catch (Exception e) {
errorWrite(response, e);
}
}
/**
* 导出多sheet
* @param fileName 文件名
* @param sheetExcelDataList sheet对象
* @throws IOException
*/
public static void writeMultiExcel(String fileName, List sheetExcelDataList) throws IOException{
HttpServletResponse response = RequestHolder.getResponse();
ServletOutputStream outputStream = response.getOutputStream();
setResponse(fileName, response);
ExcelWriter excelWriter = EasyExcel.write(outputStream).autoCloseStream(false).build();
try {
for (int i = 0,length = sheetExcelDataList.size(); i < length; i++) {
WriteSheet writeSheet = EasyExcel.writerSheet(i+1, sheetExcelDataList.get(i).getSheetName())
.head(sheetExcelDataList.get(i).getTClass())
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
excelWriter.write(sheetExcelDataList.get(i).getDataList(), writeSheet);
}
} catch (Exception e) {
errorWrite(response, e);
}finally {
// 刷新流,不加这句话,下载文件损坏打不开
outputStream.flush();
// outputStream.close();
if(excelWriter != null){
// 千万别忘记finish关闭流
excelWriter.finish();
}
}
}
/**
* 无对象导出
* @param fileName
* @param headList
* @param dataList
* @throws IOException
*/
public static void writeWithoutModel(String fileName, List> headList,List> dataList) throws IOException{
HttpServletResponse response = RequestHolder.getResponse();
try (ServletOutputStream outputStream = response.getOutputStream()){
setResponse(fileName, response);
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(outputStream).head(headList).sheet("模板").registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).doWrite(dataList);
} catch (Exception e) {
errorWrite(response, e);
}
}
/**
* 导出错误
* @param response
* @param e
* @throws IOException
*/
private static void errorWrite(HttpServletResponse response, Exception e) throws IOException {
// 重置response
response.reset();
log.error(e.getMessage(), e);
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.getWriter().println(JSON.toJSONString(BaseResponse.failure(HttpStatus.INTERNAL_SERVER_ERROR.value(), "导出失败")));
}
/**
* 设置导出信息
* @param fileName
* @param response
* @throws UnsupportedEncodingException
*/
private static void setResponse(String fileName, HttpServletResponse response) throws UnsupportedEncodingException {
// 重置response
response.reset();
response.setContentType("application/vnd.ms-excel;charset=utf-8");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码
fileName = URLEncoder.encode(fileName + DateUtil.date2Str(new Date(),"yyyy-MM-dd_HH_mm_ss") + ExcelTypeEnum.XLSX.getValue(), "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
}
}
可选项
/**
* 自定义拦截器。对第一行第一列的头超链接到:https://blog.csdn.net/HXNLYW
*
* @author gourd.hu
*/
@Slf4j
public class CustomCellWriteHandler implements CellWriteHandler {
@Override
public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell,
Head head, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
if (isHead && cell.getColumnIndex() == 0) {
CreationHelper createHelper = writeSheetHolder.getSheet().getWorkbook().getCreationHelper();
Hyperlink hyperlink = createHelper.createHyperlink(HyperlinkType.URL);
hyperlink.setAddress("https://blog.csdn.net/HXNLYW");
cell.setHyperlink(hyperlink);
}
}
}
/**
* @Description 自定义样式
* @Author gourd
* @Date 2020/1/3 13:58
* @Version 1.0
*/
public class CustomerCellStyleHandler {
public static HorizontalCellStyleStrategy getCustomerCellStyle()
{
// 头的策略
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
// 背景设置为红色
headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short)20);
headWriteCellStyle.setWriteFont(headWriteFont);
// 内容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
// 背景绿色
contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());
WriteFont contentWriteFont = new WriteFont();
// 字体大小
contentWriteFont.setFontHeightInPoints((short)20);
contentWriteCellStyle.setWriteFont(contentWriteFont);
return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
}
}
/**
* 自定义拦截器.对第一列第一行和第二行的数据新增下拉框,显示 测试1 测试2
*
* @author Jiaju Zhuang
*/
@Slf4j
public class CustomSheetWriteHandler implements SheetWriteHandler {
@Override
public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
log.info("第{}个Sheet写入成功。", writeSheetHolder.getSheetNo());
// 区间设置 第一列第一行和第二行的数据。由于第一行是头,所以第一、二行的数据实际上是第二三行
CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 2, 0, 0);
DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper();
DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {"测试1", "测试2"});
DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);
writeSheetHolder.getSheet().addValidationData(dataValidation);
}
}
导入导出用户,部门
导入实体
/**
* 部门导出导入实体
* @author gourd
* @date 2019-04-02 17:26:16
*/
@Data
public class DepartPO {
/**
* 姓名
*/
@ExcelProperty(index = 0,value = {"名称"})
private String name;
/**
* 编号
*/
@ExcelProperty(index = 1,value = {"编号"})
private String code;
/**
* 描述
*/
@ExcelProperty(index = 2,value = {"描述"})
private String description;
}
/**
* 用户导出导入实体
* @author gourd
* @date 2019-04-02 17:26:16
*/
@Data
public class UserPO {
/**
* 年龄
*/
@ExcelProperty(index = 1,value = {"基础信息","年龄"})
private Integer age;
/**
* 姓名
*/
@ExcelProperty(index = 0,value = {"基础信息","姓名"})
private String name;
/**
* 性别(M-男,F-女,X-未知)
*/
@ExcelProperty(index = 2,value = {"基础信息","性别"},converter = SexConverter.class)
@EnumValue
private SexEnum sex;
/**
* 生日
*/
@ExcelProperty(index = 3,value = "生日")
@DateTimeFormat("yyyy年MM月dd日")
private Date birth;
/**
* 邮箱
*/
@ExcelProperty(index = 4,value = {"通讯信息","邮箱"})
private String email;
/**
* 手机
*/
@ExcelProperty(index = 5,value = {"通讯信息","手机"})
private String mobilePhone;
}
/**
* @Description sheet对象
* @Author gourd
* @Date 2020/1/3 12:48
* @Version 1.0
*/
@Data
public class SheetExcelData {
/**
* 数据
*/
private List dataList;
/**
* sheet名
*/
private String sheetName;
/**
* 对象类型
*/
private Class tClass;
}
性别转换器和枚举
/**
* @author 性别类型转化器
*/
public class SexConverter implements Converter {
@Override
public Class supportJavaTypeKey() {
return String.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
/**
* 这里读的时候会调用
*
* @param cellData
* NotNull
* @param contentProperty
* Nullable
* @param globalConfiguration
* NotNull
* @return
*/
@Override
public SexEnum convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return SexEnum.getEnumByLabel(cellData.getStringValue());
}
@Override
public CellData convertToExcelData(SexEnum sexEnum, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return null;
}
}
/**
* 性别枚举工具类
*
* @author gourd
* @create 2018-07-04 15:41
**/
@JSONType(serializeEnumAsJavaBean = true)
public enum SexEnum {
M("M","男"),
F("F","女"),
X("X","未知");
@Getter
@Setter
@EnumValue
private String value;
@Getter
@Setter
private String label;
SexEnum(String value, String label) {
this.value = value;
this.label = label;
}
/**
* 根据label获取枚举
* @param label
* @return
*/
public static SexEnum getEnumByLabel (String label){
if(StringUtils.isBlank(label)){
return null;
}
for (SexEnum sexEnum : SexEnum.values()) {
if(sexEnum.getLabel().equals(label)){
return sexEnum;
}
}
return null;
}
}
controller测试
/**
* @Description excel操作
* @Author gourd
* @Date 2019/12/30 15:41
* @Version 1.0
*/
@Api(tags = "excel", description = "excel操作" )
@RestController
@RequestMapping("/excel")
@Slf4j
public class EasyExcelController {
@Autowired
private RbacUserService rbacUserService;
@Autowired
private RbacDepartService rbacDepartService;
/**
* 单sheet文件导入
*
*/
@PostMapping("/single-read")
@ResponseBody
@ApiOperation(value = "单sheet文件导入")
public BaseResponse singleImport(MultipartFile file) throws IOException {
List userPOList = EasyExcelUtil.readSingleExcel(file,new UserPO(),2);
// 导入逻辑 .......
return BaseResponse.ok("success");
}
/**
* 单sheet文件导出
*
*/
@GetMapping("/single-export")
@ResponseBody
@ApiOperation(value = "单sheet文件导出")
public void singleExport() throws IOException{
List rbacUsers = rbacUserService.findAll();
List userPOList = CollectionCopyUtil.copyList(rbacUsers, UserPO.class);
EasyExcelUtil.writeSingleExcel("单sheet导出","用户",userPOList,UserPO.class);
}
/**
* 多sheet文件导出
*
*/
@GetMapping("/multi-export")
@ResponseBody
@ApiOperation(value = "多sheet文件导出")
public void multiSheetExport() throws IOException{
List rbacUsers = rbacUserService.findAll();
List userPOList = CollectionCopyUtil.copyList(rbacUsers, UserPO.class);
List rbacDeparts = rbacDepartService.list();
List departPOList = CollectionCopyUtil.copyList(rbacDeparts, DepartPO.class);
List sheetExcelDataList = new ArrayList<>(3);
SheetExcelData userPOSheetExcelData = new SheetExcelData<>();
userPOSheetExcelData.setSheetName("用户");
userPOSheetExcelData.setTClass(UserPO.class);
userPOSheetExcelData.setDataList(userPOList);
SheetExcelData departPOSheetExcelData = new SheetExcelData<>();
departPOSheetExcelData.setSheetName("部门");
departPOSheetExcelData.setTClass(DepartPO.class);
departPOSheetExcelData.setDataList(departPOList);
sheetExcelDataList.add(userPOSheetExcelData);
sheetExcelDataList.add(departPOSheetExcelData);
EasyExcelUtil.writeMultiExcel("多sheet导出",sheetExcelDataList);
}
/**
* 无对象文件导出
*
*/
@GetMapping("/no-model-export")
@ResponseBody
@ApiOperation(value = "无对象文件导出")
public void noModelExport() throws IOException{
EasyExcelUtil.writeWithoutModel("测试无对象导出",head(),dataList());
}
private List> head() {
List> list = new ArrayList<>();
List head0 = new ArrayList<>();
head0.add("统一头");
head0.add("字符串");
List head1 = new ArrayList<>();
head1.add("统一头");
head1.add("数字" );
List head2 = new ArrayList<>();
head2.add("统一头");
head2.add("日期");
list.add(head0);
list.add(head1);
list.add(head2);
return list;
}
private List> dataList() {
List> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
List data = new ArrayList<>();
data.add("字符串" + i);
data.add(0.56);
data.add(DateUtil.date2Str(new Date(),"yyyy-MM-dd HH:mm:ss"));
list.add(data);
}
return list;
}
}
测试效果
单,多sheet的导出
excel导入,通过断点的方式演示
===============================================
代码均已上传至本人的开源项目
spring-cloud-plus:https://blog.csdn.net/HXNLYW/article/details/104635673