1、添加依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.1</version>
</dependency>
<!--xls-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
2、创建导入导出的映射实体类
导出
package com.zl.model.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Builder;
import lombok.Data;
/**
* 导出VO
*
* @author z
* @date 2022-03-14 18:01
*/
@Data
@HeadRowHeight(value = 20)
public class ExportModeSheetOneVO {
/**
* id
*/
@ExcelProperty(value = "编号",index = 0)
@ColumnWidth(value = 20)
private String id;
/**
* 名称
*/
@ExcelProperty(value = "名称",index = 1)
@ColumnWidth(value = 20)
private String name;
/**
* 类型
*/
@ExcelProperty(value = "类型",index = 2)
@ColumnWidth(value = 20)
private String type;
/**
* 描述
*/
@ExcelProperty(value = "描述",index = 3)
@ColumnWidth(value = 20)
private String des;
/**
* 创建日期
*/
@ExcelProperty(value = "创建日期",index = 4)
@ColumnWidth(value = 20)
private String createDt;
/**
* 创建者
*/
@ExcelProperty(value = "创建者",index = 5)
@ColumnWidth(value = 20)
private String createBy;
/**
* 更新日期
*/
@ExcelProperty(value = "更新日期",index = 6)
@ColumnWidth(value = 20)
private String updateDt;
/**
* 更新者
*/
@ExcelProperty(value = "更新者",index = 7)
@ColumnWidth(value = 20)
private String updateBy;
}
导入
package com.zl.model.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
/**
* 导入VO
*
* @author z
* @date 2022-03-15 16:08
*/
@Data
public class ImportModeVO {
/**
* 模型名称
*/
@ExcelProperty("名称")
private String name;
/**
* 类型
*/
@ExcelProperty("类别")
private String type;
/**
* 描述
*/
@ExcelProperty("描述")
private String des;
}
建议类型都写为String,可以减少不必要的类型转换
3、实现
Controller层代码实现:
/**
* 导入导出测试
*
* @author z
* @since 2022-03-12
*/
@RestController
@RequestMapping("/myTest")
@Api(tags = "数据导入导出")
@Slf4j
public class MyInfoController {
@Autowired
private MyInfoService myInfoService;
/**
* 模型导入
* @param file
* @return
*/
@PostMapping("/importMode")
@ApiOperation("导入")
@ApiImplicitParam(name = "file",value = "上传的数据",required = true)
public Result importMode(@RequestParam("file") MultipartFile file){
if (file==null||file.isEmpty()) {
return Result.error();
}
int flag= 0;
try {
flag = myInfoService.importMode(file);
} catch (IOException e) {
e.printStackTrace();
}
return flag==1?Result.success():Result.error();
}
/**
* 导出
* @param type 导出类别
*/
@GetMapping("/exportMode")
@ApiOperation("导出")
@ApiImplicitParam(name="type",value="导出类别",required = true)
public void exportMode(String type, HttpServletResponse response){
try {
myInfoService.exportMode(type,response);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 导出
* @param type 导出类别
*/
@GetMapping("/export")
@ApiOperation("导出1")
@ApiImplicitParam(name="type",value="导出类别",required = true)
public void export(String type, HttpServletResponse response){
try {
myInfoService.export(type,response);
} catch (Exception e) {
e.printStackTrace();
}
}
Service层实现:
/**
* 单个sheet导入
* @param file
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int importMode(MultipartFile file) throws IOException {
InputStream in =file.getInputStream();
Integer readHeadIndex = 7;
ImportExcelListenerUtil importExcelListenerUtil = new ImportExcelListenerUtil();
importExcelListenerUtil.setHeadReadIndex(readHeadIndex);
importExcelListenerUtil.setHeadEntity(new ImportModeVO());
EasyExcel.read(in, ImportModeVO.class,importExcelListenerUtil).headRowNumber(readHeadIndex) // 从第几行导入,此处为从第7行导入,即第7行是表头
.sheet("Sheet1")
.doRead();
// 每次EasyExcel的read方法读取完之后都会关闭流,我这里为了试验doReadAll方法,所以重新获取了一次
// in = file.getInputStream();
// SyncReadListener syncReadListener = new SyncReadListener();
// EasyExcel.read(in,ImportModeVO.class,syncReadListener).doReadAll();
List<ImportModeVO> list = importExcelListenerUtil.list;
for(int i=0;i<list.size();i++){
ImportModeEntity importModeEntity =new ImportModeEntity();
BeanUtils.copyProperties(list.get(i),importModeEntity);
importModeEntity.setType(Integer.parseInt(list.get(i).getType()));
String modeId=UUID.randomUUID().toString().replace("-","").toUpperCase();
importModeEntity.setId(modeId);
this.save(importModeEntity);
}
in.close();
return 1;
}
/**
* 多个sheet导入
* @param file
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int importMode(MultipartFile file) throws IOException {
InputStream in = file.getInputStream();
List<ReadSheet> sheets = EasyExcel.read(in).build().excelExecutor().sheetList();
for (ReadSheet sheet : sheets) {
//因为在获取sheetList的时候改变了通过file获取的流类型,在EasyExcel.read读的时候,采用的同一个流,会导致excel类型出错。
//解决方法:重新创建一个excel流,与获取sheet的流区别使用
InputStream io = file.getInputStream();
ImportExcelListenerUtil importExcelListenerUtil = new ImportExcelListenerUtil()
.setHeadReadIndex(readHeadIndex)
.setHeadEntity(new ImportModeVO());
EasyExcel.read(io, ImportModeVO.class,importExcelListenerUtil)
.sheet(sheet.getSheetName())
.doRead();
List<ImportModeVO> list = ImportExcelListenerUtil.list;
for (int j = 0; j < list.size(); j++) {
System.out.println(list.get(j));
}
System.out.println("这是" + sheet.getSheetName() + "的所有数据");
ImportExcelListenerUtil.list.clear();
io.close();
}
return 1;
}
/**
* 模型导出
* @param type 导出类别
*/
@Override
public void exportMode(String type, HttpServletResponse response) throws IOException {
String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
// 导出文件名
String fileName="文件导出"+time+".xlsx";
// sheet名
String sheetNameOne="导出sheet1";
String sheetNameTwo="导出sheet2";
//表头样式
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
//内容样式
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
//设置内容靠左对齐
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
//设置表头居中对齐
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
fileName = URLEncoder.encode(fileName,"UTF-8");
response.setHeader("Content-disposition","attachment;filename=" + fileName);
// 查询到数据
List<ExportModeSheetOneVO> exportModeSheetOneVOS = this.selectModelShowOne(type);
List<ExportModeSheetTwoVO> exportModeSheetTwoVOS = this.selectModelShowTwo(type);
// 写出
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
// ExportModeSheetOneVO 是导出的映射实体类,exportModeSheetOneVOS 是导出的数据集合,registerWriteHandler(new CustomCellWriteHandlerUtil()) 设置表头自适应,这是多个sheet导出,只有一个只需导出需要的sheet即可
// 不设置自适应表头:EasyExcel.writerSheet(0,sheetNameOne).head(ExportModeSheetOneVO.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet(0,sheetNameOne).head(ExportModeSheetOneVO.class).registerWriteHandler(new CustomCellWriteHandlerUtil()).build();
excelWriter.write(exportModeSheetOneVOS,writeSheet);
writeSheet = EasyExcel.writerSheet(1,sheetNameTwo).head(ExportModeSheetTwoVO.class).registerWriteHandler(new CustomCellWriteHandlerUtil()).build();
excelWriter.write(exportModeSheetTwoVOS,writeSheet);
//千万别忘记关流,finish会帮忙关流
excelWriter.finish();
}
/**
* 模型导出
* @param type 导出类别
*/
@Override
public void export(String type, HttpServletResponse response) throws IOException {
// 查询数据
List<ExportModeSheetOneVO> data= this.selectModelShowOne(type);
// 设置响应
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = "文件导出";
try {
fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xls");
// 添加过滤的列
Set<String> columnNames = new HashSet<String>();
columnNames.add("id");
...
EasyExcel.write(response.getOutputStream(), ExportModeSheetOneVO.class).excludeColumnFiledNames(columnNames).sheet("导出sheet1").doWrite(data);
} catch (Exception e) {
e.printStackTrace();
}
}
直接导出文件:
ExcelWriter excelWriter = EasyExcel.write(filePath).build();
其中 filePath 为导出文件的目录 eg:String filePath = "/home/数据导出.xlsx";
- 导入需要设置一个自定义Easyexcel的监听类
package com.zl.util;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import java.lang.reflect.Field;
import java.util.*;
public class ImportExcelListenerUtil extends AnalysisEventListener<Object> {
private List<Object> list = new ArrayList<>();
private Object headEntity;
private Integer headReadIndex = 1;
public List<Object> getList() {
return list;
}
public ImportExcelListenerUtil setList(List<Object> list) {
this.list = list;
return this;
}
public Integer getHeadReadIndex() {
return headReadIndex;
}
public ImportExcelListenerUtil setHeadReadIndex(Integer headReadIndex) {
this.headReadIndex = headReadIndex;
return this;
}
public Object getHeadEntity() {
return headEntity;
}
public ImportExcelListenerUtil setHeadEntity(Object headEntity) {
this.headEntity = headEntity;
return this;
}
@Override
public void invoke(Object excelEntity, AnalysisContext context) {
list.add(excelEntity);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
// String[] headList = {"序号", "姓名", "性别", "年龄", "文化程度", "民族", "身份证号", "工作单位/学校", "手机/电话", "备注"};
if (ObjectUtil.isEmpty(headEntity)) {
throw new ExcelAnalysisException("读取系统模板实体为:null");
}
// 验证表头 获取实体类注解@ExcelProperty标识的表头,以index标识的顺序进行比较
if (context.readRowHolder().getRowIndex() == headReadIndex - 1) {
// 获取实体类表头信息
Map<Integer, String> entityHeadMap = new LinkedHashMap<>();
List<Field> fieldList = TableInfoHelper.getAllFields(this.getHeadEntity().getClass());
for (Field field : fieldList) {
if (field.isAnnotationPresent(ExcelProperty.class)) {
ExcelProperty excel = field.getAnnotation(ExcelProperty.class);
//英文为key,中文为value
Integer key = excel.index();
String[] value = excel.value();
entityHeadMap.put(key, value.length > 0 ? value[value.length - 1] : "");
}
}
// 判断读取表头是否和实体表头一致
Set<Integer> heads = entityHeadMap.keySet();
for (Integer headIndex : heads) {
if (!entityHeadMap.get(headIndex).equals(headMap.get(headIndex))) {
throw new ExcelAnalysisException("上传模板与系统模板不匹配,请使用平台模板上传数据");
}
}
}
super.invokeHeadMap(headMap, context);
}
}
- 自定义表头需要的工具类
package com.zl.util;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 导出自适应列宽
*
* @author z
* @date 2022-03-15 14:57
*/
public class CustomCellWriteHandlerUtil extends AbstractColumnWidthStyleStrategy {
private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>();
@Override
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {
boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
if (needSetWidth) {
Map<Integer, Integer> maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo());
if (maxColumnWidthMap == null) {
maxColumnWidthMap = new HashMap<>();
CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
}
Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
if (columnWidth>= 0) {
if (columnWidth> 255) {
columnWidth = 255;
}
Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
if (maxColumnWidth == null || columnWidth> maxColumnWidth) {
maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
}
}
}
}
private Integer dataLength(List<CellData> cellDataList, Cell cell, Boolean isHead) {
if (isHead) {
return cell.getStringCellValue().getBytes().length;
} else {
CellData cellData = cellDataList.get(0);
CellDataTypeEnum type = cellData.getType();
if (type == null) {
return -1;
} else {
switch (type) {
case STRING:
return cellData.getStringValue().getBytes().length;
case BOOLEAN:
return cellData.getBooleanValue().toString().getBytes().length;
case NUMBER:
return cellData.getNumberValue().toString().getBytes().length;
default:
return -1;
}
}
}
}
}
4、测试
- 使用 postman 测试导入导出接口