- 해당 기능을 구현하기 위해서 아래와 같은 의존성을 추가하였습니다. 금일 기준 가장 최신버전으로 적용하였습니다. - 해당 의존성을 위해서 xls, xlsx, csv 파일들에 대해서 읽어오고 활용할 수 있습니다. - 또한, 간단한 화면을 구현하기 위해 Thymeleaf를 이용하여서 간단한 화면을 구성하였습니다.
- 아래와 같이 resources/templates/pages 디렉터리 내에 excelUpload.html이라는 파일을 만들었습니다. - 해당 코드는 multipart/form-data의 전송 형태로 [POST] excel/upload 엔드포인트로 서버에 엑셀을 전달하도록 구성하였습니다. - 사용자는 파일을 선택하고 업로드 버튼을 누르면 서버로 전송되는 구조입니다.
- excelUpload() 메서드에서 이를 처리합니다. 이를 통해, 최초 파일정보를 출력하고, Excel 파일을 읽어와서 Header를 출력하고, Body에 해당하는 데이터들은 리스트 객체로 구성하여 출력을 합니다. - 최종적으로 다시 /excel/upload 엔드포인트로 반환을 합니다.
package com.blog.springbootexcelpoi;
import com.blog.springbootexcelpoi.dto.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 엑셀 처리를 위한 서비스
*
* @author : leejonghoon
* @fileName : ExcelService
* @since : 2025. 2. 11.
*/@Slf4j@ServicepublicclassExcelService {
/**
* 엑셀 업로드를 수행
*
* @param file
* @param model
* @return
*/public String excelUpload(MultipartFile file, Model model) {
// 파일 정보 출력
log.debug("파일명: {}", file.getOriginalFilename());
log.debug("파일 크기: {} bytes", file.getSize());
try {
if (file.isEmpty()) {
model.addAttribute("message", "파일을 선택해주세요.");
return"/pages/excelUpload";
}
// 파일 확장자 검증Stringfilename= file.getOriginalFilename();
if (!filename.endsWith(".xlsx") && !filename.endsWith(".xls")) {
model.addAttribute("message", "Excel 파일만 업로드 가능합니다.");
return"/pages/excelUpload";
}
List<UserDto> userDtoList = newArrayList<>();
// Excel 파일 처리Workbookworkbook= WorkbookFactory.create(file.getInputStream());
Sheetsheet= workbook.getSheetAt(0);
// 헤더 행 읽기RowheaderRow= sheet.getRow(0);
for (Cell cell : headerRow) {
log.debug("{}\\t", cell.getStringCellValue());
}
// 데이터 행 읽기for (inti=1; i <= 10; i++) {
Rowrow= sheet.getRow(i);
// 행 들을 리스트 객체로 구성
userDtoList.add(UserDto.builder()
.number((int) row.getCell(0).getNumericCellValue()) // 순번
.name(row.getCell(1).getStringCellValue()) // 이름
.age((int) row.getCell(2).getNumericCellValue()) // 나이
.gender(row.getCell(3).getStringCellValue()) // 성별
.contact(row.getCell(4).getStringCellValue()) // 연락처
.build());
}
log.debug("구성한 리스트 객체 :: {}", userDtoList.toString());
log.debug("=================================================");
workbook.close();
model.addAttribute("message", "파일이 성공적으로 업로드되었습니다.");
} catch (IOException e) {
model.addAttribute("message", "파일 처리 중 오류가 발생했습니다: " + e.getMessage());
}
return"/pages/excelUpload";
}
}
💡 ExcelController - 위에서 구성한 엑셀 업로드 기능에 엑셀 다운로드 메서드를 구성하였습니다. - 해당 부분은 비즈니스 로직을 처리하는 ExcelService 내에 excelDownload()를 호출하여서 최종적으로 Resource라는 값을 출력받습니다. 이를 클라이언트에게 전달하며, header와 ContentType을 지정합니다.
1. Header : Content-Disposition: attachment; filename=userList.xlsx - 브라우저에게 이 응답을 다운로드할 파일로 처리하도록 지시합니다. - attachment: 파일을 다운로드하도록 지정합니다. - filename: 다운로드될 파일의 이름을 'userList.xlsx'로 지정합니다.
2. Content Type : Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - 응답 본문의 미디어 타입을 지정합니다. - .xlsx 파일의 공식 MIME 타입으로 지정합니다. - 브라우저가 Excel 파일임을 인식하고 적절히 처리할 수 있도록 합니다.
💡 ExcelService - 엑셀 다운로드의 비즈니스를 처리하는 excelDownload() 함수입니다.
1. 시트 생성 : createSheet()으로 시트를 생성합니다. 2. 헤더 생성 : sheet 내에 createRow() 메서드를 통해서 헤더를 생성합니다. 3. 데이터 생성 : 헤더 아래에 createRow() 메서드를 통해 데이터를 생성합니다. 4. 열 너비 자동 설정 : autoSizeColumn() 메서드를 통해 열 너비를 자동으로 생성합니다. 5. 파일 생성 : 생성된 Excel workbook의 내용을 ByteArrayOutputStream에 작성합니다. 이를 통해 바이트 형태로 변환이 됩니다. 6. 시트 종료 : close()으로 시트를 종료합니다. 7. 리소스 구성 : 구성한 바이트 데이터를 HTTP 응답에 전달할 수 있는 ByteArrayResource로 구성하여 반환합니다.