easyexcel实践操作

记录自己使用easyexcel操作过程及碰到问题的解决方法,主要使用spring,springmvc,mybatiesplus框架,数据库及其他依赖配置请自行添加。

主要参考文档:https://www.yuque.com/easyexcel/doc/api

1.需要的依赖:

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.0.5</version>
        </dependency>
    //easyexcel是在poi的基础上开发的
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>//写监听器时JSON类需要此依赖
            <version>2.0.1</version>
        </dependency>

2.实体类

我准备批量上传学生信息,创建实体类Student

package com.liao.heiban.pojo;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.sql.Date;

@Data
//@Accessors(chain = true)
@TableName("student")
public class Student implements Serializable {
    private static final long serialVersionUID = -7608888444418873339L;
    @TableId(type = IdType.AUTO)
    @ExcelProperty(index = 0)  //@ExcelProperty("id")
    private Integer id ;
    @ExcelProperty(index = 1)
    private String grade ;//班级
    @ExcelProperty(index = 2)
    private String name;//姓名
    @ExcelProperty(index =3)
    private Integer age ;//年龄
    @ExcelProperty(index = 4)
    private String sex;//性别
    @ExcelIgnore
    @DateTimeFormat("yyyy-MM-dd")
    private Date time;//入学时间
    @ExcelIgnore
    @TableField(exist = false)
    private Integer classId;//班级id
}

注意:excel表格中对应的属性都需要添加@ExcelProperty()注解index代表属性在表格中的列数,从零开始,或者可以用表格中列的名字代替,例如id,但是建议只用一种格式,其他表格中没有的属性都加上@ExcelIgnore

测试文件的数据为

 id在数据库是自增的,所以表格中的id可以不写数据或者将id这一列去掉,会自动赋值,由于excel时间格式会与Date类型不匹配,接收数据时需要要string类型接收,由于数据库和实体类已经写好,暂时就先将时间忽略,先不上传时间格式。

由于@Accessors与easyexcel冲突,使用此注解会导致读取的数据都为空,所以不要使用@Accessors注解

3.根据官方文档写出此类的监听器

package com.liao.heiban.util;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson2.JSON;
import com.liao.heiban.service.StudentListenerMapper;
import com.liao.heiban.pojo.Student;
//import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

@Slf4j
//easyexcel监听器
public class StudentListener implements ReadListener<Student> {
    //每隔100条数据存储数据库
    private static final int BATCH_COUNT = 100;
    //缓存数据
    private List<Student> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    private StudentListenerMapper studentListenerMapperrs;
    public StudentListener(){
        studentListenerMapperrs = new StudentListenerMapper();
    }
    //如果使用了spring 创建listener是将spring管理的类传过来
    public StudentListener(StudentListenerMapper studentListenerMapper){
        this.studentListenerMapperrs = studentListenerMapper;
    }

    @Override
    public void invoke(Student student, AnalysisContext analysisContext) {
        log.info("解析到一条数据", JSON.toJSONString(student));
        cachedDataList.add(student);
        if (cachedDataList.size()>=BATCH_COUNT){
            saveDate();
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    private void saveDate() {
            log.info("{}条数据,开始存储数据库!", cachedDataList.size());
            studentListenerMapperrs.save(cachedDataList);
            log.info("存储数据库成功!");
        }
    //数据解析完成调用此方法
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        saveDate();
        log.info("所有数据解析完成!");
    }
}

4.其他代码

controller层,service依赖注入

package com.liao.heiban.controller;

import com.liao.heiban.ov.HResult;
import com.liao.heiban.pojo.Student;
import com.liao.heiban.service.StudentService;
import lombok.experimental.Accessors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.List;

@RestController
@RequestMapping("/student")
@CrossOrigin
public class StudentController {
    @Autowired
    private StudentService studentService;
@PostMapping("putExcel")
    public HResult putExcel(MultipartFile file){
        //验证文件内容是否上传
//        System.out.println(file.getName());
//        try {
//            InputStream inputStream = file.getInputStream();
//            File file1 = new File("F:\\picture\\1.xlsx");
//            FileOutputStream fileOutputStream = new FileOutputStream(file1);
//            while (true){
//                int b = inputStream.read();
//                if(b==-1){
//                    break;
//                }
//                fileOutputStream.write(b);
//            }
//              inputStream.close();
//              fileOutputStream.close();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
        studentService.putExcel(file);
        return HResult.susses();
    }
}

service层代码

import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.liao.heiban.mapper.StudentMapper;
import com.liao.heiban.pojo.Student;
import com.liao.heiban.util.StudentListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

@Slf4j
@Service
public class StudentServiceImpl implements StudentService {
    @Autowired
    private StudentMapper studentMapper;
@Override
    public void putExcel(MultipartFile file) {
//第一种方式:利用文件流在服务器临时文件夹生成文件调用完成后删除,通过文件路径及文件名得到read方法的第一个参数,建议用下面第二种方法             
        try {
            InputStream is= file.getInputStream();
            String property = System.getProperty("java.io.tmpdir");
            String path = property+"xx\\";
            System.out.println(path);
            File file1 = new File(path);
            if (!file1.exists()){
                file1.mkdirs();
            }
            String file1Path = path+System.currentTimeMillis()+".xlsx";
            System.out.println(file1Path);
            File file2 = new File(file1Path);
            FileOutputStream fileOutputStream = new FileOutputStream(file2);
            while (true){
                int b = is.read();
                if(b==-1){
                    break;
                }
                fileOutputStream.write(b);
            }
            try {
                EasyExcel.read(file1Path,Student.class,new StudentListener(studentListenerMapper)).sheet().doRead();
                //官方的demo方法
//                String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
//                EasyExcel.read(file1Path,Student.class,new PageReadListener<Student>(dataList -> {
//                    for (Student demoData : dataList) {
//                        log.info("读取到一条数据{}", JSON.toJSONString(demoData));
//                        System.out.println(demoData);
//                    }
//                })).sheet().doRead();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                is.close();
                fileOutputStream.close();
                file2.delete();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

// 第二种方式,read方法中把路径当做参数调用file()方法,利用文件路径及名称生成数据流,观察EasyExcel的read方法,其中第一个参数也可以是输入流,直接用传入的Multipartfile生成输入流
//        try {
//            EasyExcel.read(file.getInputStream(),Student.class,new StudentListener(studentListenerMapper)).sheet().doRead();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
 
    }
}

创建一个类调用StudentMapper进行存储数据,这个类也被监听器中的构造方法调用所调用,生成的list就是此类方法的参数。

import com.liao.heiban.mapper.StudentMapper;
import com.liao.heiban.pojo.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
;
@Component
public class StudentListenerMapper{
    @Autowired
    StudentMapper studentMapper;
    public void save(List<Student> cachedDataList) {
        for (int i = 0; i < cachedDataList.size(); i++) {
            System.out.println(cachedDataList);
            studentMapper.insert(cachedDataList.get(i));
        }
    }
}

此方法会导致每次循环都链接数据库存储,耗费时间比较长。建议使用mybaties对xml文件进行编辑,使用事务进行批处理方式或者foreach,或者ExecutorType.BATCH

mapper层代码

package com.liao.heiban.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.liao.heiban.pojo.Student;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

@Mapper
public interface StudentMapper extends BaseMapper<Student> {
    
}

启动服务后可以在postman上模拟前端上传文件

由于我们在controller层设置返回数据,所以只能在postman上看到这个结果

 

 但是后台我们有解析之后输出list的代码,所以能在后台看见解析后的list

 可以看到两条数据已经传入,在数据库中刷新表就可以看到新增的数据了

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值