导入数据
导入1万条数据 java 不会内存溢出
使用阿里easyExcel, 批次读入数据,
以下只供参考部分工具类没有
controller
/**
* 上传excel 保存数据
* 对接系统字段配置表 判断是否已填
* 上传错误后返回错误信息和生成一个错误数据的excel用于重新处理后倒入
*
* 可开启第一行为表达式不导入
* @param files 上传的文件
* @return 成功的公用返回
* @throws Exception 异常
*/
@ApiOperationSupport(order = 500)
@ApiOperation(value = "thirdeti_上传excel数据保存",notes = "上传数据")
@PostMapping(value = "thirdexa_upload_excel" , consumes = MediaType.MULTIPART_FORM_DATA_VALUE, headers = "content-type=multipart/form-data" )
public RSuccess thirdexa_upload_excel(
@ApiParam(name="files",value = "上传的sql文件",required = true)@RequestPart("files") MultipartFile[] files) throws Exception{
VoSimpManaMuser currUser=this.getCurUserInfo_throws();
String callReturnStr=mudiFeignMfile.uploadFiles_busn_str(files, EnumFileSerPath.temp.getPath(),
null ,true, UtilJsonFast.toJSONString(currUser));
log.info("开始上传文件");
//调用微服务上传文件
UtilRFailJson.strCovertToRFail_throw(callReturnStr);
JSONObject jsonObject= UtilJsonFast.parseObject(callReturnStr);
String fileName=jsonObject.getString("firstFileReName");
log.info("文件上传完成");
/**
* excel数据和服务端匹配 是通过 阿里EasyExcel注释
* vo 里面 类 注释 @ExcelProperty(index =1) excel 表示导入绑定excel第几个字段
*/
String excelFilePath= UtilFileManaPath.getUpByEnumAddPath(EnumFileSerPath.temp,currUser.getTreeauthIdOrg().toString(),fileName);
// 这里默认读取第一个sheet ExcelUploadSecmodThirdexa 导入数据处理
ExcelUploadSecmodThirdexa excelUploadSecmodThirdexa=new ExcelUploadSecmodThirdexa(currUser,serviceSecmodThirdexa,mudiFeignMfile);
// 可改成2 如果第二行是表达式 或者是复杂表头的这边要设置
Integer loadBeg=1;
String eMsg="";
try {
EasyExcel.read(excelFilePath, VoSecmodThirdexa.class, excelUploadSecmodThirdexa).sheet().headRowNumber(loadBeg).doRead();
}catch (ExcelCommonException e) {
eMsg="导入的excel文件的格式不对,请检查";
UtilRunTrace.excepCallerLogError(eMsg,e);
throw new ExceptionCustom(new Error(VarBaseError.e0_inp,new String[]{eMsg}));
}catch (ExcelAnalysisException e){
eMsg="数据填写格式有误,请检查";
UtilRunTrace.excepCallerLogError(eMsg,e);
throw new ExceptionCustom(new Error(VarBaseError.e0_inp,new String[]{eMsg}));
}catch (Exception e){
eMsg="excel文件内容错误,请检查";
UtilRunTrace.excepCallerLogError(eMsg,e);
throw new ExceptionCustom(new Error(VarBaseError.e0_inp,new String[]{eMsg}));
}
// 返回不能导入的excel信息提供下载地址
String[] importErrorSet=ExcelUploadSecmodThirdexa.importErrorSet.get();
RSuccess re=new RSuccess();
re.setRsda(importErrorSet);
return re;
}
AnalysisEventListener 上传处理
package com.fircom.secmod.help.excelUpload;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import com.common.base.enums.EnumFileType;
import com.common.base.var.VarTime;
import com.common.buscomm.enums.EnumFileSerPath;
import com.common.buscomm.excep.ExceptionCustom;
import com.common.buscomm.ini.varLoad.VarLoadSysStaticCache;
import com.common.buscomm.util.excep.UtilRFailJson;
import com.common.buscomm.vo.error.Error;
import com.common.buscomm.vo.muser.VoSimpManaMuser;
import com.common.ini.util.UtilFileManaPath;
import com.common.inimvc.help.var.VarJdUrl;
import com.common.mvc.help.util.excel.UtilExcelMvc;
import com.fircom.secmod.help.feign.MudiFeignMfile;
import com.fircom.secmod.service.ServiceSecmodThirdexa;
import com.fircom.secmod.vo.VoSecmodThirdexa;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
/**
* 上传处理类
*/
@Slf4j
public class ExcelUploadSecmodThirdexa extends AnalysisEventListener<VoSecmodThirdexa> {
public ExcelUploadSecmodThirdexa(VoSimpManaMuser voSimpManaMuser, ServiceSecmodThirdexa serviceSecmodThirdexa,
MudiFeignMfile mudiFeignMfile) {
this.voSimpManaMuser = voSimpManaMuser;
this.serviceSecmodThirdexa = serviceSecmodThirdexa;
this.mudiFeignMfile=mudiFeignMfile;
}
/**
* 操作用户
*/
private VoSimpManaMuser voSimpManaMuser;
private ServiceSecmodThirdexa serviceSecmodThirdexa;
private MudiFeignMfile mudiFeignMfile;
/**
* 处理到第几条数据
*/
private Integer dealDataNum=0;
/***
* 导入数据异常信息
* 线程变量 本线程可全局访问
* String[0] 放错误信息
* String[1] 放错误的文件链接
*/
public static ThreadLocal<String[]> importErrorSet = new ThreadLocal<String[]>();
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<VoSecmodThirdexa> list = new ArrayList<VoSecmodThirdexa>();
/**
* 全部导入错误的vos
*/
List<VoSecmodThirdexa> listImportThirdexasError =new ArrayList<VoSecmodThirdexa>();
/**
* 全部导入错误信息
*/
String errMsgAll="";
@Override
public void invoke(VoSecmodThirdexa data, AnalysisContext context) {
log.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
if (list.size() >= BATCH_COUNT) {
saveData();
list.clear();
}
}
/**
* 导入数据
* 正常的数据导入
* 异常的数据导出
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 导入数据 正常的数据导入
if (list.size()>0) {
saveData();
}
// 导入数据返回的异常信息赋值给 importErrorSet
String[] errorData=new String[2];
if(listImportThirdexasError.size()>0){
String tempFileAllPath= UtilExcelMvc.getTempFileProPath("fircpro","secmodThirdexaOperVos.xlsx");
EnumFileSerPath enumFileSerPath=EnumFileSerPath.temp;
// 生成的文件名
String fileName="errorOut" + System.currentTimeMillis() + ".xlsx";
// 生成的文件全路径
String fileNameAll = UtilFileManaPath.getUpByEnumRunTreeauthAddPath(enumFileSerPath,fileName);
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileNameAll, VoSecmodThirdexa.class).withTemplate(tempFileAllPath).sheet().doFill(listImportThirdexasError);
String uuid=UUID.randomUUID().toString();
// 文件服务器生成记录
String callReturn= mudiFeignMfile.mfile_insert(com.common.ini.util.UtilTreeauthUse.getTreeauthOrgId_use(null,false),
voSimpManaMuser.getMuserId(),
uuid,
"导入返回的错误excel"+"_"+ DateUtil.format(new Date(), VarTime.data_allNumFormat)+".xlsx",
fileName,
enumFileSerPath.getPath(),
EnumFileType.excelType.getContentType()
);
UtilRFailJson.strCovertToRFail_throw(callReturn);
// 错误的文件连接
errorData[1]= VarJdUrl.downJdFile_mfileBusn+uuid;
}
errorData[0]=errMsgAll;
importErrorSet.set(errorData);
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", list.size());
for(VoSecmodThirdexa voSecmodThirdexa:list){
dealDataNum++;
voSecmodThirdexa.setDataNum(dealDataNum);
try {
serviceSecmodThirdexa.saveOrUpdate_thirdexa_byVo(voSecmodThirdexa,voSimpManaMuser);
} catch (ExceptionCustom exceptionCustom) {
List<Error> errors= exceptionCustom.getErrors();
String eMsg=errors.get(0).fillEMsg(VarLoadSysStaticCache.enumLanguage.getCode());
log.warn(eMsg);
errMsgAll=errMsgAll+eMsg;
VoSecmodThirdexa error=new VoSecmodThirdexa();
BeanUtil.copyProperties(voSecmodThirdexa,error);
listImportThirdexasError.add(error);
}
}
}
}
数据验证
导入失败的数据再次导出给用户
String tempFileAllPath= UtilExcelMvc.getTempFileProPath("fircpro","secmodThirdexaOperVos.xlsx");
EnumFileSerPath enumFileSerPath=EnumFileSerPath.temp;
// 生成的文件名
String fileName="errorOut" + System.currentTimeMillis() + ".xlsx";
// 生成的文件全路径
String fileNameAll = UtilFileManaPath.getUpByEnumRunTreeauthAddPath(enumFileSerPath,fileName);
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileNameAll, VoSecmodThirdexa.class).withTemplate(tempFileAllPath).sheet().doFill(listImportThirdexasError);