2012-10-22 17 views
7

Próbuję odczytać duże pliki Excela xlsx za pośrednictwem Apache POI, na przykład 40-50 MB. Występuje wyjątek pamięci. Aktualna pamięć sterty to 3 GB.Błąd podczas odczytywania dużych plików Excel (xlsx) Via Apache POI

Potrafię czytać mniejsze pliki programu Excel bez żadnych problemów. Potrzebuję sposobu, aby przeczytać duże pliki Excela, a następnie z powrotem jako odpowiedź za pośrednictwem Spring Excel.

public class FetchExcel extends AbstractView { 


    @Override 
    protected void renderMergedOutputModel(
      Map model, HttpServletRequest request, HttpServletResponse response) 
    throws Exception { 

    String fileName = "SomeExcel.xlsx"; 

    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); 

    OPCPackage pkg = OPCPackage.open("/someDir/SomeExcel.xlsx"); 

    XSSFWorkbook workbook = new XSSFWorkbook(pkg); 

    ServletOutputStream respOut = response.getOutputStream(); 

    pkg.close(); 
    workbook.write(respOut); 
    respOut.flush(); 

    workbook = null;      

    response.setHeader("Content-disposition", "attachment;filename=\"" +fileName+ "\""); 


    }  

} 

zaczynałem wyłączyć za pomocą XSSFWorkbook workbook = new XSSFWorkbook(FileInputStream in); ale to kosztowne za Apache POI API, więc przeszedłem do OPC pakietu sposób, ale nadal ten sam efekt. Nie muszę analizować ani przetwarzać pliku, po prostu go odczytać i zwrócić.

+0

Spróbuj SXSSF http://poi.apache.org/spreadsheet/index.html – Alfabravo

+1

muszę przykład. Przeszukuję sieć, ale nie mogę znaleźć przykładu do czytania dużych arkuszy za pośrednictwem SXSSF, w przeciwnym razie nie zadałbym pytania na pierwszym miejscu. – jamesT

+0

@jamesT uruchomiłeś tę opcję? -Xms1024M -Xmx2048M – chrome

Odpowiedz

6

Nie można podać, czy należy zmodyfikować arkusz kalkulacyjny, czy nie.

Może to być oczywiste, ale jeśli nie musisz modyfikować arkusza kalkulacyjnego, nie musisz go analizować i zapisywać go z powrotem, możesz po prostu odczytać bajty z pliku i zapisać bajty, tak jak w przypadku, powiedz obraz lub dowolny inny format binarny.

Jeśli musisz zmodyfikować arkusz kalkulacyjny przed wysłaniem go do użytkownika, to według mojej wiedzy może być konieczne zastosowanie innego podejścia.

Każda biblioteka, o której wiem, że odczytuje pliki Excela w języku Java, odczytuje cały arkusz kalkulacyjny w pamięci, więc do każdego arkusza kalkulacyjnego, który mógłby być przetwarzany jednocześnie, potrzebne jest 50 MB pamięci. Wymaga to, jak zauważyli inni, dostosowywania stosu dostępnego dla VM.

Jeśli musisz jednocześnie przetwarzać dużą liczbę arkuszy kalkulacyjnych i nie możesz przydzielić wystarczającej ilości pamięci, zastanów się nad użyciem formatu, który można przesyłać strumieniowo, zamiast czytać wszystko od razu w pamięci. Format CSV można otworzyć za pomocą Excela, a w przeszłości miałem dobre wyniki, ustawiając typ zawartości na application/vnd.ms-excel, ustawiając nazwę pliku załącznika na coś kończącego się na ".xls", ale w rzeczywistości zwracając CSV zadowolony. Nie próbowałem tego od paru lat, więc YMMV.

+0

Dzięki za odpowiedź. – jamesT

13

Oto przykład odczytu dużego pliku xls przy użyciu parsera sax.

public void parseExcel(File file) throws IOException { 

     OPCPackage container; 
     try { 
      container = OPCPackage.open(file.getAbsolutePath()); 
      ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(container); 
      XSSFReader xssfReader = new XSSFReader(container); 
      StylesTable styles = xssfReader.getStylesTable(); 
      XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) xssfReader.getSheetsData(); 
      while (iter.hasNext()) { 
       InputStream stream = iter.next(); 

       processSheet(styles, strings, stream); 
       stream.close(); 
      } 
     } catch (InvalidFormatException e) { 
      e.printStackTrace(); 
     } catch (SAXException e) { 
      e.printStackTrace(); 
     } catch (OpenXML4JException e) { 
      e.printStackTrace(); 
     } 

} 

protected void processSheet(StylesTable styles, ReadOnlySharedStringsTable strings, InputStream sheetInputStream) throws IOException, SAXException { 

     InputSource sheetSource = new InputSource(sheetInputStream); 
     SAXParserFactory saxFactory = SAXParserFactory.newInstance(); 
     try { 
      SAXParser saxParser = saxFactory.newSAXParser(); 
      XMLReader sheetParser = saxParser.getXMLReader(); 
      ContentHandler handler = new XSSFSheetXMLHandler(styles, strings, new SheetContentsHandler() { 

      @Override 
       public void startRow(int rowNum) { 
       } 
       @Override 
       public void endRow() { 
       } 
       @Override 
       public void cell(String cellReference, String formattedValue) { 
       } 
       @Override 
       public void headerFooter(String text, boolean isHeader, String tagName) { 

       } 

      }, 
      false//means result instead of formula 
      ); 
      sheetParser.setContentHandler(handler); 
      sheetParser.parse(sheetSource); 
     } catch (ParserConfigurationException e) { 
      throw new RuntimeException("SAX parser appears to be broken - " + e.getMessage()); 
} 
+0

Dzięki O.C dokładnie to, czego szukałem, przetwarzając ponad 250 tys. Wierszy. Doskonale działa. – Anand

+0

Bardzo dziękuję za fragment kodu. Apache POI powinien opublikować w swojej dokumentacji przykład, jak wyżej, aby łatwiej reklamować te API. – 99Sono

+0

@ O.C Dzięki za tonę !! Czy mógłbyś powiedzieć, jak rozważyć puste komórki w programie Excel za pomocą powyższego kodu? – user1799214

0

ja też w obliczu tego samego problemu OOM podczas parsowania pliku XLSX ... po dwóch dniach walki, ale w końcu okazało się, poniższy kod, który był naprawdę doskonały;

Ten kod jest oparty na sjxlsx. Czyta Xlsx i przechowuje w arkuszu HSSF.

  [code=java] 
      // read the xlsx file 
     SimpleXLSXWorkbook = new SimpleXLSXWorkbook(new File("C:/test.xlsx")); 

     HSSFWorkbook hsfWorkbook = new HSSFWorkbook(); 

     org.apache.poi.ss.usermodel.Sheet hsfSheet = hsfWorkbook.createSheet(); 

     Sheet sheetToRead = workbook.getSheet(0, false); 

     SheetRowReader reader = sheetToRead.newReader(); 
     Cell[] row; 
     int rowPos = 0; 
     while ((row = reader.readRow()) != null) { 
      org.apache.poi.ss.usermodel.Row hfsRow = hsfSheet.createRow(rowPos); 
      int cellPos = 0; 
      for (Cell cell : row) { 
       if(cell != null){ 
        org.apache.poi.ss.usermodel.Cell hfsCell = hfsRow.createCell(cellPos); 
        hfsCell.setCellType(org.apache.poi.ss.usermodel.Cell.CELL_TYPE_STRING); 
        hfsCell.setCellValue(cell.getValue()); 
       } 
       cellPos++; 
      } 
      rowPos++; 
     } 
     return hsfSheet;[/code] 
+0

Ten przykład pokazuje, jak pisać do pliku Excela, pytanie dotyczy tego, w jaki sposób zapisujemy do pliku Excela w poi. – user1707141

Powiązane problemy