SPRING/라이브러리

POI 라이브러리 정리!

steadyMan 2020. 10. 29. 17:53

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)를 참조하여 구현에 보앗습니다. 

 

엑셀 속성의 관한 설정은 다른 게시물에 게시하겠습니다.

 

감사합니다.