[이슈] Springboot, common-csv 엑셀 다운로드 구현

보라도리·2024년 4월 9일

springboot 이슈 정리

목록 보기
3/6

오늘 포스팅은 Springboot 기반 API 서버에서 엑셀 다운로드 기능 구현 예제에 대한 것이다.

기술 스택

springboot 3.2.3
kotlin
JDK21
apache.common-csv 1.10.0

엑셀 다운로드 기능 구현을 위해 apache.common-csv 1.10.0 를 사용했다.

코드 - Controller

@RestController
class UserController(
	private val getMemberInfoUseCase: GetMemberInfoUseCase,
    private val csvUtil: CsvUtil,
) {
@GetMapping("/csv")
    fun dowonloadCsv(
        response: HttpServletResponse,
        request: HttpServletRequest
    ) {
        val csvFileName = "excel/excel-${LocalDateTime.now()}.csv" // (1)
        val memberInfo = this.getMemberInfoUseCase.getMemberInfoList() // (2)
        val fileInputStream: InputStream // (3)
        try {
            val headers = listOf("no", "유저 닉네임", "유저 전화번호")
            val data = memberInfo.map { listOf(it.userId, it.nickName, it.contact) }
            csvUtil.write(csvFileName, headers, data) // (4)

            fileInputStream = FileInputStream(csvFileName)

			// (5)
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"$csvFileName\"")
            response.setHeader("Content-Type", "application/octet-stream")
            val outputStream = response.outputStream
			
            var length: Int
            val buffer = ByteArray(1024)

            while ((fileInputStream.read(buffer).also { length = it }) != -1) {
                outputStream.write(buffer, 0, length)
            }
			// (6)
            outputStream.flush()
            outputStream.close()
            fileInputStream.close()
        } catch (e: IOException) {
            throw RuntimeException("csv 파일 생성 실패")
        } finally { // (7)
            val filePath = Paths.get(csvFileName)
            val directoryPath = filePath.parent

            if (Files.exists(directoryPath)) {
                if (File(csvFileName).delete()) {
                    logger().info("========== 파일삭제 성공 ==========")
                } else {
                    logger().info("========== 파일삭제 실패 ==========")
                }
            }
        }
    }
}

(1) 엑셀파일 다운로드 시, 파일명 정의
(2) 유저 정보 조회 usecase
(3) 파일 작성을 위해 InputStream 정의
(4) headers와 data로 엑셀 파일에 담을 내용 각각 정의 및 util 클래스로 파일 작성
(5) HttpServletResponse 에 Header 설정
stream 형태로 데이터 전송을 하기에 그에 대한 설정
(6) stream flush & close 처리
(7) api 호출 종료 시, 서버에 떨군 파일 찾아서 제거

코드 - Util

@Component
class CsvUtil {

    fun write(fileName: String, headers: List<String>, data: List<List<String?>>) {
        val sw: FileWriter
        val csvPrinter: CSVPrinter
        try {
            val filePath = Paths.get(fileName) // (1)
            val directoryPath = filePath.parent // (2)

            if (!Files.exists(directoryPath)) { // (3)
                Files.createDirectories(directoryPath)
            }
            sw = FileWriter(fileName) // (4)
            csvPrinter = CSVPrinter(sw, CSVFormat.DEFAULT.builder().setHeader(*headers.toTypedArray()).build()) // (5)

            for (row in data) { // (6)
                csvPrinter.printRecord(row)
            }

            sw.flush() // (7)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}

*폴더에 파일을 떨구고, 지우는 작업을 하기에 작성된 코드이다. (1) ~ (3)
(1) 파일 이름으로부터 경로를 찾는다.
(2) 폴더 위치를 찾는다.
(3) 폴더가 존재하지 않으면, 새로 만들어준다.
(4) 파일 이름으로 파일생성기를 생성한다.
(5) 엑셀 파일 출력기를 생성한다. ( Controller에서 정의한 헤더를 format에 넣어서 생성한다. )
(6) record 데이터를 row 단위로 파일에 작성한다.
(6) 파일 생성기 flush

apache.common-csv 1.10.0 라이브러리를 이용해서 Csv 파일 생성하는 로직이다.

실행

localhost:8080/csv GET 요청을 보내면, root 폴더 아래 /excel 위치에 엑셀파일이 생성된다.

마무리

실무에서 엑셀 파일 다운로드 기능을 구현하면서, 앞으로도 종종 사용될 수 있을 거 같아 sample 코드를 남겨두고자 작성했다.

0개의 댓글