https://brilliantdevelop.tistory.com/125?category=1018911에서
FreeBoard테이블 엑셀 다운로드를 하였다.
이제 Member테이블에서도 엑셀 다운로드를 하려고 한다.
Controller에 다음과 같이 member에서의 excel다운로드기능의 메소드를 추가하려고 한다.
ExcelControoler의 excelDownloadMember 메소드
@RequestMapping("/member/excelDown")
public void excelDownloadMember(HttpServletResponse response, @ModelAttribute("searchVO")SearchVO searchVO
, @ModelAttribute("searchJob") String searchJob
, @ModelAttribute("searchHobby") String searchHobby) throws IOException{
XSSFWorkbook wb=null;
Sheet sheet=null;
Row row=null;
Cell cell=null;
wb = new XSSFWorkbook();
sheet = wb.createSheet("freeBoard");
List<MemberVO> memberList=excelService.getMemberList(searchVO,searchJob,searchHobby);
//첫행 열 이름 표기
int cellCount=0;
row = sheet.createRow(0); //0번째 행
cell=row.createCell(cellCount++);
cell.setCellValue("member아이디");
cell=row.createCell(cellCount++);
cell.setCellValue("member이름");
cell=row.createCell(cellCount++);
cell.setCellValue("member비밀번호");
// .........생략 테이블 추가할 때 마다 이 과정 계속 써줘야하나?
for(int i=0; i < memberList.size() ; i++ ) {
row=sheet.createRow(i+1); // '열 이름 표기'로 0번째 행 만들었으니까 1번째행부터
cellCount=0; //열 번호 초기화
cell=row.createCell(cellCount++);
cell.setCellValue(memberList.get(i).getMemId());
cell=row.createCell(cellCount++);
cell.setCellValue(memberList.get(i).getMemName());
cell=row.createCell(cellCount++);
cell.setCellValue(memberList.get(i).getMemPass());
//........ 테이블 추가할 때 마다 이 과정 계속 써줘야 하나?
}
// 컨텐츠 타입과 파일명 지정
response.setContentType("ms-vnd/excel");
response.setHeader("Content-Disposition", "attachment;filename=member.xlsx"); //파일이름지정.
//response OutputStream에 엑셀 작성
wb.write(response.getOutputStream());
}
테이블별로 VO가 다른데 cell에 데이터 집어넣을때마다 매번 get메소드를 직접 써주기 너무 힘들다.만약 VO의 필드가 100개라면 100개 다 써야하나??이를 Java Reflection을 이용해 처리해보도록 하자.Reflection에 대한 기초 사항은 https://brilliantdevelop.tistory.com/85?category=988809를 참고하도록 하자.get메소드를 직접 쓰지 않고도 어떤테이블, 어떤VO가 오더라도 excel다운로드를 지원해주는 util클래스를 만들자.
먼저 필드에 대한 설명을 지정할 수 있는 @annotation을 만들자.
ExcelColumn.java
package com.study.excel
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelColumn {
String headerName() default "";
}
이제 이 @ExcelColumn을 FreeBoardVO에 붙이자.
FreeBoardVO
public class FreeBoardVO {
@ExcelColumn(headerName = "글번호")
private int boNo ; /* 글 번호 */
@ExcelColumn(headerName = "글 제목")
private String boTitle ; /* 글 제목 */
@ExcelColumn(headerName = "글 분류 코드")
private String boCategory ; /* 글 분류 코드 */
@ExcelColumn(headerName = "작성자명")
private String boWriter ; /* 작성자명 */
@ExcelColumn(headerName = "비밀번호")
private String boPass ; /* 비밀번호 */
@ExcelColumn(headerName = "글 내용")
private String boContent ; /* 글 내용 */
@ExcelColumn(headerName = "조회수")
private int boHit ; /* 조회수 */
@ExcelColumn(headerName = "등록 일자")
private String boRegDate ; /* 등록 일자 */
@ExcelColumn(headerName = "수정일자")
private String boModDate ; /* 수정 일자 */
@ExcelColumn(headerName = "삭제여부")
private String boDelYn ; /* 삭제 여부 */
이제 VO가 어떤 필드가 있든 간에 excel을 다운받을 수 있도록 해주는 util프로그램을 만들자.
SimpleExcelFile.java
package com.study.excel;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import com.study.free.vo.FreeBoardVO;
import com.sun.net.httpserver.HttpServer;
public class SimpleExcelFile {
// Workbook wb = new HSSFWorkbook(); //xls
private XSSFWorkbook wb; // xlsx
private Sheet sheet;
private Row row;
private Cell cell;
private Class<?> clazz;
private List<?> list;
public SimpleExcelFile(String sheetName, Class<?> clazz, List<?> list)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
wb = new XSSFWorkbook(); // xlsx
sheet = wb.createSheet(sheetName);
this.list = list;
this.clazz = clazz;
setExcelCoulmnName();
setExcelBody();
}
private void setExcelCoulmnName() {
row = sheet.createRow(0);
Field[] fields = clazz.getDeclaredFields(); // 일단 Declared로 했는데 나중에 상속된것관계까지고려한다면 그냥 필드로 바꾸고
// setAccessible(true)로 바꿔주면 되긴함
for (int i = 0; i < fields.length; i++) {
// System.out.println("필드이름 : "+field.getName()+ " 필드 타입:"+field.getType());
ExcelColumn excelColumn = fields[i].getAnnotation(ExcelColumn.class);
if (excelColumn != null) {
cell = row.createCell(i);
cell.setCellValue(excelColumn.headerName());
}
}
}
// reflection으로 모든 메소드 실행해야지, get
private void setExcelBody()
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException {
for (int i = 0; i < list.size(); i++) {
row = sheet.createRow(i + 1);
int rowNum = 0;
Field[] fields = clazz.getDeclaredFields();
for (int j = 0; j < fields.length; j++) {
cell = row.createCell(rowNum++);
ExcelColumn excelColumn = fields[j].getAnnotation(ExcelColumn.class);
if (excelColumn != null) {
fields[j].setAccessible(true);
Object obj = fields[j].get(list.get(i));
if (obj != null)
cell.setCellValue(obj.toString());
}
}
}
}
public void write(OutputStream outputStream) throws IOException {
wb.write(outputStream);
// wb.close(); //xlsx는 close자동으로 해줌. xls 할거면 close따로 해주기
}
}
생성자에서 setExcelCoulmnName(), setExcelBody() 두 메소드를 실행하는데
이 때 java Reflection을 이용해 excel을 만든다.
이 SimpleExcelFile을 이용한다면 Controller는 다음과 같이 간단해진다.
(sheet이름도 검색어 적용되도록 살짝 변경했다..)
ExcelController.java
package com.study.excel.web;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import javax.inject.Inject;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import com.study.common.util.SimpleExcelFile;
import com.study.excel.service.IExcelService;
import com.study.free.vo.FreeBoardSearchVO;
import com.study.free.vo.FreeBoardVO;
import com.study.member.vo.MemberSearchVO;
import com.study.member.vo.MemberVO;
@Controller
public class ExcelController {
@Inject
IExcelService excelService;
@RequestMapping("/free/excelDown")
public void excelDownload(HttpServletResponse response, @ModelAttribute("searchVO")SearchVO searchVO
,@ModelAttribute("searchCategory")String searchCategory)
throws IOException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
List<FreeBoardVO> freeBoardList=excelService.getBoardList(searchVO,searchCategory);
String sheetName="";
if( StringUtils.isNotBlank(searchVO.getSearchWord()) ) {
sheetName=searchVO.getSearchType()+"_"+searchVO.getSearchWord();
}
if( StringUtils.isNotBlank(searchCategory) ) {
if(StringUtils.isNotBlank(searchVO.getSearchWord())) {
sheetName=sheetName+"_"+searchCategory;
}else {
sheetName=sheetName+searchCategory;
}
}
if(StringUtils.isBlank(searchCategory) && StringUtils.isBlank(searchVO.getSearchWord())) {
sheetName="noSearch";
}
SimpleExcelFile simpleExcelFile= new SimpleExcelFile(sheetName, FreeBoardVO.class, freeBoardList);
//생성자에서 sheet이름, columnName, Body생성
// 컨텐츠 타입과 파일명 지정
response.setContentType("ms-vnd/excel");
response.setHeader("Content-Disposition", "attachment;filename=freeBoard.xlsx");
simpleExcelFile.write(response.getOutputStream());
}
}
만약 member테이블을 excel다운로드하고싶다면
Controller에 member테이블 excel다운로드요청처리메소드만 만들면 된다.
(+ member테이블을 조회하느 service,dao 메소드도 추가하자)
@RequestMapping("/member/excelDown")
public void excelMemberDown(HttpServletResponse response, @ModelAttribute("searchVO")MemberSearchVO searchVO) throws IOException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
//엑셀에 실제로 담을 데이터 페이징 없이 전체 데이터를 list로
List<MemberVO> memberList=excelService.getMemberList(searchVO);
SimpleExcelFile simpleExcelFile= new SimpleExcelFile("memberList", MemberVO.class, memberList);
//생성자에서 sheet이름, columnName, Body생성
// 컨텐츠 타입과 파일명 지정
response.setContentType("ms-vnd/excel");
//response.setHeader("Content-Disposition", "attachment;filename=example.xls");
response.setHeader("Content-Disposition", "attachment;filename=example.xlsx");
simpleExcelFile.write(response.getOutputStream());
}
이로써 어떤 테이블을 excel로 만들더라도
그때 그때 직접 cell이름과 값을 저장하는 코드를 쓰는 것이 아니라
SimpleExcelFile을 이용해서 한번에 처리하도록 했다.
단 나는 VO에서 field1의 getter가 getField1()이라는 가정하에 작성하였다.