最近项目中要求用excel导入大量交易数据的需求,就使用阿里巴巴的easyexcel工具包,进行导入。
1、添加依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.0.5</version>
</dependency>
2、创建excel实体类,easyexcel 根据value值去匹配excel的表头。
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class InQueryBalanceExerciseCashFlowExcel{
@ExcelProperty(value = "日切日期")
private String cutDayDate;
@ExcelProperty(value = "交易编号")
private String transactionNumber;
@ExcelProperty(value = "备注")
private String remarks
}
3、配置限制文件上传大小
servlet:
multipart:
max-file-size:200MB
max-request-size:200MB
4、同步配置nginx上传文件大小限制
5、Controller层通过MultipartFile的方式接收文件、因为需求是多个文件,同时导入,所以用list
public ApiResult batcUploadFile(@RequestParam List<MutipartFile> mutipartFileList,
@RequestParam List<String> fileTypeList,
@RequestParam String engineDate,
@RequestParam String dayType){
LocalDateTime begintime = LocalDateTime.now();
.........
这里实现自己的逻辑,因为我这里是多线程同时
导入5个文件,所以这里用List<MutipartFile> mutipartFileList 接收文件
同时传了个fileTypeList进来。
LocalDateTime endtime = LocalDateTime.now();
System.out.println("总计耗时"+ChronoUnit.SECONDS.between(begintime,endtime))
}
6、service层,编写处理逻辑
protect String fileParsing(FileUploadVO vo,String waterNumber){
List<MultipartFile> filelist = vo.getMultipartFile();
List<String> filetypelist = vo.getFileType();
CountDownLatch countDownLatch = new CountDownLatch(filetypelist.size())
Map<String,Future> map = new HashMap<>();
//循环处理文件,每个文件都构建一个task,通过线程池,多线程并发导入。
int num = 0;
for(MultipartFile file : filelist ){
String filetype = filetypelist.get(num );
//将各种参数,传递给task,最主要,将countDownLatch 传进去,控制并发
TradeDataTask task = new TradeDataTask(....,countDownLatch);
Future ftask = pool.submit(task);
map.put(filetype,ftask)
}
//等待上面任务全部做完
countDownLatch.await();
........
//下面可以循环map,取出任务完成信息
for(Map.Entry<String,Future> entry:map.entrySet()){
Future ftask = entry.getValue();
if(ftask.isDone() && !ftask.isCancelled()){
message.append(ftask.get()).append(System.lineSeparator())
}
}
return message.toString();
}
7、创建TradeDataTask类,处理文件
public class TradeDataTask implements Callable<String>{
private MultipartFile multipartFile;
private String fileType;
private Class excel;
//因为是多线程,所以这里把用的dao传进来
private BaseDao baseDao
//jpa保存,比较慢,所以这里新建了5个文件对应的jdbc保存数据库操作的接口,构建的时候,由外部传入
private TradeDataInsertInte tradeDataInsert;
private CountDownLatch countNum;
.........
@Override
public String call() throws Exception{
try{
// 主要就是这一行,处理文件,excel变量,就是第二步创建的实体类class
EasyExcel.read(multipartFile.getInputStream(),excel,new
TradeDataListenner(baseDao,tradeDataInsert))
//读取excel第一个sheet并指定表头是第二行
.sheet(0).headRowNumber(2).doRead();
return "文件处理成功,或者失败"
}catch(Exception e){
....... 处理异常
}finally{
//处理完成,这里要释放资源,否则会一直卡住
countNum.countDown();
}
}
}
8、最终拿到数据,进行校验,保存入库
public class TradeDataListener extends CommonAnalysisEventListenner{
privte TradeDataInsertInte tradeDataInsert;
private BaseDao baseDao;
@Override
void saveData(List list){
........
//这里,list就拿到了,excel中的数据了,这里可以进行一些,数据校验
tradeDataInsert.saveLargeData(list);
}
}
9、一切核心就是这个EasyExcel监听器
//主要是继承的这个AnalysisEventListenner抽象类
public abstract class CommonAnalysisEventListenner<T> extends AnalysisEventListenner<T>{
//决定多所少行保存一次
private static final int BATCH_COUNT = 10000;
final List<T> catchList = new LinkedList<>();
//由子类去实现
abastract void saveData(List<T> list)
//这个方法,每解析一行,就会调用一次
@Override
public void invoke(T t,AnalysisContext analysisContext){
this.catchList.add(t)
if(this.catchList.size() >= this.BATCH_COUNT){
this.saveData(catchList)
this.catchList.clear();
}
}
//所有数据解析完成,调用
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext){saveData(catchList)}
}
现在所有的工作,都完成了,跑起来一切都OK了,大功告成。可过了十天半个月,测试服务器上导入,突然报错了,cache XXXXXXXX creation in EhcacheManager failed 一脸懵逼,
最后查询问题发现是由于springboot 启动的时候,默认会再/tmp目录下,创建一些临时文件夹,以及文件。但是linux系统,对/tmp目录下的文件,如果十天没有更新,就会自动清除,导致我们导入失败。
解决方法,网络上有人说是更改默认的目录,配置文件配置 server.tomcat.basedir=/tmp/tomcat 指定目录。经过测试,没有效果,导入文件的临时目录仍然创建在/tmp下。
其实只需要在启动springboot的时候,加上参数-Djava.io.tmpdir=/xxxx/xx(注意这个路径,必须要存在)
自己记录一下,工作中遇到的问题。好记性不如烂笔头