springboot easyexcel不创建对象导入excel 通用版


    可能最近和easyexcel比较有缘,上个月倒腾了导出,这个月就轮到了导入了~~
    Excel读取实际上是很复杂的,比如问卷场景,涉及多级表头、不固定的列、各类格式(时间、数值等等),但是不用怕,这些在 官方网站上都能找到案例!
    本文主要讲解web版不创建对象的导入excel通用版,解决了表格内容不固定和各个导入业务模块复用性!

背景知识介绍

本篇项目框架介绍:SpringBoot + MybatisPlus + EasyExcel + Knife4j

有数据对象读方式

创建类实现ReadListener

实现步骤为:

  1. 定义数据对象DemoData,与Excel中row的每一列对应上
  2. 定义监听器 implements ReadListener,将每一行映射到List<DemoData>
  3. 根据业务,存储List<DemoData>

无额外实现类,since: 3.0.0-beta1


划重点:easyexcel版本需要在3.0.0-beta1及以上!!!
如下代码为:官方举例,其中DemoData为定义的数据对象,与Excel中的列对应。

 String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
 // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
 // 这里每次会读取100条数据 然后返回过来 直接调用使用数据就行
 EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>(dataList -> {
     for (DemoData demoData : dataList) {
         log.info("读取到一条数据{}", JSON.toJSONString(demoData));
     }
 })).sheet().doRead();

不创建数据对象读方式

实现步骤为:

  1. 定义监听器extends AnalysisEventListener<Map<Integer, String>>
  2. 定义全局变量Map<Integer, String> cachedDataList,将读取的每一行数据存储在该变量中
  3. 根据业务,存储cachedDataList

实践

思路

  1. 定义抽象监听器AbstractNoModelDataListener实现AnalysisEventListener<Map<Integer, String>>,构造函数中将业务处理接口及业务上下文参数初始化
  2. 定义抽象业务处理类AbstractImportInterface,作为业务处理统一入口
  3. 定义具体业务处理类实现AbstractImportInterface

![实现思路类图](https://img-blog.csdnimg.cn/9e9c9a5cf82a406bb3709075e7a80a5e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6I2U5p6daHU=,size_20,color_FFFFFF,t_70,g_se,x_1

抽象监听类

package com.lizzy.common.excel.utils;

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

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.google.common.collect.Lists;

import lombok.extern.slf4j.Slf4j;

/**
 * EasyExcel不创建数据对象的读
 */
@Slf4j
public class AbstractNoModelDataListener extends AnalysisEventListener<Map<Integer, String>> {

	/**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 5;
    private List<Map<Integer, String>> cachedDataList = Lists.newArrayListWithExpectedSize(BATCH_COUNT);

    private AbstractImportInterface importInterface;
    private Map<String, Object> params;
    
    /**
     * 初始化业务处理的service层和其他业务参数
     * @param importInterface 处理业务的service层
     * @param params 其他业务参数
     */
    public AbstractNoModelDataListener(AbstractImportInterface importInterface, Map<String, Object> params) {
    	this.importInterface = importInterface;
    	this.params = params;
    }
    
    // 默认跳过第一行(通常情况下第一行为标题栏)
    @Override
    public void invoke(Map<Integer, String> data, AnalysisContext context) {
    	
    	// 通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据
    	// 若有跳过行数需求,可写个钩子进行处理
//    	context.getCurrentRowNum()
    	
        log.debug("解析到一条数据:{}", data.toString());
        cachedDataList.add(data);
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            cachedDataList = Lists.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        saveData();
        log.info("所有数据解析完成!");
    }

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

抽象业务处理类

package com.lizzy.common.excel.utils;

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

/**
 * 导入excel时,抽象业务处理层
 */
public interface AbstractImportInterface {
	
	/**
	 * 业务层存储excel数据逻辑
	 * @param params 业务上下文参数
	 * @param data 表格row数据
	 */
	void save(Map<String, Object> params, List<Map<Integer, String>> data);
	
}

具体业务处理类

因涉及项目私密性,此处为伪代码,请各位看官自行实现。

package com.lizzy.service;

import com.lizzy.common.excel.utils.AbstractImportInterface;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class UserImportService implements AbstractImportInterface {
	
	@Autowired
	IUserService iUserService;

	@Override
	public void save(Map<String, Object> params, List<Map<Integer, String>> data) {
		
		// 获取上下文参数		
		final String key = String.valueOf(params.get("key"));
		
		// 业务判断逻辑,此处伪代码,请自行实现
		iUserService.save(key, data);
	}
}

web整合

说明:ResponseVoResultUtil为项目定义的通用返回对象,可自行替换!

package com.lizzy.controller;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.lizzy.common.ResponseVo;
import com.lizzy.common.excel.utils.AbstractNoModelDataListener;
import com.lizzy.common.util.ResultUtil;
import com.lizzy.service.IUserService;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;

/**
 * 平台端-问卷数据管理
 *
 */
@RestController
@RequestMapping(value = { "/test" })
@Validated
@Api(tags = "测试数据")
@SuppressWarnings({ "unchecked" })
@Slf4j
public class TestController {
	
	@Autowired
	IUserService iUserService;
	
	@ApiOperation(value = "导入数据")
	@PostMapping(value = "/importData")
	public ResponseVo<?> importData(
			@ApiParam(value = "key", required = true) @Valid @RequestParam String key,
			@RequestParam(value = "file", required = false) MultipartFile file) {
		
		try {
			Map<String, Object> params = new HashMap<String, Object>();
			params.put("key", key);
			
			// 同步的返回,不推荐使用,如果数据量大会把数据放到内存里面
//			EasyExcel.read(file.getInputStream()).sheet().doReadSync();
			EasyExcel.read(file.getInputStream(), new AbstractNoModelDataListener(iUserService, params))
				.sheet().doRead();
			
			return ResultUtil.success();
			
		} catch (IOException e) {
			log.info("导入数据excel失败!", e.getMessage());
			return ResultUtil.error("导入数据excel失败!");
		}
	}
	
}

后记

记录一次通用导入模块诞生记,欢迎大家踊跃提出问题,一起学习,一起进步!!!

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值