[spring] SXSSF 엑셀다운로드

SXSSF (Streaming Usermodel API)

  • 매우 큰 사이즈의 스프레드시트를 생성해야하고 힙 공간이 제한되어 있을 때 사용되는 XFF의 API 호환 스트리밍 확장 프로그램
  • 단점으로는 약 20MB csv의 데이터 경우 임시파일의 용량이 GB 단위가 된다고 함. 그럴 경우 gzip 압축을 사용할 수 있도록 옵션을 변경하면 된다고 한다
SXSSFWorkbook wb = new SXSSFWorkbook (); 
wb.setCompressTempFiles (true); // 임시 파일이 압축됩니다

<aside> 💡 HSSF : EXCEL 2007 이전 버전(.xls) - 65535 라인까지 사용가능

XSSF : EXCEL 2007 이후 버전(2007포함 .xlsx - 65535 라인 이상 사용가능

SXSSF : XSSF의 Streaming Version으로 메모리를 적게 사용 - 65535 라인 이상 사용가능

</aside>

SXSSF 방식을 활용한 엑셀 다운로드 예시

pom.xml

  • dependency 설정
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi</artifactId>
	<version>3.13</version>
</dependency>


<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi-ooxml</artifactId>
	<version>3.13</version>
</dependency>

예시 1

public void excelDownLoad() {
	FileOutputStream fos = null;

	SXSSFWorkbook workbook = null; // 워크북		 
	SXSSFRow row = null; // 행
	SXSSFCell cell = null; // 셀		 		
	
	CellStyle styleMoneyFormat = null; // 셀 스타일	
	int index = 0; // 셀 헤더 카운트							
	int rowIndex = 1; // 행 카운트							

	List<DataDto> dataList = null;

	// 엑셀 헤더 정보 구성
	String[] cellHeader = {"번호", "테스트", "날짜", "시간"};

	try {
			dataList = mapper.getData();
	    // 워크북 생성
	    workbook = new SXSSFWorkbook();
	    workbook.setCompressTempFiles(true);

			// 워크시트 생성
	    SXSSFSheet sheet = (SXSSFSheet) workbook.createSheet("시트이름");
	    sheet.setRandomAccessWindowSize(100); // 메모리 행 100개로 제한, 초과 시 Disk로 flush

	
		sheet.setColumnWidth(2, 300); 	//셀 칼럼 크기 설정
		row = sheet.createRow(0);		// 행 생성
		// 셀 스타일 생성
		styleMoneyFormat = workbook.createCellStyle(); 
		CreationHelper ch = workbook.getCreationHelper();
		styleMoneyFormat.setDataFormat(ch.createDataFormat().getFormat("#,##0"));

		// 헤더 적용
		for(String head : cellHeader ) {
			cell = row.createCell(index++);
			cell.setCellValue(head);
		}

		for(DataDto dataDto : dataList) {
			row = sheet.createRow(rowIndex);

            cell = row.createCell(0);
            cell.setCellValue(rowIndex++); //번호

            cell = row.createCell(1);
            cell.setCellValue(dataDto.getTest());	//테스트

            cell = row.createCell(2);
            cell.setCellValue(dataDto.getDate());	//날짜

            cell = row.createCell(3);
            cell.setCellValue(dataDto.getTime());	//시간

		}
		
        String filename = "파일명.xlsx";
        String orgFileName = "TEST_TEMP_FILE_01_.xlsx"; // 서버저장파일명
        String fileDownLoadPath = "/home/excelTemp/"

        // 파일생성
        fos = new FileOutputStream(fileDownLoadPath + orgFileName);
        workbook.write(fos);

        paramMap.put("filePath", fileDownLoadPath);
        paramMap.put("realFilNm", orgFileName);
        paramMap.put("viewFileNm", filename);

    } catch (Exception e) {
        if(fos != null) try { fos.close(); } catch(Exception ignore) {}
    } finally {
        try {
            workbook.close();
            workbook.dispose();
            if(fos != null) try { fos.close(); } catch(Exception ignore) {}
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

예시 2

// flush되기 전까지 메모리에 들고있는 행의 갯수
int ROW_ACCESS_WINDOW_SIZE = 500;

XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook, ROW_ACCESS_WINDOW_SIZE);

SXSSFSheet objSheet = null;
SXSSFRow objRow = null;
SXSSFCell objCell = null; // 셀 생성

// 제목 폰트
Font font = sxssfWorkbook.createFont();
font.setFontHeightInPoints((short) 9);
font.setBold(Boolean.TRUE);
font.setFontName("맑은고딕");

// 제목 스타일에 폰트 적용, 정렬
CellStyle styleHd = sxssfWorkbook.createCellStyle(); // 제목 스타일
styleHd.setFont(font);
styleHd.setAlignment(CellStyle.ALIGN_CENTER);
styleHd.setVerticalAlignment(CellStyle.VERTICAL_CENTER);

styleHd.setBorderTop(CellStyle.BORDER_THIN);
styleHd.setBorderBottom(CellStyle.BORDER_THIN);
styleHd.setBorderRight(CellStyle.BORDER_THIN);
styleHd.setBorderLeft(CellStyle.BORDER_THIN);
styleHd.setBottomBorderColor(IndexedColors.BLACK.getIndex());
styleHd.setTopBorderColor(IndexedColors.BLACK.getIndex());
styleHd.setRightBorderColor(IndexedColors.BLACK.getIndex());
styleHd.setLeftBorderColor(IndexedColors.BLACK.getIndex());

objSheet = sxssfWorkbook.createSheet("사업현황"); // 워크시트 생성

// 스타일 미리 적용
for(int i = 0; i < 1; i++) {
	objRow = objSheet.createRow(i);
	for(int j = 0; j < 35; j++) {
		objCell = objRow.createCell(j);
		objCell.setCellStyle(styleHd);
		objSheet.setColumnWidth(j, (short) 6000);
	}
}

// 0번째 열
objRow = objSheet.getRow(0);
objRow.setHeight((short) 0x150);

// 연번
objCell = objRow.getCell(0);
objCell.setCellValue("연번");

// 광역
objCell = objRow.getCell(1);
objCell.setCellValue("광역");

// 자치단체
objCell = objRow.getCell(2);
objCell.setCellValue("자치단체");

// 유형
objCell = objRow.getCell(3);
objCell.setCellValue("유형");

// 사업명
objCell = objRow.getCell(4);
objCell.setCellValue("사업명");

// 접수일
objCell = objRow.getCell(5);
objCell.setCellValue("접수일");

int rowNum = 1;

// 제목 스타일
CellStyle cellStyle = sxssfWorkbook.createCellStyle(); 
cellStyle.setFillForegroundColor( HSSFColor.GREY_25_PERCENT.index );
cellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);

// list는 실제 데이터 List입니다.
for(CovidSupportVo vo : list ) {
		// 열
    objRow = objSheet.createRow(rowNum);
    objRow.setHeight((short) 0x150);

    objCell = objRow.createCell(0);
    objCell.setCellValue( rowNum );
    
	  // 로직처리
		...

	++rowNum;
    
}

response.setContentType("Application/Msexcel");
response.setHeader("Content-Disposition", "ATTachment; Filename=" + URLEncoder.encode("사업관리", "UTF-8") + ".xls");

OutputStream fileOut = response.getOutputStream();
sxssfWorkbook.write(fileOut);
fileOut.close();

response.getOutputStream().flush();
response.getOutputStream().close();

// sxssfWorkbook.dispose(); 임시파일 삭제

예시 3

  • DB에서 가져오는 data형식이 map과 list를 오가는 구조여서 자료형을 중간에 변경하는 케이스
/*
 *  SXSSFWorkBook 방식으로 excel 다운로드 처리하기.
 */
public void excelDownload(HttpServletRequest request, HttpServletResponse response, Map<String, Object> ExcelMap,  String fileName, String templateFile, String string) throws ParsePropertyException, IOException, org.apache.poi.openxml4j.exceptions.InvalidFormatException {

	ByteArrayOutputStream bos = new ByteArrayOutputStream();
	SXSSFWorkbook workbook = null;
	Row row = null;
	Cell cell = null;
	Sheet sheet1 = null;
	CellStyle cellStyle = null;

	String[] headerName = {
			"순번","카드번호","결제금액"
	};

	String[] headerKey = {
			"ROW","CARD_NO","PRICE"
	};

	int indexRow = 1;
	int indexCell = 0;
	int index = 0;

	try {
			ArrayList<Map<String,Object>> list = (ArrayList<Map<String,Object>>) ExcelMap.get("dataList");
			String writeDate = (String) ExcelMap.get("date");
			System.out.println("list check : " + list);
	
			//워크북 생성
			workbook = new SXSSFWorkbook(100);
			workbook.setCompressTempFiles(true);
	
			//워크시트 생성
			Sheet sheet = workbook.createSheet("sheet1");
	
			//셀스타일 생성
			cellStyle = workbook.createCellStyle();
			CreationHelper helper = workbook.getCreationHelper();
			cellStyle.setDataFormat(helper.createDataFormat().getFormat("#,##0"));
			cellStyle.setFillBackgroundColor(IndexedColors.YELLOW.getIndex());
			cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
	
			//행 생성
			row = sheet.createRow(0);
			//header create
			for(String head : headerName){
				cell = row.createCell(indexCell++);
				cell.setCellValue(head);
				cell.setCellStyle(cellStyle);
			}
			
	
			//body 
			for(int i=0; i<list.size(); i++){
	
				row = sheet.createRow(indexRow++);
				Map<String, Object> maps = (Map<String, Object>) list.get(i);
				for(int j=0; j<headerKey.length;j++){
					if(maps.containsKey(headerKey[j])){
						Object value = maps.get(headerKey[j]);
						cell = row.createCell(cellInt++);
						cell.setCellValue(String.valueOf(value));
					}else{
						cell = row.createCell(cellInt++);
						cell.setCellValue("-");
					}
				}
			}
	
			response.setCharacterEncoding("utf-8");
			response.setContentType("application/ms-excel");
			response.setHeader("Content-Disposition", "attachment; filename=\\"" + fileName + ".xlsx\\";");
	
			workbook.write(bos);
			bos.flush();
			bos.close();

			ServletOutputStream out = response.getOutputStream();
			out.write(bos.toByteArray());
			out.flush();
			out.close();

	} catch (IOException e) {
			e.printStackTrace();
	} finally {
			workbook.close();
	}
}

레퍼런스

https://j-dev.tistory.com/entry/SpringPOI-대용량-엑셀-다운로드

https://poi.apache.org/components/spreadsheet/how-to.html#sxssf

https://goodthinking.tistory.com/52

https://xzio.tistory.com/1985