• 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写入新的文件,来达到类似修改的操作。

  • 相关阅读:
    django学习----http协议
    线程进程和协程
    内置函数
    我所了解的递归
    函数的闭包和装饰器
    字符串的内置方法
    格式化输入输出
    逻辑运算符和操作运算符
    python流程控制
    计算机网络原理
  • 原文地址:https://www.cnblogs.com/resentment/p/6414210.html
Copyright © 2020-2023  润新知