前言:
项目架构:
前端框架:Angular6 + ng-zorro-antd
后端架构:SSM + Dubbo:springMVC + spring + myBatis-plus
接下来请跟随小编来看看Excel导入是如何实现的呢?
正文:
前端
一。效果图
二。前端交互核心代码
customReq = (item: UploadXHRArgs) => {
// 构建一个 FormData 对象,用于存储文件或其他参数
const formData = new FormData();
// tslint:disable-next-line:no-any
formData.append('file', item.file as any);
formData.append('id', '1000');
const req = new HttpRequest('POST', item.action, formData, {
reportProgress: true,
withCredentials: true
});
// 始终返回一个 `Subscription` 对象,nz-upload 会在适当时机自动取消订阅
return this.httpClient.request(req).subscribe((event: HttpEvent<{}>) => {
console.log('event------', event);
if (event.type === HttpEventType.UploadProgress) {
if (event.total > 0) {
// tslint:disable-next-line:no-any
(event as any).percent = event.loaded / event.total * 100;
}
// 处理上传进度条,必须指定 `percent` 属性来表示进度
item.onProgress(event, item.file);
} else if (event instanceof HttpResponse) {
// 处理成功
item.onSuccess(event.body, item.file, event);
const responseBody = JSON.parse(JSON.stringify(event.body));
console.log('responseBody' + JSON.stringify(responseBody));
if (responseBody.code === ResponseCode.SUCCESSCODE) {
this.tipMsgService.createMessage(ResponseCode.SUCCESSTIP, responseBody.message);
} else if (responseBody.code === ResponseCode.FAILCODE) {
this.tipMsgService.error('温馨提示', responseBody.message);
} else if (responseBody.code === ResponseCode.UPLOADCODE) {
this.tipMsgService.error('温馨提示', '部分导入信息失败,已下载相关错误新信息请查看');
const errorListId = responseBody.data;
const url = 'demo-web/foo/exportErrorFooById/' + errorListId;
const downURL = this.http.getserverUrl(url);
window.open(downURL);
URL.revokeObjectURL(downURL);
}
}
}, (err) => {
// 处理失败
item.onError(err, item.file);
this.tipMsgService.error('温馨提示', '抱歉,上传文件失败');
});
}
后端
一.pom文件引入核心jar包
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
二。导入业务逻辑核心代码
public ItooResult importTemplate(MultipartFile multipartFile, HttpServletRequest request, HttpServletResponse response) {
response.setContentType("UTF-8");
//日期转换字符串的格式
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
try {
//校验文件是否存在
String fileName = multipartFile.getOriginalFilename();
if (fileName == null) {
log.error("导入失败,fileName is null");
return ItooResult.build(ItooResult.FAIL, "传入的文件为空");
}
//map<fooGroupName, FooGroupEntity>定义map,方便后续数据存取
Map<String, FooGroupEntity> fooGroupEntityMap = new HashMap<>(16);
//Excel解析成list
Map<Serializable, Serializable> map = new HashMap<>(4);
map.put(SHEET_NAME, "用户信息");
map.put(CLASS, FooImportTemplate.class);
List<FooImportTemplate> fooImportTemplateList = ExcelUtil.importExcel(Objects.requireNonNull(fileName), map, request, response);
if (fooImportTemplateList.size() == 0) {
return ItooResult.build(ItooResult.FAIL, "请检查导入的数据");
}
//定义导入错误数据集合并存redis中形式<String, FooTemplate>
List<FooTemplate> errorFooList = new ArrayList<>();
String errorListId = BaseUuidUtils.base58Uuid();
Long size = redisTemplate.opsForSet().size(errorListId);
size = size == null ? 0L : size;
//循环处理数据
for (FooImportTemplate excelFoo : fooImportTemplateList) {
//约束:userCode,userName必填,同时要导入userCode不能与数据库汇总的其他userCode相同
if (!this.verify(excelFoo)) {
FooTemplate fooTemplate = new FooTemplate();
BeanUtils.copyProperties(excelFoo, fooTemplate);
fooTemplate.setStartTime(simpleDateFormat.format(excelFoo.getStartTime()));
errorFooList.add(fooTemplate);
redisTemplate.opsForZSet().add(errorListId, fooTemplate, size + 1);
continue;
}
//判断组是否存在,不存在则查询或创建
String fooGroupName = excelFoo.getFooGroupName();
if (fooGroupEntityMap.get(fooGroupName) == null) {
//fooGroupName不存在map中,则查询数据库,如果没有则创建
FooGroupEntity fooGroupEntity = fooGroupService.findByDeptName(fooGroupName);
if (fooGroupEntity == null) {
//创建fooGroup,保存到数据库
fooGroupEntity = new FooGroupEntity();
//todo
fooGroupEntity.setId(IdWorker.getIdStr());
fooGroupEntity.setParentId("0");
fooGroupEntity.setDeptName(fooGroupName);
fooGroupService.save(fooGroupEntity);
}
//将fooGroupEntity放入map中,下次就不用再查询数据库了
fooGroupEntityMap.put(fooGroupName, fooGroupEntity);
}
//new出FooEntity对象,通过BeanUtils把相同属性进行拷贝,然后处理不能拷贝的特殊属性
FooEntity fooEntity = new FooEntity();
BeanUtils.copyProperties(excelFoo, fooEntity);
fooEntity.setFooGroupId(fooGroupEntityMap.get(fooGroupName).getId());
//保存到数据库
this.save(fooEntity);
}
//不符合条件的信息返回
if (errorFooList.size() > 0){
return ItooResult.build("0001", "部分导入失败", errorListId);
}
} catch (Exception e) {
return ItooResult.build(ItooResult.FAIL, "导入用户异常");
}
return ItooResult.build(ItooResult.SUCCESS, "导入用户成功");
}
在处理导入的核心业务逻辑中,有两个点需要特殊处理:(1)校验 (2)导入部分失败,此处代码示例做了这两部分的处理:
2.1 导入文件的数据校验
private boolean verify(FooImportTemplate fooImportTemplate) {
//判断非空字段是否有值
if (StringUtils.isEmpty(fooImportTemplate.getUserCode())) {
fooImportTemplate.setFailReason("userCode未填写");
log.warn("导入失败,userCode未填写,excelFooEntity-{}", fooImportTemplate);
}
if (StringUtils.isEmpty(fooImportTemplate.getUserName())) {
fooImportTemplate.setFailReason("userName未填写");
log.warn("导入失败,userName未填写,excelFooEntity-{}", fooImportTemplate);
}
//判断用户名是否重复
if (fooDao.queryByUserCode(fooImportTemplate.getUserCode()).size() > 0) {
fooImportTemplate.setFailReason("userCode已存在");
log.warn("导入失败,userCode已存在,excelFooEntity-{}", fooImportTemplate);
return false;
}
return true;
}
2.2 部分导入失败,将信息记录,同时返回给前端,并且在返回的数据中涵盖错误原因
//约束:userCode,userName必填,同时要导入userCode不能与数据库汇总的其他userCode相同
if (!this.verify(excelFoo)) {
FooTemplate fooTemplate = new FooTemplate();
BeanUtils.copyProperties(excelFoo, fooTemplate);
fooTemplate.setStartTime(simpleDateFormat.format(excelFoo.getStartTime()));
errorFooList.add(fooTemplate);
redisTemplate.opsForZSet().add(errorListId, fooTemplate, size + 1);
continue;
}
结语:
与此篇文章相关的博客:
《下载excel模板》:https://blog.csdn.net/yxf15732625262/article/details/84639283
《excel导出》:https://blog.csdn.net/yxf15732625262/article/details/84593696