EasyExcel 实现数据的导入

EasyExcel 实现数据的导入

项目环境:

jdk 17+element-plus +vue 3

1.前端使用element-plus 部署文件的提交部件,:http-request绑定一个向前端发送数据的一个方法。

<el-dialog v-model="importExcelDialogVisible" title="导入线索Excel" width="55%" center>
      <el-upload ref="uploadRef" method="post"
       :http-request="uploadFile" :auto-upload="false" :limit="1">
        <template #trigger>
          <el-button type="primary">选择Excel文件</el-button>
          &nbsp;仅支持后缀名为.xls或.xlsx的文件
        </template>
        <br />
        <br />
        <div>重要提示:</div>
        <ul>
          <li>上传仅支持后缀名为.xls或.xlsx的文件;</li>
          <li>给定Excel文件的第一行将视为字段名;</li>
          <li>请确认您的文件大小不超过50MB;</li>
          <li>日期值以文本形式保存,必须符合yyyy-MM-dd格式;</li>
          <li>日期时间以文本形式保存,必须符合yyyy-MM-dd HH:mm:ss的格式;</li>
        </ul>
      </el-upload>

      <template #footer>
        <span class="dialog-footer">
          <el-button @click="importExcelDialogVisible = false">关 闭</el-button>
          <el-button class="ml-3" type="success" @click="submitUpload">上 传</el-button>
        </span>
      </template>
    </el-dialog>

submitUpload 实现文件提交出发el-upload 的submit()方法

  submitUpload() {
      this.$refs.uploadRef.submit();
    },
    uploadFile(files) {
      // console.log(files)
      let fileName = files.file.name;
      // 判断是不是excel文件
      if (fileName.endsWith(".xlsx") || fileName.endsWith(".xls")) {
        // 提交文件到后台
        let formData = new FormData();
        formData.append("file", files.file);
        doPost("/api/clue/importExcel", formData).then((resp) => {
          if (resp.data.code === 200) {
            // 隐藏弹框
            this.importExcelDialogVisible = false;

            // 刷新(父组件传递给子组件一个局部刷新方法)
            this.reload();
            messageTitle("导入文件成功!", "success");
          } else {
            messageTitle("导入文件失败!", "error");
          }
        });
      } else {
        messageTitle("文件类型不正确!", "warning");
      }
    },

2.后端引入easyExcel 依赖

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

界面层处理前端的请求:

  @PostMapping("/api/clue/importExcel")
    public R importClues(MultipartFile file) throws IOException {
        System.out.println(file);
        boolean read = service.readExcel(file.getInputStream());
        return read?R.OK():R.FAIL();
    }

业务逻辑层处理:

 @Override
    public boolean readExcel(InputStream inputStream) {
        try {
            EasyExcel.read(inputStream, TClue.class, new UploadExcelListener(clueMapper)).sheet().doRead();
            return true;
        } catch (Exception e) {
            return false;
        }
    }
easyExcel 读取数据的核心监听器的处理。(查看官网即可)
package com.dqw.config.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.dqw.domain.pojo.TClue;
import com.dqw.domain.util.JsonUtils;
import com.dqw.mapper.TClueMapper;
import lombok.extern.slf4j.Slf4j;

import java.util.List;


/**
 *
 */
@Slf4j
public class UploadExcelListener implements ReadListener<TClue> {

    private TClueMapper tClueMapper;
    public UploadExcelListener() {
    }

    public UploadExcelListener(TClueMapper tClueMapper) {
        this.tClueMapper = tClueMapper;
    }

    /**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;

    /**
     * 缓存的数据
     */
    private List<TClue> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    @Override
    public void invoke(TClue tClue, AnalysisContext analysisContext) {
        log.info("解析到一条数据:{}", JsonUtils.BeantoString(tClue));
        cachedDataList.add(tClue);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }
    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        log.info("所有数据解析完成!");
    }

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

处理excel表中何类对应的字段

采用@ExcelProperty

    @ExcelProperty(value = "活动ID")
    private Integer activityId;

    /**
    * 姓名
    */
    @ExcelProperty(value = "姓名")
    private String fullName;

    /**
    * 称呼
    */
    @ExcelProperty(value = "称呼",converter = AppellationConverter.class)
    private Integer appellation;
负责人	所属活动	姓名	称呼	手机号	微信号	QQ号	邮箱	年龄	职业	年收入	地址	是否贷款	意向状态	意向产品	线索状态	线索来源	线索描述	下次联系时间
1	46	王杰	先生	13700000000	13700000000	230989432	wangjie@163.com	32	工程师	10	北京亦庄	需要	有意向	比亚迪e2	未联系	车展会	近期在看车	2023/11/27 20:33:25
7	47	张怡然	女士	13700000001	13700000001			28		8	河北廊坊	不需要	有意向	海豚	将来联系	网络广告	通过打电话获取的线索	2023/11/30 10:33:51
12	7	张翔宇	先生	13876903226	13876903226	1298094321		26		9	天津和平	需要	意向不明	秦PLUS EV	需要条件	视频直播	有购车意向,需要跟踪	2023/11/15 10:30:00
19	46	王世坤	先生	13700000000	13700000000	209836613	wangjie@163.com	32	工程师	10	北京亦庄	不需要	意向不明	秦PLUS DM-i	未联系	视频直播	近期在看车	2023/12/27 9:20:21
21	47	张珊珊	女士	13700000001	13700000001			28		8	河北廊坊	需要	无意向	汉DM	将来联系	视频直播	通过打电话获取的线索	2023/11/30 13:33:51
生
女士

看上图:称呼是非数据的字符串类型,但是接受数据的实体类是该字段是Integer类型,因此需要进行数据转换。

package com.dqw.config.converter;


import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ReadConverterContext;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.dqw.ServerApplication;
import com.dqw.domain.constants.Constants;
import com.dqw.domain.pojo.TDicValue;

import java.util.List;


public class AppellationConverter implements Converter<Integer> {

    @Override
    public Integer convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        // 读取excel数据时 执行该转换器
        // 1. 读取该值
        String stringValue = cellData.getStringValue();

        // 2. 通过该值 获取对应的id (根据自己的需求实现数据的转化)
        List<TDicValue> tDicValues = (List<TDicValue>) ServerApplication.CACHEMAP.get(Constants.APPELLATION);
        for (TDicValue tDicValue : tDicValues) {
            if (tDicValue.getTypeValue().equals(stringValue)) {
                return tDicValue.getId();
            }
        }
        return -1;
    }

}


对注解2 那块代码进行解释:

由于称呼信息经常被单独请求调用查询,为了避免多次没必要的数据库IO,设置了一个定时任务每隔一定的间隔时间就从数据库中获取数据并且将数据存入内存中,每次访问数据时都会优先出内存中获取数据,而不是从数据库中读取,加快了访问速度和减少了磁盘读写次数。

上述代码经实验没有任何错误,仅此记录。

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值