easyexcel導入獲取表頭並且表頭為不固定列


1、Controller層

@PostMapping("upload")
    @ResponseBody
    public HashMap<String, Object> upload(@RequestParam(value = "file") MultipartFile file,
                                          @RequestParam(value = "ztId") int ztId,
                                          @RequestParam(value = "year") int year,
                                          @RequestParam(value = "month") int month) throws IOException {

        //初始化監聽器
        ZwmxzListener zwmxzListener = new ZwmxzListener(zwmxzService, ztId, year, month);
        //解析數據
        EasyExcel.read(file.getInputStream(), zwmxzListener).sheet(0).doReadSync();

        HashMap<String, Object> hashMap = new HashMap<>();
        //獲取校驗錯誤信息,並返回給前端
        List<String> errMessage = zwmxzListener.getErrMessage();
        if (errMessage.isEmpty()) {
            hashMap.put("success", true);
        } else {
            hashMap.put("success", false);
            hashMap.put("errMessage", errMessage);
        }
        return hashMap;
    }

2、Listener監聽器

package cn.xxxxxx.xxxxx.xxx.utils;

import cn.xxxxxx.xxxxx.xxx.services.ZwmxzService;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

//繼承AnalysisEventListener<Map<Integer, String>>
public class ZwmxzListener extends AnalysisEventListener<Map<Integer, String>> {
    //定義每多少條數據進行數據庫保存
    private static final int BATCH_COUNT = 128;
    private int ztId;
    private int year;
    private int month;
    private ZwmxzService zwmxzService;
    //建一個errMessage集合保存校驗有問題的結果
    private List<String> errMessage;
    //用list集合保存解析到的結果
    private List<Map<Integer, Map<Integer, String>>> list;


    //重構,把傳來的值賦給對應的屬性
    public ZwmxzListener(ZwmxzService zwmxzService, int ztId, int year, int month) {
        this.ztId = ztId;
        this.year = year;
        this.month = month;
        this.zwmxzService = zwmxzService;
        list = new ArrayList<>();
        errMessage = new ArrayList<>();
    }

    /**
     * 重寫invokeHeadMap方法,獲去表頭,如果有需要獲取第一行表頭就重寫這個方法,不需要則不需要重寫
     *
     * @param headMap Excel每行解析的數據為Map<Integer, String>類型,Integer是Excel的列索引,String為Excel的單元格值
     * @param context context能獲取一些東西,比如context.readRowHolder().getRowIndex()為Excel的行索引,表頭的行索引為0,0之后的都解析成數據
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        logger.info("解析到一條頭數據:{}, currentRowHolder: {}", headMap.toString(), context.readRowHolder().getRowIndex());
        Map<Integer, Map<Integer, String>> map = new HashMap<>();
        map.put(context.readRowHolder().getRowIndex(), headMap);
        list.add(map);
    }

    /**
     * 重寫invoke方法獲得除Excel第一行表頭之后的數據,
     * 如果Excel第二行也是表頭,那么也會解析到這里,如果不需要就通過判斷context.readRowHolder().getRowIndex()跳過
     *
     * @param data    除了第一行表頭外,數據都會解析到這個方法
     * @param context 和上面解釋一樣
     */
    @Override
    public void invoke(Map<Integer, String> data, AnalysisContext context) {
        logger.info("解析到一條數據:{}, currentRowIndex: {}----", data.toString(), context.readRowHolder().getRowIndex());
        Map<Integer, Map<Integer, String>> map = new HashMap<>();
        map.put(context.readRowHolder().getRowIndex(), data);
        list.add(map);
        // 達到BATCH_COUNT了,需要去存儲一次數據庫,防止數據幾萬條數據在內存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存儲完成清理 list
            list.clear();
        }
    }

    /**
     * 解析到最后會進入這個方法,需要重寫這個doAfterAllAnalysed方法,然后里面調用自己定義好保存方法
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 這里也要保存數據,確保最后遺留的數據也存儲到數據庫
        saveData();
        logger.info("所有數據解析完成!");
    }

    /**
     * 加上存儲數據庫
     */
    private void saveData() {
        logger.info("{}條數據,開始存儲數據庫!", list.size());
        //用errMessage集合保存zwmxzService.save()返回校驗錯誤信息,根據自己需要來寫就行了
        errMessage.addAll(zwmxzService.save(list, ztId, year, month, errMessage.size() > 0));
    }

    public int getZtId() {
        return ztId;
    }

    public void setZtId(int ztId) {
        this.ztId = ztId;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }


    public ZwmxzService getZwmxzService() {
        return zwmxzService;
    }

    public void setZwmxzService(ZwmxzService zwmxzService) {
        this.zwmxzService = zwmxzService;
    }

    public List<String> getErrMessage() {
        return errMessage;
    }

    public void setErrMessage(List<String> errMessage) {
        this.errMessage = errMessage;
    }

    public List<Map<Integer, Map<Integer, String>>> getList() {
        return list;
    }

    public void setList(List<Map<Integer, Map<Integer, String>>> list) {
        this.list = list;
    }

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

3、ServiceImpl實現類寫導入的數據校驗,根據自己的業務需求來寫校驗規則,當然也可以在監聽器里面就校驗,然后調用ServiceImpl實現類來保存數據

 @Transactional(rollbackFor = Exception.class)
    @Override
    public List<String> save(List<Map<Integer, Map<Integer, String>>> list, int ztId, int year, int month, boolean hasErr) {
        logger.info("--start---導入判斷----");
        List<String> error = new ArrayList<>();
        List<Zwmxz> newZwmxz = new ArrayList<>();
        String pattern = "^[-]?\\d+(\\.\\d+)?$";
        //遍歷傳過來的數據集合
        for (var item : list) {
            //每一行
            for (var row : item.entrySet()) {
                //獲取行的索引
                Integer rowIndex = row.getKey();
                //索引為0表示為表頭
                if (rowIndex == 0) {
                    String leftYear = row.getValue().get(1);
                    String rightYear = row.getValue().get(2);
                    logger.info("---leftYear={}--------rightYear={}----------", leftYear, rightYear);
                    String left = judgeYear(leftYear, year, "B");
                    if (left != null) {
                        error.add(left);
                    }
                    String right = judgeYear(rightYear, year, "C");
                    if (right != null) {
                        error.add(right);
                    }
                } else {
                    rowIndex++;
                    logger.info("---rowIndex={}--------", rowIndex);
                    Zwmxz it = new Zwmxz();
                    //遍歷每一行的每一列數據,並校驗
                    for (var col : row.getValue().entrySet()) {
                        switch (col.getKey()) {
                            case 1:
                                if (col.getValue() == null) {
                                    error.add("序號為【" + rowIndex + "】的數據的B列【月份】不可為空!");
                                } else {
                                    if (Pattern.matches(pattern, col.getValue().trim())) {
                                        if (Integer.parseInt(col.getValue().trim()) == month) {
                                            it.setMonth(month);
                                        } else {
                                            error.add("序號為【" + rowIndex + "】的數據的B列【月份】與當前期間的月份不一致!");
                                        }
                                    } else {
                                        error.add("序號為【" + rowIndex + "】的數據的B列【月份】需要是正常的阿拉伯數字月份!");
                                    }
                                }
                                break
                           ......................................................case 16:
                                if (col.getValue() == null) {
                                    it.setYe(BigDecimal.ZERO);
                                } else {
                                    if (Pattern.matches(pattern, col.getValue().trim())) {
                                        it.setYe(new BigDecimal(col.getValue()));
                                    } else {
                                        it.setYe(BigDecimal.ZERO);
                                    }
                                }
                                break;
                            case 17:
                                it.setSdocId(col.getValue());
                                break;
                            default:
                                break;
                        }
                    }
                    it.setZtId(ztId);
                    it.setYear(year);
                    Date date = new Date();
                    it.setUpdatedAt(date);
                    it.setCreatedAt(date);
                    newZwmxz.add(it);
                }
            }
        }
        // 以上數據 若存在一條報錯的數據,則全部導入失敗!
        if (error.size() > 0 || hasErr) {
            zwmxzRepository.deleteAllByZtIdAndYearAndMonth(ztId, year, month);
        } else {
            for (var it : newZwmxz) {
                zwmxzRepository.save(it);
            }
        }
        return error;
    }


 private String judgeYear(String year1, int year2, String position) {
        String pattern = "^[0-9]+$";
        if (year1 == null) {
            return "Excel的" + position + "列的表頭年份不可為空!";
        } else {
            String trim = year1.trim();
            if (trim.length() >= 4) {
                String substring = trim.substring(0, 4);
                logger.info("-----year1:{}", substring);
                if (Pattern.matches(pattern, substring)) {
                    if (Integer.parseInt(substring) != year2) {
                        return "Excel的" + position + "列的表頭年份與當前期間年份不一致!";
                    }
                } else {
                    return "Excel的" + position + "列的表頭年份不規范!";
                }
            } else {
                return "Excel的" + position + "列的表頭年份不規范!";
            }
        }
        return null;
    }

 導入的前端參考https://www.cnblogs.com/pzw23/p/12981617.html

 

注意:

(上面的代碼就不更改了,下面說一下特殊情況)

List<Map<Integer, Map<Integer, String>>> list里面,每次最好第一個Map<Integer, Map<Integer, String>>都是表頭,然后在獲取數據的時候,根據表頭的map來遍歷,根據表頭的key去拿每一行數據的value,判空!因為有些excel行的數據是不跟表頭一一對應的,比如說列數據少了或是多了,比如說表頭map={0='序號',1='編號',2='姓名',3='性別',4='年齡',5='收入'},而數據map可能是{0='1',1='xxx01',2='小武',3='男'},也可能是{0='1',1='xxx01',2='小武',3='男',4='',5=''},還可能是多出的數據{0='1',1='xxx01',2='小武',3='男',4='',5='',6='**asd'},主要看excel是怎么操作的,比如刪除列數據用delete鍵刪除的是第一種,注意這方面的特殊性。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM