当前位置:网站首页>EasyExcel读取写入简单使用
EasyExcel读取写入简单使用
2022-07-07 07:53:00 【wnfee】
官方文档 https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read
一. 读取
1.1 定义实体类
@Data
public class EmailImportParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "数据添加方式 1 系统自动通过邮箱域名查找备案的公司名称 2 手动输入")
@NotNull
@ExcelProperty(index = 0)
private Integer dataType;
@ApiModelProperty(value = "备用公司名称")
@ExcelProperty(index = 1)
private String inputCompanyName;
@ApiModelProperty(value = "邮箱地址")
@NotBlank
@ExcelProperty(index = 2)
private String email;
}
- 如果不使用标题
@ExcelProperty(index = 0)
,标题从0
开启- 如果使用标题
@ExcelProperty("数据添加方式")
1.2 不检查数据批量导入
接口实现
@Override
@SneakyThrows
public Boolean importExcel(MultipartFile file) {
EasyExcel.read(file.getInputStream(), EmailImportParam.class, new EmailDataListener(this)).sheet().doRead();
return true;
}
添加EmailDataListener
package com.tophant.pentestinfoinv.listener;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.tophant.pentestinfoinv.common.domain.query.EmailAddParam;
import com.tophant.pentestinfoinv.common.domain.query.EmailImportParam;
import com.tophant.pentestinfoinv.service.IEmailService;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @Description email excel导入监听类
* @Author WanFei
* @Date 2022/7/4 16:43
**/
@Slf4j
public class EmailDataListener implements ReadListener<EmailImportParam> {
/**
* 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 100;
private List<EmailImportParam> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private IEmailService emailService;
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param emailService
*/
public EmailDataListener(IEmailService emailService) {
this.emailService = emailService;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(EmailImportParam data, AnalysisContext context) {
log.info("解析到一条数据:{}", JSONUtil.toJsonStr(data));
cachedDataList.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", cachedDataList.size());
List<EmailAddParam> emailAddParams = cachedDataList.stream().map(emailImportParam -> new EmailAddParam()
.setEmails(Set.of(emailImportParam.getEmail()))
.setDataType(emailImportParam.getDataType())
.setInputCompanyName(emailImportParam.getInputCompanyName()))
.collect(Collectors.toList());
emailAddParams.forEach(emailService::create);
log.info("存储数据库成功!");
}
}
1.3 检查数据一次性导入
@Override
@SneakyThrows
public Boolean importExcel(MultipartFile file) {
File newFile = MultipartFileUtil.multipartFileToFile(file);
Assert.isFalse(ObjectUtil.isNull(file) || ObjectUtil.isNull(newFile), "文件不能为空!");
// 获取文件类型
String fileType = FileUtil.getType(newFile);
log.info("上传文件的扩展名: {}", fileType);
Assert.notNull(fileType, "上传文件拓展名为空!");
Assert.isTrue(StrUtil.containsAnyIgnoreCase(fileType, "xls", "xlsx"), StrUtil.format("不允许上传文件类型: {}", fileType));
List<EmailImportParam> emailList = new ArrayList<>();
EasyExcel.read(file.getInputStream()).head(EmailImportParam.class)
.sheet()
.registerReadListener(new AnalysisEventListener<EmailImportParam>() {
@Override
public void invoke(EmailImportParam data, AnalysisContext context) {
emailList.add(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
log.info("数据读取完毕");
}
}).doRead();
Set<String> emails = emailList.stream().map(EmailImportParam::getEmail).collect(Collectors.toSet());
InfoInvUtil.checkDomain(emails);
// 查询已存在邮箱
Set<String> repeatEmails = this.lambdaQuery()
.eq(Email::getDelFlag, false)
.in(Email::getEmail, emails)
.select(Email::getEmail)
.list()
.stream()
.map(Email::getEmail)
.collect(Collectors.toSet());
Assert.isFalse(CollUtil.isNotEmpty(repeatEmails), StrUtil.format("邮箱: {} 已存在, 请检查excel文件", CollUtil.join(repeatEmails, ", ")));
// 添加到数据库
List<EmailAddParam> emailAddParams = emailList.stream().map(emailImportParam -> new EmailAddParam()
.setEmails(Set.of(emailImportParam.getEmail()))
.setDataType(emailImportParam.getDataType())
.setInputCompanyName(emailImportParam.getInputCompanyName()))
.collect(Collectors.toList());
emailAddParams.forEach(this::create);
return true;
}
MultipartFileUtil
工具类
package com.tophant.pentestinfoinv.common.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
/**
* File转换工具
*
* @author [email protected]
* @date 06-21-0021-2021
*/
@Slf4j
public class MultipartFileUtil {
/**
* MultipartFile转换为File
*
* @param file multipartFile
* @return File
* @throws Exception
*/
public static File multipartFileToFile(MultipartFile file) {
try {
File toFile = null;
if (file.equals("") || file.getSize() <= 0) {
file = null;
} else {
InputStream ins = null;
ins = file.getInputStream();
toFile = new File(Objects.requireNonNull(file.getOriginalFilename()));
inputStreamToFile(ins, toFile);
ins.close();
}
return toFile;
} catch (Exception e) {
log.warn("MultipartFile转化为File失败!");
return null;
}
}
/**
* 获取流文件
* @param ins
* @param file
*/
private static void inputStreamToFile(InputStream ins, File file) {
try {
OutputStream os = new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
ins.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
二. 写入
2.1 写入
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
EasyExcel.write(byteArrayOutputStream, EmailExcelVO.class)
// 注册自定义拦截器
.registerWriteHandler(new CustomCellWriteConfig())
.registerWriteHandler(new CustomColumnWidthConfig())
.registerWriteHandler(new CustomRowHeightConfig())
.sheet("邮箱")
.doWrite(excelVOList);
2.2 自定义拦截器
2.2.1 设置样式
package com.tophant.component.starter.excel.common.config;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.*;
import java.util.List;
/**
* @Description 设置样式
* @Author WanFei
* @Date 2022/5/13 12:13
*/
public class CustomCellWriteConfig implements CellWriteHandler {
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
CellStyle cellStyle = workbook.createCellStyle();
// 居中
cellStyle.setAlignment(HorizontalAlignment.LEFT);
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
// 设置边框
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderLeft(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);
cellStyle.setBorderTop(BorderStyle.THIN);
// 自动换行
cellStyle.setWrapText(true);
// 配置生效
cell.setCellStyle(cellStyle);
}
}
2.2.2 自适应宽度
package com.tophant.component.starter.excel.common.config;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.util.CollectionUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Description 自适应宽度
* @Author WanFei
* @Date 2022/5/13 13:59
*/
public class CustomColumnWidthConfig extends AbstractColumnWidthStyleStrategy {
private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>();
@Override
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {
boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
if (needSetWidth) {
Map<Integer, Integer> maxColumnWidthMap = CACHE.computeIfAbsent(writeSheetHolder.getSheetNo(), k -> new HashMap<>());
Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
if (columnWidth >= 0) {
if (columnWidth > 254) {
columnWidth = 254;
}
Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
Sheet sheet = writeSheetHolder.getSheet();
sheet.setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
}
}
}
}
/**
* 计算长度
* @param cellDataList
* @param cell
* @param isHead
* @return
*/
private Integer dataLength(List<WriteCellData<?>> 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:
// 换行符(数据需要提前解析好)
int index = cellData.getStringValue().indexOf("\n");
return index != -1 ?
cellData.getStringValue().substring(0, index).getBytes().length + 1 : cellData.getStringValue().getBytes().length + 1;
case BOOLEAN:
return cellData.getBooleanValue().toString().getBytes().length;
case NUMBER:
return cellData.getNumberValue().toString().getBytes().length;
default:
return -1;
}
}
}
}
}
2.2.3 自适应行高
package com.tophant.component.starter.excel.common.config;
import com.alibaba.excel.write.style.row.AbstractRowHeightStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import java.util.Iterator;
/**
* @Description 自适应行高
* @Author WanFei
* @Date 2022/5/13 14:03
*/
public class CustomRowHeightConfig extends AbstractRowHeightStyleStrategy {
/**
* 默认高度
*/
private static final Integer DEFAULT_HEIGHT = 300;
@Override
protected void setHeadColumnHeight(Row row, int relativeRowIndex) {
}
@Override
protected void setContentColumnHeight(Row row, int relativeRowIndex) {
Iterator<Cell> cellIterator = row.cellIterator();
if (!cellIterator.hasNext()) {
return;
}
// 默认为 1行高度
Integer maxHeight = 1;
while (cellIterator.hasNext()) {
Cell cell = cellIterator.next();
switch (cell.getCellType()) {
case STRING:
if (cell.getStringCellValue().contains("\n")) {
int length = cell.getStringCellValue().split("\n").length;
maxHeight = Math.max(maxHeight, length);
}
break;
default:
break;
}
}
row.setHeight((short) (maxHeight * DEFAULT_HEIGHT));
}
}
边栏推荐
- Mongodb creates an implicit database as an exercise
- 高数_第1章空间解析几何与向量代数_向量的数量积
- The request object parses the request body and request header parameters
- 网上可以开炒股账户吗安全吗
- A wave of open source notebooks is coming
- Parameter sniffing (1/2)
- Parameter sniffing (2/2)
- [ORM framework]
- Postman interface test II
- ORM -- database addition, deletion, modification and query operation logic
猜你喜欢
Postman interface test V
ORM model -- associated fields, abstract model classes
【acwing】789. 数的范围(二分基础)
Official media attention! The list of top 100 domestic digital collection platforms was released, and the industry accelerated the healthy development of compliance
ISP、IAP、ICP、JTAG、SWD的编程特点
Appx代碼簽名指南
ISP、IAP、ICP、JTAG、SWD的编程特点
Weekly recommended short videos: what are the functions of L2 that we often use in daily life?
Postman interface test IV
一文讲解单片机、ARM、MUC、DSP、FPGA、嵌入式错综复杂的关系
随机推荐
IPv4套接字地址结构
Do you have a boss to help look at this error report and what troubleshooting ideas are there? Oracle CDC 2.2.1 flick 1.14.4
Bean operation domain and life cycle
Some test points about coupon test
Please ask me a question. I started a synchronization task with SQL client. From Mysql to ADB, the historical data has been synchronized normally
The request object parses the request body and request header parameters
Pit encountered by vs2015 under win7 (successful)
Writing file types generated by C language
STM32中AHB总线_APB2总线_APB1总线这些是什么
Download Text, pictures and ab packages used by unitywebrequest Foundation
Internship log - day04
大整数类实现阶乘
【剑指Offer】42. 栈的压入、弹出序列
ORM -- logical relation and & or; Sort operation, update record operation, delete record operation
Flinkcdc failed to collect Oracle in the snapshot stage. How do you adjust this?
Bean 作⽤域和⽣命周期
uboot机构简介
Can I open a stock trading account online? Is it safe
STM32 Basics - memory mapping
The new activity of "the arrival of twelve constellations and goddesses" was launched