通用导入导出方案

基于阿里的easyexcel的通用的导入导出方案

ExcelUploadResult


/**
 * excel文件上传的结果
 */
@Data
public class ExcelUploadResult {
    /**
     * 总行数
     */
    private int total;
    /**
     * 成功的数量
     */
    private int successTotal;
    /**
     * 失败的数据集
     */
    private List errorRows = new ArrayList<>();

    /**
     * 转化为String
     */
    public final String convert() {
        StringBuilder result = new StringBuilder();
        int total = this.getTotal();
        int successTotal = this.getSuccessTotal();
        result.append(I18nUtil.getMessage("total") + ": " + total + ", ");
        result.append(I18nUtil.getMessage("success_total") + ": " + successTotal + ";");
        List errorRows = this.getErrorRows();
        for (ExcelErrorRow errorRow : errorRows) {
            result.append("\n");
            Integer num = errorRow.getNum();
            result.append(I18nUtil.getMessage("row") + num + ", ");
            String reason = errorRow.getReason();
            result.append(I18nUtil.getMessage("fail_reason") + ": " + reason);
        }
        return result.toString();
    }


}

ElTableColumn

@Data
public class ElTableColumn implements Serializable ,Comparable {

    private static final long serialVersionUID = -8735750649377906399L;
    /**
     * 展示序号,在第一列为0,第二列为1,以此类推
     */
    private Integer index;

    /**
     * 字段名称 默认取实体类中对应的字段名
     */
    private String field;

    /**
     * 中英文展示名称
     */
    private String label;

    /**
     * 表格宽度
     */
    private int width;

    /**
     * 对齐方式 left/right/center
     */
    private String align;

    /**
     * 自定义formatter方法
     */
    private String formatter;

    /**
     * 列是否固定在左侧或者右侧,true 表示固定在左侧 left, right
     */
    private String fixed;

    /**
     * 是否显示,true-默认显示
     */
    private Boolean display;

    /**
     * 是否导出,true-默认导出
     */
    private Boolean export;

    /**
     * 是否为模板字段,true-默认导出
     */
    private Boolean template;

    /**
     * 是否可为空,true-导入校验用
     */
    private Boolean  empty;

    /**
     * 长度,100-导入校验用
     */
    private Integer length;

    /**
     * 正则表达式-导入校验用
     */
    private String reg;

    /**
     * 错误描述,导入校验用
     */
    private String errorMsg;

    /**
     * 校验数据用
     */
    private String cacheKey;


    @Override
    public int compareTo(ElTableColumn anotherColumn) {
        return index - anotherColumn.getIndex();
    }

    @Override
    public String toString() {
        return "ElTableColumn{" +
                "index=" + index +
                ", prop='" + field + '\'' +
                ", label='" + label + '\'' +
                ", width=" + width +
                ", align='" + align + '\'' +
                ", formatter='" + formatter + '\'' +
                ", fixed='" + fixed + '\'' +
                ", display=" + display +
                ", export=" + export +
                ", template=" + template +
                ", empty=" + empty +
                ", length=" + length +
                ", reg='" + reg + '\'' +
                ", errorMsg='" + errorMsg + '\'' +
                ", cacheKey='" + cacheKey + '\'' +
                '}';
    }
}

ExcelErrorRow

@Data
public class ExcelErrorRow {

    /**
     * 行数
     */
    private Integer num;

    /**
     * 失败原因
     */
    private String reason;

    public ExcelErrorRow() {
    }

    public ExcelErrorRow(int num, String reason) {
        this.num = num;
        this.reason = reason;
    }

    public ExcelErrorRow(int num) {
        this.num = num;
    }

}

UploadDataListener

@Slf4j
public class UploadDataListener extends AnalysisEventListener {

    /**
     * excel 上传结果
     */
    private ExcelUploadResult excelUploadResult = new ExcelUploadResult();

    /**
     * 额外参数
     */
    private Map params;

    /**
     * 默认值:true
     * true:忽略错误的记录,导入正确的记录
     * false:若出现一条错误记录,则都不导入
     */
    private boolean flag = true;

    public ExcelUploadResult getExcelUploadResult() {
        return excelUploadResult;
    }

    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int BATCH_SIZE = 10000;
    List list = new ArrayList();
    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private IBaseService baseService;

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param baseService
     */
    public UploadDataListener(IBaseService baseService) {
        this.baseService = baseService;
    }


    public UploadDataListener(IBaseService baseService, Map params) {
        this.baseService = baseService;
        this.params = params;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param t       one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(T t, AnalysisContext context) {
        // 设置总条数
        excelUploadResult.setTotal(excelUploadResult.getTotal() + 1);

        Integer rowIndex = context.readRowHolder().getRowIndex();
        boolean bool = baseService.checkExcelRow(t, excelUploadResult.getErrorRows(), rowIndex + 1, params);
        if (bool) {
            // 设置成功总条数
            excelUploadResult.setSuccessTotal(excelUploadResult.getSuccessTotal() + 1);
            list.add(t);
        }
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_SIZE) {
            saveData();
            // 存储完成清理 list
            list.clear();
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        log.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveData() {
        log.info("{}条数据,开始存储数据库!", list.size());
        try {
            if (CollectionUtils.isNotEmpty(list)) {
                flag = baseService.filterData(list, excelUploadResult.getErrorRows(),excelUploadResult);
                if (flag) {
                    List data = baseService.convertData(this.list);
                    baseService.saveBatch(data, BATCH_SIZE);
                }
            }

        } catch (Exception e) {
            excelUploadResult.setSuccessTotal(excelUploadResult.getSuccessTotal() - list.size());
            log.error("导入excel保存数据库出错", e);

        }
        log.info("存储数据库成功!");
    }


    @Override
    public void onException(Exception exception, AnalysisContext context) {
        log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
        // 如果是某一个单元格的转换异常 能获取到具体行号
        // 如果要获取头的信息 配合invokeHeadMap使用
        excelUploadResult.setTotal(excelUploadResult.getTotal() + 1);
        List errorRows = excelUploadResult.getErrorRows();
        ExcelErrorRow errorRow = new ExcelErrorRow();
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
            log.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(), excelDataConvertException.getColumnIndex());
            errorRow.setNum(excelDataConvertException.getRowIndex() + 1);
            errorRow.setReason("第" + (excelDataConvertException.getColumnIndex() + 1) + "列解析异常");
        } else {
            errorRow.setNum(-1);
            errorRow.setReason("解析失败,请检查数据格式");
            log.error("解析失败", exception);
        }
        errorRows.add(errorRow);
    }

}

TableColumn

@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface TableColumn {

    /**
     * 展示序号,在第一列为0,第二列为1,以此类推
     */
    int index() default 0;

    /**
     * 中英文展示名称
     */
    String label() default "";

    /**
     * 表格宽度
     */
    int width() default 150;

    /**
     * 对齐方式 left/right/center
     */
    String align() default CENTER;

    /**
     * 自定义formatter方法
     */
    String formatter() default "";

    /**
     * 列是否固定在左侧或者右侧,true 表示固定在左侧 left, right
     */
    String fixed() default "true";

    /**
     * 居中对齐
     */
    String CENTER = "center";

    /**
     * 左对齐
     */
    String LEFT = "left";

    /**
     * 右对齐
     */
    String RIGHT = "right";

    /**
     * 是否显示,true-默认显示
     */
    boolean display() default true;

    /**
     * 是否导出,true-默认导出
     */
    boolean export() default true;

    /**
     * 是否为模板字段,true-默认导出
     */
    boolean template() default true;

    /**
     * 校验数据用
     * key值对应的数据是否在缓存存在
     */
    String  cacheKey() default "";

    /**
     * 是否可为空,true-导入校验用
     */
    boolean empty() default true;

    /**
     * 长度,100-导入校验用
     */
    int length() default 100;

    /**
     * 正则表达式-导入校验用
     */
    String reg() default "";

    /**
     * 错误描述,导入校验用
     */
    String errorMsg() default "";
}

BaseServiceImpl

/**
 * @Description: ServiceImpl基类
 */
@Slf4j
public class BaseServiceImpl, T> extends ServiceImpl implements IBaseService {

    @Override
    public QueryWrapper buildQueryWrapper(T t) {
        QueryWrapper queryWrapper = new QueryWrapper<>(t);
        queryWrapper.orderByDesc("id");
        return queryWrapper;
    }

    @Override
    public void exportXlsTemplate(HttpServletResponse response, Class clazz, String fileName, List list) {
        try {
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

            List elTableColumns = ElTableColumnUtil.getAllTableColumns(clazz);
            List> dataList = new ArrayList<>();
            dataList.add(list);
            if (CollectionUtils.isNotEmpty(elTableColumns)) {
                List> heads = getHeads(elTableColumns, true);
                EasyExcel.write(response.getOutputStream(), clazz).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("sheet1").head(heads).doWrite(dataList);
            } else {
                EasyExcel.write(response.getOutputStream(), clazz).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("sheet1").doWrite(dataList);
            }

        } catch (Exception e) {
            log.error("导出Excel失败", e);
        }
    }

    @Override
    public void exportXlsTemplate(HttpServletResponse response, Class clazz, String fileName) {
        this.exportXls(response, clazz, fileName, new ArrayList<>(), true);
    }

    @Override
    public void exportXls(HttpServletResponse response, Class clazz, String fileName, List list) {
        this.exportXls(response, clazz, fileName, list, false);
    }

    @Override
    public  void exportDataByXls(HttpServletResponse response, Class clazz, String fileName, List list) {
        this.exportXls(response, clazz, fileName, list, false);
    }

    private  void exportXls(HttpServletResponse response, Class clazz, String fileName, List list, boolean isTemplate) {
        try {
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

            List elTableColumns = ElTableColumnUtil.getAllTableColumns(clazz);
            if (CollectionUtils.isNotEmpty(elTableColumns)) {
                List> heads = getHeads(elTableColumns, isTemplate);
                List> dataList = new ArrayList<>();
                if (!isTemplate) {
                    dataList = getDataList(elTableColumns, list);
                }
                EasyExcel.write(response.getOutputStream(), clazz).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("sheet1").head(heads).doWrite(dataList);
            } else {
                EasyExcel.write(response.getOutputStream(), clazz).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("sheet1").doWrite(list);
            }

        } catch (Exception e) {
            log.error("导出Excel失败", e);
        }

    }

    @Override
    public ExcelUploadResult importXls(MultipartFile file, Class clazz) {
        return importXls(file, clazz, new HashMap<>());
    }

    @Override
    public ExcelUploadResult importXls(MultipartFile file, Class clazz, Map params) {
        UploadDataListener uploadDataListener = new UploadDataListener(this, params);

        try {
            EasyExcel.read(file.getInputStream(), clazz, uploadDataListener).sheet().doRead();
        } catch (Exception e) {
            log.error("导入Excel失败", e);
        }
        ExcelUploadResult excelUploadResult = uploadDataListener.getExcelUploadResult();

        return excelUploadResult;
    }

    @Override
    public ExcelUploadResult syncImportXls(MultipartFile file, Class clazz, Map params) {

        //excel 上传结果
        ExcelUploadResult excelUploadResult = new ExcelUploadResult();
        List errorRows = excelUploadResult.getErrorRows();

        try {
            List list = EasyExcel.read(file.getInputStream()).head(clazz).sheet().doReadSync();
            for (int i = 0; i < list.size(); i++) {
                T record = list.get(i);
                checkExcelRow(record, errorRows, i + 1, params);
            }
            this.saveBatch(list);
        } catch (Exception e) {
            log.error("导入Excel失败", e);
        }

        return excelUploadResult;
    }


    @Override
    public boolean checkExcelRow(T record, List errorRows, int rowNum, Map params) {
        try {
            Class clazz = record.getClass();
            final Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                //打开私有访问
                field.setAccessible(true);
                //获取属性值
                Object value = field.get(record);
                if (value != null) {
                    if (value instanceof String) {
                        String valStr = (String) value;
                        if (!valStr.trim().isEmpty()) {
                            return true;
                        }
                    } else {
                        return true;
                    }
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 获取需要导出的字段,以便生成表头
     */
    protected List> getHeads(List elTableColumns, boolean isTemplate) {
        List> heads = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(elTableColumns)) {
            heads = elTableColumns.stream().filter(column -> {
                if (isTemplate) {
                    if (column.getTemplate() == true) {
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    return true;
                }

            }).map(column -> {
                return Arrays.asList(column.getLabel());
            }).collect(Collectors.toList());
        }

        return heads;
    }

    @Override
    public boolean filterData(List list, List errorRows,ExcelUploadResult excelUploadResult) {
        return true;
    }

    @Override
    public List convertData(List list) {
        return list;
    }

    /**
     * 获取需要导出的字段,生成导出的数据列表
     */
    private  List> getDataList(List elTableColumns, List rows) {
        List> dataList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(elTableColumns) && CollectionUtils.isNotEmpty(rows)) {

            rows.forEach(row -> {
                List l = new ArrayList<>();
                Class clazz = row.getClass();
                elTableColumns.forEach(column -> {
                    String field = column.getField();
                    try {
                        Field f = clazz.getDeclaredField(field);
                        f.setAccessible(true);
                        Object value = f.get(row);
                        String formatter = column.getFormatter();
                        if ((value instanceof Date) && StringUtils.isNotBlank(formatter)) {
                            value = DateUtil.format((Date) value, formatter);
                        }

                        l.add(value);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                });
                dataList.add(l);
            });
        }

        return dataList;
    }

}


ElTableColumnUtil

import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Description: ElementUi Util
 * Company: 顺丰科技有限公司国际业务科技部
 *
 * @Author: 80003512
 * Date: 2019/4/12 11:34
 */
public class ElTableColumnUtil {

    /**
     * 获取所有加注解的字段
     *
     * @param clazz
     * @return
     */
    public static List getAllTableColumns(Class clazz) {
        List list = new ArrayList<>();
        try {
            Field[] fields = clazz.getDeclaredFields();
            int i = 0;
            for (Field field : fields) {
                if (field.isAnnotationPresent(TableColumn.class)) {
                    String fieldName = field.getName();
                    TableColumn[] columns = field.getAnnotationsByType(TableColumn.class);
                    for (TableColumn column : columns) {
                        ElTableColumn elTableColumn = new ElTableColumn();
                        // 国际化label
                        String label = column.label();
                        if (StringUtils.isBlank(label)) {
                            elTableColumn.setLabel(fieldName);
                        } else {
                            elTableColumn.setLabel(I18nUtil.getMessage(label));
                        }
                        elTableColumn.setField(fieldName);
                        elTableColumn.setFormatter(column.formatter());
                        elTableColumn.setAlign(column.align());
                        elTableColumn.setIndex(i);
                        elTableColumn.setWidth(column.width());
                        elTableColumn.setEmpty(column.empty());
                        elTableColumn.setLength(column.length());
                        elTableColumn.setReg(column.reg());
                        elTableColumn.setErrorMsg(column.errorMsg());
                        elTableColumn.setCacheKey(column.cacheKey());
                        boolean export = column.export();
                        boolean display = column.display();
                        boolean template = column.template();
                        elTableColumn.setDisplay(display);
                        elTableColumn.setExport(export);
                        elTableColumn.setTemplate(template);
                        list.add(elTableColumn);
                        i++;
                    }
                }
            }
            Collections.sort(list);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }
}

你可能感兴趣的:(通用导入导出方案)