之前介绍了如何读取较大文件的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写入新的文件,来达到类似修改的操作。