큰 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로 특정 행을 가져 오는 것을 도와 줄 수 있습니까?
당신이 시작 요소를 얻을 때 당신은 그냥 행의 행 번호를 볼 수 없으며, 원하는 행이 아니면 다음 행이 시작될 때까지 건너 뛰시겠습니까? – Gagravarr
@Gagravarr : Tx. 네, 그랬어요. 그러나 대안이 있는지 궁금해하고있었습니다. 이후 여전히 전체 XML을 파싱해야했습니다. 한가지 더 큰 xls 파일을 읽으려면 어떻게 코드를 수정할 수 있습니까? xls 파일을 읽는 중 오류가 발생합니다 (xlsx는 잘 작동합니다) -> org.apache.poi.openxml4j.exceptions.InvalidOperationException : 'D : \ Test \ conversions.xls'지정한 파일을 열 수 없습니다. – ParagJ
. 여기서 낮은 수준의 작업을하고 있으며 두 형식이 매우 다릅니다. 일반적으로 둘 모두에서 작동하는 코드를 원한다면 usermodel – Gagravarr