IT/development

[springBoot] 엑셀파일 읽기(feat. ajax)

알 수 없는 사용자 2023. 8. 23. 21:27
반응형

목차

    프로세스: 화면에서 엑셀 업로드 시 엑셀의 내용을 읽어서 테이블에 저장

    의존성 추가(gradle)

    //springBoot ver: 2.7.7
    //poi 의존성 추가
    implementation 'org.apache.poi:poi:5.1.0'
    implementation 'org.apache.poi:poi-ooxml:5.1.0'

    service

    @Service
    @Slf4j
    @RequiredArgsConstructor
    @Transactional(readOnly = true)
    public class ExcelService {
    
    	private final ExcelMapper excelMapper;
    
        /**
         * 엑셀업로드 화면
         * @param mv
         * @return
         */
        @Override
        public ModelAndView uploadForm(ModelAndView mv) {
            mv.setViewName("upload");
            return mv;
        }
    
        /**
        * 엑셀 데이터 db 저장
        * @return
        */
        @Transactional
        public ResponseEntity insertExcelInfo(MultipartFile file) {
            //resultMap
            Map<String, Object> resultMap = new HashMap<>();
    
            try {
    
                Workbook workbook = new XSSFWorkbook(file.getInputStream());
                Sheet sheet = workbook.getSheetAt(0);
                //엑셀 데이터 Vo
                ExcelVo excelVo;
    
                /**************** 엑셀 데이터 임시 테이블에 저장 ********************************************************/
                int lastRowNum = sheet.getLastRowNum();
                //1번 째 행은 header라서 2번 째 행부터 루프
                for (int rowIndex = 1; rowIndex <= lastRowNum; rowIndex++) {
                    Row cells = sheet.getRow(rowIndex);
                    //Vo에 엑셀 데이터 세팅
                    excelVo = createExcelVoFromCells(cells);
                    //db 저장
                    excelMapper.insertExcelInfo(excelVo);
                }
                /********************************************************************************/
    
                resultMap.put("code", ResultType.SUCCESS.getCode());
                return new ResponseEntity<>(resultMap, HttpStatus.OK);
    
            } catch (IOException ie) {
                resultMap.put("code", ResultType.FAIL.getCode());
                resultMap.put("message", "Failed to Process the Excel File: " + ie.getMessage());
                return new ResponseEntity<>(resultMap, HttpStatus.BAD_REQUEST);
    
            } catch (Exception e) {
                resultMap.put("code", ResultType.FAIL.getCode());
                resultMap.put("message", "Exception: " + e.getMessage());
                return new ResponseEntity<>(resultMap, HttpStatus.BAD_REQUEST);
            }
        }
    
        /**
         * 엑셀파일 데이터 Vo에 세팅
         * @param cells
         * @param empId
         * @return
         */
        public ExcelVo createExcelVoFromCells(Row cells) {
            ExcelVo excelVo = new ExcelVo();
            excelVo.setEmpNo(getStringCellValue(cells, 0));        //사원번호
            excelVo.setEmpNm(getStringCellValue(cells, 1));        //성명
            excelVo.setDepartmentNm(getStringCellValue(cells, 2)); //부서
            excelVo.setEmpEmail(getStringCellValue(cells, 3));     //이메일
            excelVo.setEmpPosition(getStringCellValue(cells, 4));  //직급
            //... 등등 기타 필드 세팅
            return excelVo;
        }
    
        /**
         * 엑셀에서 문자열 타입 값 가져옴
         * @param row
         * @param cellIndex
         * @return
         */
        public String getStringCellValue(Row row, int cellIndex) {
            Cell cell = row.getCell(cellIndex);
            return (cell != null) ? cell.getStringCellValue() : null;
        }
    }

    RestController

    @RestController
    @Slf4j
    @RequiredArgsConstructor
    @RequestMapping(value = "/api/excel")
    public class ExcelRestController {
        
        private final ExcelService excelService;
        
        /**
         * 엑셀파일 db 저장
         * @param file
         * @return
         */
        @PostMapping("/send")
        public ResponseEntity insertExcelInfo(@RequestParam("file") MultipartFile file) {
            return excelService.insertExcelInfo(file);
        }
    }

    Controller

    @Controller
    @Slf4j
    @RequiredArgsConstructor
    @RequestMapping("/excel")
    public class ExcelController {
    
    	private final ExcelService excelService;
    
        /**
         * 엑셀 업로드 화면
         * @param mv
         * @return
         */
        @GetMapping(value = "/upload")
        public ModelAndView uploadForm(ModelAndView mv) {
            return excelService.uploadForm(mv);
        }
    }

    Vo

    @Data
    //엑셀 데이터 Vo
    public class ExcelVo {
    
        private String empNo;                               //사원번호
        private String empId;                               //아이디
        private String empNm;                               //성명
        private String departmentNm;                        //부서
        private String empEmail;                            //이메일
        private String empPosition;                         //직급
    }

    html(upload.html)

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/default.html}"> <!-- 레이아웃 부분은 무시 -->
    
    <!-- content(레이아웃 부분은 이 포스팅에선 무시) -->
    <th:block layout:fragment="content">
    <div class="contents" id="upload-form">
        <!-- 메뉴 -->
        <div class="section">
                <input type="file" id="filefrm" class="btn-file" title="파일찾기" accept=".xlsx" @change="handleFileChange" />
                <label for="filefrm">파일 첨부</label>
                <button type="button" class="btn btn-normal" @click="upload">업로드</button>
        </div>
    </div>
    </th:block>
    
    <!-- Script -->
    <script layout:fragment="script" th:inline="javascript" type="text/javascript">
    //Vue.js 사용
    new Vue({
       //element id
       el: '#upload-form',
       //vue에서 관리할 데이터
       data: {
             /********* 데이터 저장 필드 *****************/
             selectedFile: null,    //첨부파일
       },
       //methods
       methods: {
           /**
            * 첨부파일
            */
           handleFileChange(e) {
               this.selectedFile = e.target.files[0];
           },
           /**
            * 첨부파일 업로드
            */
           upload() {
    
               if (confirm("업로드 하시겠습니까?")) {
    
                   const formData = new FormData();
                   const url = '/api/excel/send';
                   formData.append('file', this.selectedFile);
    
                   //post http request
                   axios.post(url, formData, {
                       headers: {'Content-Type': 'multipart/form-data'}
                   })
                       .then(function (response) {
                           console.log(response.data.code);
                           if (response.data.code === '1') {
                               alert("등록 되었습니다.");
                           } else {
                               console.log(response.data.message);
                               alert("등록 실패했습니다.");
                           }
                       }.bind(this))
                       .catch(function (error) {
                           alert("등록 실패했습니다.");
                           console.log(error);
                       });
               }
           },
      },
    });
    
    </script>
    
    </html>

    결과(보안상 개인정보는 가렸음)

    실제 프로젝트 소스에서 편집한 코드라 메소드명이나 코드상 어색한 부분이 있을 수도 있음

    개인 스터디 기록을 메모하는 공간이라 틀린점이 있을 수 있습니다.

    틀린 점 있을 경우 댓글 부탁드립니다.

    반응형