일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- GC
- g1
- Security
- OAuth
- 비동기
- RabbitMQ
- java
- spring boot
- assertj
- JWT
- Spring
- 리팩토링
- Refactoring
- clean code
- 페이스북
- jvm
- 스프링부트
- 스프링 부트
- gdg
- tomcat
- load balancing
- JPA
- 페이징
- 시큐리티
- 클린코드
- oauth2
- Producer
- 권한
- apache
- 스프링
Archives
- Today
- Total
허원철의 개발 블로그
Spring Boot - Excel Upload & Download 본문
이번 글은 Spring Boot를 이용하여 Excel을 Upload 와 Download을 다루는 예제 글입니다.
자바진영에서 엑셀을 Upload, Download하기 위해 POI를 가장 많이 사용합니다. 엑셀 버전마다 다를 수 있는데, xls의 경우는 poi, xlsx의 경우는 poi-ooxml를 추가하면 됩니다. (xls의 경우, Excel(5.0/95)이하는 불가능한 걸로 알고 있습니다.)
1. Gradle
1 2 3 4 5 6 7 8 9 | dependencies { compile('org.apache.poi:poi-ooxml:3.16') // .xlsx compile('org.apache.poi:poi:3.16') // .xls compile('org.projectlombok:lombok:1.16.6') compile('org.springframework.boot:spring-boot-starter-web') testCompile('org.springframework.boot:spring-boot-starter-test') } | cs |
2. Upload
- Java 8의 Function, Stream을 이용하여 Component를 추가해보겠습니다.
2-1. Component
1) MultipartFile과 Function을 이용하여 Component를 만들어줍니다.
1 2 3 4 5 6 7 8 | @Component public class ExcelReadComponent { public <T> List<T> readExcelToList(final MultipartFile multipartFile, final Function<Row, T> rowFunc) throws IOException, InvalidFormatException { // ... } } | cs |
2) File을 Workbook으로 변환하기 전에 엑셀 파일인지 검증해줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public <T> List<T> readExcelToList(final MultipartFile multipartFile, final Function<Row, T> rowFunc) throws IOException, InvalidFormatException { final Workbook workbook = readWorkbook(multipartFile); // ... } private Workbook readWorkbook(MultipartFile multipartFile) throws IOException, InvalidFormatException { verifyFileExtension(multipartFile); return multipartFileToWorkbook(multipartFile); } private void verifyFileExtension(MultipartFile multipartFile) throws InvalidFormatException { if( !isExcelExtension(multipartFile.getOriginalFilename()) ) { throw new InvalidFormatException("This file extension is not verify"); } } private boolean isExcelExtension(String fileName) { return fileName.endsWith(ExcelConfig.XLS) || fileName.endsWith(ExcelConfig.XLSX); } private boolean isExcelXls(String fileName) { return fileName.endsWith(ExcelConfig.XLS); } private boolean isExcelXlsx(String fileName) { return fileName.endsWith(ExcelConfig.XLSX); } | cs |
3) Workbook으로 변환 뒤, Stream을 이용하여 Row 갯수 만큼 Domain로 바꾸고 해당 Stream을 List로 변환해줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 | public <T> List<T> readExcelToList(final MultipartFile multipartFile, final Function<Row, T> rowFunc) throws IOException, InvalidFormatException { final Workbook workbook = readWorkbook(multipartFile); final Sheet sheet = workbook.getSheetAt(0); final int rowCount = sheet.getPhysicalNumberOfRows(); return IntStream .range(0, rowCount) .mapToObj(rowIndex -> rowFunc.apply(sheet.getRow(rowIndex))) .collect(Collectors.toList()); } | cs |
2-2. Controller & Domain
1) 위에서 작성한 Component를 이용하여 JSON 형태로 반환해줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 | @RestController @RequestMapping("upload") public class UploadExcelController { @Autowired ExcelReadComponent excelReadComponent; @PostMapping("excel") public List<Product> readExcel(@RequestParam("file") MultipartFile multipartFile) throws IOException, InvalidFormatException { return excelReadComponent.readExcelToList(multipartFile, Product::new); } } | cs |
2) Row를 이용하여 Domain 객체를 만들어줍니다. (간단한 예제이기 때문에 유효성 검사가 없으니 상세한 작업을 원한다면 추가해줘야 할 것 입니다.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @Data @AllArgsConstructor public class Product implements Serializable { private String uniqueId; private String name; private String comment; public Product(Row row) { this(row.getCell(0).getStringCellValue(), row.getCell(1).getStringCellValue(), row.getCell(2).getStringCellValue()); } } | cs |
3. Download
- spring 4.x 이전에는 AbstractExcelView를 사용하였으나, 이후에는 AbstractXlsView, AbstractXlsxView, AbstractXlsxStreamingView를 이용해야 합니다.
- 각각의 Component로 등록을 해준 뒤, ModelAndView에서 해당 name으로 반환할 때, 인식할 수 있도록 @EnableWebMvc를 추가해줍니다.
- 각각의 AbstractXXXView는 내부적으로 기본 셋팅(Workbook 등등...) 되어 있기 때문에, 공통적으로 buildExcelDocument()를 오버라이딩하면 됩니다.
- 그래서 공통적으로 사용할 ExcelCommonUtil을 만들었습니다.
1 2 3 4 5 6 7 | @Override protected void buildExcelDocument(Map<String, Object> model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { new ExcelCommonUtil(workbook, model, response).createExcel(); } | cs |
- workbook은 해당 Excel형식에 맞는 인스턴스 객체가 됩니다. (XSSFWorkbook, SXSSFWorkbook, HSSFWorkbook 등)
- model은 Controller에서 ModelAndView에서 담은 Map을 뜻합니다.
- response는 클라이언트에게 반환해줄 객체입니다.
3-1. ExcelCommonUtil
1 2 3 4 5 6 7 8 9 10 | public void createExcel() { // 1) setFileName(response, mapToFileName()); // 2) Sheet sheet = workbook.createSheet(); // 3) createHead(sheet, mapToHeadList()); // 4) createBody(sheet, mapToBodyList()); } | cs |
1) Download될 Excel 파일에 대해 명명을 해줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | private void setFileName(HttpServletResponse response, String fileName) { response.setHeader("Content-Disposition", "attachment; filename=\"" + setFileExtension(fileName) + "\""); } private String setFileExtension(String fileName) { if ( workbook instanceof XSSFWorkbook) { fileName += ".xlsx"; } if ( workbook instanceof SXSSFWorkbook) { fileName += ".xlsx"; } if ( workbook instanceof HSSFWorkbook) { fileName += ".xls"; } return fileName; } | cs |
2) 해당 Excel 파일에 하나의 시트를 만들어줍니다. (단순 예제이기 때문에 시트 하나만 만들어줬습니다.)
3) head에 해당하는 데이터를 넣어줍니다.
1 2 3 | private void createHead(Sheet sheet, List<String> headList) { createRow(sheet, headList, 0); } | cs |
4) body에 해당하는 데이터를 넣어줍니다.
1 2 3 4 5 6 | private void createBody(Sheet sheet, List<List<String>> bodyList) { int rowSize = bodyList.size(); for (int i = 0; i < rowSize; i++) { createRow(sheet, bodyList.get(i), i + 1); } } | cs |
※ createHead, createBody에서 공통으로 쓰인 메소드
1 2 3 4 5 6 7 8 9 | private void createRow(Sheet sheet, List<String> cellList, int rowNum) { int size = cellList.size(); Row row = sheet.createRow(rowNum); for (int i = 0; i < size; i++) { row.createCell(i) .setCellValue(cellList.get(i)); } } | cs |
3-2. Controller
- ModelAndView를 이용하여 각각의 Component를 호출해줍니다.
1 2 3 4 5 6 7 8 9 10 11 | @Controller @RequestMapping("download") public class DownloadExcelController { @GetMapping("excel-xls") public ModelAndView xlsView() { return new ModelAndView("excelXlsView", getDefaultMap()); } // ... } | cs |
참고
- 조금 더 자세한 코드 내용은 깃헙을 참고하시기 바랍니다.
'web' 카테고리의 다른 글
Spring Boot - CORS (410) | 2017.06.25 |
---|---|
Spring Boot - Async 제대로 사용하기 (407) | 2017.05.18 |
Spring Boot - Data JPA & ModelMapper (414) | 2017.04.30 |
Spring Boot - MyBatis (396) | 2017.04.13 |
Spring Boot + JPA + Time package Issue (437) | 2017.03.22 |
Comments