之前介紹了如何讀取較大文件的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寫入新的文件,來達到類似修改的操作。