ToBe끝판왕

[ Poi 라이브러리 ] JAVA로 Excel 다운로드 기능 구현 (2) 본문

■ 공부 기록/기능 구현

[ Poi 라이브러리 ] JAVA로 Excel 다운로드 기능 구현 (2)

업그레이드중 2024. 4. 7. 15:58
반응형

 

 


 

 

Apache POI 라이브러리 + 달력 형식의 Excel 파일 다운로드

 

 

 

▶  기본적인 환경설정

•  InteliJ + SpringFramework + Maven 프로젝트
•  JDK 버전 : 1.8
•  SpringFramework 버전 : 5.2.3 release
•  Apache POI 버전 : 4.1.2
•  Servlet Api 버전 : 3.1.0

 

 

 

▶  예제 기획

•   view 페이지에서 년도, 월을 서택하고 해당 월에 맞는 달력을 보여줌

•   프로젝트 내 있는 Excel 파일에서 셀 스타일 복사하여 달력에 적용

•   셀스타일 + 달력 적용하여 새로운 Excel 파일 생성

•   해당 Excel 파일 다운로드 ( 경로 지정 )

 

 

•  View 페이지 ( Excel2.jsp )

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>엑셀다운로드 과제 페이지</title>
</head>
<script type="text/javascript">


</script>
<body>
<form name="calendar" action="/calendar.do" method="get" style="text-align:center;">
    <div style="text-align:center;">
        <select id="year" name="year">
            <option value="2024">2024</option>
        </select>
    </div>
    <div style="text-align:center;">
        <select id="month" name="month">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
            <option value="4">4</option>
            <option value="5">5</option>
            <option value="6">6</option>
            <option value="7">7</option>
            <option value="8">8</option>
            <option value="9">9</option>
            <option value="10">10</option>
            <option value="11">11</option>
            <option value="12">12</option>
        </select>
    </div>
    <div style="text-align:center;">
        <button type="submit">달력 엑셀다운</button>
    </div>
</form>
</body>
</html>

  

 

 

•  Controller( CalController.java )

@Controller
public class CalController {

    @RequestMapping(value = "/calendar.do", method = RequestMethod.GET)
        public void downloadCalendar( HttpServletRequest request, HttpServletResponse response) throws IOException {

		// 엑셀파일 경로 변수 선언 및 초기화
                String localFile = "excel/localExcel.xlsx";
                String newFile = "C:/excel/copyExcel.xlsx";

		// 디렉토리 생성 관련 변수 선언 및 초기화
                String path = "C:/excel";
                File Folder = new File(path);

                // 해당 디렉토리가 없을경우 디렉토리를 생성합니다.
                if (!Folder.exists()) {
                    try{
                        Folder.mkdir(); //폴더 생성합니다.
                        System.out.println("폴더가 생성되었습니다.");
                    }
                    catch(Exception e){
                        e.getStackTrace();
                    }
                }else {
                    System.out.println("이미 폴더가 생성되어 있습니다.");
                }


            try {

                // FileInputStream 객체 생성 ( src/main/resources/excel 에 있는 Excel 파일 읽기 )
                // Poi 라이브러리 사용 > Excel 파일을 Workbook 객체로 로드 + XSSF( .xlsx )생성 > localWorkbook 변수 선언

                Resource resource = new ClassPathResource(localFile);
                InputStream inputStream = resource.getInputStream();

                XSSFWorkbook localWorkBook = new XSSFWorkbook(inputStream);


                // java.lang.IllegalArgumentException: Sheet index (0) is out of range (no sheets) 발생
                // Sheet 가 있는지 체크 필요
                int sheetCount = localWorkBook.getNumberOfSheets();

                if(sheetCount < 1)
                {
                    System.out.println("기존 엑셀파일에 시트가 없습니다.");
                    return;
                }

                // 셀 스타일 행, 열 관련 전역변수 저장
                // 복사할 셀 스타일의 셀 행과 열 변수 선언 및 초기화
                int endRow = 0;
                int endColumn = 0;

                // localWorkBook 객체의 첫번째 시트를 가져옴
                XSSFSheet worksheet = localWorkBook.getSheetAt(0);

                // worksheet의 실제 행 갯수를 가져옴
                endRow = worksheet.getPhysicalNumberOfRows();

                // endRow 까지 반복하며 각 행의 Cell 갯수 체크
                for(int i = 0; i < endRow; i++) {
                    // i번쨰 행의 실제 Cell 갯수를 가져옴
                    int maxCells = worksheet.getRow(i).getPhysicalNumberOfCells();
                    // 각 행의 Cell 중 가장 큰값 저장
                    if(maxCells > endColumn) endColumn = maxCells;
                }

                // 달력 form 생성
                // 로컬 Excel 파일의 Cell 스타일을 복사할 새 Excel 파일 Workbook 객체 생성
                XSSFWorkbook newWorkBook = new XSSFWorkbook();

                // 달력 불러오기 관련 변수 선언 및 초기화
                int year = Integer.parseInt(request.getParameter("year"));
                int month = Integer.parseInt(request.getParameter("month"));

                // 달력 Form 생성 메서드
                makeCalendar(localWorkBook, newWorkBook, year, month, endRow, endColumn);

                // 새로운 Excel 파일 저장
                FileOutputStream fos = new FileOutputStream(newFile);
                newWorkBook.write(fos);
                fos.close();

                // 새로운 Excel 파일 다운로드 매서드
                downloadFile(response, newFile);

                // Excel 파일에 대한 모든 변경사항 저장 > workBook.close() 메서드 사용 필수
                // workBook.close()를 안쓴 경우, > Excel 파일 다운로드 시, 손상된 엑셀파일 다운로드 오류 발생 가능
                // Excel 파일을 닫고 + 메모리 확보의 역할
                localWorkBook.close();
                newWorkBook.close();

            } catch (IOException e) {
                e.printStackTrace();
            }

        }
}

 

 

 

•  downloadCalendar 내 달력 form 생성 메서드 + 셀 스타일 복사 메서드

private static void makeCalendar(XSSFWorkbook localWorkBook, XSSFWorkbook newWorkBook, int year, int month, int endRow, int endColumn) {

            // 시트 가져오기
            XSSFSheet calendarSheet = localWorkBook.getSheetAt(0);

            // 시트 유무 파악
            // newWorkBook 객체에 있는 시트 갯수 저장
            int sheetCount = newWorkBook.getNumberOfSheets();


            XSSFSheet newSheet = null;

            if(sheetCount == 0 ) {
                newSheet = newWorkBook.createSheet(calendarSheet.getSheetName());
            } else {
                newSheet = newWorkBook.getSheetAt(0);
            }

            // 년도, 월 값 세팅 > index 0행에 세팅
            XSSFRow calendarRow = newSheet.createRow(0);

            calendarRow.createCell(1).setCellValue(year + "년");
            calendarRow.createCell(2).setCellValue(month + "월");


            // 요일 Header 세팅
            // Header 요일 배열 선언 및 초기화
            String[] daysOfWeek = {"일", "월", "화", "수", "목", "금", "토"};

            // 헤더값 설정할 행 생성 > index 1행에 헤더값들이 세팅되어야 함
            XSSFRow headerRow = newSheet.createRow(1);

            // 헤더값들은 간격을 두고 cell에 세팅되어야 한다.
            for (int i = 0; i < daysOfWeek.length; i++) {
                int cellIndex = (i * 3) + 2;
                headerRow.createCell(cellIndex).setCellValue(daysOfWeek[i]);

            }

            // 달력 일수 Excel 파일 세팅
            // Calendar 클래스는 추상클래스이므로 직접 객체 생성 불가능
            // 월은 0부터 시작하기 때문에 1을 빼야 한다.
            Calendar cal = Calendar.getInstance();
            cal.set(year, month-1, 1);

            // 해당 월의 시작 요일 계산 > 현재 Calendar 객체가 가리키는 날짜의 요일 반환
            // get() 메서드에서는 일요일은 1을 반환, 일반적으로 요일은 0(일요일) ~ 6(토요일)이기 때문에 -1을 한다.
            int startDay = cal.get(Calendar.DAY_OF_WEEK) -1;
            // 해당 월의 마지막 날짜 계산
            // getActualMaximum()메서드는 특정달의 마지막 날짜를 가져올때 사용
            int lastDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
            //
            int totalDay = startDay + lastDay;

            int currDay = 1;
            for(int i = 0; i < totalDay; i++) {
                if(i < startDay) {
                    copyCellStyles(localWorkBook, newWorkBook, endRow, endColumn, i, 0);
                } else {
                    copyCellStyles(localWorkBook, newWorkBook, endRow, endColumn, i, currDay);
                    currDay++;
                }
            }
        }
        

        // 기존 Excel 파일 Cell 스타일 복사 메서드
        private static void copyCellStyles(XSSFWorkbook styleWorkbook, XSSFWorkbook calWorkbook, int styleEndRow, int styleEndColumn, int dataIdx, int data) {

                // 기존 엑셀파일 i번째 Sheet 가져오기
                XSSFSheet styleSheet = styleWorkbook.getSheetAt(0);

                // 새로운 엑셀파일 i번째 Sheet 생성
                // 시트 유무 파악
                int sheetCount = calWorkbook.getNumberOfSheets();

                XSSFSheet newSheet = null;

                if(sheetCount == 0) {
                    newSheet = calWorkbook.createSheet(styleSheet.getSheetName());
                } else {
                    newSheet = calWorkbook.getSheetAt(0);
                }

                //데이터삽입 시작 위치 ( 달력의 시작 Row Index )
                int startIdx = 2;
                //데이터를 삽입할 행 위치
                int startRowShare = dataIdx / 7;
                //데이터를 삽입할 시작 행위치 계산
                int startRow = startIdx + (startRowShare * styleEndRow);
                //데이터를 삽입할 끝 행 위치 계산
                int endRow = startRow + styleEndRow;
                //데이터를 삽입할 시작 열위치 계산
                int startColumn = (dataIdx % 7) * styleEndColumn;
                //데이터를 삽입할 끝 열위치 계산
                int endColumn = startColumn + styleEndColumn;

                int i = 0;
                for (int j = startRow; j < endRow; j++) {
                    // 기존 엑셀파일 Sheet의 i번째 행 가져오기
                    XSSFRow originalRow = styleSheet.getRow(i);
                    // 새로운 엑셀파일 Sheet에 j번쨰 행 생성 / 행이 없는 경우 행 생성
                    XSSFRow newRow = newSheet.getRow(j);
                    if(newRow == null) newRow = newSheet.createRow(j);

                    for (int k = startColumn; k < endColumn; k++) {
                        // 기존 행의 k번째 셀 가져오기
                        XSSFCell originalCell = originalRow.getCell(k%3);
                        // 새로운 행에 k번째 셀 생성
                        XSSFCell newCell = newRow.getCell(k);
                        if(newCell == null) newCell = newRow.createCell(k);

                        // 날짜 값 셋팅 > 첫번째 행의 마지막 열에 데이터를 삽입하는 경우
                        if(j == startRow && k == endColumn -1 && data > 0) {
                            newCell.setCellValue(data);
                        }

                        if(originalCell != null) {
                            // 새로운 셀에 새로운 스타일 생성
                            XSSFCellStyle newCellStyle = calWorkbook.createCellStyle();
                            // 새로운 스타일에 기존 셀의 스타일 복사
                            newCellStyle.cloneStyleFrom(originalCell.getCellStyle());
                            // 새로운 셀에 복사한 스타일 적용
                            newCell.setCellStyle(newCellStyle);
                        }
                    }
                    i++;
                }
            }

 

 

 

•  downloadCalendar 내 Excel 다운로드 메서드

// 생성된 Excel 파일 다운로드 메서드
        private static void downloadFile(HttpServletResponse response, String filePath) {

            try {
                // 다운로드할 Excel 파일을 filepath를 통해서 읽어온다.
                File file = new File(filePath);
                // FileInputStream 객체 생성
                FileInputStream inputStream = new FileInputStream(file);

                // 컨텐츠 타입 및 파일명 지정
                // HTTP 응답 content-Type 의 헤더를 Excel파일의 MME 타입 "" 로 설정
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                response.setHeader("Content-Disposition", "attachment;filename=copyExel.xlsx");
                // Content-length 헤더를 설정하여 클라이언트에게 전송되는 데이터 크기를 설정
                response.setContentLength((int) file.length());
                // 클라이언트에게 전송할 데이터를 출력할 OutputStream 객체 생성
                OutputStream outputStream = response.getOutputStream();
                // 데이터 읽을 버퍼 설정 ( 배열의 크기 1024 )
                byte[] buffer = new byte[1024];

                int bytesRead;
                // inputStream에서 데이터를 읽고 buffer에 저장( 데이터가 남아있지 않을때 까지 ) / 읽은 byte수를 byteRead에 저장
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    // buffer 배열의 0번 Index부터 byteRead의 길이만큼 데이터를 ouputStream(출력스트림)으로 전송
                    outputStream.write(buffer, 0, bytesRead);
                }
                // inputStream 객체 닫기
                inputStream.close();
                // outputStream에 남아있는 데이터를 강제로 내보냄( 데이터가 제대로 저장되는걸 도움 )
                outputStream.flush();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

 

 

 

•  결과물

- C:/excel1 디렉터리가 생성되고 그 안에 copyExcel.xlsx 파일이 다운로드 되게 된다.

- View 페이지에서 select 태그 값으로 2024년 1월을 선택한 경우

반응형
Comments