欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用Springboot+poi上傳并處理百萬級數(shù)據(jù)EXCEL

 更新時間:2020年12月04日 09:57:40   作者:ONROAD0612  
這篇文章主要介紹了使用Springboot+poi上傳并處理百萬級數(shù)據(jù)EXCEL,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

1 Excel上傳

針對Excel的上傳,采用的是比較常規(guī)的方法,其實和文件上傳是相同的。具體源碼如下:

  @PostMapping(value = "", consumes = "multipart/*", headers = "content-type=multipart/form-data")
  public Map<String, Object> addBlacklist(
      @RequestParam("file") MultipartFile multipartFile, HttpServletRequest request
  ) {
    //判斷上傳內(nèi)容是否符合要求
    String fileName = multipartFile.getOriginalFilename();
    if (!fileName.matches("^.+\\.(?i)(xls)$") && !fileName.matches("^.+\\.(?i)(xlsx)$")) {
      return returnError(0,"上傳的文件格式不正確");
    }
 
    String file = saveFile(multipartFile, request);
    int result = 0;
    try {
      result = blacklistServcice.addBlackLists(file);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return returnData(result);
  }
 
  private String saveFile(MultipartFile multipartFile, HttpServletRequest request) {
    String path;
    String fileName = multipartFile.getOriginalFilename();
    // 判斷文件類型
    String realPath = request.getSession().getServletContext().getRealPath("/");
    String trueFileName = fileName;
    // 設置存放Excel文件的路徑
    path = realPath + trueFileName;
    File file = new File(path);
    if (file.exists() && file.isFile()) {
      file.delete();
    }
    try {
      multipartFile.transferTo(new File(path));
    } catch (IOException e) {
      e.printStackTrace();
    }
 
    return path;
  }

上面的源碼我們可以看見有一個saveFile方法,這個方法是將文件存在服務器本地,這樣方便后續(xù)文件內(nèi)容的讀取,用不著一次讀取所有的內(nèi)容從而導致消耗大量的內(nèi)存。當然這里大家如果有更好的方法希望能留言告知哈。

2 Excel處理工具源碼

import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
 
import java.io.InputStream;
import java.sql.SQLException;
import java.util.*;
 
/**
 * XSSF and SAX (Event API)
 */
public abstract class XxlsAbstract extends DefaultHandler {
  private SharedStringsTable sst;
  private String lastContents;
  private int sheetIndex = -1;
  private List<String> rowlist = new ArrayList<>();
  public List<Map<String, Object>> dataMap = new LinkedList<>(); //即將進行批量插入的數(shù)據(jù)
  public int willSaveAmount; //將要插入的數(shù)據(jù)量
  public int totalSavedAmount; //總共插入了多少數(shù)據(jù)
  private int curRow = 0;    //當前行
  private int curCol = 0;    //當前列索引
  private int preCol = 0;    //上一列列索引
  private int titleRow = 0;  //標題行,一般情況下為0
  public int rowsize = 0;  //列數(shù)
 
  //excel記錄行操作方法,以sheet索引,行索引和行元素列表為參數(shù),對sheet的一行元素進行操作,元素為String類型
  public abstract void optRows(int sheetIndex, int curRow, List<String> rowlist) throws SQLException;
 
  //只遍歷一個sheet,其中sheetId為要遍歷的sheet索引,從1開始,1-3
 
  /**
   * @param filename
   * @param sheetId sheetId為要遍歷的sheet索引,從1開始,1-3
   * @throws Exception
   */
  public void processOneSheet(String filename, int sheetId) throws Exception {
    OPCPackage pkg = OPCPackage.open(filename);
    XSSFReader r = new XSSFReader(pkg);
    SharedStringsTable sst = r.getSharedStringsTable();
 
    XMLReader parser = fetchSheetParser(sst);
 
    // rId2 found by processing the Workbook
    // 根據(jù) rId# 或 rSheet# 查找sheet
    InputStream sheet2 = r.getSheet("rId" + sheetId);
    sheetIndex++;
    InputSource sheetSource = new InputSource(sheet2);
    parser.parse(sheetSource);
    sheet2.close();
  }
 
  public XMLReader fetchSheetParser(SharedStringsTable sst)
      throws SAXException {
    XMLReader parser = XMLReaderFactory.createXMLReader();
    this.sst = sst;
    parser.setContentHandler(this);
    return parser;
  }
 
  public void endElement(String uri, String localName, String name) {
    // 根據(jù)SST的索引值的到單元格的真正要存儲的字符串
    try {
      int idx = Integer.parseInt(lastContents);
      lastContents = new XSSFRichTextString(sst.getEntryAt(idx))
          .toString();
    } catch (Exception e) {
 
    }
 
    // v => 單元格的值,如果單元格是字符串則v標簽的值為該字符串在SST中的索引
    // 將單元格內(nèi)容加入rowlist中,在這之前先去掉字符串前后的空白符
    if (name.equals("v")) {
      String value = lastContents.trim();
      value = value.equals("") ? " " : value;
      int cols = curCol - preCol;
      if (cols > 1) {
        for (int i = 0; i < cols - 1; i++) {
          rowlist.add(preCol, "");
        }
      }
      preCol = curCol;
      rowlist.add(curCol - 1, value);
    } else {
      //如果標簽名稱為 row ,這說明已到行尾,調(diào)用 optRows() 方法
      if (name.equals("row")) {
        int tmpCols = rowlist.size();
        if (curRow > this.titleRow && tmpCols < this.rowsize) {
          for (int i = 0; i < this.rowsize - tmpCols; i++) {
            rowlist.add(rowlist.size(), "");
          }
        }
        try {
          optRows(sheetIndex, curRow, rowlist);
        } catch (SQLException e) {
          e.printStackTrace();
        }
        if (curRow == this.titleRow) {
          this.rowsize = rowlist.size();
        }
        rowlist.clear();
        curRow++;
        curCol = 0;
        preCol = 0;
      }
    }
  }
}

3 解析成功后的數(shù)據(jù)處理

首先我們將源碼展示出來,然后再具體說明

public int addBlackLists(String file) throws ExecutionException, InterruptedException {
    ArrayList<Future<Integer>> resultList = new ArrayList<>();
    XxlsAbstract xxlsAbstract = new XxlsAbstract() {
 
      //針對數(shù)據(jù)的具體處理
      @Override
      public void optRows(int sheetIndex, int curRow, List<String> rowlist) {
 
        /**
         * 判斷即將插入的數(shù)據(jù)是否已經(jīng)到達8000,如果到達8000,
         * 進行數(shù)據(jù)插入
         */
        if (this.willSaveAmount == 5000) {
 
          //插入數(shù)據(jù)
          List<Map<String, Object>> list = new LinkedList<>(this.dataMap);
          Callable<Integer> callable = () -> {
            int count = blacklistMasterDao.addBlackLists(list);
            blacklistRecordMasterDao.addBlackListRecords(list);
            return count;
          };
          this.willSaveAmount = 0;
          this.dataMap = new LinkedList<>();
          Future<Integer> future = executor.submit(callable);
          resultList.add(future);
        }
 
        //匯總數(shù)據(jù)
        Map<String, Object> map = new HashMap<>();
        map.put("uid", rowlist.get(0));
        map.put("createTime", rowlist.get(1));
        map.put("regGame", rowlist.get(2));
          map.put("banGame", rowlist.get(2));
        this.dataMap.add(map);
        this.willSaveAmount++;
        this.totalSavedAmount++;
      }
    };
    try {
      xxlsAbstract.processOneSheet(file, 1);
    } catch (Exception e) {
      e.printStackTrace();
    }
 
    //針對沒有存入的數(shù)據(jù)進行處理
    if(xxlsAbstract.willSaveAmount != 0){
      List<Map<String, Object>> list = new LinkedList<>(xxlsAbstract.dataMap);
      Callable<Integer> callable = () -> {
        int count = blacklistMasterDao.addBlackLists(list);
        blacklistRecordMasterDao.addBlackListRecords(list);
        return count;
      };
      Future<Integer> future = executor.submit(callable);
      resultList.add(future);
    }
 
    executor.shutdown();
    int total = 0;
    for (Future<Integer> future : resultList) {
      while (true) {
        if (future.isDone() && !future.isCancelled()) {
          int sum = future.get();
          total += sum;
          break;
        } else {
          Thread.sleep(100);
        }
      }
    }
    return total;
  }

針對上面的源碼,我們可以發(fā)現(xiàn),我們需要將讀取到的EXCEL數(shù)據(jù)插入到數(shù)據(jù)庫中,這里為了減小數(shù)據(jù)庫的IO和提高插入的效率,我們采用5000一批的批量插入(注意:如果數(shù)據(jù)量過大會導致組成的SQL語句無法執(zhí)行)。

這里需要獲取到一個最終執(zhí)行成功的插入結果,并且插入執(zhí)行很慢。所有采用了Java多線程的Future模式,采用異步的方式最終來獲取J執(zhí)行結果。

通過上面的實現(xiàn),樓主測試得到最終一百萬條數(shù)據(jù)需要四分鐘左右的時間就可以搞定。如果大家有更好的方法,歡迎留言。

補充知識:Java API SXSSFWorkbook導出Excel大批量數(shù)據(jù)(百萬級)解決導出超時

之前使用簡單的HSSFWorkbook,導出的數(shù)據(jù)不能超過

后來改成SXSSFWorkbook之后可以導出更多,但是

而且我之前的代碼是一次性查出所有數(shù)據(jù),幾十萬條,直接就超時了。

之前的代碼是一次性查出所有的結果,list里面存了幾十萬條數(shù)據(jù)。因為功能設計的問題,我這一個接口要同時處理三個功能:

再加上查詢SQL的效率問題,導致請求超時。

現(xiàn)在為了做到處更大量的數(shù)據(jù)只能選擇優(yōu)化。優(yōu)化查詢的sql這里就不講了,只講導出功能的優(yōu)化。

其實就是分批次處理查詢結果:

這樣做的好處是查詢速度變快,封裝速度也變快,整體速度變快就不會出現(xiàn)超時,而且,每次分頁查出的結果放到list中不會出現(xiàn)占用JVM內(nèi)存過大的情況。避免出現(xiàn)內(nèi)存溢出導致系統(tǒng)崩潰。

再次優(yōu)化:

上面這樣做雖然可以導出,但是代碼看起來不美觀:

這樣看起來就簡潔很多了。

經(jīng)驗證,查詢加封裝EXCEL7000條數(shù)據(jù)處理只需要1秒

以上這篇使用Springboot+poi上傳并處理百萬級數(shù)據(jù)EXCEL就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • MybatisPlus使用代碼生成器遇到的小問題(推薦)

    MybatisPlus使用代碼生成器遇到的小問題(推薦)

    這篇文章主要介紹了MybatisPlus使用代碼生成器遇到的小問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • springboot定時任務SchedulingConfigurer異步多線程實現(xiàn)方式

    springboot定時任務SchedulingConfigurer異步多線程實現(xiàn)方式

    這篇文章主要介紹了springboot定時任務SchedulingConfigurer異步多線程實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • java實現(xiàn)時鐘效果

    java實現(xiàn)時鐘效果

    這篇文章主要為大家詳細介紹了java實現(xiàn)時鐘效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • Spring項目里將SQL語句寫在.sql文件中的方法

    Spring項目里將SQL語句寫在.sql文件中的方法

    這篇文章主要介紹了Spring項目里如何將SQL語句寫在.sql文件中的方法,文中給出了詳細的介紹和示例代碼,相信對大家的學習或者工作具有一定的參考借鑒價值,有需要的朋友們下面來一起看看吧。
    2017-01-01
  • Java interrupt()方法使用實例介紹

    Java interrupt()方法使用實例介紹

    一個線程在未正常結束之前, 被強制終止是很危險的事情. 因為它可能帶來完全預料不到的嚴重后果比如會帶著自己所持有的鎖而永遠的休眠,遲遲不歸還鎖等。 所以你看到Thread.suspend, Thread.stop等方法都被Deprecated了
    2023-02-02
  • java上乘武功入門--反射

    java上乘武功入門--反射

    反射是Java的一種機制,一般也叫做反射機制,本文會講反射機制是什么和怎么使用,喜歡的朋友可以觀看一下,希望能給你帶來幫助
    2021-07-07
  • 淺談如何優(yōu)雅地停止Spring Boot應用

    淺談如何優(yōu)雅地停止Spring Boot應用

    這篇文章主要介紹了淺談如何優(yōu)雅地停止Spring Boot應用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05
  • Java中float類型的范圍及其與十六進制的轉(zhuǎn)換例子

    Java中float類型的范圍及其與十六進制的轉(zhuǎn)換例子

    這篇文章主要介紹了Java中float類型的范圍及其與十六進制的轉(zhuǎn)換例子,是Java入門學習中的基礎知識,需要的朋友可以參考下
    2015-10-10
  • 通過springboot+mybatis+druid配置動態(tài)數(shù)據(jù)源

    通過springboot+mybatis+druid配置動態(tài)數(shù)據(jù)源

    這篇文章主要介紹了通過springboot+mybatis+druid配置動態(tài)數(shù)據(jù)源,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,,需要的朋友可以參考下
    2019-06-06
  • ElasticSearch學習之Es索引Api操作

    ElasticSearch學習之Es索引Api操作

    這篇文章主要為大家介紹了ElasticSearch學習之Es索引Api操作詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01

最新評論