POI解析多excel多sheet檔案(單檔案百萬級以下)生成指定檔案入Hive
臨下班前有個需求,有個同事有一份excel資料需要匯入到hive中,到手後發現需要匯入的excel檔案有5個,且每個excel有60個sheet,每個sheet檔案是頂行的,由於檔案是xls格式的,單excel檔案資料量大概在390萬左右,且sheet表有的有標題,有的是空行,且有的sheet要解析有的不要。
直接用poi解析xls格式形式進行解析,結果在new HSSFWorkbook(inputStream)這一步對輸入檔案流進行裝載的時候發生記憶體問題(java.lang.OutOfMemoryError:Javaheapspace (堆記憶體溢位) java.lang.OutOfMemoryError:GCoverheadlimitexceeded (當垃圾回收器釋放空間佔用較多時間時丟擲))無法進行下部解析,嘗試轉為xlsx格式,同樣發生類似的問題。
在網上檢視大資料量解析excel博文,發現excel2007以上版有OPCPackage包能進行解析,理是根據行號範圍批量將內容加入到記憶體中非一次性加入,這樣就解決了記憶體不足的問題。但是,嘗試了幾篇網上的例子均不能原執行成功,因為那哥們這件事挺急需要第二天給智博會演示相關資料,也就沒在這種方法上繼續進行嘗試,轉而尋求更快捷方式,將單個檔案的sheet數降為10個(資料量在六十幾萬),這樣再搭配多執行緒就完美匯出檔案了,再上傳到hdfs上驗證結束。
所需依賴包:
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.9</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-excelant</artifactId> <version>3.9</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.9</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>3.9</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>3.9</version> </dependency>
解析檔案:
package com.ali.scheduler.util; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; public class ReadExcel { private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) throws IOException { ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); final List<File> files = getFileList("D:/阿里-拆分表/"); Date date = new Date(); System.out.println("startdate-->"+sdf.format(date)); long startTime = date.getTime(); for (int i = 0; i < files.size(); i++) { final int index = i; fixedThreadPool.execute(new Runnable() { public void run() { try { parseExcel(files.get(index).getAbsolutePath()); } catch (Exception e) { System.err.println("["+files.get(index)+"]檔案處理異常!\n"+e.getMessage()); } } }); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } fixedThreadPool.shutdown(); while (true) {//等待所有任務都執行結束 if (fixedThreadPool.isTerminated()) {//所有的子執行緒都結束了 System.out.println("共耗時:"+(System.currentTimeMillis()-startTime)/1000.0+"s"); break; } } // System.out.println(getFileList("D:/阿里/")); } public static void parseExcel(String filePath) throws Exception{ // String filePath = "D:/阿里/test.xls"; boolean isExcel2003 = filePath.toLowerCase().endsWith("xls")?true:false; int sheetNum = 0;//工作區間 List<Object[]> datas = new ArrayList<Object[]>();//用來存資料 Date date = new Date(); System.out.println(filePath+"startdate-->"+sdf.format(date)); String fName=new File(filePath).getName(); fName = fName.substring(0,fName.lastIndexOf(".")); if(isExcel2003){ datas = readXLS(filePath, sheetNum,date,fName); }else{ datas = readXLSX(filePath, sheetNum,date,fName); } // System.out.println(datas); try { exportFile(datas,new File("D:/pinganfile/result/"+fName)); } catch (Exception e) { e.printStackTrace(); } } public static List<File> getFileList(String strPath) { File dir = new File(strPath); File[] files = dir.listFiles(); // 該檔案目錄下檔案全部放入陣列 List<File> filelist = new ArrayList<>(); if (files != null) { for (int i = 0; i < files.length; i++) { // String fileName = files[i].getName(); if (files[i].isDirectory()) { // 判斷是檔案還是資料夾 getFileList(files[i].getAbsolutePath()); // 獲取檔案絕對路徑 } else{ // String strFileName = files[i].getAbsolutePath(); filelist.add(files[i]); } } } return filelist; } private static int exportFile(List<Object[]> datas, File file) throws Exception { BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "utf-8"));// 附加 // 新增資料 int index = 0; StringBuffer sb = new StringBuffer(); for (int i = 0; i < datas.size(); i++) { Object[] data =datas.get(i); for(int j=0;j<data.length;j++){ sb.append(data[j]+Constant.COLUMN_DELIMITER);//\177 } bw.write(sb.toString()); sb.setLength(0); bw.newLine(); if (index % 50 == 0) { bw.flush(); } } bw.close(); return index; } private static List<Object[]> readXLS(String filePath, int sheetNum,Date date,String fName) throws IOException { FileInputStream inputStream = new FileInputStream(filePath); System.out.println(fName+"輸入流封裝用時:"+((System.currentTimeMillis()-date.getTime())/1000.0)+"s"); HSSFWorkbook workbook = new HSSFWorkbook(inputStream); System.out.println(fName+"輸入流裝載WorkBook用時:"+((System.currentTimeMillis()-date.getTime())/1000.0)+"s"); List<Object[]> datas = new ArrayList<Object[]>();//用來存資料 /*** step1: 獲取Excel的工作區間總數*/ int sheetNo = workbook.getNumberOfSheets();//取得工作區間的個數 System.out.println(fName+"共有sheet數:"+sheetNo); for (int i = 0; i < sheetNo; i++) { // if (i != sheetNum) {//判斷是否為需要取得工作區間 // continue; // } /*** step2:取得所需工作區間(下標從0開始) */ HSSFSheet sheet = workbook.getSheetAt(i); if (sheet == null || sheet.getSheetName().toUpperCase().equals("SQL")) { return datas; } /*** step3:getPhysicalNumberOfRows獲取總共有多少行資料因為中間空行的話,則讀取出來的資料不準確 */ // int hasRowNum = sheet.getPhysicalNumberOfRows(); /** 獲取的是最後一行的編號(編號從0開始)。*/ int hasRowNum = sheet.getLastRowNum()+1; if(hasRowNum == 0){//sheet中所有行都沒有內容 System.out.println("["+fName+"]"+sheet.getSheetName()+"共有"+(hasRowNum)+"條資料需要處理"); return datas; }else{ System.out.println("["+fName+"]"+sheet.getSheetName()+"共有"+(hasRowNum-1)+"條資料需要處理"); } //已經處理了的行數 int procssedNum = 0; //預設從第二行讀取(第一行表頭或空行不讀) int jj=1; //指定檔名從第三行讀取 if(fName.endsWith("_1") && i==0){ jj=2; } for (int j = jj;j<hasRowNum ; j++) { /** step4: 獲取每一行 */ HSSFRow row = sheet.getRow(j); /** step5 : 去除空行 */ if (row != null) { /** step6: 獲取每一行的長度 */ int length = row.getLastCellNum(); if (length > 0) { Object[] data = new Object[length];//定義一個集合,裝每一行的數值 for (int m = 0; m < length; m++) { /** step7: 獲取每一行的每一列的值 */ if(row.getCell(m).getCellType()==HSSFCell.CELL_TYPE_NUMERIC){ data[m] = Double.valueOf(row.getCell(m).getNumericCellValue()).intValue(); }else{ data[m] = row.getCell(m); } } /** step8: 存資料 */ datas.add(data); } procssedNum++; if(procssedNum%5000==0){ System.out.println("["+fName+"]"+sheet.getSheetName()+"已處理 "+procssedNum+" 條資料!"); } } } } System.out.println("讀取"+fName+"WorkBook內容用時:"+((System.currentTimeMillis()-date.getTime())/1000.0)+"s"); /** step9: 關閉輸入流 */ inputStream.close(); /** step10: 返回資料 */ return datas; } private static List<Object[]> readXLSX(String filePath, int sheetNum,Date date,String fName) throws IOException { FileInputStream inputStream = new FileInputStream(new File(filePath)); System.out.println(fName+"輸入流封裝用時:"+((System.currentTimeMillis()-date.getTime())/1000.0)+"s"); XSSFWorkbook workbook = new XSSFWorkbook(inputStream); System.out.println(fName+"輸入流裝載WorkBook用時:"+((System.currentTimeMillis()-date.getTime())/1000.0)+"s"); List<Object[]> datas = new ArrayList<Object[]>();//定義一個list用來存資料 /*** step1: 獲取Excel的工作區間的數量*/ int sheetNo = workbook.getNumberOfSheets(); for(int i=0;i<sheetNo;i++){ // if(i != sheetNum){ // continue; // } /** step2: 獲取某一工作區間 */ XSSFSheet sheet = workbook.getSheetAt(i); if(sheet == null || sheet.getSheetName().toUpperCase().equals("SQL")){ return datas; } /*** step3:getPhysicalNumberOfRows獲取總共有多少行資料因為中間空行的話,則讀取出來的資料不準確 */ // int hasRowNum = sheet.getPhysicalNumberOfRows(); /** 獲取的是最後一行的編號(編號從0開始)。*/ int hasRowNum = sheet.getLastRowNum()+1; if(hasRowNum == 0){//sheet中所有行都沒有內容 System.out.println("["+fName+"]"+sheet.getSheetName()+"共有"+(hasRowNum)+"條資料需要處理"); return datas; }else{ System.out.println("["+fName+"]"+sheet.getSheetName()+"共有"+(hasRowNum-1)+"條資料需要處理"); } //已經處理了的行數 int procssedNum = 0; //預設從第二行讀取(第一行表頭或空行不讀) int jj=1; //指定檔名從第三行讀取 if(fName.endsWith("_1") && i==0){ jj=2; } /** step4: 取每一行的資料 */ for(int j=jj;j<hasRowNum;j++){ XSSFRow row = sheet.getRow(j); /** step5: 去空行 */ if(row == null){ continue; } /** step6: 取每一行的長度 */ int length = row.getLastCellNum(); Object[] data = new Object[length];//定義一個數組用來存資料 /** step7: 取每一列的資料 */ for(int k=0; k<length; k++){ XSSFCell cell = row.getCell(k); if(cell.getCellType()==XSSFCell.CELL_TYPE_NUMERIC){ data[k] = Double.valueOf(cell.getNumericCellValue()).intValue(); }else{ data[k] = cell; } } /** step8: 存資料 */ datas.add(data); procssedNum++; if(procssedNum%5000==0){ System.out.println("["+fName+"]"+sheet.getSheetName()+"已處理 "+procssedNum+" 條資料!"); } } /** step9: 關閉輸入流 */ inputStream.close(); } System.out.println("["+fName+"]"+"共耗時:"+(System.currentTimeMillis()-date.getTime())/1000.0+"s"); /** step10: 返回資料 */ return datas; } }
完。
相關推薦
POI解析多excel多sheet檔案(單檔案百萬級以下)生成指定檔案入Hive
臨下班前有個需求,有個同事有一份excel資料需要匯入到hive中,到手後發現需要匯入的excel檔案有5個,且每個excel有60個sheet,每個sheet檔案是頂行的,由於檔案是xls格式的,單excel檔案資料量大概在390萬左右,且shee
Apache POI讀寫Excel文件入門(支援XLS和XLSX格式)
值得一提的是,POI的全稱是Poor Obfuscation Implementation,意為“簡陋又模糊的實現”,這和slf4j(Simple Log Facade for Java,Java簡單日誌門面)的取名有異曲同工之妙。這兩個東西實際上是非常強大的,但是它們的作者卻說自己的東西很poor、很sim
為什麼我在eclipse中新建一個java web專案的時候多出了幾個檔案(Jax-Ws-Web Services 等等)我原先的項
如截圖所示,可能是因為選擇的檢視為JAVAEE所以就會出現下面的情況 檢視切換java沒估計原專案面搞web service 只需要開啟Java檢視即可 window選單--->Open
Android連線伺服器,從伺服器獲取資料,以及從伺服器下載檔案(單,多執行緒)
首先需要在Eclipse中建立一個伺服器,在其中存入要下載的檔案,具體可參考之前的伺服器篇。 ScollView可以上下滑動 另外還有,android中的網路連線與之前java中可以通用,可以參照之前伺服器客戶端通訊篇。 新增的許可權
python3.5進階(三)-------------實現多工之協程(生成器,迭代器)
1.迭代器:迭代是訪問集合元素的一種方式,迭代器是可以記住遍歷的位置的物件,迭代器物件從集合的第一個元素開始訪問,直到所有訪問結束,迭代器只能前進不能後退。判斷一個數據型別是否可以迭代,看是否能for迴圈。如(字串,列表,元祖...)序列可以迭代,數字不能迭代,或通過isintance([11,12
Poi解析對比excel表格
##前言 這次不是Android的技術分享,是java的,當然把poi的程式碼放到Android中也可以用,畢竟同源嘛 為啥會有這個文章呢,因為我老婆是會計嘛,她有時候會讓我幫忙對賬,兩個excel檔案,順序也不同,需要我來對比出哪裡有問題,也就是數不太對應,我想了一下,如果好幾百個
java解析json檔案(省,市,區)
[{"code":"11","name":"北京市"},{"code":"12","name":"天津市"},{"code":"13","name":"河北省"},{"code":"14","name":"山西省"},{"code":"15","name":"內蒙古自治區"},{"code":"21","na
MODE ——計算了 任意多個數字的平均值(知識點:for的迴圈)
問題描述: 輸入浮點數值 判斷是否繼續輸入 輸出N時候 退出for迴圈 計算出來 輸入所有數字的平均值 執行結果: [[email protected] C]# ./a.out Th
Redis多機資料庫的實現(叢集、複製、sentinel)
1.複製 Redis中,使用者通過執行slaveof命令或者設定slaveof選項,讓一個伺服器去複製另外一個伺服器,被複制的伺服器為主伺服器,對主伺服器進行復制的伺服器稱為從伺服器。 舊版本複製功能分為:1)同步:將從伺服器
獲取多選下拉框(select標籤設定multiple屬性)的值
<select multiple>不能直接獲取value,需要藉助該元素的options屬性。如下: <select id="select" multiple> <option value="1">1111</option> &
多執行緒:中斷(interrupt、interrupted、executor)
一個執行緒執行完畢之後會自動結束,如果在執行過程中發生異常也會提前結束。 InterruptedException 通過呼叫一個執行緒的 interrupt() 來中斷該執行緒,如果該執行緒處於阻塞、限期等待或者無限期等待狀態,那麼就會丟擲 InterruptedException,從而
多人遊戲對戰技術(坦克大戰、狀態同步)
用狀態同步的方式實現一個坦克大戰的小遊戲,這也是一次全新的嘗試,從遊戲的效果來看,在正常的網路速度下效果符合預期。這裡跟大家分享下游戲客戶端中用到的關鍵技術點。 一、 同步方式的選擇,狀態同步or 幀同步? 狀態同步: 同步的是遊戲中的各種狀態,遊戲
2018牛客暑期多校第二場J(二維陣列陣列+hash)
題面:farm White Rabbit has a rectangular farmland of n*m. In each of the grid there is a kind of plant. The plant in the j-th column of t
JavaScript將頁面表格資料匯出為Excel、CSV格式檔案(結合JQuery EasyUI的grid )
function Prints() { //獲取grid 資料 var data = JSON.stringify($('#datagrid').datagrid('getData').rows); //ale
牛客暑假多校——A-Ternary String(找規律+尤拉降冪)(模板)
題目描述 A ternary string is a sequence of digits, where each digit is either 0, 1, or 2. Chiaki has a ternary string s which can self-repr
android 使用epublib開源框架解析epub檔案(章節內容、書籍選單)
前期準備 jsoup(可以可把html標籤,解析為物件): 進入正題 如果你是吧 .equb 格式的檔案放到 assets 檔案下,你可以這樣獲取book物件。 MainActivity.java import android.cont
分享一個仿微信多選圖cordova外掛(相簿內帶相機功能)
上面的外掛有些問題: 1,android沒有相簿目錄,而且如果相簿圖片過多的使用者開啟相簿需要載入很長時間。 2,ios上選圖介面不自適應。 3,相簿內沒有相機拍照入口。 以上問題並不能滿足我的需求,所以自己打算寫一個外掛敬上。 仿微信多選圖cordov
Python 讀寫操作Excel —— 安裝第三方庫(xlrd、xlwt、xlutils)
保存數據 下載 實用 第三方 直接 install pytho 方法 xls 數據處理是 Python 的一大應用場景,而 Excel 則是最流行的數據處理軟件。因此用 Python 進行數據相關的工作時,難免要和 Excel 打交道。 如果僅僅是要以表單形式保存數據,可
Pandas讀取檔案(read_csv與read_table 的區別)
pandas 載入檔案方式: 注意,read_csv和read_table都是是載入帶分隔符的資料,每一個分隔符作為一個數據的標誌,但二者讀出來的資料格式還是不一樣的,read_table是以製表符 \t 作為資料的標誌,也就是以行為單位進行儲存。 read_cs
IIS7的整合模式下如何讓自定義的HttpModule不處理靜態檔案(.html .css .js .jpeg等)請求
轉載:https://www.cnblogs.com/opencoder/p/5854454.html ASP.NET 4.0後Web.config檔案的Module配置節點有一個可選項叫preCondition如下面程式碼所示: <system.webServer> <mod