====== Excel Service ======
===== 개요 =====
Excel 파일 포맷을 다룰 수 있는 자바 라이브러리를 제공하여, 사용자들이 데이터를 Excel 파일 포맷으로 다운받거나, 대량의 Excel 데이터를 시스템에 올릴 수 있도록 지원하기 위한 서비스이다.\\
Excel 서비스는 **Apache POI** 오픈소스를 사용하여 구현하였으며 **주요 Excel접근** 기능 외에 **Excel 다운로드**, **Excel 파일 업로드** 등의 기능이 있다.\\
Excel 서비스 3.0버전에서는 기존 버전을 refactoring 하였다.\\
기존의 메소드(xls, xlsx)을 지원하는 메소드들의 이름을 하나로 하여 Parameter 방식으로 구분자를 추가하였다.\\
또한, 기존에 iBatis 뿐만 아니라 MyBatis도 지원하는 클래스를 추가하였다.
===== 설명 =====
==== 주요기능 ====
=== Excel 파일 생성 ===
엑셀 파일을 생성하여 지정된 위치에 저장하는 기능을 제공한다.\\
Workbook 인스턴스를 생성하여 Excel sheet를 추가 생성할 수 있다.
엑셀 버전에 따라 엑셀 97~2003버전(xls)인 HSSFWorkbook, 엑셀 2007이상(xlsx)의 XSSFWorkbook 클래스를 사용할 수 있으며, 각 클래스별 사용 법(method)는 동일하다.
== Sample Source ==
String sheetName1 = "first sheet";
String sheetName2 = "second sheet";
StringBuffer sb = new StringBuffer();
// 엑셀 필요버전에 맞는 확장자를 선택하면 됨
sb.append(fileLocation).append("/").append("testWriteExcelFile.xls");
sb.append(fileLocation).append("/").append("testWriteExcelFile.xlsx");
// Workbook을 필요버전에 맞는 클래스를 선택하면 됨
Workbook wb = new HSSFWorkbook(); // xls 버전
Workbook wb = new SXSSFWorkbook(); //xlsx 버전
wb.createSheet(sheetName1);
wb.createSheet(sheetName2);
wb.createSheet();
=== Excel 파일 수정 ===
엑셀 파일 내 셀의 내용을 변경하고 저장한다.\\
저장된 엑셀파일을 로드할 수 있으며 지정한 sheet에 row와 cell 객체를 생성하여 텍스트를 저장하고 수정할 수 있다.
== Sample Source ==
// xls엑셀 파일 로드
Workbook wb = excelService.loadWorkbook(filename);
// xlsx엑셀 파일 로드
XSSFWorkbook wb = null;
wb = excelService.loadWorkbook(sb.toString(), wb);
log.debug("testModifyCellContents after loadWorkbook....");
Sheet sheet = wb.getSheetAt(0);
Font f2 = wb.createFont();
CellStyle cs = wb.createCellStyle();
cs = wb.createCellStyle();
cs.setFont(f2);
cs.setWrapText(true);
Row row = sheet.createRow(rownum);
row.setHeight((short) 0x349);
Cell cell = row.createCell(cellnum);
// xls 엑셀방식일 경우
cell.setCellType(Cell.CELL_TYPE_STRING);  
cell.setCellValue(new HSSFRichTextString(content));
// xlsx 엑셀방식일 경우
cell.setCellType(XSSFCell.CELL_TYPE_STRING);
cell.setCellValue(new XSSFRichTextString(content));
cell.setCellStyle(cs);
sheet.setColumnWidth(20, (int) ((50 * 8) / ((double) 1 / 20)));
FileOutputStream out = new FileOutputStream(filename);
wb.write(out);
out.close();
=== Excel 문서 속성 수정 ===
엑셀 파일 문서의 속성(Header, Footer)을 수정한다.\\
Header 및 Footer 클래스로 엑셀문서의 Header와 Footer의 값과 속성을 설정할 수 있다.
== Sample Source ==
// 엑셀 파일 로드
Workbook wb = excelService.loadWorkbook(filename); // xls 버전
Workbook wb = excelService.loadWorkbook(filename, new XSSFWorkbook()); // xlsx 버전
LOGGER.debug("testModifyCellContents after loadWorkbook....");
Sheet sheet = wb.createSheet("doc test sheet");
Row row = sheet.createRow(1);
Cell cell = row.createCell(1);
cell.setCellValue(new HSSFRichTextString("Header/Footer Test")); // xls 버전
cell.setCellValue(new XSSFRichTextString("Header/Footer Test")); // xlsx 버전
// Header
Header header = sheet.getHeader();
header.setCenter("Center Header");
header.setLeft("Left Header");
header.setRight(HSSFHeader.font("Stencil-Normal", "Italic") + HSSFHeader.fontSize((short) 16) + "Right Stencil-Normal Italic font and size 16"); // xls 버전
header.setRight(XSSFOddHeader.stripFields("&IRight Stencil-Normal Italic font and size 16")); // xlsx 버전
// Footer
// xls 버전
Footer footer = sheet.getFooter();
footer.setCenter(HSSFHeader.font("Fixedsys", "Normal") + HSSFHeader.fontSize((short) 12) + "- 1 -");
// xlsx 버전
Footer footer = (XSSFOddFooter) sheet.getFooter();
footer.setCenter(XSSFOddHeader.stripFields("Fixedsys"));
footer.setLeft("Left Footer");
footer.setRight("Right Footer");
// 엑셀 파일 저장
FileOutputStream out = new FileOutputStream(filename);
wb.write(out);
out.close();
=== 셀 내용 추출 ===
엑셀 파일을 읽어 특정 셀의 값을 얻어온다.\\
HSSFCell 클래스의 getRichStringCellValue, getNumericCellValue, getStringCellValue 등 다양한 type의 Cell 내용을 추출할 수 있다.
== Sample Source ==
Workbook wbT = excelService.loadWorkbook(filename); // xls 버전
Workbook wbT = excelService.loadWorkbook(filename, new XSSFWorkbook()); // xlsx 버전
Sheet sheetT = wbT.getSheet("cell test sheet");
for (int i = 0; i < 100; i++) {
	Row row = sheet.createRow(i);
	for (int j = 0; j < 5; j++) {
		Cell cell = row.createCell(j);
		cell.setCellValue(new HSSFRichTextString("row " + i + ", cell " + j)); // xls 버전
		cell.setCellValue(new XSSFRichTextString("row " + i + ", cell " + j)); // xlsx 버전
		cell.setCellStyle(cs);
	}
}
=== 셀 속성 추출 ===
특정 셀의 속성(폰트, 사이즈 등)을 수정한다.\\
HSSFFont, HSSFCellStyle 등의 클래스를 이용하여 셀의 폰트, 사이즈 등의 셀 속성을 수정할 수 있다.
== Sample Source ==
// 엑셀 파일 로드
Workbook wb = excelService.loadWorkbook(filename); // xls 버전
Workbook wb = excelService.loadWorkbook(filename, new XSSFWorkbook()); //xlsx 버전
Sheet sheet = wb.createSheet("cell test sheet2");
sheet.setColumnWidth((short) 3, (short) 200);	// column Width
CellStyle cs = wb.createCellStyle();
Font font = wb.createFont();
font.setFontHeight((short) 16);
font.setBoldweight((short) 3);
font.setFontName("fixedsys");
cs.setFont(font);
cs.setAlignment(CellStyle.ALIGN_RIGHT);	// cell 정렬
cs.setWrapText( true );
for (int i = 0; i < 100; i++) {
	HSSFRow row = sheet.createRow(i);
	row.setHeight((short)300); // row의 height 설정
  
	for (int j = 0; j < 5; j++) {
		HSSFCell cell = row.createCell((short) j);
		cell.setCellValue(new HSSFRichTextString("row " + i + ", cell " + j)); // xls 버전
		cell.setCellValue(new XSSFRichTextString("row " + i + ", cell " + j)); // xlsx 버전
		cell.setCellStyle( cs );
	}
}
// 엑셀 파일 저장
FileOutputStream out = new FileOutputStream(filename);
wb.write(out);
out.close();
=== 공통 템플릿 사용 ===
공통 템플릿을 사용하여 일관성을 유지한다.
jXLS 오픈소스를 사용하여 작성된 템플릿에 지정된 값을 저장한다.
== Sample Source ==
List persons = new ArrayList();
PersonHourVO person = new PersonHourVO();
person.setName("Yegor Kozlov");
person.setId("YK");
person.setMon(5.0);
person.setTue(8.0);
person.setWed(10.0);
person.setThu(5.0);
person.setFri(5.0);
person.setSat(7.0);
person.setSun(6.0);
persons.add(person); 
PersonHourVO person1 = new PersonHourVO();
person1.setName("Gisella Bronzetti");
person1.setId("GB");
person1.setMon(4.0);
person1.setTue(3.0);
person1.setWed(1.0);
person1.setThu(3.5);
person1.setSun(4.0);
persons.add(person1); 
Map beans = new HashMap();
beans.put("persons", persons);
XLSTransformer transformer = new XLSTransformer();
transformer.transformXLS(filename, beans, sbResult.toString());
== Excel Template ==
${persons.name}	${persons.id}	${persons.mon}	${persons.tue}	${persons.wed}	${persons.thu}	${persons.fri}	${persons.sat}	${persons.sun}	$[C4+D4+E4+F4+G4+H4+I4]
		Total Hrs:	$[SUM(C4)]	$[SUM(D4)]	$[SUM(E4)]	$[SUM(F4)]	$[SUM(G4)]	$[SUM(H4)]	$[SUM(I4)]	$[SUM(J4)]
{{:egovframework:rte:fdl:template.png|}}
== Template 적용결과 ==
{{:egovframework:rte:fdl:template_result.png|}}
==== Excel 다운로드 ====
== Configuration ==
	
== Sample Source ==
Controller 클래스 작성
Map 사용
@RequestMapping("/sale/listExcelCategory.do")
public ModelAndView selectCategoryList() throws Exception {
	List
VO 사용
@RequestMapping("/sale/listExcelVOCategory.do")
public ModelAndView selectCategoryVOList() throws Exception {
	List lists = new ArrayList();
	
	UsersVO users = new UsersVO();
	
	//Map mapCategory = new HashMap();
	users.setId("0000000001");
	users.setName("Sample Test");
	users.setDescription("This is initial test data.");
	users.setUseYn("Y");
	users.setRegUser("test");
	
	lists.add(users);
	
	users.setId("0000000002");
	users.setName("test Name");
	users.setDescription("test Deso1111");
	users.setUseYn("Y");
	users.setRegUser("test");
	
	lists.add(users);
	
	Map map = new HashMap();
	map.put("category", lists);
	
	return new ModelAndView("categoryExcelView", "categoryMap", map);
}
View 클래스 작성(xls 버전)
public class CategoryExcelView extends AbstractExcelView {
	private static final Logger LOGGER  = LoggerFactory.getLogger(CategoryExcelView.class);
	@Override
	protected void buildExcelDocument(Map model, HSSFWorkbook wb, HttpServletRequest req, HttpServletResponse resp) throws Exception {
        HSSFCell cell = null;
        LOGGER.debug("### buildExcelDocument start !!!");
        HSSFSheet sheet = wb.createSheet("User List");
        sheet.setDefaultColumnWidth(12);
        // put text in first cell
        cell = getCell(sheet, 0, 0);
        setText(cell, "User List");
        // set header information
        setText(getCell(sheet, 2, 0), "id");
        setText(getCell(sheet, 2, 1), "name");
        setText(getCell(sheet, 2, 2), "description");
        setText(getCell(sheet, 2, 3), "use_yn");
        setText(getCell(sheet, 2, 4), "reg_user");
        LOGGER.debug("### buildExcelDocument cast");
        Map map= (Map) model.get("categoryMap");
        List
View 클래스 작성(xlsx 버전)
public class CategoryPOIExcelView extends AbstractPOIExcelView {
	private static final Logger LOGGER  = LoggerFactory.getLogger(CategoryPOIExcelView.class);
	@Override
	protected void buildExcelDocument(Map model, XSSFWorkbook wb, HttpServletRequest req, HttpServletResponse resp) throws Exception {
        XSSFCell cell = null;
        LOGGER.debug("### buildExcelDocument start !!!");
        XSSFSheet sheet = wb.createSheet("User List");
        sheet.setDefaultColumnWidth(12);
        // put text in first cell
        cell = getCell(sheet, 0, 0);
        setText(cell, "User List");
        // set header information
        setText(getCell(sheet, 2, 0), "id");
        setText(getCell(sheet, 2, 1), "name");
        setText(getCell(sheet, 2, 2), "description");
        setText(getCell(sheet, 2, 3), "use_yn");
        setText(getCell(sheet, 2, 4), "reg_user");
        LOGGER.debug("### buildExcelDocument cast");
        Map map= (Map) model.get("categoryMap");
        List categories = (List) map.get("category");
        boolean isVO = false;
        if (categories.size() > 0) {
        	Object obj = categories.get(0);
        	isVO = obj instanceof UsersVO;
        }
        for (int i = 0; i < categories.size(); i++) {
        	if (isVO) {	// VO
        		LOGGER.debug("### buildExcelDocument VO : {} started!!", i);
        		UsersVO category = (UsersVO) categories.get(i);
	            cell = getCell(sheet, 3 + i, 0);
	            setText(cell, category.getId());
	            cell = getCell(sheet, 3 + i, 1);
	            setText(cell, category.getName());
	            cell = getCell(sheet, 3 + i, 2);
	            setText(cell, category.getDescription());
	            cell = getCell(sheet, 3 + i, 3);
	            setText(cell, category.getUseYn());
	            cell = getCell(sheet, 3 + i, 4);
	            setText(cell, category.getRegUser());
	            LOGGER.debug("### buildExcelDocument VO : {} end!!", i);
        	 } else {	// Map
        		LOGGER.debug("### buildExcelDocument Map : {} started!!", i);
        		Map category = (Map) categories.get(i);
 	            cell = getCell(sheet, 3 + i, 0);
 	            setText(cell, category.get("id"));
 	            cell = getCell(sheet, 3 + i, 1);
 	            setText(cell, category.get("name"));
 	            cell = getCell(sheet, 3 + i, 2);
 	            setText(cell, category.get("description"));
 	            cell = getCell(sheet, 3 + i, 3);
 	            setText(cell, category.get("useyn"));
 	            cell = getCell(sheet, 3 + i, 4);
 	            setText(cell, category.get("reguser"));
 	            LOGGER.debug("### buildExcelDocument Map : {} end!!", i);
        	 }
        }
    }
}
==== Excel 업로드 ====
== Configuration ==
	
	
	
	
	
  * class : egovframework.rte.fdl.excel.impl.EgovExcelServiceImpl
  * propertyPath : xml형식의 엑셀 형식정보 위치
  * mapClass : 개발자가 작성한 VO와 Query의 mapping을 위한 클래스
  * mapBeanName : Excel Cell과 VO를 mapping 구현 Bean name (mapClass보다 우선함)
  * sqlMapClient : ibatis의 sqlMapClient(ibatis 사용시 적용)
  * sqlSessionTemplate : mybatis의 sqlSessionTemplate(mybatis 사용시 적용)
== Sample Source ==
VO 클래스 작성
public class EmpVO implements Serializable {
  
	private BigDecimal empNo;
	private String empName;
	private String job;
  
	public BigDecimal getEmpNo() {
		return empNo;
	}
  
  public void setEmpNo(BigDecimal empNo) {
  	this.empNo = empNo;
  }
  
	public String getEmpName() {
		return empName;
	}
	
	public void setEmpName(String empName) {
		this.empName = empName;
	}
	
	public String getJob() {
		return job;
	}
	
	public void setJob(String job) {
		this.job = job;
	}
}
  * 엑셀과 Query의 mapping을 위한 VO클래스
Mapping 클래스 작성
public class EgovExcelTestMapping extends EgovExcelMapping {
	private static final Logger LOGGER = LoggerFactory.getLogger(EgovExcelTestMapping.class);
	@Override
	public EmpVO mappingColumn(Row row) {
		Cell cell0 = row.getCell(0);
    	        Cell cell1 = row.getCell(1);
    	        Cell cell2 = row.getCell(2);
		EmpVO vo = new EmpVO();
		vo.setEmpNo(new BigDecimal(cell0.getNumericCellValue()));
		vo.setEmpName(EgovExcelUtil.getValue(cell1));
		vo.setJob(EgovExcelUtil.getValue(cell2));
		LOGGER.debug("########### vo is {}", vo.getEmpNo());
		LOGGER.debug("########### vo is {}", vo.getEmpName());
		LOGGER.debug("########### vo is {}", vo.getJob());
		return vo;
	}
}
  * 엑셀과 VO의 mapping을 위한 mapping클래스
  * **EgovExcelMapping** 클래스를 상속받아서 **mappingColumn** 메소드를 오버라이드하여 구현
  * **HSSFCell** 클래스에서 엑셀 값을 추출하여 Query를 실행시키기 위한 VO와 mapping
== Query ==