2012-04-25 7 views
1

큰 xls 또는 xlsx 파일 (약 30MB 이상, 70,000 개 이상의 행)을 읽고 싶습니다. OutOfMemory 오류가 발생할 때까지 Apache POI eaily를 사용하여 작은 Excel 파일을 읽을 수있었습니다.Apache POI Event API를 사용하여 특정 행을 읽는 방법은 무엇입니까?

성능 및 메모리 사용은 저에게 중요한 문제입니다. 많은 게시물을 통해 메모리 공간이 문제라면 XSSF의 경우 기본 XML 데이터를 가져 와서 XSSF 및 SAX (Event API)를 사용하여 직접 처리 할 수 ​​있습니다. 음, 흥미 롭다. 이제 xlsx 파일 전체를 아무 문제없이 읽을 수있다. 이벤트 API를 사용하지 않을 때 거의 GB 미만의 메모리 (70MB 미만)를 소비했습니다 (-Xmx가 1024m로 설정된 경우 최대 1GB로 계속 정지했습니다).

하지만 지금은 읽기 프로세스를 사용자 지정하고 특정 행만 Excel에서 읽을 수 있도록하려고합니다. org.apache.poi.ss.usermodel.Sheet # getRow (int rownum)를 사용하면 쉽게이 작업을 수행 할 수 있습니다. 하지만 이벤트 API를 사용하면 모든 행을 중단없이 읽을 수 있으므로 특정 행을 읽는 것이 어려워집니다 (예 : 그냥 행 번호 2,3,5 등. 내 전체 코드는 다음과 같습니다.

import java.io.InputStream; 
import java.util.Iterator; 
import java.util.Vector; 
import org.apache.poi.xssf.eventusermodel.XSSFReader; 
import org.apache.poi.xssf.model.SharedStringsTable; 
import org.apache.poi.xssf.usermodel.XSSFRichTextString; 
import org.apache.poi.openxml4j.opc.OPCPackage; 
import org.xml.sax.Attributes; 
import org.xml.sax.ContentHandler; 
import org.xml.sax.InputSource; 
import org.xml.sax.SAXException; 
import org.xml.sax.XMLReader; 
import org.xml.sax.helpers.DefaultHandler; 
import org.xml.sax.helpers.XMLReaderFactory; 

/** 
* XSSF and SAX (Event API) 
*/ 
public class FromHowTo { 
    public void processAllSheets(String filename) throws Exception { 
     OPCPackage pkg = OPCPackage.open(filename); 
     XSSFReader r = new XSSFReader(pkg); 
     SharedStringsTable sst = r.getSharedStringsTable(); 

     XMLReader parser = fetchSheetParser(sst); 

     Iterator<InputStream> sheets = r.getSheetsData(); 
     while(sheets.hasNext()) { 
      InputStream sheet = sheets.next(); 
      InputSource sheetSource = new InputSource(sheet); 
      parser.parse(sheetSource); 
      sheet.close(); 
     } 
    } 

    public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException { 
     XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser"); 
     ContentHandler handler = new SheetHandler(sst); 
     parser.setContentHandler(handler); 
     return parser; 
    } 

    /** 
    * See org.xml.sax.helpers.DefaultHandler javadocs 
    */ 
    private static class SheetHandler extends DefaultHandler { 
     private SharedStringsTable sst; 
     private String lastContents; 
     private boolean nextIsString; 
     Vector values = new Vector(10); 

     private SheetHandler(SharedStringsTable sst) { 
      this.sst = sst; 
     } 

     public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { 
      // c => cell 

      if(name.equals("c")) { 
       // Figure out if the value is an index in the SST 
       String cellType = attributes.getValue("t"); 
       //System.out.println(cellType); 
       if(cellType != null && cellType.equals("s")) { 
        nextIsString = true; 
       } else { 
        nextIsString = false; 
       } 
      } 
      // Clear contents cache 
      lastContents = ""; 
     } 

     public void endElement(String uri, String localName, String name) throws SAXException { 
      // Process the last contents as required. 
      // Do now, as characters() may be called more than once 
      if(nextIsString) { 
       try { 
        int idx = Integer.parseInt(lastContents); 
        lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString(); 
       } catch (NumberFormatException e) { 
       } 
      } 

      // v => contents of a cell 
      // Output after we've seen the string contents 
      if(name.equals("v")) { 
       values.add(lastContents); 
      } 

      if(name.equals("row")) { 
       System.out.println(values); 
       values.removeAllElements(); 
      } 
     } 

     public void characters(char[] ch, int start, int length) throws SAXException { 
      lastContents += new String(ch, start, length); 
     } 
    } 

    public static void main(String[] args) throws Exception { 
     FromHowTo howto = new FromHowTo(); 
     howto.processAllSheets(args[0]); 
    } 
} 

Apache POI 3.7에서 JRE7을 사용하고 있습니다. 누군가가 이벤트 API로 특정 행을 가져 오는 것을 도와 줄 수 있습니까?

+0

당신이 시작 요소를 얻을 때 당신은 그냥 행의 행 번호를 볼 수 없으며, 원하는 행이 아니면 다음 행이 시작될 때까지 건너 뛰시겠습니까? – Gagravarr

+0

@Gagravarr : Tx. 네, 그랬어요. 그러나 대안이 있는지 궁금해하고있었습니다. 이후 여전히 전체 XML을 파싱해야했습니다. 한가지 더 큰 xls 파일을 읽으려면 어떻게 코드를 수정할 수 있습니까? xls 파일을 읽는 중 오류가 발생합니다 (xlsx는 잘 작동합니다) -> org.apache.poi.openxml4j.exceptions.InvalidOperationException : 'D : \ Test \ conversions.xls'지정한 파일을 열 수 없습니다. – ParagJ

+0

. 여기서 낮은 수준의 작업을하고 있으며 두 형식이 매우 다릅니다. 일반적으로 둘 모두에서 작동하는 코드를 원한다면 usermodel – Gagravarr

답변

5

각 행 시작 요소에는 행 번호가 있습니다. 속성에서 검색 할 수 있습니다.

long rowIndex = Long.valueOf (attributes.getValue ("r")));

이벤트 모델은 모든 행에 통과하지만 당신은 그가 인덱스를 얻을 수 있고,하는 endElement에 따라 데이터를 처리

+0

오, 정말 고마워요 –

+0

@VictorHugo 항상 환영합니다 – user1363516

+0

@ user1363516이 대신 행 인덱스를 반환하지 않습니다 그것은 세포를 제공하고 있습니다 – Mandrek