这段时间做了excel 数据导入,用的阿里巴巴的easyExcel 的代码。下面是官网和githab 代码地址。需要将github 上的代码拉下来,方便查看demo.关于Easyexcel | Easy ExcelEasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目,在尽可能节约内存的情况下支持读写百M的Excel。https://easyexcel.opensource.alibaba.com/docs/current/
GitHub - alibaba/easyexcel: 快速、简洁、解决大文件内存溢出的java处理Excel工具快速、简洁、解决大文件内存溢出的java处理Excel工具. Contribute to alibaba/easyexcel development by creating an account on GitHub.https://github.com/alibaba/easyexcel
仔细查看上面的代码,模仿上面的代码就可以写出来了。
对象不能使用@Accessors(chain = true) 注解,不然会出现读取不到数据的情况
@Getter
@Setter
@EqualsAndHashCode
public class DemoData {
private String string;
private Date date;
private Double doubleData;
}
只需要根据官网demo来实现或者继承他们自己的内部类即可,创建的类放哪里都行
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener implements ReadListener {
}
@Override
public void invokeHead(Map> headMap, AnalysisContext context) {}
使用invokeHead 方法需要我们先定义哪几行是表头,下面代码中的.headRowNumber(3) 可以定义哪几行是表头。3待办前三行都是表头,invokeHead 方法会读取前三行,我们可以根据invokeHead 来校验表头
EasyExcel.read(file.getInputStream(), YclJzLqJcVo.class, yclJzLqExcelDataListener).sheet().headRowNumber(3).doRead();
@Override
public void invoke(YclJzLqJcVo data, AnalysisContext context) {}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {}
@ExcelProperty(index = 1, value = "进场日期")
@ExcelProperty注解中的index 属性代表该字段读取的excel 上的第几列,index =1 代表该字段读取excel 上的第二列,index =0 读取第一列。
package easttrans.pitchdatacore.domain.core;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
/**
*
*
*
*
* @author guankong
* @since 2022-10-26
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="改性沥青模板映射类", description="")
public class YclGxLqJcVo extends CommonVo implements Serializable {
private static final long serialVersionUID=1L;
@ApiModelProperty(value = "id")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ExcelProperty(index = 0, value = "序号")
@ApiModelProperty(value = "序号")
private Integer serialNum;
@ExcelProperty(index = 1, value = "进场日期")
@ApiModelProperty(value = "进场日期")
private Date enterDate;
@ExcelProperty(index = 2, value = "品牌及标号")
@ApiModelProperty(value = "品牌及标号")
private String brand;
@ExcelProperty(index = 3, value = "堆放地点")
@ApiModelProperty(value = "堆放地点")
private String place;
@ExcelProperty(index = 4, value = "用途")
@ApiModelProperty(value = "用途面层")
private String ratio;
@ExcelProperty(index = 5, value = "质保单编号")
@ApiModelProperty(value = "质保单编号")
private String qaNum;
@ExcelProperty(index = 6, value = "同批数量(t)")
@ApiModelProperty(value = "同批数量(t)")
private Double count;
@ExcelProperty(index = 7, value = "委托单或任务单编号")
@ApiModelProperty(value = "委托单或任务单编号")
private String taskNum;
@ExcelProperty(index = 8, value = "针入度0.1mm")
@ApiModelProperty(value = "针入度0.1mm")
private Double zrd;
}
这里AnalysisEventListener
@Slf4j
public final class YclJzLqExcelDataListener extends AnalysisEventListener {
private final YclLqJcVo yclLqJcVo;
private YclLqJcService yclLqJcService;
/**
* 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 100;
private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/**
* 存储异常提醒消息
*/
private Map result = new HashMap<>();
/**
* excel表头是否正确,false 有误,true 正确。
*/
private boolean format = false;
/**
* 头信息
*/
Map headMap = new HashMap<>();
/**
* 返回提示语
*/
public List tips = new ArrayList<>();
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param
*/
public YclJzLqExcelDataListener(YclLqJcService yclLqJcService,YclLqJcVo yclLqJcVo) {
this.yclLqJcService = yclLqJcService;
this.yclLqJcVo = yclLqJcVo;
}
/**
* 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
*
* @param exception
* @param context
* @throws Exception
*/
@Override
public void onException(Exception exception, AnalysisContext context) {
log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
int col = 0,row =0;
String title = "";
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
col = excelDataConvertException.getColumnIndex();
row = excelDataConvertException.getRowIndex();
title = this.headMap.get(col);
}
}
@Override
public void invokeHeadMap(Map headMap, AnalysisContext context) {
this.headMap = headMap;
}
/**
* 这里会一行行的返回头
*
* @param headMap
* @param context
*/
@Override
public void invokeHead(Map> headMap, AnalysisContext context) {
log.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
// 如果想转成 Map
// 方案1: 不要implements ReadListener 而是 extends AnalysisEventListener
// 方案2: 调用 ConverterUtils.convertToStringMap(headMap, context) 自动会转换
Map integerStringMap = ConverterUtils.convertToStringMap(headMap, context);
String s = integerStringMap.get(0);
if (s != null && s.equals(Constants.JZ_Asp_Mob_Ins_Account)){
//如果表头包含该内容,则为true
format = true;
}
}
@Override
public void invoke(YclJzLqJcVo data, AnalysisContext context) {
result.put("format",format);
if (!format){
//format 为false 时
throw new ExcelAnalysisStopException(Constants.formatTemplateNoTrue);
}
//查询改性沥青数据库,判断该数据库中的报告编号是否与cachedDataList 中的报告编号重复,重复不导入
LambdaQueryWrapper yclJzLqJcVoLambdaQueryWrapper = new LambdaQueryWrapper<>();
yclJzLqJcVoLambdaQueryWrapper.eq(YclLqJcVo::getReportNum,data.getReportNum());
yclJzLqJcVoLambdaQueryWrapper.eq(YclLqJcVo::getType,Constants.JZLQ_TYPE);
List list = yclLqJcService.list(yclJzLqJcVoLambdaQueryWrapper);
if (list.size()>0){
//表示有数据,该数据不能新增进数据库
saveTips(context.readRowHolder().getRowIndex(),Constants.Duplicate_Import_Information,tips);
return;
}
cachedDataList.add(data);
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
saveData();
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
yclLqJcService.saveBatch(collect);
log.info("存储数据库成功!");
}
/**
* 返回数据
* @return 返回读取的数据集合
**/
public Map getResult(){
return result;
}
/**
* 设置读取的数据集合
* @param result 设置读取的数据集合
**/
public void setResult(Map result) {
this.result = result;
}
}
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
@ApiOperation(value = "沥青进场检验台账导入数据", notes = "沥青进场检验台账导入数据")
public EastTransResult importExcel(@RequestParam("file") MultipartFile file, YclLqJcVo yclLqJcVo) throws IOException {
EastTransResult
返回的数据是 Map
private Map
result = new HashMap<>();
然后设置了该result 的get,set 方法。
public MapgetResult(){ return result; } public void setResult(Map result) { this.result = result; }
然后可以在listener 类里面result.put("key","val");
后面在controller 中获取该类listener 的getResult 方法就可以获取这个map 了。
如果数据导入太慢,可以使用多线程【二十四】springboot使用EasyExcel和线程池实现多线程导入Excel数据_小z♂的博客-CSDN博客_springboot 线程池 写入数据springboot使用EasyExcel和线程池实现多线程导入Excel数据https://blog.csdn.net/weixin_56995925/article/details/125944749
java实现excel的导入导出(带参数校验:非空校验、数据格式校验)_Mr_yu_shao的博客-CSDN博客_java导入excel怎样校验经过简单的封装完成了一个带参数校验的简单使用案例,直接引入项目即用https://blog.csdn.net/Mr_yu_shao/article/details/126459849