亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

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

瀏覽:82日期:2022-06-16 14:23:09

1 Excel上傳

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

@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(); // 判斷文件類(lèi)型 String realPath = request.getSession().getServletContext().getRealPath('/'); String trueFileName = fileName; // 設(shè)置存放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; }

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

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<>(); //即將進(jìn)行批量插入的數(shù)據(jù) public int willSaveAmount; //將要插入的數(shù)據(jù)量 public int totalSavedAmount; //總共插入了多少數(shù)據(jù) private int curRow = 0; //當(dāng)前行 private int curCol = 0; //當(dāng)前列索引 private int preCol = 0; //上一列列索引 private int titleRow = 0; //標(biāo)題行,一般情況下為0 public int rowsize = 0; //列數(shù) //excel記錄行操作方法,以sheet索引,行索引和行元素列表為參數(shù),對(duì)sheet的一行元素進(jìn)行操作,元素為String類(lèi)型 public abstract void optRows(int sheetIndex, int curRow, List<String> rowlist) throws SQLException; //只遍歷一個(gè)sheet,其中sheetId為要遍歷的sheet索引,從1開(kāi)始,1-3 /** * @param filename * @param sheetId sheetId為要遍歷的sheet索引,從1開(kāi)始,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的索引值的到單元格的真正要存儲(chǔ)的字符串 try { int idx = Integer.parseInt(lastContents); lastContents = new XSSFRichTextString(sst.getEntryAt(idx)) .toString(); } catch (Exception e) { } // v => 單元格的值,如果單元格是字符串則v標(biāo)簽的值為該字符串在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 { //如果標(biāo)簽名稱(chēng)為 row ,這說(shuō)明已到行尾,調(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ù)處理

首先我們將源碼展示出來(lái),然后再具體說(shuō)明

public int addBlackLists(String file) throws ExecutionException, InterruptedException { ArrayList<Future<Integer>> resultList = new ArrayList<>(); XxlsAbstract xxlsAbstract = new XxlsAbstract() { //針對(duì)數(shù)據(jù)的具體處理 @Override public void optRows(int sheetIndex, int curRow, List<String> rowlist) { /** * 判斷即將插入的數(shù)據(jù)是否已經(jīng)到達(dá)8000,如果到達(dá)8000, * 進(jìn)行數(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(); } //針對(duì)沒(méi)有存入的數(shù)據(jù)進(jìn)行處理 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; }

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

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

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

補(bǔ)充知識(shí):Java API SXSSFWorkbook導(dǎo)出Excel大批量數(shù)據(jù)(百萬(wàn)級(jí))解決導(dǎo)出超時(shí)

之前使用簡(jiǎn)單的HSSFWorkbook,導(dǎo)出的數(shù)據(jù)不能超過(guò)

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

后來(lái)改成SXSSFWorkbook之后可以導(dǎo)出更多,但是

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

之前的代碼是一次性查出所有的結(jié)果,list里面存了幾十萬(wàn)條數(shù)據(jù)。因?yàn)楣δ茉O(shè)計(jì)的問(wèn)題,我這一個(gè)接口要同時(shí)處理三個(gè)功能:

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

再加上查詢(xún)SQL的效率問(wèn)題,導(dǎo)致請(qǐng)求超時(shí)。

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

其實(shí)就是分批次處理查詢(xún)結(jié)果:

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

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

再次優(yōu)化:

上面這樣做雖然可以導(dǎo)出,但是代碼看起來(lái)不美觀:

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

這樣看起來(lái)就簡(jiǎn)潔很多了。

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

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

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

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

以上這篇使用Springboot+poi上傳并處理百萬(wàn)級(jí)數(shù)據(jù)EXCEL就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持好吧啦網(wǎng)。

標(biāo)簽: excel
相關(guān)文章:
主站蜘蛛池模板: 日本a及毛片免费视频 | 成人亲子乱子伦视频 | 91短视频在线观看免费最新 | 黄色网址哪里有 | 国产精品乱码在线观看 | 亚洲欧美激情综合首页 | 香蕉视频网站免费观视频 | 欧美做爰xxxⅹ在线视频hd | 欧美一级毛片久久精品 | 免费一级大毛片a一观看不卡 | 亚洲十欧美十日韩十国产 | 美国特级a毛片免费网站 | 在线观看精品国内福利视频 | 高h喷水荡肉爽文1v1 | 美女在线看永久免费网址 | 日韩久久精品视频 | 亚洲激情视频图片 | 免费国产成人手机在线观看 | 欧美在线免费观看 | 国产亚洲精品一区二区在线观看 | 青青青免费网站在线观看 | 国产精品videossex激情 | 国产99视频精品免视看7 | 亚洲福利在线观看 | 在线观看日韩精品 | 色多多成视频人在线观看 | 成人看片黄a免费看视频 | 欧美久色 | 41sao.can在线观看国产 | 国产破处在线视频 | 国产亚洲精品资源一区 | 国产成人19禁在线观看 | 亚洲精品99久久久久久欧美版 | 在线成人福利视频 | 国产www在线观看 | 亚洲第一视频网站 | 欧美18性欧美丶黑吊 | 久久久精品久久久久特色影视 | 国产盗摄福利视频 | 免费看一级黄色毛片 | 免费涩涩在线视频网 |