POI-處理大Excel文件(xlsx寫)


之前介紹了如何讀取較大文件的excel文件,但是都無法進行文件的寫入操作,在寫大文件的情況下就會出現oom

錯誤模擬

同之前一樣,設置heap大小為100m用於模擬,之后創建簡單的方法來創建一個大的xlsx

    public static void main(String[] args) throws Exception {
        // 創建Excel的工作書冊 Workbook,對應到一個excel文檔
        XSSFWorkbook wb = new XSSFWorkbook();

        // 創建Excel的工作sheet,對應到一個excel文檔的tab
        XSSFSheet sheet = wb.createSheet("sheet1");

        for (int i = 0; i < 100000; i++) {
            // 創建Excel的sheet的一行
            XSSFRow row = sheet.createRow(i);
            // 創建一個Excel的單元格
            XSSFCell cell = row.createCell(0);
            // 給Excel的單元格設置樣式和賦值
            cell.setCellValue("hello world");
        }
        FileOutputStream os = new FileOutputStream("d:\\workbook.xlsx");
        wb.write(os);
        os.close();
    }

運行之后就將報出oom

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
	at org.apache.xmlbeans.impl.store.Cur$Locations.<init>(Cur.java:493)
	at org.apache.xmlbeans.impl.store.Locale.<init>(Locale.java:168)
	at org.apache.xmlbeans.impl.store.Locale.getLocale(Locale.java:242)
	at org.apache.xmlbeans.impl.store.Locale.newInstance(Locale.java:593)
	at org.apache.xmlbeans.impl.schema.SchemaTypeLoaderBase.newInstance(SchemaTypeLoaderBase.java:198)
	at org.apache.poi.POIXMLTypeLoader.newInstance(POIXMLTypeLoader.java:84)
	at org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst$Factory.newInstance(Unknown Source)
	at org.apache.poi.xssf.usermodel.XSSFRichTextString.<init>(XSSFRichTextString.java:87)
	at org.apache.poi.xssf.usermodel.XSSFCell.setCellValue(XSSFCell.java:426)
	at blog.excel.WriteXls.main(WriteXls.java:28)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

SXSSF

POI提供了SXSSF的方式可以流式的創建十分大的xlsx文件,SXSSF使用了window的概念,如果數據行已經超出window的范圍,那么就無法修改其內容。
這個窗口的大小可以在構造函數中設定new SXSSFWorkbook(int windowSize) 也可以在sheet中設定SXSSFSheet#setRandomAccessWindowSize(int windowSize),其默認值為SXSSFWorkbook.DEFAULT_WINDOW_SIZE(100)。
還要注意SXSSF會創建一些臨時文件這個需要在finally中顯示地通過調用dispose方法清除,而且臨時文件也占用一定硬盤,可以通過wb.setCompressTempFiles(true)設置workbook的臨時文件使用壓縮來減少硬盤占用。下面是的一個例子:

    public static void main(String[] args)  {
        SXSSFWorkbook wb =  null;
        try {
            wb=new SXSSFWorkbook();
            Sheet sh = wb.createSheet();
            for (int rownum = 0; rownum < 1000000; rownum++) {
                Row row = sh.createRow(rownum);
                for (int cellnum = 0; cellnum < 10; cellnum++) {
                    Cell cell = row.createCell(cellnum);
                    String address = new CellReference(cell).formatAsString();
                    cell.setCellValue(address);
                }
            }

            FileOutputStream out = new FileOutputStream("d://xsxxf.xlsx");
            wb.write(out);
            out.close();
        }catch (Exception ex){
            ex.printStackTrace();
        } finally {
            // 刪除臨時文件
            if(wb!=null){
                wb.dispose();
            }
        }
    }

通過上面的操作可以產生大型的xlsx文件。SXXSF可以使用原有的xssf,這里創建一個template.xlsx第一個單元格輸入內容

    public static void main(String[] args)  {
        SXSSFWorkbook wb =  null;
        try {
			InputStream is = new FileInputStream("d://template.xlsx");
			XSSFWorkbook xwb = new XSSFWorkbook(is);
			wb=new SXSSFWorkbook(xwb);
			Sheet sh = wb.getSheet("Sheet1");
			System.out.println(sh.getRow(0));
		}catch (Exception ex){
            ex.printStackTrace();
        } finally {
            // 刪除臨時文件
            if(wb!=null){
                wb.dispose();
            }
        }
	}
--------------輸出---------------
null

這里取出的第一行是null,無法讀取原文件,但是可以追加文件

    public static void main(String[] args)  {
        SXSSFWorkbook wb =  null;
        try {
            InputStream is = new FileInputStream("d://template.xlsx");
            XSSFWorkbook xwb = new XSSFWorkbook(is);
            wb=new SXSSFWorkbook(xwb);
            Sheet sh = wb.getSheet("Sheet1");
            for (int rownum =1; rownum < 1000000; rownum++) {//這里必須從template之后的行開始寫,不然會報出“Attempting to write a row[X] in the range [X,X] that is already written to disk.”
                Row row = sh.createRow(rownum);
                for (int cellnum = 0; cellnum < 10; cellnum++) {
                    Cell cell = row.createCell(cellnum);
                    String address = new CellReference(cell).formatAsString();
                    cell.setCellValue(address);
                }
            }
            FileOutputStream out = new FileOutputStream("d://xsxxf.xlsx");
            wb.write(out);
            out.close();
        }catch (Exception ex){
            ex.printStackTrace();
        } finally {
            // 刪除臨時文件
            if(wb!=null){
                wb.dispose();
            }
        }
    }

總結

SXSSF可以解決寫大文件的問題,但是無法進行修改文件原有的內容,也不支持讀源文件。如果需要,可以結合之前的讀大文件,然后將讀到的內容通過SXSSF寫入新的文件,來達到類似修改的操作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM