java实现excel的导入导出(带参数校验:非空校验、数据格式校验)

这篇具有很好参考价值的文章主要介绍了java实现excel的导入导出(带参数校验:非空校验、数据格式校验)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、简单说明

本次封装引入阿里开源框架EasyExcel,EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。 github地址:GitHub - alibaba/easyexcel: 快速、简洁、解决大文件内存溢出的java处理Excel工具 。64M内存20秒读取75M(46W行25列)的Excel(3.0.2+版本)

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.1.1</version>
        </dependency>

结构图如下:

java实现excel的导入导出(带参数校验:非空校验、数据格式校验)

1.1结构说明:

1.annotation:注解

@ExcelPropertyCheck(自己写的注解用作导入数据校验)

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface ExcelPropertyCheck {
    boolean required() default true; ----是否为空,默认不为空。

    boolean checkFormat() default false; ----是否进行格式检验,默认不进行。

    int type() default -1; ----格式检验类型,int 已经支持的类型有 0->ip、1->端口、2->时间日期格式

    int length() default -1; ----长度校验, int 字符串的长度,-1不进行校验

}

@ExcelProperty(框架自带的,用于标记excel传输类和一些通用的导入导出配置)

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelProperty {

    String[] value() default {""}; ----导出时对应字段的表头名称

    int index() default -1;----排列顺序,最好不要配默认按字段顺序。

    int order() default Integer.MAX_VALUE; ----同上

    Class<? extends Converter<?>> converter() default AutoConverter.class; ----转换器

    String format() default ""; ----格式划输出
}

特别提醒:!!!

@ExcelPropertyCheck该注解作用类上时只支持required,其余属性无效。字段上的注解配置会覆盖类上的配置。  

2.constant:常量类,格式校验的类型定义,如电话号码、日期、IP地址等。

/**
 * excel导入字段类型常量
 */
public class ExcelPropertyType {
    //时间日期格式校验
    public static final int DATE_TIME = 0;
}

3.converter:转换器

读写均可使用,实现Converter重写方法即可。

如将excel 中的日期转为 LocalDateTime 或者将LocalDateTime 转为excel表中的日期

或者将数据库的枚举值0,1,2,3导入到excel文件变成对应的中文汉字

/**
 * String and string converter
 *
 */
public class CustomStringStringConverter implements Converter<String> {
    /**
     * 这里读的时候会调用
     *
     * @param context
     * @return
     */
    @Override
    public String convertToJavaData(ReadConverterContext<?> context) {
        return "自定义:" + context.getReadCellData().getStringValue();
    }

    /**
     * 这里是写的时候会调用 不用管
     *
     * @return
     */
    @Override
    public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {
        return new WriteCellData<>(context.getValue());
    }

}

上述代码中泛型为String 表示此字段经过处理后最后返回的类型为String 

4.listener:监听器

ReadListener<T>(EasyExcel提供的)

ReadListener<T>读监听器,框架提供。提供读取excel不同时期的监听方法。
分别为:
onException()-------------->“读取发生异常时监听”、
invokeHead()----------------->“读取表头信息监听”、
invoke()--------------------->“读取每行数据监听”、
doAfterAllAnalysed()----------->“所有数据读取完毕监听”。

BaseListener<T>我们自己封装的读监听器,在里面结合注解配置完成数据校验。

BaseListener<T>我们自己封装的读监听器,在里面结合注解配置完成数据校验。
对外提供的字段:
    private final List<Map<String, Object>> mapData = new ArrayList<>();
    private final List<T> data = new ArrayList<>();
    private final Map<Integer, String> errorMessageMap = new HashMap<>();
    //非空校验map
    private final Map<String, Boolean> nullAbleFieldMap = new HashMap<>();
    //格式校验map
    private final Map<String, String> checkFormatFieldMap = new HashMap<>();
    //长度校验map    
    private final Map<String, Integer> checkLengthFieldMap = new HashMap<>();
    //枚举值校验map
    private final Map<String, String[]> checkEnumFieldMap = new HashMap<>();
字段 描述 备注       
mapData excel读取完毕的Map数据 Map<String,Object>
data excel读取完毕的对于实体List List<T>
errorMessageMap 错误信息map Map<Integer,String>;key为错误行号,value为描述。
nullAbleFieldMap
非空校验map
Map<String, Boolean>
checkFormatFieldMap
格式校验map:电话号码,ip,日期
Map<String, String>
checkLengthFieldMap
长度校验map
Map<String, Integer>
checkEnumFieldMap
枚举值检验map
Map<String, String[]>

  

结合注解做参数校验或者格式校验的实现思路(反射加泛型)

 步骤一:在表头读取的监听方法里利用反射判断检验注解ExcelPropertyCheck加在了那些ExcelDTO哪些属性上,并且ExcelPropertyCheck注解的具体属性是什么将这些存入分门别类的map里面。

步骤二:在读取每行数据的时候,结合步骤一的map,判断数据是否需要校验,并且从map中取出校验的类型是什么,再去完成校验,如果校验失败则将错误信息放入errorMessageMap,当前读取的excel这一行数据全部校验通过则数据放入data 里面

完整的BaseListener<T>代码如下:

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.holder.ReadSheetHolder;
import com.baomidou.mybatisplus.core.toolkit.BeanUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.*;


public class BaseListener<T> implements ReadListener<T> {

    private final Logger logger = LoggerFactory.getLogger(BaseListener.class);
    private Class<?> headClazz;
    //读取数据map形式
    private final List<Map<String, Object>> mapData = new ArrayList<>();
    //读取数据实体类泛型形式
    private final List<T> data = new ArrayList<>();
    //非空校验map
    private final Map<String, Boolean> nullAbleFieldMap = new HashMap<>();
    //格式校验map
    private final Map<String, String> checkFormatFieldMap = new HashMap<>();
    //长度校验map    
    private final Map<String, Integer> checkLengthFieldMap = new HashMap<>();
    //枚举值校验map
    private final Map<String, String[]> checkEnumFieldMap = new HashMap<>();
    //数据校验错误信息map key:错误的行号  value:错误信息描述
    private final Map<Integer, String> errorMessageMap = new HashMap<>();

    public List<Map<String, Object>> getMapData() {
        return mapData;
    }

    public List<T> getData() {
        return data;
    }

    public Map<Integer, String> getErrorMessageMap() {
        return errorMessageMap;
    }

    public BaseListener() {
    }

    /**
     * @param headClazz excel model 类对象
     */
    public BaseListener(Class<?> headClazz) {
        this.headClazz = headClazz;
    }

    /**
     * 读取发生异常时的方法
     *
     * @param exception
     * @param context
     * @throws Exception
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        logger.debug("发生了异常");
    }

    /**
     * 读取头信息
     *
     * @param headMap
     * @param context
     */
    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        ExcelPropertyCheck clazzHeadAnno = this.headClazz.getAnnotation(ExcelPropertyCheck.class);
        Field[] declaredFields = headClazz.getDeclaredFields();
        if (clazzHeadAnno != null && clazzHeadAnno.required()) {
            for (Field declaredField : declaredFields) {
                nullAbleFieldMap.put(declaredField.getName(), true);
            }
        }

        for (Field declaredField : declaredFields) {
            ExcelPropertyCheck annotation = declaredField.getAnnotation(ExcelPropertyCheck.class);
            if (annotation != null) {
                if (annotation.checkFormat()) {
                    checkFormatFieldMap.put(declaredField.getName(), annotation.type() + "");
                }
                if (annotation.required()) {
                    nullAbleFieldMap.put(declaredField.getName(), true);
                } else {
                    nullAbleFieldMap.remove(declaredField.getName());
                }
                if (annotation.required() && annotation.length() != -1) {
                    checkLengthFieldMap.put(declaredField.getName(), annotation.length());
                }
                if (annotation.required() && annotation.value().length != 0) {
                    checkEnumFieldMap.put(declaredField.getName(), annotation.value());
                }

            }
        }
    }

    /**
     * 读取每一行数据
     *
     * @param t
     * @param analysisContext
     */
    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        int rowIndex = ((ReadSheetHolder) analysisContext.currentReadHolder()).getRowIndex() + 1;
        StringBuilder error = new StringBuilder();
        Field[] declaredFields = t.getClass().getDeclaredFields();
        //必填校验和格式校验
        for (Field declaredField : declaredFields) {
            try {
                declaredField.setAccessible(true);
                if (nullAbleFieldMap.get(declaredField.getName()) != null) {
                    if (nullAbleFieldMap.get(declaredField.getName())) {
                        Object o = declaredField.get(t);
                        if (!Objects.nonNull(o)) {
                            error.append(declaredField.getName()).append("为空;");
                        } else {
                            //字段不为空进行长度校验
                            if (checkLengthFieldMap.get(declaredField.getName()) != null) {
                                if (String.valueOf(o).length() > checkLengthFieldMap.get(declaredField.getName())) {
                                    error.append(declaredField.getName()).append("长度错误;");
                                }
                            }
                            if (checkEnumFieldMap.get(declaredField.getName()) != null) {
                                if (Integer.parseInt(String.valueOf(o)) == -1) {
                                    error.append(declaredField.getName()).append("枚举值错误;");
                                }
                            }
                        }
                    }
                }
                //是否需要进行格式校验
                if (checkFormatFieldMap.get(declaredField.getName()) != null) {
                    String res = check(String.valueOf(declaredField.get(t)), Integer.valueOf(checkFormatFieldMap.get(declaredField.getName())));
                    if (StringUtils.hasText(res)) {
                        error.append(res);
                    }
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        if (StringUtils.hasText(error.toString())) {
            errorMessageMap.put(rowIndex, error.toString());
        }
        mapData.add(BeanUtils.beanToMap(t));
        data.add(t);
    }


    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        logger.info("excel解析完毕,共解析{}条数据,错误数据{}条,错误详情{}", data.size(), errorMessageMap.size(), errorMessageMap);
    }

    private String check(String str, Integer checkType) {
        switch (checkType) {
            case 0:
                return DateUtil.isDateTimeFormat(str) ? "" : "日期格式错误;";
            default:
                return "";
        }
    }
}

如果导入时除了简单校验还需要特色校验如数据库去重,业务判断等,请另外写一个xxxReadListener 来继承BaseListener<T> 泛型为对应excel传输对象。重写监听方法时请务必super()一下,否则基本校验就没了!

5.model:excel传输对象

注解配置

为了不污染实体类和字段冗余请在这里定义excel传输对象加注解。

@Data
@EqualsAndHashCode
@ExcelPropertyCheck
public class DemoExcelDto {
    @ExcelProperty("id")
    @ExcelPropertyCheck(required = false)
    private Long id;         //记录标识
    @ExcelProperty("导入时间")
    @ExcelPropertyCheck(required = true, checkFormat = true, type = ExcelPropertyType.DATE_TIME)
    private LocalDateTime importTime;
}

二、使用说明

1.读Excel

EasyExcelUtils.importExcel(file, IpDepartsExcelDto.class, listener)

@RequestMapping("importExcel")
@ResponseBody
public Result importIps(MultipartFile file) {
    //XXXReadListener listener = new XXXReadListener (XXXExcelDto.class);
    //没有复杂的校验就用BaseListener
    BaseListener<DemoExcelDto> listener = new BaseListener<>();
    //获取校验错误信息
    Map errorMap = EasyExcelUtils.importExcel(file, IpDepartsExcelDto.class, listener);
    //获取读取完毕的数据,即使发生校验错误也会继续读取
    List<IpDeparts> ipDeparts = EasyExcelUtils.dto2model(listener.getData() ,new IpDeparts());
    if (errorMap.size() != 0) {
        //errorMap校验数据不为空就是excel数据校验有不通过的数据。
        return Result.failure(CommonResultStatus.IMPORT_FAIL, errorMap.toString());
    } else {
        //mapper保存数据库。
        mapper.saveAll(ipDeparts);
        return Result.success(CommonResultStatus.OK);
    }
}

2.写Excel

EasyExcelUtils.downloadExcel(response, fileName, ipDepartsExcelDtoList, IpDepartsExcelDto.class)

    @GetMapping("download")
    public void download(HttpServletResponse response) throws UnsupportedEncodingException {
        String fileName = URLEncoder.encode("test", "utf-8");
        List<DemoExcelDto> demoExcelDtos = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            DemoExcelDto demoExcelDto = new DemoExcelDto(i, "2020-08-01");
            demoExcelDtos.add(demoExcelDto);
        }
        try {
            EasyExcelUtils.downloadExcel(response, fileName, demoExcelDtos, DemoExcelDto.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

三、EasyExcelUtils(简单的导入导出工具类)

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.util.MapUtils;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.BeanUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class EasyExcelUtils {
    /**
     * 导出excel
     *
     * @param response
     * @param fileName 文件名
     * @param data     List<数据集合>
     * @throws IOException
     */
    public static <T> void downloadExcel(HttpServletResponse response, String fileName, List<T> data, Class<?> clazz) throws IOException {
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            // 这里需要设置不关闭流
            EasyExcel.write(response.getOutputStream(), clazz).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).autoCloseStream(Boolean.FALSE).sheet("sheet1")
                    .doWrite(data);
        } catch (Exception e) {
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = MapUtils.newHashMap();
            map.put("status", "failure");
            map.put("message", "下载文件失败" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }
    }

    /**
     * 读取excel 文件
     *
     * @param file     文件
     * @param cl       类对象
     * @param listener 参数校验监听器
     */
    public static <T> Map importExcel(MultipartFile file, Class<T> cl, BaseListener<T> listener) {
        try {
            EasyExcel.read(file.getInputStream(), cl, listener).sheet().doRead();
            return listener.getErrorMessageMap();
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * excleDto 转为对应model实体类
     *
     * @param data
     * @param t
     * @param <T>
     * @return
     */
    public static <T> List<T> dto2model(Collection<?> data, T t) {
        return data.stream().map(e -> {
            T t1 = null;
            try {
                t1 = (T) t.getClass().newInstance();
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            BeanUtils.copyProperties(e, t1);
            return t1;
        }).collect(Collectors.toList());
    }
}

四、总结:

经过简单的封装完成了一个带参数校验的简单使用案例,直接引入项目后在controller直接调用EasyExcelUtils的 "importExcel"方法和"downloadExcel"就能实现excel的导入导出,是不是很方便呢。

有问题留言大家一起交流学习。文章来源地址https://www.toymoban.com/news/detail-430294.html

到了这里,关于java实现excel的导入导出(带参数校验:非空校验、数据格式校验)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 建库、建表、修改表、复制表、字符类型、数值类型、枚举类型、日期时间类型、检索目录、数据导入命令、数据导入步骤、数据导出命令、非空、默认值、唯一索

    Top 案例1:表管理 案例2:数据类型 案例3:数据批量处理 案例4:表头基本约束 1.1 问题 建库练习 建表练习 修改表练习 1.2 方案 在MySQL50主机完成练习。 1.3 步骤 实现此案例需要按照如下步骤进行。 步骤一:建库练习 库名命名规则: 仅可以使用数字、字母、下划线、不能纯

    2024年02月12日
    浏览(45)
  • Spring Boot中Excel数据导入导出的高效实现

    🌟 前言 欢迎来到我的技术小宇宙!🌌 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。📚 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。🔍 🤖 洛可可白 :个人主页 🔥 个人专栏 :✅前端技术 ✅后端技术 🏠 个人

    2024年03月15日
    浏览(43)
  • Apache POI实现Excel导入读取数据和写入数据并导出

    Apache POI POI介绍 Apache POI是用Java编写的免费开源的跨平台的Java API,Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能,其中使用最多的就是使用POI操作Excel文件。 maven坐标: POI结构: 入门案例 ExcelTest .java文件 从Excel文件读取数据

    2024年02月12日
    浏览(46)
  • poi实现excel文件导入导出(基本数据导出、含格式导出、含批注导出、含图片图表导出)——springboot

    本文主要是介绍springboot + poi实现基本的excel文件导入导出,包含数据导出导入时数据的其他需求校验,导出含有批注信息、导出含有图片信息、导出含有图表信息等的介绍等等,主要是一个demo尽可能简单明了的来介绍相关功能即可。有什么问题可以在留言哦!并在文章末尾附

    2024年02月08日
    浏览(81)
  • Django中利用Admin后台实现Excel/CSV的导入更新数据库和导出数据到Excel/CSV

    本文基于Django自带的admin 后台实现Excel,csv,Json等格式文件的导入并更新后台数据库。 核心是引入 django-import-export 模块。 我们先创建一个app:app01 python manage.py startapp app01 然后在app01文件夹下的models.py 中写入如下模型数据: 指定清华镜像会快很多 修改两处: 注册import_exp

    2023年04月09日
    浏览(48)
  • java poi导入Excel、导出excel

    java poi导入Excel、导出excel ReadPatientExcelUtil PoiUtils FileUtils

    2024年02月15日
    浏览(43)
  • java 实现数据导入导出

    官方文档(完整功能展示):地址 依赖 导出 1.随便一个实体类(默认不管加不加@ExcelProperty的注解的所有字段都会参与读写,若某字段不想参与,可在字段上添加@ExcelIgnore。若只想加@ExcelProperty才参与,则类上加@ExcelIgnoreUnannotated)更多注解使用 2.编一个方法用于返回要导出

    2024年02月13日
    浏览(54)
  • excel文件导入或导出Java代码示例

       判断excel的格式,同时兼容2003和2007  获取行数据  //获取excel列表内的对应数据格式 3、以下为可能会用到的导出实例文件,上传文件服务器的过程  File格式转换MultipartFile格式的例子   -------------------------------------以下无正文-----------------------------------------------------------

    2024年02月16日
    浏览(40)
  • Easy Excel参数校验实现

    应用场景 在导入需求中,往往要求用户输入值的合法性,如是否为空的校验、正则校验、存在性校验,在以往实现方案中,通常是使用for循环来实现的,但是在Excel工具解析文件的过程中,其实已经逐行解析数据过一遍,如果在业务逻辑中再次for循环读取一次,实则是没有必

    2024年02月12日
    浏览(34)
  • 【Go】excelize库实现excel导入导出封装(三),基于excel模板导出excel

    大家好,这里是符华~ 关于excelize库实现excel导入导出封装,我已经写了两篇了,我想要的功能基本已经实现了,现在还差一个模板导出,这篇文章就来讲讲如何实现用模板导出excel。 前两篇: 【Go】excelize库实现excel导入导出封装(一),自定义导出样式、隔行背景色、自适应

    2024年01月25日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包