栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

easyexcel导入导出包含 自定义列宽,下拉框,日期校验,批量导入入库完整开发流程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

easyexcel导入导出包含 自定义列宽,下拉框,日期校验,批量导入入库完整开发流程

1. 引入maven依赖

    com.alibaba
    easyexcel
    2.2.7

2. 创建工具类

2.1 ExcelUtil.java(设置头背景,浅绿,宋体,对齐方式)

import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;

public class ExcelUtil {
    
    public static HorizontalCellStyleStrategy getStyleStrategy() {
        // 头的策略  样式调整
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 头背景 浅绿
        headWriteCellStyle.setFillForegroundColor(IndexedColors.LIGHT_GREEN.getIndex());
        WriteFont headWriteFont = new WriteFont();
        // 头字号
        headWriteFont.setFontHeightInPoints((short) 14);
        // 字体样式
        headWriteFont.setFontName("宋体");
        headWriteCellStyle.setWriteFont(headWriteFont);
        // 自动换行
        headWriteCellStyle.setWrapped(true);
        // 设置细边框
        headWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        headWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        headWriteCellStyle.setBorderRight(BorderStyle.THIN);
        headWriteCellStyle.setBorderTop(BorderStyle.THIN);
        // 设置边框颜色 25灰度
        headWriteCellStyle.setBottomBorderColor(IndexedColors.GREY_25_PERCENT.getIndex());
        headWriteCellStyle.setTopBorderColor(IndexedColors.GREY_25_PERCENT.getIndex());
        headWriteCellStyle.setLeftBorderColor(IndexedColors.GREY_25_PERCENT.getIndex());
        headWriteCellStyle.setRightBorderColor(IndexedColors.GREY_25_PERCENT.getIndex());
        // 水平对齐方式
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 垂直对齐方式
        headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 内容的策略 宋体
        WriteCellStyle contentStyle = new WriteCellStyle();
        // 设置垂直居中
        contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 设置 水平居中
        contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        WriteFont contentWriteFont = new WriteFont();
        // 内容字号
        contentWriteFont.setFontHeightInPoints((short) 12);
        // 字体样式
        contentWriteFont.setFontName("宋体");
        contentStyle.setWriteFont(contentWriteFont);
        // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentStyle);
    }
}

2.2 CustomCellWriteUtil.java(自适应列宽)

import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class CustomCellWriteUtil extends AbstractColumnWidthStyleStrategy {
    private static final int MAX_COLUMN_WIDTH = 255;
    private Map> CACHE = new HashMap(8);

    public CustomCellWriteUtil() {
    }

    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
        if (needSetWidth) {
            Map maxColumnWidthMap = (Map) CACHE.get(writeSheetHolder.getSheetNo());
            if (maxColumnWidthMap == null) {
                maxColumnWidthMap = new HashMap(16);
                CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
            }

            Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
            if (columnWidth >= 0) {
                if (columnWidth > 255) {
                    columnWidth = 255;
                }
                Integer maxColumnWidth = (Integer) ((Map) maxColumnWidthMap).get(cell.getColumnIndex());
                if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
                    ((Map) maxColumnWidthMap).put(cell.getColumnIndex(), columnWidth);
                    writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
                }
            }
        }
    }

    private Integer dataLength(List cellDataList, Cell cell, Boolean isHead) {
        if (isHead) {
            return cell.getStringCellValue().getBytes().length;
        } else {
            CellData cellData = (CellData) cellDataList.get(0);
            CellDataTypeEnum type = cellData.getType();
            if (type == null) {
                return -1;
            } else {
                switch (type) {
                    case STRING:
                        return cellData.getStringValue().getBytes().length;
                    case BOOLEAN:
                        return cellData.getBooleanValue().toString().getBytes().length;
                    case NUMBER:
                        return cellData.getNumberValue().toString().getBytes().length;
                    default:
                        return -1;
                }
            }
        }
    }
}

2.3 DropDownSetField.java(自定义标记导出excel的下拉框注解)

import java.lang.annotation.*;


@documented
// 作用在字段上
@Target(ElementType.FIELD)
// 运行时有效
@Retention(RetentionPolicy.RUNTIME)
public @interface DropDownSetField {
    // 固定下拉内容
    String[] source() default {};

    // 注解内的名称,解析时要注意对应
    String name() default "";
}

2.4 ResolveDropAnnotationUtil.java

import java.util.Map;
import java.util.Optional;


public class ResolveDropAnnotationUtil {

    public static String[] resove(DropDownSetField dropDownSetField, String[] strings) {
        if (!Optional.ofNullable(dropDownSetField).isPresent()) {
            return null;
        }
 
        // 获取固定下拉信息
        String[] source = dropDownSetField.source();
        if (null != source && source.length > 0) {
            return source;
        }
 
        if (null != strings && strings.length > 0) {
            try {
                String[] dynamicSource = strings;
                if (null != dynamicSource && dynamicSource.length > 0) {
                    return dynamicSource;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    
 
    //插入到map中
    private void insertMap(Map map, String[] params, DropDownSetField dropDownSetField, int i) {
        String[] sources = ResolveDropAnnotationUtil.resove(dropDownSetField, params);
        if (null != sources && sources.length > 0) {
            map.put(i, sources);
        }
    }
}
3. 具体用法

TestController.java

package com.example.demo;

public class TestController {

    
    @RequestMapping("/upload")
    @ResponseBody
    public ResultData upload(MultipartFile file) {
        try {
            // XxxSaveDataListener 导入监听器  在下面
            EasyExcel.read(file.getInputStream(), Xxximport.class, new XxxSaveDataListener(XxxService)).sheet().doRead();
            return ResultData.ok("上传成功");
        } catch (IOException e) {
            return ResultData.ok(e.getMessage());
        }
    }


    
    @PostMapping("/export")
    @ResponseBody
    public void exportXxx(HttpServletResponse response,
                          @RequestBody List xxximports) {
        String fileName = "ExportData.xlsx";
        OutputStream fileOutputStream = null;
        List exportDataList = new ArrayList<>();
        try {
            for (Xxximport xxximport : xxximports) {
                Xxximport xxximport1 = new Xxximport();
                xxximport1.setParentLibraryId(xxximport.getParentLibraryId());
                xxximport1.setCode(xxximport.getCode());
                xxximport1.setName(xxximport.getName());
                //转换指标类型;
                xxximport1.setTargetTypeId(getByTypeIdReturnName(xxximport.getTargetTypeId()));
                xxximport1.setOrigin(xxximport.getOrigin());
                //转换 定性 定量
                Long category = Long.parseLong(xxximport.getCategory());
                xxximport1.setCategory(category == 0 ? "定性" : "定量");
                xxximport1.setDefinition(xxximport.getDefinition());
                xxximport1.setStatisticalcaliber(xxximport.getStatisticalcaliber());
                xxximport1.setStandard(xxximport.getStandard());
                xxximport1.setStatisticUnit(xxximport.getStatisticUnit());
                xxximport1.setPrecisions(xxximport.getPrecisions());
                xxximport1.setRule(xxximport.getRule());
                xxximport1.setEffectDate(xxximport.getEffectDate());
                xxximport1.setExpireDate(xxximport.getExpireDate());
                exportDataList.add(xxximport1);
            }
            //文件以流形式返回前端下载
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            response.setContentType("application/x-download");
            response.setCharacterEncoding("UTF-8");
            response.addHeader("Pargam", "no-cache");
            response.addHeader("Cache-Control", "no-cache");
            response.flushBuffer();
            fileOutputStream = response.getOutputStream();
            ExcelWriter excelWriter = EasyExcel.write(fileOutputStream, Xxximport.class)
                    .registerWriteHandler(new CustomCellWriteUtil())
                    .registerWriteHandler(ExcelUtil.getStyleStrategy())
                    .build();
            WriteSheet sheet = EasyExcel.writerSheet(0, "指标库导出数据").build();
            excelWriter.write(exportDataList, sheet);
            excelWriter.finish();
            fileOutputStream.flush();
            fileOutputStream.close();
        } catch (Exception e) {
            log.error("导出出错,原因:{}", e);
        }
    }

    @GetMapping("/download/dropDownTemplate")
    public void downTemplate(HttpServletResponse response) {
        try {
            // 获取该类声明的所有字段
            Field[] fields = Xxximport.class.getDeclaredFields();
            // 响应字段对应的下拉集合
            Map map = new HashMap<>();
            Field field = null;
            // 循环判断哪些字段有下拉数据集,并获取
            for (int i = 0; i < fields.length; i++) {
                field = fields[i];
                // 解析注解信息
                DropDownSetField dropDownSetField = field.getAnnotation(DropDownSetField.class);
                if (null != dropDownSetField) {
                    String name = dropDownSetField.name();
                    if (!StringUtils.isEmpty(name)) {
                        // DropDownNameEnum 对应下拉框注解里面的字段名称,要和导出实体类中的name属性对应上
                        if (name.equals(DropDownNameEnum.TARGET_CATEGORY.getName())) {
                            List list = new ArrayList<>();
                            list.add("定性");
                            list.add("定量");
                            String[] params = list.toArray(new String[list.size()]);
                            insertMap(map, params, dropDownSetField, i);
                        }
                    } else {
                        insertMap(map, null, dropDownSetField, i);
                    }

                }
            }
            //文件以流形式返回前端下载
            String fileName = "importTemplate.xlsx";
            OutputStream fileOutputStream = null;
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            response.setContentType("application/x-download");
            response.setCharacterEncoding("UTF-8");
            response.addHeader("Pargam", "no-cache");
            response.addHeader("Cache-Control", "no-cache");
            response.flushBuffer();
            fileOutputStream = response.getOutputStream();
            ExcelWriter excelWriter = EasyExcel.write(fileOutputStream, Xxximport.class)
                    .registerWriteHandler(new XxximportHandler(map))
                    .registerWriteHandler(new CustomCellWriteUtil())
                    .registerWriteHandler(ExcelUtil.getStyleStrategy()).build();
            WriteSheet sheet = EasyExcel.writerSheet(0, "指标库导入模板").build();
            excelWriter.write(null, sheet);
            excelWriter.finish();
            fileOutputStream.flush();
            fileOutputStream.close();
        } catch (Exception e) {
            log.info("下载指标库导入模板出错,原因:{}", e);
        }
    }

    
    private void insertMap(Map map, String[] params, DropDownSetField dropDownSetField, int i) {
        String[] sources = ResolveDropAnnotationUtil.resove(dropDownSetField, params);
        if (null != sources && sources.length > 0) {
            map.put(i, sources);
        }
    }
}
4. 创建下拉框枚举类,导入导出实体类及监听器

4.1 下拉框枚举类
DropDownNameEnum .java

public enum DropDownNameEnum {
    TARGET_CATEGORY("Drop001","类别"),
    PERIOD_SET("Drop002", "周期");
    
    private String code;
    
    private String name;

    private DropDownNameEnum(String code, String name){
        this.code = code;
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public String getName() {
        return name;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public void setName(String name) {
        this.name = name;
    }
}

4.2 导入实体类

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.gaiaworks.cn.opm.biz.util.excel.DropDownSetField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Xxximport {

    
    @ExcelProperty(value = "父指标")
    private String parentLibraryId;

    
    @ExcelProperty(value = "代码")
    private String code;

    
    @ExcelProperty(value = "名称")
    private String name;

    
    @ExcelProperty(value = "类型")
    private String targetTypeId;

    
    @ExcelProperty(value = "来源")
    private String origin;

    
    @ExcelProperty(value = "类别")
    @DropDownSetField(name = "类别")
    private String category;
    
    @ExcelProperty(value = "定义")
    private String definition;

    
    @ExcelProperty(value = "口径")
    private String statisticalcaliber;

    
    @ExcelProperty(value = "评分标准")
    private String standard;


    @ExcelProperty(value = "统计单位")
    private String statisticUnit;

    @ExcelProperty(value = "精度")
    private String precisions;

    @ExcelProperty(value = "计算规则")
    private String rule;

    
    @ExcelProperty(value = "有效开始日期",index = 12)
    private String effectDate;

    
    @ExcelProperty(value = "有效结束日期",index = 13)
    private String expireDate;

}

4.3 导入模板的读取类及实现批量入库监听器
XxxSaveDataListener.java

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;


// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class XxxSaveDataListener extends AnalysisEventListener {

    
    private static final int BATCH_COUNT = 30;
    List list = new ArrayList();
    
    private XxxService xxxService;

    
    public XxxSaveDataListener(XxxService xxxService) {
        this.xxxService = xxxService;
    }

    
    @Override
    public void invoke(Xxximport data, AnalysisContext context) {
        list.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            list.clear();
        }
    }

    
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        log.error("解析失败,但是继续解析下一行:" + exception.getMessage());
        // 如果是某一个单元格的转换异常 能获取到具体行号
        // 如果要获取头的信息 配合invokeHeadMap使用
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
            log.info("第" + excelDataConvertException.getRowIndex() + "行,第" + excelDataConvertException.getColumnIndex() + "列解析异常");
        }
    }

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

    
    @Transactional
    public void saveData() {
        // service->mapper->batchInsert(list) 实现批量入库
    }
}

4.4 下拉框校验及日期校验
XxximportHandler.java

import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class XxximportHandler implements SheetWriteHandler {

    // 这里的map对应步骤3中的insertMap插入的下拉框自定义数据集合
    private Map map = null;

    public XxximportHandler(Map map) {
        this.map = map;
    }

    @Override
    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        // 这里可以对cell进行任何操作
        Sheet sheet = writeSheetHolder.getSheet();
        DataValidationHelper helper = sheet.getDataValidationHelper();
        // k 为存在下拉数据集的单元格下表 v为下拉数据集
        map.forEach((k, v) -> {
            // 下拉列表约束数据
            DataValidationConstraint constraint = helper.createExplicitListConstraint(v);
            // 设置下拉单元格的首行 末行 首列 末列
            CellRangeAddressList rangeList = new CellRangeAddressList(1, 65536, k, k);
            // 设置约束
            DataValidation validation = helper.createvalidation(constraint, rangeList);
            // 阻止输入非下拉选项的值
            validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
            validation.setShowErrorBox(true);
            validation.setSuppressDropDownArrow(true);
            validation.createErrorBox("提示", "请选择下拉框内的数据");
            sheet.addValidationData(validation);
        });
        //设置验证生效的范围(excel起始行,结束行,起始列,结束列)
        CellRangeAddressList addressList = new CellRangeAddressList(1, 65536, 12, 13);
        //设置验证方式(Date(1990, 1, 1)是excel的日期函数,能成功解析,写成"1990-01-01"解析失败)
        //需要其他日期格式,修改第四个参数"yyyy-MM-dd",eg:"yyyy-MM-dd HH:mm:ss"
        DataValidationConstraint constraint = helper.createDateConstraint(DataValidationConstraint.OperatorType.BETWEEN, "Date(1990, 1, 1)", "Date(9999, 12, 31)", "yyyy-MM-dd");
        //创建验证对象
        DataValidation dataValidation = helper.createvalidation(constraint, addressList);
        //错误提示信息
        dataValidation.createErrorBox("提示", "请输入[yyyy-MM-dd]格式日期,范围:[1990-01-01,9999-12-31]");
        dataValidation.setShowErrorBox(true);
        //验证和工作簿绑定
        sheet.addValidationData(dataValidation);
    }
}
5. 实现效果

下拉框校验,仅能选择下拉框内的数据,不按规则输入效果如下:

日期校验,仅能输入yyyy-MM-dd格式(需要其他格式,在4.4中修改),不按规则输入效果如下:

6. 补充校验

如果需要校验其他类型,修改4.4中的helper后的构造方法即可

DataValidationConstraint constraint = helper.createDateConstraint(DataValidationConstraint.OperatorType.BETWEEN, "Date(1990, 1, 1)", "Date(9999, 12, 31)", "yyyy-MM-dd");
package org.apache.poi.ss.usermodel;

import org.apache.poi.ss.util.CellRangeAddressList;


public interface DataValidationHelper {
	// 创建公式列表约束
	DataValidationConstraint createFormulaListConstraint(String listFormula);
    // 创建显式列表约束
	DataValidationConstraint createExplicitListConstraint(String[] listOfValues);
    // 创建数字约束
	DataValidationConstraint createNumericConstraint(int validationType,int operatorType, String formula1, String formula2);
	// 创建文本长度约束
	DataValidationConstraint createTextLengthConstraint(int operatorType, String formula1, String formula2);
	// 创建小数约束
	DataValidationConstraint createDecimalConstraint(int operatorType, String formula1, String formula2);
	// 创建整数约束
	DataValidationConstraint createIntegerConstraint(int operatorType, String formula1, String formula2);
	// 创建日期约束
	DataValidationConstraint createDateConstraint(int operatorType, String formula1, String formula2,String dateFormat);
	// 创建时间约束
	DataValidationConstraint createTimeConstraint(int operatorType, String formula1, String formula2);
	// 创建自定义约束
	DataValidationConstraint createCustomConstraint(String formula);
	// 创建验证
	DataValidation createvalidation(DataValidationConstraint constraint,CellRangeAddressList cellRangeAddressList);
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/351047.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号