fork-join、easyPOI解析excel速度

fork-join、easyPOI解析excel速度

fork-join 完ok easyPOI

现在是比较注重速度的时代,例如导入excel表格,需要将数据入库,那么如果数据量很大的时候,速度就是很好的体验感了

看代码吧

控制层

package com.jay.controller;

import com.jay.service.StudentService;
import com.jay.util.ExcelUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;


@Controller
@Api(tags = {"批量导入学生信息"})
@RequestMapping("/student")
public class StudentController {
    @Autowired
    private StudentService studentService;

    @ApiOperation(value = "通过EasyPoi的方式批量导入学生信息-1", httpMethod = "POST", notes = "批量导入业主信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "文件流对象,接收数组格式", required = true, dataType = "MultipartFile")
    })
    @RequestMapping(value = "/batchUploadStudent_easyPOI.do", method = RequestMethod.POST)
    @ResponseBody
    public String batchUploadOwner_easyPOI(@RequestParam(value = "file") MultipartFile file) throws Exception {
        studentService.batchUploadStudent_easyPOI(file);
        return "success";
    }

    @ApiOperation(value = "通过forkjoin的方式批量导入学生信息-2", httpMethod = "POST", notes = "批量导入业主信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "文件流对象,接收数组格式", required = true, dataType = "MultipartFile")
    })
    @RequestMapping(value = "/batchUploadStudent_forkjoin.do", method = RequestMethod.POST)
    @ResponseBody
    public String batchUploadOwner(@RequestParam(value = "file") MultipartFile file) throws Exception {
        Workbook workbook = ExcelUtil.getWorkbook(file.getInputStream());
        studentService.batchUploadStudent_forkjoin(workbook);
        return "success";
    }
}


业务层

package com.jay.service;

import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.multipart.MultipartFile;


public interface StudentService {
    /**
     * 通过easyPOI读取数据
     * @param file
     * @return
     * @author xiagwei
     * @date 2020/4/18 4:09 PM
     */
    String batchUploadStudent_easyPOI(MultipartFile file) throws Exception;

    /**
     * 通过fork-join的方式读取数据
     * @param workbook
     * @return
     * @author xiagwei
     * @date 2020/4/18 4:10 PM
     */
    String batchUploadStudent_forkjoin(Workbook workbook);
}

业务层实现类
package com.jay.service;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.jay.model.Student;
import com.jay.util.StudentExcelImportWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@Slf4j
@Service
public class StudentServiceImpl implements StudentService {
    @Autowired
    private StudentExcelImportWrapper studentExcelImportWrapper;

    @Override
    public String batchUploadStudent_easyPOI(MultipartFile file) throws Exception {
        long startTime = System.currentTimeMillis();
        List<Student> studentList = ExcelImportUtil.importExcel(file.getInputStream(), Student.class, new ImportParams());
        log.info("********通过EasyPOI读取文件总耗时是={},读取到的数据总条数是={}", (System.currentTimeMillis() - startTime) + "毫秒", studentList.size());
        return null;
    }

    @Override
    public String batchUploadStudent_forkjoin(Workbook workbook) {
        long startTime = System.currentTimeMillis();
        List<Student> studentList = studentExcelImportWrapper.importExcel(workbook);
        log.info("********通过Fork-Join的方式读取文件总耗时是={},读取到的数据条数是={}", (System.currentTimeMillis() - startTime) + "毫秒", studentList.size());
        return null;
    }
}

fork-join的业务处理类

package com.jay.util;


import com.jay.model.Student;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.stream.Collectors;


@Slf4j
@Component
public class StudentExcelImportWrapper {



   public List<Student> importExcel(Workbook workbook) {
       //设置4条线程
       ForkJoinPool forkJoinPool = new ForkJoinPool(4);;
       //选择sheet页码
       Sheet sheet = workbook.getSheetAt(0);
       //开启任务, 参数是起始行,总行数, 这个实体类对象继承了RecursiveTask
       JoinTask joinTask = new JoinTask(1, sheet.getLastRowNum(), sheet);
       //开启异步处理任务
       List<Student> importVOList = forkJoinPool.invoke(joinTask);
       //excel内部去重
//        List<Student> noRepeatImportVOList = importVOList.stream().filter(DistinctUtil.distinctByKey(Student::getStudentNo)).collect(Collectors.toList());
       List<Student> noRepeatImportVOList = importVOList;
       return noRepeatImportVOList;
   }

   class JoinTask extends RecursiveTask<List<Student>> {
       private int start;
       private int end;
       private Sheet sheet;
       private int total;

       public JoinTask(int start, int end, Sheet sheet) {
           this.start = start;
           this.end = end;
           this.sheet = sheet;
           this.total = sheet.getLastRowNum();
       }

       @Override
       protected List<Student> compute() {
           //数据异常
           if (start > end || total < end) {
               return new ArrayList<>(1);
           }
           //人为设定的,看自己定,没200行一个解析
           if (end - start <= 200) {
//                return getData(sheet, start, end).stream().filter(DistinctUtil.distinctByKey(Student::getStudentNo)).collect(Collectors.toList());
               return getData(sheet, start, end);
           } else {
               //二分法,将数据平均分成两块
               int mid = (start + end) / 2;
               //递归调用,左边是序号小的那一块
               JoinTask rightTask = new JoinTask(start, mid, sheet);
               //递归调用,右边是序号大的那一块
               JoinTask leftTask = new JoinTask(mid + 1, end, sheet);

               //写法一
               rightTask.fork();
               List<Student> leftList =  leftTask.compute();
               List<Student> rightList = rightTask.join();
               //写法二
               //invokeAll(rightTask, leftTask);
               //List<Student> leftList = leftTask.join();
               //List<Student> rightList = rightTask.join();
               //将左右两边数据合并
               leftList.addAll(rightList);
               return leftList;
           }
       }
   }

   /**
    * 分页读取Excel中的数据
    *
    * @param sheet
    * @param start 开始页
    * @param end   结束页
    * 
    * @return
    */
   private List<Student> getData(Sheet sheet, int start, int end) {
       List<Student> mapList = new ArrayList<>();
       //解析单元格的方法,本demo是直接挨个读取每个单元格的,当然也可以通过注解的方式来实现
       for (int i = start; i <= end; i++) {
           Student student = null;
           try {
               Row row = sheet.getRow(i);
               student = new Student();
//                student.setClassName(ExcelUtil.getKeyValue(row.getCell(0)));
//                student.setStudentName(ExcelUtil.getKeyValue(row.getCell(1)));
//                student.setStudentMobile(ExcelUtil.getKeyValue(row.getCell(2)));
//                student.setIdCard(ExcelUtil.getKeyValue(row.getCell(3)));
               student.setStudentNo(ExcelUtil.getKeyValue(row.getCell(4)));
//                student.setIdCard(ExcelUtil.getKeyValue(row.getCell(5)));
               System.err.println("jjjjj");
           } catch (Exception e) {
               log.info("***************税号={},文件名={},数据解析出现异常={}", e);
               continue;
           }
           mapList.add(student);
       }
       return mapList;
   }
}

jar包
	<!--easypoi-->
	<dependency>
		<groupId>cn.afterturn</groupId>
		<artifactId>easypoi-spring-boot-starter</artifactId>
		<!--<version>3.3.0</version>-->
		<version>4.0.0</version>
		<!--<version>4.1.0</version>-->
	</dependency>
	<!--easypoi-->
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值