线程池(五)----举例 多线程校验导入的excel数据并给错误的单元格加标注

一、需求:如这样的模板:

线程池(五)----举例 多线程校验导入的excel数据并给错误的单元格加标注_第1张图片

填写用户名和年龄,后台判断长度小于2视为错误数据,现需要把填写正确的数据行录入数据库,把错误行标红并加批注说明错误的原因:

线程池(五)----举例 多线程校验导入的excel数据并给错误的单元格加标注_第2张图片

二、代码:

线程池(五)----举例 多线程校验导入的excel数据并给错误的单元格加标注_第3张图片

线程池(五)----举例 多线程校验导入的excel数据并给错误的单元格加标注_第4张图片

1、pom:


  4.0.0
  com.demo
  import-excel
  0.0.1-SNAPSHOT
  war

  
    org.springframework.boot
    spring-boot-starter-parent
    1.4.1.RELEASE
  
  
    
      org.springframework.boot
      spring-boot-starter-web
    

    
      org.apache.poi
      poi-ooxml
      3.12
    
    
      org.apache.commons
      commons-lang3
      3.4
    
    
      commons-collections
      commons-collections
      3.2.1
    

  

2、dto:省略所有get、set方法

(1)

package com.demo.dto;

public class UserDTO {
    private String userName;
    private Integer age;
}

(2) 

package com.demo.dto;
import java.util.List;

public class ImportUserDTO {

    //结果
    private List userDTOS;


    //错误信息
    private List errorMessages;
}

3、thread:采用多线程校验excel数据

package com.demo.thread;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
public class ThreadPoolTaskExecutorConfig {
    private static int CORE_POOL_SIZE = 5;
    private static int MAX_POOL_SIZE = 1000;
    @Bean(name="threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor serviceJobTaskExecutor(){
        ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();
        //线程池维护线程的最少数量
        poolTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
        //线程池维护线程的最大数量
        poolTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
        //线程池所使用的缓冲队列
        poolTaskExecutor.setQueueCapacity(200);
        //线程池维护线程所允许的空闲时间
        poolTaskExecutor.setKeepAliveSeconds(30000);
        poolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        System.out.println(poolTaskExecutor);
        return poolTaskExecutor;
    }
}

4、util:

(1)

package com.demo.util;

import org.apache.commons.lang3.StringUtils;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class DateUtil {

  
    public static final String YMDHMSXXX = "yyyy-MM-dd'T'HH:mm:ssXXX";

    public static final String YMDHMSX = "yyyyMMddHHmmssSSS";
    /**
     * yyyy-MM-dd HH:mm:ss
     */
    public static final String YMDHMS = "yyyy-MM-dd HH:mm:ss";

    public static final String DATE_PATTERN_DAY = "yyyy-MM-dd";

    /**
     * yyyy/MM/dd HH:mm:ss
     */
    public static final String YMDHMS_SLASH = "yyyy/MM/dd HH:mm:ss";
    /**
     * dd-MM-yyyy
     */
    public static final String DMY = "dd-MM-yyyy";
    /**
     * MM-dd-yyyy
     */
    public static final String MDY = "MM-dd-yyyy";
    /**
     * dd-MM-yyyy HH:mm
     */
    public static final String DMYHM = "dd-MM-yyyy HH:mm";

    /**
     * yyyy.MM.dd
     */
    public static final String YYYYMMDD_POINT = "yyyy.MM.dd";

    /**
     * yyyy/MM/dd
     */
    public static final String YYYYMMDD_SLASH = "yyyy/MM/dd";

    /**
     *  时间起始时间 00:00:00
     */
    public static final String START_TIME_STR = " 00:00:00";

    /**
     *  时间结束时间 23:59:59
     */
    public static final String END_TIME_STR = " 23:59:59";


    public static final String YMD = "yyyy-MM-dd";
    public static final String YMDP = "yyyy.MM.dd";

    public static final String YYYYMMDD = "yyyyMMdd";

    public static final String YYYYMM = "yyyyMM";

    /**
     * yyyyMMddHHmmss
     */
    public static final String YYYYMMDD_NOSEPARATOR = "yyyyMMddHHmmss";

    public static String format(Date date, String formatPattern) {
        if (date == null) {
            return "";
        }
        SimpleDateFormat f = new SimpleDateFormat(formatPattern);
        return f.format(date);
    }

}

(2) 

package com.demo.util;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;

import java.text.DecimalFormat;
import java.util.Date;

public class ExcelUtil {

    /**
     *
     * @description: 取单元格中的内容,包含时间格式
     */
    public static String getSheetCellTimeValue(Row row, int cellNo) {
        Cell cell = row.getCell(cellNo);
        DecimalFormat df = new DecimalFormat("0");
        String name = "";
        if (cell != null) {
            if (cell.getCellType() == HSSFCell.CELL_TYPE_NUMERIC) {
                String cellText = "";
                if(HSSFDateUtil.isCellDateFormatted(cell)) {
                    Date dateTemp = cell.getDateCellValue();
                    cellText = DateUtil.format(dateTemp, DateUtil.YYYYMMDD_SLASH);
                    name = String.valueOf(cellText);
                } else {
                    cellText = df.format(cell.getNumericCellValue());
                    name = String.valueOf(cellText);
                    if (null != name && name.indexOf(".") > 0) {
                        name = name.substring(0, name.indexOf("."));
                    }
                }
            } else {
                name = cell.getStringCellValue().trim();
            }
        } else {
            name = "";
        }
        return name == null?"":name.trim();
    }
}

5、task:

package com.demo.task;

import com.demo.dto.ImportUserDTO;
import com.demo.dto.UserDTO;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

public class UserTask implements Callable {

    //待处理的数据
    private List> datas;

    //行起始下标
    private Integer rowIndex;

    private Workbook workbook;

    private Sheet sheet;


    public UserTask(List> datas, int rowIndex, Workbook workbook,Sheet sheet) {
        this.datas = datas;
        this.rowIndex = rowIndex;
        this.workbook = workbook;
        this.sheet = sheet;
        //第一行为标题
        this.rowIndex  = rowIndex+1;
    }

    @Override
    public ImportUserDTO call() throws Exception {

        ImportUserDTO importUserDTO = new ImportUserDTO();
        List errorMsgList = new ArrayList<>();
        List userDTOS = new ArrayList<>();
        //总行数
        int rowNum = datas.size();
        //列下标,因为要在出错的单元格标注
        Map colMap = new HashMap<>();

        for(int i=0;i errorMsgList, Integer colIndex, String errorMsg) throws Exception {
        errorMsgList.add(errorMsg);
        //设置错误字体为红色
        CellStyle style = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setFontHeightInPoints((short) 11);
        font.setFontName("微软雅黑");
        font.setColor(Font.COLOR_RED);
        //style.setAlignment(HorizontalAlignment.CENTER);
        style.setFont(font);
        Cell deliveryTimeCell = sheet.getRow(rowIndex).getCell(colIndex);
        deliveryTimeCell.setCellStyle(style);
        //添加批注
        XSSFDrawing p = ((XSSFSheet)sheet).createDrawingPatriarch();
        XSSFCell cell = (XSSFCell) sheet.getRow(rowIndex).getCell(colIndex);
        if(cell == null){
            //如果你获取的单元格是空进行创建新的cell,再追加批注,不然null指针
            XSSFRow r = (XSSFRow) sheet.getRow(rowIndex);
            cell = r.createCell(colIndex);
        }
        cell.setCellStyle(style);
        XSSFComment comment = p.createCellComment(new XSSFClientAnchor(0, 0, 0, 0,
                (colIndex), rowIndex, colIndex+2, rowIndex+2));
        XSSFRichTextString rtf = new XSSFRichTextString(errorMsg);
        XSSFFont commentFormatter = (XSSFFont) workbook.createFont();
        rtf.applyFont(commentFormatter);
        comment.setString(rtf);
        cell.setCellComment(comment);

        throw new Exception();
    }
}

6、service:

package com.demo.service.serviceImpl;

import com.demo.dto.ImportUserDTO;
import com.demo.dto.UserDTO;
import com.demo.service.ImportService;
import com.demo.task.UserTask;
import com.demo.util.ExcelUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.apache.commons.collections.CollectionUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Future;

@Service("importService")
public class ImportServiceImpl implements ImportService {

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * 根据模板读取正确数据保存到user表,并把错误数据标红加批注
     *   姓名  年龄
     * @param is
     * @param fileName
     */
    @Override
    public void saveUser(InputStream is, String fileName) throws IOException {
        //所有数据
        List> datas = new ArrayList<>();
        boolean isExcel2007 = fileName.matches("^.+\\.(?i)(xlsx)$");
        Workbook wb = null;
        if (!isExcel2007) {
            // 当excel是2003时,创建excel2003
            wb = new HSSFWorkbook(is);
        } else {
            // 当excel是2007时,创建excel2007
            wb = new XSSFWorkbook(is);
        }
        //只读取第一个页脚的数据
        Sheet sheet = wb.getSheetAt(0);
        //行数
        int rows = sheet.getLastRowNum();
        //第一行为标题,从第二行开始获取
        for (int i=2; i <= rows; i++) {
            List dataOfRow = new ArrayList<>();
            Row rowData = sheet.getRow(i);
            //循环列
            for (int j = 0; j<2; j++) {
                String cellData = ExcelUtil.getSheetCellTimeValue(rowData, j);
                dataOfRow.add(StringUtils.isBlank(cellData) ? "" : cellData);
            }
            datas.add(dataOfRow);
        }
        //解析数据datas
        List futures = new ArrayList<>();
        for (int i = 0; i < datas.size(); i += 100) {
            int startIndex = i;
            int endIndex = startIndex + 100 > datas.size() ? datas.size() : startIndex + 100;
            UserTask task = new UserTask(datas.subList(startIndex, endIndex),i+1,wb,sheet);
            Future future = threadPoolTaskExecutor.submit(task);
            futures.add(future);
        }
        //收集数据
        List userDTOS = new ArrayList<>();
        List errorMsgList = new ArrayList<>();
        try{
            for(Future future:futures){
                ImportUserDTO importUserDTO = (ImportUserDTO) future.get();
                userDTOS.addAll(importUserDTO.getUserDTOS());
                errorMsgList.addAll(importUserDTO.getErrorMessages());
            }
        }catch (Exception e){

        }
        //插入正确填写的数据
        System.out.println("正确数据:"+userDTOS.size());
        //输出错误模板
        if(CollectionUtils.isNotEmpty(errorMsgList)){
            String errorFilePath = "E:"+File.separator + "error" + File.separator
                    +UUID.randomUUID().toString()+".xlsx";
            File fileDir = new File("E:"+File.separator + "error");
            if (!fileDir.exists()) {
                fileDir.mkdirs();
            }
            File errorFile = new File(errorFilePath);
            if (errorFile.exists()) {
                errorFile.delete();
            }
            FileOutputStream out = new FileOutputStream(errorFilePath);
            wb.write(out);
            out.close();
          }
    }
}

7、rest:

package com.demo.rest;

import com.demo.service.ImportService;
import org.springframework.beans.factory.annotation.Autowired;
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 java.io.IOException;
import java.io.InputStream;

@RequestMapping("/import")
@RestController
public class ImportController {

    @Autowired
    private ImportService importService;

    /**
     * 保存excel的信息到数据库
     * @param file
     */
    @RequestMapping("/import")
    public void importRest(@RequestParam("excelFile") MultipartFile file){
        try {
            InputStream is = file.getInputStream();
            String fileName = file.getOriginalFilename();
            importService.saveUser(is,fileName);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

8、启动类:

package com.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String args[]){
        SpringApplication.run(Application.class,args);
    }
}

9、application.properties配置文件:

server.port=9999

测试:

线程池(五)----举例 多线程校验导入的excel数据并给错误的单元格加标注_第5张图片

请求后E盘error目录下会生成一个错误的excel文件,打卡即和开头所述的一样。 

你可能感兴趣的:(多线程编程,excel)