欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

如何让excel文件读取变得更简单

程序员文章站 2022-05-31 12:45:04
今天给大家安利一款excel文件导入神器,easyexcel,官方地址:( "https://github.com/alibaba/easyexcel" )。 在官网文档中有介绍了其性能。 从上面的性能测试可以看出easyexcel在解析耗时上比poiuserModel模式弱了一些。主要原因是我内部 ......

今天给大家安利一款excel文件导入神器,easyexcel,官方地址:()。
在官网文档中有介绍了其性能。
如何让excel文件读取变得更简单
如何让excel文件读取变得更简单
如何让excel文件读取变得更简单
如何让excel文件读取变得更简单
从上面的性能测试可以看出easyexcel在解析耗时上比poiusermodel模式弱了一些。主要原因是我内部采用了反射做模型字段映射,中间我也加了cache,但感觉这点差距可以接受的。但在内存消耗上差别就比较明显了,easyexcel在后面文件再增大,内存消耗几乎不会增加了。但poi usermodel就不一样了,简直就要爆掉了。想想一个excel解析200m,同时有20个人再用估计一台机器就挂了。

如何使用呢

1、引入maven依赖

   <dependency>
        <groupid>com.alibaba</groupid>
        <artifactid>easyexcel</artifactid>
        <version>2.0.5</version>
</dependency>

由于改jar包对poi进行了一些封装,因此需要注释掉项目引用的poi依赖,不然会有版本冲突。
2、创建接收excel数据的实体

@data
public class person {

    @excelproperty(value = "姓名",index = 1)
    private string name;

    @excelproperty("性别")
    private string sex;

    @excelproperty("年龄")
    private int age;
}

@excelproperty 这个注解用于指定该属性对应excel文件中的哪一列数据。里面有两个属性,一个是value,另一个是index(从0开始),这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配。
3、增加person监听器

@slf4j
public class personlistener extends analysiseventlistener<person> {

    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int batch_count = 5;
    list<person> list = new arraylist();

    @override
    public void invoke(person person, analysiscontext analysiscontext) {
        log.info("解析到一条数据:{}",person);
        if (list.size() >= batch_count) {
            savedata();
            list.clear();
        }
    }

    @override
    public void doafterallanalysed(analysiscontext analysiscontext) {
        savedata();
        log.info("所有数据解析完成!");
    }
    @override
    public void onexception(exception exception, analysiscontext context) {
        log.error("解析失败,但是继续解析下一行", exception);

    }
    /**
     * 加上存储数据库
     */
    private void savedata(){
        log.info("{}条数据,开始存储数据库!", list.size());
        log.info("存储数据库成功!");
    }
}

4、文件上传方法

@restcontroller
@slf4j
public class personcontroller {
    
    @postmapping("importfile")
    public string readperson(multipartfile file){
        
        personlistener personlistener = new personlistener();
        try {

            // headrownumber(2) 这里可以设置1,因为头就是一行。如果多行头,可以设置其他值。不传入也可以,他没有指定头,也就是默认1行
            easyexcel.read(file.getinputstream(), person.class,personlistener).sheet().headrownumber(1).doread();

        }catch (ioexception ioe){

            log.info("读取excel异常={}",ioe);
        }
        return "";
    }
}

这样代码基本就算完成了。接下来我们看看如何在

spring框架中使用

我们知道,在spring中文件入库的时候可能需要调用service,service调用dao入库。那我们如何在这个personlistener中调用service呢。
首先,在personcontroller中注入service.

    @autowired
    private personservice personservice;

然后,在personlistener中增加一个有参构造器。

   private personservice personservice;
    
    public personlistener(personservice personservice){

        this.personservice = personservice;
    }

最后在personcontroller中new personlistener的时候将service传进来即可。

    @autowired
    private personservice personservice;
    @postmapping("importfile")
    public string readperson(multipartfile file){

        personlistener personlistener = new personlistener(personservice);
        try {

            // headrownumber(2) 这里可以设置1,因为头就是一行。如果多行头,可以设置其他值。不传入也可以,他没有指定头,也就是默认1行
            easyexcel.read(file.getinputstream(), person.class,personlistener).sheet().headrownumber(2).doread();

        }catch (ioexception ioe){

            log.info("读取excel异常={}",ioe);
        }
        return "";
    }

接下来我们在看一下,有一个需求是这样的,我们需要统计一下导入成功数和失败数并且要不失败数写入到一个excel中返回给页面,如何实现?
我们调整一下personlistener

@slf4j
public class personlistener extends analysiseventlistener<person> {

    private int successcount = 0; // 成功量
    
    private int exceptioncount = 0; // 异常量

    private list<person> exceptionlist = new arraylist<>(); // 异常数据

    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int batch_count = 5;
    list<person> list = new arraylist();

    private personservice personservice;

    public personlistener(personservice personservice){

        this.personservice = personservice;
    }

    @override
    public void invoke(person person, analysiscontext analysiscontext) {
        log.info("解析到一条数据:{}",person);
        successcount++;
                list.add(person);
        if (list.size() >= batch_count) {
            savedata();
            list.clear();
        }
    }

    @override
    public void doafterallanalysed(analysiscontext analysiscontext) {
        savedata();
        log.info("所有数据解析完成!");
    }
    @override
    public void onexception(exception exception, analysiscontext context) {
        log.error("解析失败,但是继续解析下一行", exception);

        person person  = (person)context.readrowholder().getcurrentrowanalysisresult();
        exceptionlist.add(person);
        exceptioncount++;

    }
    /**
     * 加上存储数据库
     */
    private void savedata(){
        log.info("{}条数据,开始存储数据库!", list.size());
        log.info("存储数据库成功!");
    }
    /**
     * 插入结果返回
     * @return
     */
    public map<string,object> getdata(){

        map<string,object> map = new hashmap<>();
        map.put("success",successcount);
        map.put("exception",exceptioncount);
        return map;
    }

    /**
     * 失败数据返回
     * @return
     */
    public list<person> getexceptionlist(){

        return exceptionlist;
    }
}

在personcontroller中将异常数据写入文件。

@restcontroller
@slf4j
public class personcontroller {

    @autowired
    private personservice personservice;

    @postmapping("importfile")
    public string readperson(multipartfile file){

        personlistener personlistener = new personlistener(personservice);
        try {

            // headrownumber(1) 这里可以设置1,因为头就是一行。如果多行头,可以设置其他值。不传入也可以,他没有指定头,也就是默认1行
            easyexcel.read(file.getinputstream(), person.class,personlistener).sheet().headrownumber(1).doread();

        }catch (ioexception ioe){

            log.info("读取excel异常={}",ioe);
        }
        map<string, object> data = personlistener.getdata();
        string exception = data.get("exception") + "";
        int exceptioncount = integer.parseint(exception);
        if(exceptioncount>0){
            string filename = system.currenttimemillis() + ".xlsx";
            // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
            easyexcel.write("e://upload/file/"+filename, person.class).sheet("模板").dowrite(personlistener.getexceptionlist());
            data.put("filename",filename);
        }
        return data.tostring();
    }
}

我们写个页面测试一下

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>文件上传</title>
</head>
<body>
<form action="/importfile" method="post" enctype="multipart/form-data">
    <input type="file"  name="file"/>
    <button type="submit">提交</button>
</form>
</body>
</html>

下面是测试的日志文件

2019-10-20 11:21:59.809  info 17748 --- [nio-8080-exec-1] c.zhixie.easyexcel.demo.personlistener   : 解析到一条数据:person(name=执偕, sex=男, age=18)
2019-10-20 11:21:59.811  info 17748 --- [nio-8080-exec-1] c.zhixie.easyexcel.demo.personlistener   : 解析到一条数据:person(name=执偕2, sex=男, age=19)
2019-10-20 11:21:59.811  info 17748 --- [nio-8080-exec-1] c.zhixie.easyexcel.demo.personlistener   : 解析到一条数据:person(name=执偕3, sex=男, age=20)
2019-10-20 11:21:59.811  info 17748 --- [nio-8080-exec-1] c.zhixie.easyexcel.demo.personlistener   : 解析到一条数据:person(name=执偕4, sex=男, age=21)
2019-10-20 11:21:59.812  info 17748 --- [nio-8080-exec-1] c.zhixie.easyexcel.demo.personlistener   : 解析到一条数据:person(name=执偕5, sex=男, age=22)
2019-10-20 11:21:59.812  info 17748 --- [nio-8080-exec-1] c.zhixie.easyexcel.demo.personlistener   : 5条数据,开始存储数据库!
2019-10-20 11:21:59.812  info 17748 --- [nio-8080-exec-1] c.zhixie.easyexcel.demo.personlistener   : 存储数据库成功!
2019-10-20 11:21:59.812  info 17748 --- [nio-8080-exec-1] c.zhixie.easyexcel.demo.personlistener   : 解析到一条数据:person(name=执偕6, sex=男, age=23)
2019-10-20 11:21:59.812  info 17748 --- [nio-8080-exec-1] c.zhixie.easyexcel.demo.personlistener   : 解析到一条数据:person(name=执偕7, sex=男, age=24)
2019-10-20 11:21:59.813  info 17748 --- [nio-8080-exec-1] c.zhixie.easyexcel.demo.personlistener   : 解析到一条数据:person(name=执偕8, sex=男, age=25)
2019-10-20 11:21:59.813  info 17748 --- [nio-8080-exec-1] c.zhixie.easyexcel.demo.personlistener   : 解析到一条数据:person(name=执偕9, sex=男, age=26)
2019-10-20 11:21:59.813  info 17748 --- [nio-8080-exec-1] c.zhixie.easyexcel.demo.personlistener   : 4条数据,开始存储数据库!
2019-10-20 11:21:59.813  info 17748 --- [nio-8080-exec-1] c.zhixie.easyexcel.demo.personlistener   : 存储数据库成功!
2019-10-20 11:21:59.813  info 17748 --- [nio-8080-exec-1] c.zhixie.easyexcel.demo.personlistener   : 所有数据解析完成!

今天就介绍到这了,文中的代码我已上传到码云上,
地址:https://gitee.com/javaxiaocaiji/zhixie-code-example/tree/master/easyexcel

如果文章对您有帮助,请记得点赞关注哟~
欢迎大家关注我的公众号,每日技术推送文章供大家学习参考。

情系it>