1. 程式人生 > >POI解析多excel多sheet檔案(單檔案百萬級以下)生成指定檔案入Hive

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解析excelsheet檔案檔案百萬以下生成指定檔案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