easyexcle-导入

51 阅读2分钟

easyexcle-导入

将excel数据导入,如果有数据异常,则导出异常数据,并在前面加入两列,错误行号和错误信息

实现

@Data
public abstract class BaseImportData {
    /**
     * 错误行号
     */
    private Integer rowIndex;
    /**
     * 错误信息
     */
    private String errorMsg;
}
​
@Slf4j
public abstract class AbstractEventListener<M, T extends BaseImportData> extends AnalysisEventListener<T> {
​
    private static final int BATCH_COUNT = 1000;
    private List<T> invalidDataList = new ArrayList<>();
    private List<M> dataList = new ArrayList<>();
    private Set<T> duplicateDataSet = new HashSet<>();
​
    private int batchCount;
​
    public AbstractEventListener() {
        this(BATCH_COUNT);
    }
​
    public AbstractEventListener(int batchCount) {
        this.batchCount = batchCount;
    }
​
    @Override
    public void invoke(T data, AnalysisContext context) {
        Integer rowIndex = context.readRowHolder().getRowIndex() + 1;
        data.setRowIndex(rowIndex);
​
        before(data, context);
        if (!duplicateDataSet.add(data)){
            //如果数据重复
            data.setErrorMsg("重复数据");
            invalidDataList.add(data);
            return;
        }
​
        if (!validate(data, invalidDataList, dataList)) {
            return;
        }
        if (dataList.size() >= BATCH_COUNT) {
            saveBatch(dataList);
            dataList.clear();
        }
    }
​
​
    protected void before(T data, AnalysisContext context) {
        //do nothing
​
    }
​
    protected boolean validate(T data, List<T> invalidDataList, List<M> dataList) {
        M model = createModel(data);
        if (!doValidate(data, model)) {
            invalidDataList.add(data);
            return false;
        }
        dataList.add(model);
        return true;
    }
​
    /**
     * 验证数据完整性
     * 1.校验数据完整性
     * 2.校验数据唯一性
     *
     * @param data
     * @param model
     * @return
     */
    protected abstract boolean doValidate(T data, M model);
​
    /**
     * 保存批量数据
     *
     * @param dataList
     */
    protected abstract void saveBatch(List<M> dataList);
​
    /**
     * 创建对象
     *
     * @param data
     * @return
     */
    protected abstract M createModel(T data);
​
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("解析完成");
        if (!dataList.isEmpty()) {
            saveBatch(dataList);
            dataList.clear();
        }
        duplicateDataSet.clear();
    }
​
    /**
     * 获取无效数据
     *
     * @return
     */
    public List<T> getInvalidDataList() {
        return invalidDataList;
    }
​
}
​
@Service
public class EasyExcelService {
​
    public OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
        //创建本地文件
        fileName = java.net.URLEncoder.encode(fileName + ".xlsx", "UTF-8");
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        return response.getOutputStream();
    }
​
    public <T extends BaseImportData> boolean importData(MultipartFile file, AbstractEventListener<?, T> listener, Class<T> clazz,
                                                         Supplier<OutputStream> supplier) throws IOException {
        EasyExcel.read(file.getInputStream(), clazz, listener).sheet().doRead();
        List<T> invalidDataList = listener.getInvalidDataList();
        if (CollectionUtils.isEmpty(invalidDataList)) {
            return true;
        }
​
        List<List<String>> header = getErrorHeader(clazz);
​
        WriteCellStyle errorCellStyle = new WriteCellStyle();
        errorCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
        errorCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
        WriteCellStyle normalCellStyle = new WriteCellStyle();
        // 创建样式策略
           /* HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(
                    normalCellStyle, // 头样式
                    Arrays.asList(errorCellStyle, errorCellStyle) // 内容样式
            );*/
        //列样式,前两列标色
        AbstractVerticalCellStyleStrategy verticalCellStyleStrategy = new AbstractVerticalCellStyleStrategy() {
            @Override
            protected WriteCellStyle headCellStyle(Head head) {
                Integer columnIndex = head.getColumnIndex();
                if (columnIndex == 0 || columnIndex == 1) {
                    return errorCellStyle;
                }
                return null;
            }
​
            @Override
            protected WriteCellStyle contentCellStyle(Head head) {
                Integer columnIndex = head.getColumnIndex();
                if (columnIndex == 0 || columnIndex == 1) {
                    return errorCellStyle;
                }
                return null;
            }
        };
        List<List<Object>> lists = convertToErrorData(invalidDataList);
        EasyExcel.write(supplier.get()).head(header).registerWriteHandler(verticalCellStyleStrategy).sheet().doWrite(lists);
        return false;
    }
​
​
    private <T extends BaseImportData> List<List<Object>> convertToErrorData(List<T> invalidDataList) {
        List<List<Object>> errorData = new ArrayList<>();
​
        for (T data : invalidDataList) {
            List<Object> rowData = new ArrayList<>();
​
            // 添加错误行号(第一列)
            rowData.add(data.getRowIndex());
​
            // 添加错误信息(第二列)
            rowData.add(data.getErrorMsg());
​
            // 添加原始数据
            Field[] fields = data.getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(ExcelProperty.class)) {
                    try {
                        field.setAccessible(true);
                        rowData.add(field.get(data));
                    } catch (IllegalAccessException e) {
                        rowData.add(null);
                    }
                }
            }
​
            errorData.add(rowData);
        }
​
        return errorData;
    }
​
    /**
     * 获取导出表头
     * @param clazz
     * @return
     * @param <T>
     */
    private <T extends BaseImportData> List<List<String>> getErrorHeader(Class<T> clazz) {
        Field[] declaredFields = clazz.getDeclaredFields();
        List<List<String>> originHeader = Stream.of(declaredFields).filter(field -> field.isAnnotationPresent(ExcelProperty.class))
                .map(field -> field.getAnnotation(ExcelProperty.class))
                .map(excel -> new ArrayList<>(Arrays.asList(excel.value()))).collect(Collectors.toList());
​
        List<List<String>> header = new ArrayList<>();
        header.add(Arrays.asList("错误行号"));
        header.add(Arrays.asList("错误信息"));
        header.addAll(originHeader);
        return header;
    }
​
}
​

示例

@Data
public class SimpleImportData extends BaseImportData {
​
   
    @ExcelProperty(value = "姓名", index = 0)
    private String name;
​
    @ExcelProperty(value = "居住地", index = 1)
    private String address;
​
    @ExcelProperty(value = "年龄", index = 2)
    private Integer age;
​
​
}
​
@Slf4j
public class SimpleImportListener extends AbstractEventListener<SimpleImportModel, SimpleImportData> {
​
    @Override
    protected boolean doValidate(SimpleImportData data, SimpleImportModel model) {
        if (StrUtil.isBlank(data.getName())) {
            data.setErrorMsg("用户姓名为空");
            return false;
        }
        BeanUtils.copyProperties(data,model);
        return true;
    }
​
    @Override
    protected void saveBatch(List<SimpleImportModel> dataList) {
        log.info("保存数据:{}", JSON.toJSONString(dataList));
    }
​
    @Override
    protected SimpleImportModel createModel(SimpleImportData data) {
        return new SimpleImportModel();
    }
}
​
@Data
public class SimpleImportModel {
    private String name;
    private String address;
    private Integer age;
}
​