一、spring boot 操作excel

1. 技术选型

java本身并不支持读取excel,所有读取excel需要借助一些框架。目前有几种方式:

  1. Apache POI
  2. easyexcel

POI提供的对Excel操作不仅消耗内存而且解压时完全在内存中完成,内存消耗非常大。

  • easyExcel的确比poi方便,但是它的读需要编写监听器
  • 建议大数据用easyExcel,因为大数据时poi对于内存消耗非常大
  • 由于apache poi和jxl,excelPOI都有一个严重的问题,就是非常消耗内存,特别处理数据量多时,速度慢并且时有异常发生,所以改用由阿里研发的easyExcel更可靠一些,它的官方建议对于1000行以内的采用原来poi的写法一次读写,但于1000行以上的数据,有用了一行行进行解析的方案,这样避免了内存的溢出。
  • EasyExcel扩展功能很多,且Api式调用真的轻松很多

1.1 EasyExcel

官方网站: https://yuque.com/easyexcel
https://github.com/alibaba/easyexcel

EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。

EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。

EasyExcel能减少内存占用的原因是它没有将文件一次性加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

EasylExcel的特点:
1、poi和jxl非常的消耗内存,并发上来后会OOM或是JVM频繁的full gc。

2、EasylExcel对poi进行了封装,使用简单,节省内存。

3、EasyExcel没有把文件数据一次性全部加载到内存中,而是从磁盘一行一行读取数据,逐个解析,并将解析结果以观察者的模式通知处理(AnalysisEventListener)。

1.2 POI

POI是Apache提供的开源代码库,这个代码库用来提供Java客户端对Microsoft Office格式文件的读写操作。

POI为“Poor Obfuscation Implementation”的首字母缩写,意为“简洁版的模糊实现”。

POI有五种结构

HSSF——提供读写Microsoft Excel(2003)格式档案功能

XSSF——提供读写Microsoft Excel OOXML(2007-今)格式档案的功能

HWPF——提供读写Microsoft Word格式档案的功能

HSLF——提供读写Microsoft PPT格式档案功能

HDGF——提供读写Microsoft Visio格式档案的功能

二、EasyExcel使用

0. 工作中使用总结

  • 目前读取excel文件不再需要指定ExcelTypeEnum,即excel的版本,会自动处理
  • 之前创建ExcelReader都是自己new,现在是通过EasyExcelFactory创建,更加简单和具备通用性。
  • 之前每解析一行的回调的invoke()方法,通用对象Object是list集合,目前是HashMap集合。

1. maven 引入

repo仓库搜索 关键字 EasyExcel ,使用最新版本。

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

2. demo1:excel写入文件

@HeadRowHeight(20)	// 表头行高
@ColumnWidth(15)		// 表头行宽
@Data
public class DemoData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private String ignore;
}
public class ExcelServiceTest {

    /**
     * 最简单的写
     * <p>1. 创建excel对应的实体对象 参照{@link DemoData}
     * <p>2. 直接写即可
     */
    @Test
    public void simpleWrite1() {
        // 写法1
        String fileName = "simpleWrite" + System.currentTimeMillis() + ".xlsx";
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可
        EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
    }

    @Test
    public void simpleWrite2(){
        // 写法2
        String fileName = "simpleWrite" + System.currentTimeMillis() + ".xlsx";
        // 这里 需要指定写用哪个class去写
        ExcelWriter excelWriter = null;
        try {
            excelWriter = EasyExcel.write(fileName, DemoData.class).build();
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            excelWriter.write(data(), writeSheet);
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    private List<DemoData> data() {
        List<DemoData> list = new ArrayList<DemoData>();
        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }

}

3. demo2:SpringBoot项目中集成EasyExcel实现Excel文件的下载

SpringBoot项目中集成EasyExcel实现Excel文件的下载
参考URL: https://blog.csdn.net/riemann_/article/details/103648431

如下:demo测试通过, 利用 response.getOutputStream() 、HttpServletResponse response 来设置响应头等信息。

    @RequestMapping(value = "/xxx/getExcel", method = RequestMethod.GET)
    public void getExcel(HttpServletResponse response) throws IOException {

        //配置文件名
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
        String datetime = sdf.format(new Date());
        String fileName = URLEncoder.encode("下载excel", "UTF-8") + datetime;

        //响应内容格式
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        
        List<UserVO> list = excelService.getUserVOData();
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可
        EasyExcel.write(response.getOutputStream(), UserCheckData.class).sheet("下载excel").doWrite(list);

    }

UserCheckData.class 是自定义的excel对应的java类;其中的
@ExcelProperty注解中的value就是表头的信息,index是在第几列,没有加注解的不会导出。

@HeadRowHeight(20)	// 表头行高
@ColumnWidth(15)		// 表头行宽
@Data
public class SiUserCheckData{

    /**
     * 用户名
     */
    @ExcelProperty(value = "用户名", index = 0)
    private String username;

    /**
     * 手机号
     */
    @ExcelProperty(value = "手机号", index = 0)
    private String mobile;
}

如上demo,经过测试,注意点如下:

  • response 来设置响应头等信息,记得要放在EasyExcel.write 写流之前。
  • controller方法写成void即可。

response的三个属性:编码、类型、头文件

流写出时,关键是配置response的三个属性:编码、类型、头文件

1、ContentType:传输文件类型

application/octet-stream------------------自动匹配文件类型

application/force-download--------------强制下载

text/pain---------------------------------------文本文件

application/vnd.ms-excel-----------------excel文件

2、CharacterEncoding:编码采用通用的UTF-8

3、Header:setHeader(name,value),有两个参数


示例:

response.setContentType("application/vnd.ms-excel");

response.setCharacterEncoding("UTF-8");

response.setHeader("Content-Disposition","attachment;filename=problems"+AMDateUtil.date2String(new Date(),"yyyyMMdd")+".xlsx");

4. demo3:SpringBoot项目中集成EasyExcel实现Excel文件导入(同步处理数据)

SpringBoot项目中集成EasyExcel实现Excel文件上传至MySQL
参考URL: https://blog.csdn.net/riemann_/article/details/103639254
使用easyExcel导入大批量数据
参考URL: https://blog.csdn.net/qq_36109477/article/details/104909535
[推荐]springboot整合阿里easyexcel2.x实现海量数据excel导入导出demo
参考URL: https://www.cnblogs.com/zhengwj-joker/p/12808979.html

整体思路:

  1. 引入easyExcel maven。
  2. 定义导入导出exel字段对应的实体类。(使用easyExcel相关注解)
  3. 自定义 上传Excel的监听类 实现 AnalysisEventListener<你自己定义的excel模型类>。
    核心就是这个监听器类,实现相关方法。
    经过测试: 监听器这个类不能够被Spring管理,每次使用单独的new出来。
// 该类无法交给spring管理
public class ConfigFilterListener  extends AnalysisEventListener<ConfigFilterImport> {

    private static final Logger logger = LoggerFactory.getLogger(ConfigFilterListener.class);

    private static final int BATCH_COUNT = 10000;

    List<ConfigFilterImport> list = new ArrayList<>();

    private ConfigFilterDao configFilterDao;

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

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param configFilter
     *            one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param analysisContext
     */
    @Override
    public void invoke(ConfigFilterImport configFilter, AnalysisContext analysisContext) {
        list.add(configFilter);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            list.clear();
        }
    }

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

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

参考原博主URL(https://www.cnblogs.com/zhengwj-joker/p/12808979.html),即可。

4.1 AnalysisEventListener

上文中,自定义 上传Excel的监听类 实现 AnalysisEventListener<你自己定义的excel模型类>,

其中, invoke() 和 doAfterAllAnalysed() 是必须实现的方法。

在 invoke() 中,我们将数据封装到 list 中,再在控制器中,通过 getter() 方法获取数据,这样我们就可以获取到 easyexcel 帮我们解析好的数据,再将数据进行类型转化,这样,我们就可以对数据进行写入操作。

注意:

  • 每解析一行会回调invoke()方法。
  • 整个excel解析结束会执行doAfterAllAnalysed()方法

那么,我们要使用spring bean应该怎么做呢?
思路:监听器对象 可以在创建的时候把dao当做参数传进去。

我们自定义监听器类的构造体传入,需要用到bean,如一些Dao

    /**
     *
     * 不要使用自动装配
     * 将dao当参数传进来
     */
    public DemoDataListener(TeacherDao teacherDao) {
        this.teacherDao = teacherDao;
    }
/**
     * 添加数据库用到的dao
     */
    @Autowired
    private TeacherDao teacherDao;
    /**
     * 最简单的读
     */
    @Test
    public void simpleRead() {
        String fileName =  "/Users/lubingyang/Desktop/hhhh.xlsx";

        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        /**
         * 参数1 要读取的文件
         * 参数2 要读取的数据对应的实体类类对象
         * 参数3 监听器对象 可以在创建的时候把dao当做参数传进去
         */
        EasyExcel.read(fileName, Teacher.class, new DemoDataListener(teacherDao)).sheet().doRead();
    }

三、工作常用参考

1. easyexcel 上传返回结果给前端(返回成功/失败数量及原因)

基于EasyExcel的读取exl并返回成功/失败数量及原因,并将导入失败exl的导出到系统路径中
参考URL: https://blog.csdn.net/qq_37361535/article/details/105687708
https://blog.csdn.net/weixin_42059737/article/details/103891013
参考URL: https://blog.csdn.net/weixin_42059737/article/details/103891013

整体思路:
自定义类bean,spring管理,专门处理统计哪些入库成功,哪些失败,在自定义excel监听器构造函数中传入。

controller层如何返回给界面,哪些成功或失败呢?

controller层,demo如下:

	@ResponseBody
	@RequestMapping(value = "orderImport", method = RequestMethod.POST, produces = Constant.CONTENT_TYPE_UTF8)
	public RestResponse orderImport(HttpServletRequest request,  @RequestParam(value = "file") MultipartFile file) throws Exception {
		UserVO userVo = this.getCurrentUser(request);

		// 初始化ajax返回对象 默认是操作成功
		RestResponse build = RestResponse.build();

		EasyExcel.read(file.getInputStream(), new NoModleDataListener(orderService, userVo, build)).sheet().doRead();
		// 直接返回  如果错误这个对象的值已被改变 如果没有错误值没有被改变 返回的是默认成功的对象
		return build;
		// return orderService.importOrder(userVo, file);
	}

建议直接参考原文。

2. easyexcel 导入数据校验

easyExcel+validation+正则实现excel导入校验
参考URL: https://www.yuque.com/oushidazhutou/lwb5fl/mwymlq

四、参考

EasyExcel2.0 实现模板下载、导入和导出功能
参考URL: https://liuyanzhao.com/10060.html
EasyExcel操作API与示例
参考URL: https://www.it610.com/article/1296387796709220352.htm
EasyExcel读取文件-同步处理数据
参考URL: https://www.cnblogs.com/ngrzr/p/11982697.html
SpringBoot图文教程14—阿里开源EasyExcel「为百万数据读写设计」
参考URL: https://my.oschina.net/u/3555122/blog/4355952
惊了!如何通过阿里 EasyExcel 7 行代码, 优雅地实现 Excel 文件导出功能?
参考URL: https://blog.csdn.net/wutian842929/article/details/106994662/
easyExcel+validation+正则实现excel导入校验
参考URL: https://www.yuque.com/oushidazhutou/lwb5fl/mwymlq

03-14 07:20