电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

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

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

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

1 Excel上傳

針對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(); // 判斷文件類型 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; }

上面的源碼我們可以看見有一個saveFile方法,這個方法是將文件存在服務(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ù),對sheet的一行元素進(jìn)行操作,元素為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標(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)簽名稱為 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)到達(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(); } //針對沒有存入的數(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; }

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

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

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

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

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

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

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

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

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

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

再加上查詢SQL的效率問題,導(dǎo)致請求超時。

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

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

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

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

再次優(yōu)化:

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

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

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

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

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

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

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

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

標(biāo)簽: excel
相關(guān)文章:
主站蜘蛛池模板: 重庆网站建设,重庆网站设计,重庆网站制作,重庆seo,重庆做网站,重庆seo,重庆公众号运营,重庆小程序开发 | 中高频感应加热设备|高频淬火设备|超音频感应加热电源|不锈钢管光亮退火机|真空管烤消设备 - 郑州蓝硕工业炉设备有限公司 | 硫酸钡厂家_高光沉淀硫酸钡价格-河南钡丰化工有限公司 | 权威废金属|废塑料|废纸|废铜|废钢价格|再生资源回收行情报价中心-中废网 | 亚洲工业智能制造领域专业门户网站 - 亚洲自动化与机器人网 | 泰国专线_泰国物流专线_广州到泰国物流公司-泰廊曼国际 | 微型实验室真空泵-无油干式真空泵-微型涡旋耐腐蚀压缩机-思科涡旋科技(杭州)有限公司 | 酒糟烘干机-豆渣烘干机-薯渣烘干机-糟渣烘干设备厂家-焦作市真节能环保设备科技有限公司 | 水稻烘干机,小麦烘干机,大豆烘干机,玉米烘干机,粮食烘干机_巩义市锦华粮食烘干机械制造有限公司 水环真空泵厂家,2bv真空泵,2be真空泵-淄博真空设备厂 | 蜘蛛车-高空作业平台-升降机-高空作业车租赁-臂式伸缩臂叉装车-登高车出租厂家 - 普雷斯特机械设备(北京)有限公司 | CE认证_产品欧盟ROHS-REACH检测机构-商通检测 | 众品家具网-家具品牌招商_家具代理加盟_家具门户的首选网络媒体。 | 北京租车公司_汽车/客车/班车/大巴车租赁_商务会议/展会用车/旅游大巴出租_北京桐顺创业租车公司 | 高清视频编码器,4K音视频编解码器,直播编码器,流媒体服务器,深圳海威视讯技术有限公司 | 南京泽朗生物科技有限公司-液体饮料代加工_果汁饮料代加工_固体饮料代加工 | 板框压滤机-隔膜压滤机-厢式压滤机生产厂家-禹州市君工机械设备有限公司 | 无味渗透剂,泡沫抑尘剂,烷基糖苷-威海威能化工有限公司 | 道达尔润滑油-食品级润滑油-道达尔导热油-合成导热油,深圳道达尔代理商合-深圳浩方正大官网 | 门禁卡_智能IC卡_滴胶卡制作_硅胶腕带-卡立方rfid定制厂家 | 超声波焊接机_超音波熔接机_超声波塑焊机十大品牌_塑料超声波焊接设备厂家 | 不锈钢螺丝 - 六角螺丝厂家 - 不锈钢紧固件 - 万千紧固件--紧固件一站式采购 | 免费分销系统 — 分销商城系统_分销小程序开发 -【微商来】 | 大功率金属激光焊接机价格_不锈钢汽车配件|光纤自动激光焊接机设备-东莞市正信激光科技有限公司 定制奶茶纸杯_定制豆浆杯_广东纸杯厂_[绿保佳]一家专业生产纸杯碗的厂家 | 烟气在线监测系统_烟气在线监测仪_扬尘检测仪_空气质量监测站「山东风途物联网」 | 电子厂招聘_工厂招聘_普工招聘_小时工招聘信息平台-众立方招工网 | 跨境物流_美国卡派_中大件运输_尾程派送_海外仓一件代发 - 广州环至美供应链平台 | 酒店品牌设计-酒店vi设计-酒店标识设计【国际级】VI策划公司 | 锂辉石检测仪器,水泥成分快速分析仪-湘潭宇科分析仪器有限公司 | b2b网站大全,b2b网站排名,找b2b网站就上地球网 | 对夹式止回阀_对夹式蝶形止回阀_对夹式软密封止回阀_超薄型止回阀_不锈钢底阀-温州上炬阀门科技有限公司 | 杭州成人高考_浙江省成人高考网上报名 | 渣油泵,KCB齿轮泵,不锈钢齿轮泵,重油泵,煤焦油泵,泊头市泰邦泵阀制造有限公司 | 赛默飞Thermo veritiproPCR仪|ProFlex3 x 32PCR系统|Countess3细胞计数仪|371|3111二氧化碳培养箱|Mirco17R|Mirco21R离心机|仟诺生物 | 除尘器布袋骨架,除尘器滤袋,除尘器骨架,电磁脉冲阀膜片,卸灰阀,螺旋输送机-泊头市天润环保机械设备有限公司 | 微妙网,专业的动画师、特效师、CG模型设计师网站! - wmiao.com 超声波电磁流量计-液位计-孔板流量计-料位计-江苏信仪自动化仪表有限公司 | 电动葫芦|环链电动葫芦-北京凌鹰名优起重葫芦 | 金属清洗剂,防锈油,切削液,磨削液-青岛朗力防锈材料有限公司 | 自动检重秤-动态称重机-重量分选秤-苏州金钻称重设备系统开发有限公司 | 铝合金重力铸造_铝合金翻砂铸造_铝铸件厂家-东莞市铝得旺五金制品有限公司 | 卷筒电缆-拖链电缆-特种柔性扁平电缆定制厂家「上海缆胜」 | 喷砂机厂家_自动除锈抛丸机价格-成都泰盛吉自动化喷砂设备 |