簡體   English   中英

如何在java中通過Apache POI Excel輸出大型csv文件?

[英]How to output large csv file through Apache POI Excel in java?

努力通過 Apache POI java 將 300k 行寫入 csv 文件。 我一直在嘗試從一個 30 萬行的 excel 文件生成一個 csv 文件。 每次,當它嘗試寫入輸出 csv 文件時,我都會收到 GCOutMemory 錯誤。 我什至嘗試為每 10 萬行拆分寫入。 輸出文件大小不斷增長,但我沒有看到 system.println 語句沒有被打印出來。

import javafx.beans.binding.StringBinding;
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryType;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ReadWrite {
    private static Logger logger= LoggerFactory.getLogger(ReadWrite.class);

    public static void main(String[] args) {
        try {
            long startReading = System.currentTimeMillis();
            Path path = Paths.get("/Users/venkatesh/Documents/Citiout_files/citiout300k_2sheets.xlsx");

            byte[] result = new byte[0];
            try {
                result = Files.readAllBytes(path);
            } catch (IOException e) {
                e.printStackTrace();
            }
            InputStream is = new ByteArrayInputStream(result);

            Workbook workbook = WorkbookFactory.create(is);

            long readDone = System.currentTimeMillis() - startReading;
            logger.info("read time " + readDone);



            Sheet sheet = workbook.getSheetAt(1);
            Row firstRow = sheet.getRow(0);
            int headcol = firstRow.getLastCellNum();
            long startTransform = System.currentTimeMillis();
            firstRow.createCell(headcol++).setCellValue("Sold Amount1");
            firstRow.createCell(headcol++).setCellValue("CF_Quantity1");
            firstRow.createCell(headcol++).setCellValue("CF_Quantity2");
            firstRow.createCell(headcol++).setCellValue("CF_TradePrice");
            firstRow.createCell(headcol++).setCellValue("CF_ForwardPrice");
            firstRow.createCell(headcol++).setCellValue("CF_UnrealizedPL");
            firstRow.createCell(headcol++).setCellValue("CF_Quantity1Round");
            firstRow.createCell(headcol++).setCellValue("CF_Quantity2Round");
            firstRow.createCell(headcol++).setCellValue("CF_FXLotKeyNoTradeDate");
            firstRow.createCell(headcol++).setCellValue("CF_FXRoundedKeyNoTradeDate");
            firstRow.createCell(headcol++).setCellValue("CF_SettlementDate");
            for (int i = 1; i <=sheet.getLastRowNum()+1; i++) {
                String jj="";
                Row nRow = sheet.getRow(i-1);
                for(Cell c:nRow) {
                    if (c.getColumnIndex()==3 && i!=1) {
                        Calendar cal = Calendar.getInstance();
                        Date date1 = new SimpleDateFormat("dd-MMM-yyyy").parse(c.getStringCellValue());
                        cal.setTime(date1);
                        jj = String.valueOf(cal.get(Calendar.MONTH)+1) + "/" + String.valueOf(cal.get(Calendar.DAY_OF_MONTH)) + "/" + String.valueOf(cal.get(Calendar.YEAR));
                    }
                }
                int count = nRow.getLastCellNum();
                //System.out.println(nRow.getCell(3).getClass());
                nRow.createCell(count++).setCellFormula("G" + i + "*-1");
                nRow.createCell(count++).setCellFormula("E" + i + "/" + "G" + i);
                nRow.createCell(count++).setCellFormula("G" + i + "/E" + i);
                nRow.createCell(count++).setCellFormula("ROUND(ABS(T" + i + "/S" + i + "),6)");
                nRow.createCell(count++).setCellFormula("ROUND(K" + i + ",6)");
                nRow.createCell(count++).setCellFormula("ROUND(N" + i + ",2)");
                nRow.createCell(count++).setCellFormula("ROUND(S" + i + ",0)");
                nRow.createCell(count++).setCellFormula("ROUND(T" + i + ",0)");
                nRow.createCell(count++).setCellFormula("CONCATENATE(T" + i + "," + "\"~\"" + ",S" + i + ")");
                nRow.createCell(count++).setCellFormula("CONCATENATE(X" + i + "," + "\"~\"" + ",Y" + i + ")");
                nRow.createCell(count++).setCellValue(jj);
                c.setCellValue(DateUtil.getExcelDate(calendar.getTime()));

            }
            long endTransform = System.currentTimeMillis() - startTransform;
            System.out.println("Transformations time " + endTransform);
            final FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
            FileWriter writer= new FileWriter(new enter code hereFile("/Users/venkatesh/Documents/cit300k.csv"));
            StringBuilder data = new StringBuilder();
            Iterator<Row> rowIterator = workbook.getSheetAt(1).iterator();

            try {
                while (rowIterator.hasNext()) {

                    Row row = rowIterator.next();


                    Iterator<Cell> cellIterator = row.cellIterator();
                    while (cellIterator.hasNext()) {
                        Cell cell = cellIterator.next();

                        CellType type = cell.getCellType();
                        if (type == CellType.BOOLEAN) {
                            data.append(cell.getBooleanCellValue());
                        } else if (type == CellType.NUMERIC) {
                            data.append(cell.getNumericCellValue());

                        } else if (type == CellType.STRING) {
                            data.append(cell.getStringCellValue());
                        } else if (type == CellType.FORMULA) {
                            switch (evaluator.evaluateFormulaCell(cell)) {
                                case STRING:
                                    data.append(cell.getStringCellValue());
                                    break;
                                case NUMERIC:
                                    data.append(cell.getNumericCellValue());
                                    break;
                            }
                        } else if (type == CellType.BLANK) {
                        } else {
                            data.append(cell + "");
                        }
                        data.append(",");
                    }
                    writer.append(data.toString());
                    writer.append('\n');
                }
            } catch(Exception e){
                e.printStackTrace();
            }
            finally{
                if(writer!=null){
                    writer.flush();
                    writer.close();
                }
            }

            for (MemoryPoolMXBean mpBean: ManagementFactory.getMemoryPoolMXBeans()) {
                if (mpBean.getType() == MemoryType.HEAP) {
                    System.out.printf(
                            "Name: %s: %s\n",
                            mpBean.getName(), mpBean.getUsage()
                    );
                }
            }
            try {
                workbook.close();
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }
}


20-01-12 19:52:49:267  INFO main ReadWrite:64 - read time 11354
Transformations time 38659
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.util.TreeMap$Values.iterator(TreeMap.java:1031)
    at org.apache.poi.xssf.usermodel.XSSFRow.cellIterator(XSSFRow.java:117)
    at org.apache.poi.xssf.usermodel.XSSFRow.iterator(XSSFRow.java:132)
    at org.apache.poi.xssf.usermodel.XSSFEvaluationSheet.getCell(XSSFEvaluationSheet.java:86)
    at org.apache.poi.ss.formula.WorkbookEvaluator.evaluateFormula(WorkbookEvaluator.java:402)
    at org.apache.poi.ss.formula.WorkbookEvaluator.evaluateAny(WorkbookEvaluator.java:275)
    at org.apache.poi.ss.formula.WorkbookEvaluator.evaluate(WorkbookEvaluator.java:216)
    at org.apache.poi.xssf.usermodel.BaseXSSFFormulaEvaluator.evaluateFormulaCellValue(BaseXSSFFormulaEvaluator.java:56)
    at org.apache.poi.ss.formula.BaseFormulaEvaluator.evaluateFormulaCell(BaseFormulaEvaluator.java:185)
    at ReadWrite.main(ReadWrite.java:150)

所以現在我們有了一個可用的堆棧跟蹤,很明顯,在寫入 CSV 文件時問題不會發生。 當您評估電子表格公式時,它實際上正在發生。 我的猜測是該公式正在對工作表中的所有行求和......或類似的東西。

這是一個問題,可能沒有簡單的解決方案。

以下是POI 文檔的說明:

文件大小/內存使用

  • Excel 文件格式存在一些固有的限制。 這些是在類SpreadsheetVersion中定義的。 只要您有足夠的主內存,您就應該能夠處理達到這些限制的文件。 對於使用默認 POI 類的大文件,您可能需要大量內存。
    • 如果需要,有一些方法可以克服主內存限制:
    • 為了寫入非常大的文件, SXSSFWorkbook允許將數據流式寫入文件(對您可以執行的操作有一定的限制,因為只有部分文件保存在內存中)。
    • 要讀取非常大的文件,請查看示例XLSX2CSV ,該示例顯示了如何以流式方式讀取文件(同樣對可以從文件中讀取的信息有一些限制,但有很多方法可以獲取大部分信息如有必要)。

您顯然遇到了這些內存限制。 基本上,POI 試圖將過多的電子表格加載到內存中……而您正在評估電子表格公式……並且您正在填充堆。

一種解決方案是增加 Java 堆大小。 或者,如果您已經將所有可用 RAM 用於堆,請在具有更多 RAM 的機器上運行轉換。 如今,許多標准 PC 都有 16GB 內存。 也許是時候進行硬件升級了? 但我猜你已經想到了這一點。

如果增加堆大小不可行,那么您將需要重寫您的應用程序以使用SXSSFWorkbook 此外,您可能需要將使用公式計算的方法替換為在本機 Java中以與電子表格的逐行流兼容的方式進行計算。 (這將取決於公式的作用。)

查看 POI 文檔中的鏈接示例以獲取想法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM