본문 바로가기
text/Java

java excel read 문제 해결 (XSSFWorkbook heap space OOM)

by hoonzii 2021. 5. 7.
반응형

이전 포스트 했던 excel 관련해 발생한 문제가 있어 정리 해보려고 한다.

 

java excel 처리 정리

 

java excel 처리 정리

매번 엑셀을 java로 읽을때마다 찾아보는게 귀찮아서 정리한다. 사용 모듈 apache poi jar 리스트 - poi-3.11.jar - poi-ooxml-3.11.jar - poi-ooxml-schemas-3.11.jar - xmlbeans-2.6.0.jar 1. 엑셀 파일 ..

hoonzi-text.tistory.com

 

문제는 특정 엑셀 파일의 경우, 파일을 열다가 java.lang.OutOfMemoryError: Java heap space 문제가 발생했다. 

 

나는 apache tomcat에 jsp를 이용한 간단한 파일 업로드/다운로드 페이지를 만들어 놓은 것이였는데

서버에 엑셀파일을 올린뒤, 해당 엑셀 파일을 읽는 와중에 오류가 난것이였다.

 

초보 개발자 답게, 원인보다는 에러메세지에 집중해 먼저 실행되는 java 파일의 vm을 변경해 주었다.

vm argument 변경

이제 되겠지? 하고 돌렸으나 역시 동일한 문제가 발생했고,

역시 초보 개발자 답게, 다음 스텝으로

eclipse.ini를 건드려 준다.

eclipse.ini 변경

좋아! 이젠 진짜 되겠지? :) 하고 돌렸으나 역시 동일한 문제가 발생했다.

답이 없을까?

(타부서에게 얘기해 이 엑셀은 메모리문제가 발생하니 해로운 엑셀이다. 라고 말할수 밖에 없는것이다.)

 

에러가 나는 지점은 여기 였다.

XSSFWorkbook을 읽는데 문제가 발생

저 부분에 대해 찾아보니

stackoverflow.com/questions/6069847/java-lang-outofmemoryerror-java-heap-space-while-reading-excel-with-apache-poi

 

java.lang.OutOfMemoryError: Java heap space while reading excel with Apache POI

My file is 9MB and I got this error while loading workbook. XSSFWorkbook workbook = new XSSFWorkbook(excelFilePath); this line causes to java.lang.OutOfMemoryError: Java heap space How can I s...

stackoverflow.com

나와 동일한 문제로 골머리를 썪는 개발자 역시 스택오버플로우에 존재하고 있었고,

답변 역시

그냥 올려라. 의심하지 말고 올려라

첫번째 답변에 대해선 해봤으나 안됐으니 두번째 답변을 볼 차례이다.

XSSF + Sax를 이용해 reading 하는 방법이 있다고 나와있다.

XSSF가 문제가 많네... (다 안읽음)

그래서 XSSF+Sax 방법은 어디있는가 구글링....

 

그래서 찾았다.

javavoa-mok.tistory.com/58

 

Spring(JAVA) 엑셀 대용량 업로드

Spring으로 개발한다면 대부분은 poi 라이브러리를 사용해서 프로젝트를 진행합니다. poi 라이브러리는 너무 좋습니다. 엑셀을 파싱해서 데이터를 불러오기 때문입니다. 하지만 데이터의 건수가

javavoa-mok.tistory.com

/**
 * <pre>
 * 출처 : https://javavoa-mok.tistory.com/58
 * </pre>
 * 
 * @author hermeswing
 */
public class ExcelSheetHandler implements SheetContentsHandler {
    
    private int                currentCol = -1;
    private int                currRowNum = 0;
    
    String                     filePath   = "";
    
    private List<List<String>> rows       = new ArrayList<List<String>>();    //실제 엑셀을 파싱해서 담아지는 데이터
    private List<String>       row        = new ArrayList<String>();
    private List<String>       header     = new ArrayList<String>();
    
    public static ExcelSheetHandler readExcel( File file ) throws Exception {
        
        ExcelSheetHandler sheetHandler = new ExcelSheetHandler();
        try {
            
            //org.apache.poi.openxml4j.opc.OPCPackage
            OPCPackage                 opc         = OPCPackage.open( file );
            
            //org.apache.poi.xssf.eventusermodel.XSSFReader
            XSSFReader                 xssfReader  = new XSSFReader( opc );
            
            //org.apache.poi.xssf.model.StylesTable
            StylesTable                styles      = xssfReader.getStylesTable();
            
            //org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable
            ReadOnlySharedStringsTable strings     = new ReadOnlySharedStringsTable( opc );
            
            //엑셀의 시트를 하나만 가져오기입니다.
            //여러개일경우 while문으로 추출하셔야 됩니다.
            InputStream                inputStream = xssfReader.getSheetsData().next();
            
            //org.xml.sax.InputSource
            InputSource                inputSource = new InputSource( inputStream );
            
            //org.xml.sax.Contenthandler
            ContentHandler             handle      = new XSSFSheetXMLHandler( styles, strings, sheetHandler, false );
            
            XMLReader                  xmlReader   = SAXHelper.newXMLReader();
            xmlReader.setContentHandler( handle );
            
            xmlReader.parse( inputSource );
            inputStream.close();
            opc.close();
            
        } catch ( Exception e ) {
            //에러 발생했을때 하시고 싶은 TO-DO 
        }
        
        return sheetHandler;
        
    }//readExcel - end
    
    public List<List<String>> getRows() {
        return rows;
    }
    
    @Override
    public void startRow( int arg0 ) {
        this.currentCol = -1;
        this.currRowNum = arg0;
    }
    
    @Override
    public void cell( String columnName, String value, XSSFComment var3 ) {
        int iCol     = ( new CellReference( columnName ) ).getCol();
        int emptyCol = iCol - currentCol - 1;
        
        for ( int i = 0; i < emptyCol; i++ ) {
            row.add( "" );
        }
        currentCol = iCol;
        row.add( value );
    }
    
    @Override
    public void headerFooter( String arg0, boolean arg1, String arg2 ) {
        //사용안합니다.
    }
    
    @Override
    public void endRow( int rowNum ) {
        if ( rowNum == 0 ) {
            header = new ArrayList( row );
        } else {
            if ( row.size() < header.size() ) {
                for ( int i = row.size(); i < header.size(); i++ ) {
                    row.add( "" );
                }
            }
            rows.add( new ArrayList( row ) );
        }
        row.clear();
    }
    
    public void hyperlinkCell( String arg0, String arg1, String arg2, String arg3, XSSFComment arg4 ) {
        // TODO Auto-generated method stub
        
    }
}
// 엑셀 데이터 양식 example
/*    A열               B열
1행   test@naver.com    Seoul
2행   mouse@gmail.com   Busan
3행   apple@daum.net    Jeju
*/
 
 
//해당 파일은 업로드파일
String filePath = "test.xlsx";
 
File file = new File(filePath);
 
ExcelSheetHandler excelSheetHandler = ExcelSheetHandler.readExcel(file);
List<List<String>> excelDatas = excelSheetHandler.getRows();
 
//excelDatas  >>>>>    [[ test@naver.com, Seoul ],[ mouse@gmail.com, Busan ], [ apple@daum.net, Jeju ]]

for(List<String> dataRow : excelDatas){
    for(String str : dataRow){ // row 하나를 읽어온다.
        System.out.println(str); // cell 하나를 읽어온다.
    }
}

 

 

가져온 엑셀을 읽을 경우 애로사항이 하나 있는데 병합된 셀의 경우 빈칸으로 읽는 다는 점이다.

내 경우, columns 별로만(세로 로만) 병합이 이루어지는 파일이 였기 때문에 이전 값을 기억했다가

특정 셀일 경우 + 빈칸인 경우 -> 이전 값을 가져와 그대로 사용하는 로직으로 구현했다.

 

이방법으로 크기가 큰 엑셀을 읽을 수 있음은 물론

기존 병합셀+기존셀 읽는 시간이 엄청 줄어든것을 확인했다.

 

미래의 내가 동일한 삽질 하지 않기를 바라며 기록한다.

반응형

댓글