EasyExcel导出及校验后导入前后台功能实现
-
- 一、引入maven依赖
-
- 二、实体类及工具类
-
- 1. 导入时的自定义校验注解
- 2. 格式校验工具类
- 3. Excel导入的公用监听
- 4. 导入时校验失败返回的对象
- 5. 导入时的校验方法返回的参数
- 6.表对象
- 三、Excel导出
-
- 1. Controller层代码
- 2. 前台代码(Get)
- 3. 前台代码(Post)
- 四、Excel导入
-
- 1. Controller层代码
- 2. Service层代码
- 3. ServiceImpl层代码
- 4. 前台代码
一、引入maven依赖
1. 在pom.xml中引入maven依赖
<properties>
<poi.version>4.0.0</poi.version>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.6</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>${poi.version}</version>
</dependency>
</dependencies>
二、实体类及工具类
1. 导入时的自定义校验注解
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target(ElementType.FIELD)
@Retention(RUNTIME)
@Documented
public @interface ExamStemDtoAnnotation {
boolean validIsBlank() default false;
boolean validIsBoolean() default false;
boolean validIsTime() default false;
boolean validIsSplitByComma() default false;
boolean validIsNumber() default false;
boolean validIsLong() default false;
boolean validIsDouble() default false;
String errorDesc() default "";
String timeFormat() default "";
}
2. 格式校验工具类
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.lang.reflect.Field;
import java.time.format.DateTimeFormatter;
import java.util.List;
import static cn.hutool.core.util.NumberUtil.*;
public class ExcelValid {
public static void validField(Object object, List<String> errorDescList, Class<?> zClass) throws IllegalAccessException {
Field[] fields = zClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
ExamStemDtoAnnotation examStemDtoAnnotation = field.getAnnotation(ExamStemDtoAnnotation.class);
if (ObjectUtil.isNotNull(examStemDtoAnnotation)) {
if (examStemDtoAnnotation.validIsBlank()) {
String readMsg = (String) field.get(object);
if (readMsg!=null && StrUtil.isBlank(readMsg)) {
errorDescList.add(examStemDtoAnnotation.errorDesc());
} else {
if(readMsg==null){
errorDescList.add(examStemDtoAnnotation.errorDesc());
}else {
String fieldValueStr = readMsg.replaceAll("[\r\n]", "");
ReflectUtil.setFieldValue(object, field, fieldValueStr.trim());
}
}
}
if (examStemDtoAnnotation.validIsBoolean()) {
String readMsg = (String) field.get(object);
if (readMsg == null || "".equals(readMsg) ) {
errorDescList.add(examStemDtoAnnotation.errorDesc());
}else {
if((!"是".equals(readMsg) && !"否".equals(readMsg))){
errorDescList.add(examStemDtoAnnotation.errorDesc());
}
}
}
if (examStemDtoAnnotation.validIsTime()) {
String timeFormat = examStemDtoAnnotation.timeFormat();
String readMsg = (String) field.get(object);
if (readMsg==null || "".equals(readMsg)) {
String content = examStemDtoAnnotation.errorDesc() + ",时间格式应为:" + timeFormat;
errorDescList.add(content);
}else {
if(!isDateVail(readMsg, timeFormat)){
String content = examStemDtoAnnotation.errorDesc() + ",时间格式应为:" + timeFormat;
errorDescList.add(content);
}
}
}
if (examStemDtoAnnotation.validIsNumber()) {
String readMsg = (String) field.get(object);
if (readMsg == null || "".equals(readMsg)) {
errorDescList.add(examStemDtoAnnotation.errorDesc());
}else {
if(!isNumber(readMsg)){
errorDescList.add(examStemDtoAnnotation.errorDesc());
}
}
}
if (examStemDtoAnnotation.validIsLong()) {
String readMsg = (String) field.get(object);
if (readMsg == null || "".equals(readMsg)) {
errorDescList.add(examStemDtoAnnotation.errorDesc());
}else {
if(!isLong(readMsg)){
errorDescList.add(examStemDtoAnnotation.errorDesc());
ReflectUtil.setFieldValue(object, field, null);
}
}
}
if (examStemDtoAnnotation.validIsDouble()) {
String readMsg = (String) field.get(object);
if (readMsg == null || "".equals(readMsg)) {
errorDescList.add(examStemDtoAnnotation.errorDesc());
}else {
if(!isInteger(readMsg) && !isDouble(readMsg)){
errorDescList.add(examStemDtoAnnotation.errorDesc());
}
}
}
if (examStemDtoAnnotation.validIsSplitByComma()) {
String readMsg = (String) field.get(object);
if (readMsg == null || "".equals(readMsg)) {
errorDescList.add(examStemDtoAnnotation.errorDesc());
}else {
if(!isSplitByComma(readMsg)){
errorDescList.add(examStemDtoAnnotation.errorDesc());
}
}
}
}
}
}
public static Integer parseBooleanStrToInteger(String str){
return "是".equals(str)?1:0;
}
public static Boolean parseBooleanStrToBoolean(String str){
return "是".equals(str);
}
private static boolean isNumber(String str) {
try {
if("".equals(str)){
return false;
}else {
Integer.parseInt(str);
}
return true;
} catch (NumberFormatException e) {
e.printStackTrace();
return false;
}
}
private static boolean isDateVail(String date,String format) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(format);
boolean flag = true;
try {
if("".equals(date)){
return false;
}else {
dtf.parse(date);
}
} catch (Exception e) {
flag = false;
}
return flag;
}
private static boolean isSplitByComma(String str) {
boolean flag = true;
String comma1 = ",";
String comma2 = ",";
try {
if("".equals(str)){
return false;
}else {
if(str.contains(comma1)){
String[] split = str.split(comma1);
return true;
}else if(str.contains(comma2)){
String[] split = str.split(comma2);
return true;
}
}
} catch (Exception e) {
flag = false;
}
return flag;
}
}
3. Excel导入的公用监听
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
import lombok.SneakyThrows;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class ExcelListener<T> extends AnalysisEventListener<T> {
private List<T> list;
private Class<?> zClass;
private String sheetName;
private Boolean checkFlag;
public ExcelListener(List<T> list,Class<?> zClass,String sheetName) {
this.list = list;
this.zClass = zClass;
this.sheetName = sheetName;
this.checkFlag = true;
}
@SneakyThrows
@Override
public void invoke(T data, AnalysisContext context) {
String name = context.readSheetHolder().getSheetName();
if(!sheetName.equals(name)){
checkFlag = false;
}else {
List<String> errorDescList = new ArrayList<>();
ExcelValid.validField(data,errorDescList,zClass);
ReadRowHolder readRowHolder = context.readRowHolder();
Integer rowIndex = readRowHolder.getRowIndex() +1;
Method setId = zClass.getMethod("setRowIndex", Integer.class);
setId.invoke(data, rowIndex);
Method setError = zClass.getMethod("setErrorDescList", List.class);
if(errorDescList.size()>0){
errorDescList = errorDescList.stream().distinct().collect(Collectors.toList());
}
setError.invoke(data, errorDescList);
list.add(data);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
public Boolean getCheckFlag() {
return checkFlag;
}
public List<T> getList() {
return list;
}
}
4. 导入时校验失败返回的对象
import lombok.Data;
import java.util.List;
@Data
public class ErrorData {
private Integer row;
private List<String> errorMsgList;
}
5. 导入时的校验方法返回的参数
import lombok.Data;
import java.util.List;
@Data
public class ImportData {
List<?> excelList;
List<ErrorData> errorDataList;
Boolean checkFlag;
}
6.表对象
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelIgnore;
import com.alibaba.excel.metadata.AbstractCell;
import com.community.common.excel.ExamStemDtoAnnotation;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
public class FactoryMonthInfoExcel extends AbstractCell {
@ExamStemDtoAnnotation(errorDesc ="管理区域为空" ,validIsBlank = true)
@Excel(name = "管理区域",orderNum = "1",width = 20)
private String factoryName;
@ExamStemDtoAnnotation(errorDesc ="区域编号不能为空且只能为数字" ,validIsLong = true)
@Excel(name = "区域编号",orderNum = "2",width = 30)
private String factoryId;
@ExamStemDtoAnnotation(errorDesc ="送餐只能为数字",validIsNumber = true)
@Excel(name = "送餐",orderNum = "3")
private String foodDelivery;
@ExamStemDtoAnnotation(errorDesc ="关怀数只能为数字" ,validIsNumber = true)
@Excel(name = "关怀数",orderNum = "4")
private String careNum;
@ExamStemDtoAnnotation(errorDesc ="加梯数只能为数字" ,validIsNumber = true)
@Excel(name = "加梯数",orderNum = "5")
private String laddersNum;
@ExamStemDtoAnnotation(errorDesc ="减排只能为数字" ,validIsNumber = true)
@Excel(name = "减排",orderNum = "6")
private String ersNum;
@ExamStemDtoAnnotation(errorDesc ="时间格式错误" ,validIsBlank = true,validIsTime = true,timeFormat="yyyy年MM月")
@Excel(name = "时间(月)",orderNum = "7",width = 20)
private String date;
@ExcelIgnore
private Long id;
@ExcelIgnore
private List<String> errorDescList;
}
三、Excel导出
1. Controller层代码
@GetMapping("/exportFactoryMonthInfoExcel")
@ApiOperation(value="导出小区月度指标数据配置Excel")
public void exportFactoryMonthInfoExcel(HttpServletResponse response,Long factoryId) {
List<FactoryMonthInfoExcel> factoryMonthInfos = iFactoryMonthInfoService.getFactoryMonthInfoExcelList(factoryId);
ExcelUtil.exportExcel(factoryMonthInfos,"小区月度指标数据配置","小区月度指标数据配置", FactoryMonthInfoExcel.class,"小区月度指标数据配置"+ DateFormatUtil.formatDateToTimeStr(new Date()) +".xls",response);
}
2. 前台代码(Get)
exportExcel() {
var url =
vueConfig.deal + '://' + vueConfig.webIp + ':' + vueConfig.webPort
location.href =
url +
'/FactoryMonthInfo/exportFactoryMonthInfoExcel?factoryId=' +
parseInt(this.factoryId)
},
3. 前台代码(Post)
import axios from 'axios'
import jsFileDownload from 'js-file-download'
exportExcel() {
var url =
vueConfig.deal + '://' + vueConfig.webIp + ':' + vueConfig.webPort
let openUrl = url + '/FactoryTarget/ExportFactoryTarget'
axios({
method: 'post',
url: openUrl,
responseType: 'blob',
data: this.queryParam,
}).then((res) => {
if (res && res.data) {
if (res.headers['content-disposition']) {
var filename = res.headers['content-disposition'].split(
'attachment;filename='
)[1]
jsFileDownload(res.data, filename)
}
}
})
},
四、Excel导入
1. Controller层代码
@PostMapping("/importFactoryMonthInfoExcel")
@ApiOperation(value = "导入小区月度指标配置数据Excel")
@ResponseBody
public BoardMessage importFactoryMonthInfoExcel(@RequestParam("file") MultipartFile file) {
ImportData importData = iFactoryMonthInfoService.checkExcel(file);
if (importData != null && importData.getCheckFlag()) {
List<ErrorData> errorDataList = importData.getErrorDataList();
if (errorDataList != null && errorDataList.size() > 0) {
return new BoardMessage(ErrorCode.getSuccess(), "导入失败!请检查导入文档的格式是否正确", errorDataList, " importFactoryMonthInfoExcel", null);
} else {
List<?> excelList = importData.getExcelList();
List<FactoryMonthInfoExcel> successList = new ArrayList<>();
for (Object item : excelList) {
successList.add((FactoryMonthInfoExcel) item);
}
Boolean flag = iFactoryMonthInfoService.importFactoryMonthInfoExcel(successList);
if (flag) {
return new BoardMessage(ErrorCode.getSuccess(), "导入成功", flag, " importFactoryMonthInfoExcel", null);
}
}
}
return new BoardMessage(ErrorCode.getFail(), "导入失败!请检查导入文档的格式是否正确", false, "importFactoryMonthInfoExcel", null);
}
2. Service层代码
ImportData checkExcel(MultipartFile file);
Boolean importFactoryMonthInfoExcel(List<FactoryMonthInfoExcel> successList);
3. ServiceImpl层代码
@Override
@SuppressWarnings("unchecked")
public ImportData checkExcel(MultipartFile file) {
ImportData importData = new ImportData();
List<ErrorData> errorDataList = new ArrayList<>();
try {
List<FactoryMonthInfoExcel> factoryMonthInfoExcelList = new ArrayList<>();
ExcelListener excelListener = new ExcelListener(factoryMonthInfoExcelList, FactoryMonthInfoExcel.class, "小区月度指标数据配置");
EasyExcel.read(file.getInputStream(), FactoryMonthInfoExcel.class, excelListener).sheet().headRowNumber(2).doRead();
Boolean checkFlag = excelListener.getCheckFlag();
importData.setCheckFlag(checkFlag);
if(checkFlag){
List<FactoryMonthInfoExcel> excelList = excelListener.getList();
if (excelList != null && excelList.size() > 0) {
List<Long> allFactoryIds = factoryPlusService.getSubrangeFactoryIdList(0L);
List<Long> factoryIds = excelList.stream().filter(x -> x.getFactoryId() != null && !"".equals(x.getFactoryId())).map(FactoryMonthInfoExcel::getFactoryId).map(Long::valueOf).collect(Collectors.toList());
List<FactoryMonthInfo> factoryMonthInfo = factoryMonthInfoMapper.getFactoryMonthInfoList(factoryIds);
for (FactoryMonthInfoExcel excelRow : excelList) {
if (excelRow.getFactoryId() != null && !allFactoryIds.contains(Long.valueOf(excelRow.getFactoryId()))) {
excelRow.getErrorDescList().add("编号为" + excelRow.getFactoryId() + "的区域不存在");
}
if (excelRow.getFactoryId() != null && factoryMonthInfo != null && factoryMonthInfo.size() > 0) {
List<FactoryMonthInfo> collectList = factoryMonthInfo.stream()
.filter(x -> x.getFactoryId() != null && x.getFactoryName() != null && x.getMonthStr() != null)
.filter(item ->
item.getFactoryName().equals(excelRow.getFactoryName()) &&
item.getFactoryId().equals(Long.valueOf(excelRow.getFactoryId())) &&
item.getMonthStr().equals(excelRow.getDate())
).collect(Collectors.toList());
if (collectList.size() > 0) {
FactoryMonthInfo target = collectList.get(0);
excelRow.setId(target.getId());
}
}
List<String> errorDescList = excelRow.getErrorDescList();
if (errorDescList.size() > 0) {
ErrorData errorData = new ErrorData();
errorData.setRow(excelRow.getRowIndex());
errorData.setErrorMsgList(errorDescList);
errorDataList.add(errorData);
}
}
} else {
ErrorData errorData = new ErrorData();
errorData.setRow(0);
errorData.setErrorMsgList(Collections.singletonList("未读取到数据"));
errorDataList.add(errorData);
}
importData.setErrorDataList(errorDataList);
importData.setExcelList(excelList);
}
} catch (Exception e) {
log.error(Thread.currentThread().getStackTrace()[1].getMethodName() + e.getMessage());
ErrorData errorData = new ErrorData();
errorData.setRow(0);
errorData.setErrorMsgList(Collections.singletonList("导入失败!请检查导入文档的格式是否正确"));
errorDataList.add(errorData);
importData.setErrorDataList(errorDataList);
}
return importData;
}
@Override
public Boolean importFactoryMonthInfoExcel(List<FactoryMonthInfoExcel> successList) {
try {
List<FactoryMonthInfo> factoryMonthInfos = new ArrayList<>();
for (FactoryMonthInfoExcel factoryMonthInfoExcel : successList) {
FactoryMonthInfo factoryMonthInfo = new FactoryMonthInfo();
BeanUtils.copyProperties(factoryMonthInfoExcel, factoryMonthInfo);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 00:00:00");
factoryMonthInfo.setDate(simpleDateFormat.parse(factoryMonthInfoExcel.getDate()+"01日 00:00:00"));
factoryMonthInfo.setAddTime(new Date());
factoryMonthInfo.setFoodDelivery(Integer.valueOf(factoryMonthInfoExcel.getFoodDelivery()));
factoryMonthInfo.setFactoryId(Long.valueOf(factoryMonthInfoExcel.getFactoryId()));
factoryMonthInfo.setCareNum(Integer.valueOf(factoryMonthInfoExcel.getCareNum()));
factoryMonthInfo.setLaddersNum(Integer.valueOf(factoryMonthInfoExcel.getLaddersNum()));
factoryMonthInfo.setErsNum(Integer.valueOf(factoryMonthInfoExcel.getErsNum()));
factoryMonthInfo.setAddTime(new Date());
factoryMonthInfo.setMenId(0);
factoryMonthInfos.add(factoryMonthInfo);
}
return saveOrUpdateBatch(factoryMonthInfos);
} catch (Exception e) {
log.error(Thread.currentThread().getStackTrace()[1].getMethodName() + e.getMessage());
return false;
}
}
4. 前台代码
<el-dialog title="导入小区年度数据信息表" :visible.sync="dialog03" width="30%" center :closeOnClickModal="false" :show-close="false" @close="closeDialog03()">
<el-row :gutter="20">
<el-col :span="20">
<el-form style="text-align:center" label-width="60px" ref="uploadformRef">
<el-form-item>
<!-- 导入文件方式 -->
<el-upload drag :limit=limitNum :auto-upload="false" accept=".xls,.xlsx" :action="UploadUrl()" :before-upload="beforeUploadFile" :on-change="fileChange" :on-exceed="exceedFile"
:on-success="handleSuccess" :on-error="handleError" :file-list="fileList">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">只能上传xlsx/xls文件</div>
</el-upload>
</el-form-item>
</el-form>
</el-col>
</el-row>
<div slot="footer" class="dialog-footer">
<el-button size="mini" @click="closeDialog03()">取消</el-button>
<el-button size="mini" type="primary" @click="uploadFile">立即上传</el-button>
</div>
</el-dialog>
UploadUrl: function () {
return ''
},
beforeUploadFile(file) {
let extension = file.name.substring(file.name.lastIndexOf('.') + 1)
const whiteList = ['xls', 'xlsx']
if (whiteList.indexOf(extension) === -1) {
this.$message.error('上传文件只能是xls、xlsx格式')
return false
}
},
fileChange(file, fileList) {
this.fileList.push(file.raw)
},
exceedFile(files, fileList) {
this.$message.warning(
`只能选择 ${this.limitNum} 个文件,当前共选择了 ${
files.length + fileList.length
} 个`
)
},
handleSuccess(res, file, fileList) {
this.$message.success('文件上传成功')
},
handleError(err, file, fileList) {
this.$message.error('文件上传失败')
},
uploadFile() {
if (this.fileList.length === 0) {
this.$message.warning('请选择需要上传的文件')
} else {
let form = new FormData()
this.fileList.map((element) => {
form.append('file', element)
})
xiaoquYmData.importFactoryTargetExcel(form).then((val) => {
if (val.status == 0) {
if (val.data == true) {
this.$message({
type: 'success',
message: '导入成功!',
})
this.dialog03 = false
} else {
this.showErrowInfor(val.data)
}
} else {
this.$message({
type: 'success',
message: '导入失败,请重新操作!',
})
}
})
}
},