POI 라이브러리 정리!
POI 라이브러리를 사용한 엑셀다운로드작업을 하게되어 정리합니다.
전자정부프레임워크환경에서 작업하였습니다.
POI라이브러리 에는 HSSF, XSSF, SXSSF 3가지 클래스가 존재하는데 각각의 차이점은
HSSF : 엑셀 97 ~ 2003버전
XSSF : 엑셀 2007버전이상
SXSSF : 가장 최근에 나온 성능개선버전
이라고 합니다.
저는 SXSSF를 사용하였습니다.
maven을 빌드도구로 사용하시는 분들은
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0.pr4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.9.1</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
위 4가지의 라이브러리를 추가해야 합니다.
전자정부프레임워크 사용자분들은 pom.xml에
xmlbeans 라이브러리가 추가되어 있을텐데 이 라이브러리와 충돌이 발생하여 에러가 발생하기 때문에
이를 제외하셔야 구현이 가능합니다.
다음으로
전자정부프레임워크 기준 egov-com-servlet.xml 파일에서
BeanNameViewResolver 의 order가 InternalResourceViewResolver 혹은 BeanNameViewResolver 클래스 보다 높아야 합니다.
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" p:order="0"/>
<bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView"/>
<property name="order" value="1"/>
</bean>
jsp 보다 자바 클래스파일을 우선으로 리턴하기위합니다.
그 다음 필요에 맞게 VO를 생성하시고
workbook 객체를 리턴할 클래스를 만들어 Spring 에서 엑셀다운로드 시 뷰를 리턴해주기 위해 많이 사용하는 abstractView 클래스를 참조합니다.
workbook 객체를 받아와서 인코딩처리후 다운로드가 될수 있도록 workbook객체를 리턴해줍니다.
workbook 이란 poi를 통해 생성한 객체로 하나의 엑셀파일을 뜻합니다.
package egovframework.dds.bst;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.web.servlet.view.AbstractView;
public class BstExcelMergeModel extends AbstractView {
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
// 엑셀 파일명 설정
String workbookName = (String)model.get("workbookName");
// 브라우저 별 인코딩 작업
String browser = request.getHeader("User-Agent");
if (browser.indexOf("MSIE") > -1) {
fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
} else if (browser.indexOf("Trident") > -1) { // IE11
fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
} else if (browser.indexOf("Firefox") > -1) {
fileName = "\"" + new String(fileName.getBytes("UTF-8"), "8859_1") + "\"";
} else if (browser.indexOf("Opera") > -1) {
fileName = "\"" + new String(fileName.getBytes("UTF-8"), "8859_1") + "\"";
} else if (browser.indexOf("Chrome") > -1) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < fileName.length(); i++) {
char c = fileName.charAt(i);
if (c > '~') {
sb.append(URLEncoder.encode("" + c, "UTF-8"));
} else {
sb.append(c);
}
}
fileName = sb.toString();
} else if (browser.indexOf("Safari") > -1){
fileName = "\"" + new String(fileName.getBytes("UTF-8"), "8859_1")+ "\"";
} else {
fileName = "\"" + new String(fileName.getBytes("UTF-8"), "8859_1")+ "\"";
}
response.setContentType("application/download;charset=utf-8");
response.setHeader("Content-Disposition", "attachment; filename=\"" + workbookName + "\";");
response.setHeader("Content-Transfer-Encoding", "binary");
OutputStream os = null;
SXSSFWorkbook workbook = null;
try {
workbook = (SXSSFWorkbook) model.get("workbook");
os = response.getOutputStream();
// 파일생성
workbook.write(os);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(workbook != null) {
try {
workbook.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(os != null) {
try {
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
이번엔 jsp에서의 처리를 보겠습니다.
엑셀 업로드를 함께 하려면 form태그에 multipart/form-data 를 추가해야 하나 여기서는 엑셀 다운로드만 살펴보겠습니다.
<script>
ExcelDownload() {
var form = document.goform;
form.action ="/bst/bst/ExcelDownload.do";
form.submit();
}
</script>
<form id="goform">
<buttton type="button" class="btn btn-success" onclick="ExcelDownload()">엑셀다운로드</button>
</form>
처리하는데 필요하신 데이터가 있다면 form 태그안에 추가 하시면되고 action 경로는 생성할 컨트롤러의 mapping경로로 잡아주시면됩니다.
이제 Controller를 보겠습니다.
엑셀에 표시될 데이터를 impl에 전달하고 생성이 완료된 workbook 을 받아 위에서 생성한 view를 리턴하는 클래스 파일로 전달합니다. 저 workbook 객체가 하나의 엑셀파일이됩니다.
데이터는 꼭 HashMap이 아니여도 무관하여 편의에 따라 전달만 해주시면 됩니다.
@RequestMapping(value="/bst/bst/ExcelDownload.do")
public String downloadExeclFile(HttpServletRequest request, HttpServletResponse response, ModelMap model
,@ModelAttribute("BstExcelDownload") BstExcelDownloadVO BstExcelDownloadVO) {
// 엑셀에 표현할 데이터를 담기위해 HashMap 객체 생성
Map<String, List<?>> DataMap = new HashMap<String, List<?>>();
List<?> ExcelCount = null;
List<?> ExcelList = null;
// SXFFWorkbook 객체 선언
SXSSFWorkbook workbook = null;
// 데이터 Map에 담기
DataMap.put("ExcelCount", ExcelCount);
DataMap.put("ExcelList", ExcelList);
// 데이터를 반영하고 엑셀을 생성할 impl에 Map 전달합니다.
try {
workbook = bstManageService.excelFileDownloadProcess(DataMap);
} catch (Exception e) {
e.printStackTrace();
}
String ExcelFileName = "엑셀파일이름 명시"
model.addAttribute("workbook", workbook);
model.addAttribute("workbookName", FileName);
// 위에서 생성한 클래스파일 리턴
return "BstExcelMergeModel";
}
Service 에 메소드를 추가하셨다고 가정 하고 그에 대한 impl만 코드를 보도록 하겠습니다.
impl 에서 데이터매핑 및 디자인을 하여 하나의 엑셀파일인 workbook 객체를 리턴해줍니다.
@Override
public SXSSFWorkbook makeSimpleExcelWorkbook(Map<String, List<?>> DataMap, String TermDate) throws Exception {
// 워크북 생성(하나의 엑셀파일)
SXSSFWorkbook workbook = new SXSSFWorkbook();
// 엑셀파일의 가로 한줄
Row row = null;
// 엑셀의 Cell 한칸
Cell cell = null;
// 엑셀 스타일 적용 수 많은 옵션이 있는데 다른 게시글에 따로 작성하겠습니다.
// 하나의 CellStyle 객체
CellStyle ExampleStyle = workbook.createCellStyle();
// 텍스트 가운데 정렬
ExampleStyle.setAlignment(CellStyle.ALIGN_CENTER);
ExampleStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
//Font 객체생성
Font ExampleFont = workbook.createFont();
// 폰트명을 직접 입력하여 폰트를 지정합니다.
ExampleFont.setFontName("맑은 고딕");
// 텍스트 크기를 지정합니다. 지정하려는 크기의 곱하기 20한 숫자를 입력합니다.
// ex) 18 -> 360
ExampleFont.setFontHeight((short)360);
// 생성한 폰트를 CellStyle에 적용합니다.
ExampleStyle.setFont(ExampleFont);
// 이제 시트를 생성합니다.
// 여러 시트를 생성할 경우 시트명이 중복되면 에러가 발생합니다.
SXSSFSheet sheet = workbook.createSheet("시트명");
// 자바와 동일하게 인덱스가 0부터 시작합니다.(엑셀의 가로 첫번째줄 생성)
row = sheet.createRow(0);
// 첫줄의 첫번째 셀 생성
cell = row.createCell(0);
// 임의로 생성하겠습니다. 반복문 처리 하시면 됩니다.
// 셀에 텍스트 입력
cell.setCellValue("1번");
// 셀의 스타일을 지정합니다. 이처럼 CellStyle의 다양한 속성을 활용하여 스타일을 만들고
// 직접 셀에 적용하면 됩니다.
cell.setCellStyle(ExampleStyle);
cell = row.createCell(1);
cell.setCellValue("2번");
cell.setCellStyle(ExampleStyle);
cell = row.createCell(2);
cell.setCellValue("3번");
cell.setCellStyle(ExampleStyle);
row = sheet5.createRow(1);
cell = row.createCell(0);
cell.setCellValue("1-1번");
cell.setCellStyle(ExampleStyle);
cell = row.createCell(1);
cell.setCellValue("2-1번");
cell.setCellStyle(ExampleStyle);
cell = row.createCell(2);
cell.setCellValue("3-1번");
cell.setCellStyle(ExampleStyle);
// 최종적으로 위에 만들어진 workbook 객체를 리턴합니다.
return workbook;
}
마이자몽님의 블로그(https://myjamong.tistory.com/111)를 참조하여 구현에 보앗습니다.
엑셀 속성의 관한 설정은 다른 게시물에 게시하겠습니다.
감사합니다.