본문 바로가기
Spring-boot

spring-boot 엑셀 업로드

by 조민욱 2024. 6. 12.

우선 프론트 단에서는 파일업로드 input을 만들어둔다 

이 후 엑셀 파일은 다음과 같이 입력할 수 있게 해준다.

상단 비워져있는 부분은 설명으로 사용 

다음 행은 제목으로 사용 

 

이후 백엔드 코드

    @PostMapping("/uploadExcel")
    public ResponseEntity<String> uploadSlotExcel(
            @ModelAttribute SlotExcelUploadRequestDto uploadDto,
            @RequestParam("file") MultipartFile file
    ) throws IOException {
        if (file.isEmpty()) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("파일이 비어 있습니다.");
        }
        try {
            InputStream inputStream = file.getInputStream();
            Workbook workbook = new XSSFWorkbook(inputStream);

            slotService.saveExcelData(workbook, uploadDto);
            return ResponseEntity.ok("파일이 성공적으로 업로드되었습니다.");
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("파일 업로드 중 오류가 발생했습니다.");
        }
    }

 

 

Service 코드

    @Transactional
    public void saveExcelData(Workbook workbook, SlotExcelUploadRequestDto uploadDto) {
        Sheet sheet = workbook.getSheetAt(0);
        Iterator<Row> rows = sheet.iterator();
        int rowNum = 0;
        while (rows.hasNext()) {
            Row currentRow = rows.next();
            // 행을 하나씩 찾아서 다음행이 있는지 없는지 확인
            rowNum++;
            if (rowNum > 2) {
            // 행이 2번째 이상이면 아래 parseRowToSlot메소드 실행
                List<SlotEntity> slots = parseRowToSlot(currentRow, uploadDto);
                if (slots != null) {
                    slotRepository.saveAll(slots);
                }
            }
        }
    }
    private List<SlotEntity> parseRowToSlot(Row row, SlotExcelUploadRequestDto uploadDto) {
        try {
            int i = 0;
            String link = row.getCell(i++).getStringCellValue();
            String mainKeyWord = row.getCell(i++).getStringCellValue();
            String subKeyWord = row.getCell(i++).getStringCellValue();
            String commercial = row.getCell(i++).getStringCellValue();
            String sellerMemo = row.getCell(i++).getStringCellValue();
            Cell startDateCell = row.getCell(i++);
            LocalDate startDate;
            if (startDateCell != null && startDateCell.getCellType() == CellType.NUMERIC) {
                startDate = startDateCell.getDateCellValue().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            } else {
                throw new IllegalArgumentException("시작일 셀의 값이 유효하지 않습니다.");
            }
            long workDay = (long) row.getCell(i++).getNumericCellValue();
            LocalDate endDate = startDate.plusDays(workDay);
            int slotQuantity = (int) row.getCell(i++).getNumericCellValue();

// 일딴 SlotInfo 테이블에 저장 (한개만 저장)
            SlotInfoEntity slotInfo = new SlotInfoEntity();
            slotInfo.setUrl(link);
            slotInfo.setMainKeyword(mainKeyWord);
            slotInfo.setSubKeyword(subKeyWord);
            slotInfo.setCommercial(commercial);
            slotInfo.setStartDate(String.valueOf(startDate));
            slotInfo.setEndDate(String.valueOf(endDate));
            slotInfoRepository.save(slotInfo);

// 이 후 기존에 "슬롯 수량"이라는 숫자를 받아서 그 수량만큼 슬롯을 추가할 수 있게 List로 입력
            List<SlotEntity> slots = new ArrayList<>();
            for (int j = 0; j < slotQuantity; j++) {
                SlotEntity slot = new SlotEntity();
                slot.setWorkDay(workDay);
                slot.setMemoContent(sellerMemo);
                slot.setStartDate(String.valueOf(startDate));
                slot.setEndDate(String.valueOf(endDate));

                LocalDateTime koreaTime = LocalDateTime.now(ZoneId.of("Asia/Seoul"));
                slot.setCreateSlotTime(koreaTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

                CategoryEntity category = categoryRepository.findById(Long.valueOf(uploadDto.getCategoryId()))
                        .orElseThrow(() -> new RuntimeException("카테고리가 유효하지 않습니다."));

                MemberEntity user = memberRepository.findByUserId(uploadDto.getUserId())
                        .orElseThrow(() -> new RuntimeException("UserId가 유효하지 않습니다."));

                slot.setCategory(category);
                slot.setUserId(user);
                slot.setSlotInfo(slotInfo);

                slots.add(slot);
            }

            return slots;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

 

 

 

프론트 코드 

  const fileUpload = async (e: any) => {
    e.preventDefault()
    if (!file) {
      alert('파일을 선택해 주세요.')
      return
    }

    try {
      const response = await uploadExcel(file, userId, params.categoryId)
      if (response.ok) {
        alert('파일 업로드 성공!')
      } else {
        alert('파일 업로드 실패!')
      }
    } catch (error) {
      alert('업로드 중 오류가 발생했습니다.')
      console.error('업로드 중 오류:', error)
    }
  }
  
  
  export async function uploadExcel(
  file: File,
  userId: string,
  categoryId: number,
) {
  const formData = new FormData()
  formData.append('file', file)
  formData.append('userId', userId)
  formData.append('categoryId', categoryId.toString())

  console.log(categoryId.toString())

  try {
    const response = await fetch(
      process.env.NEXT_PUBLIC_API_ADDRESS + 'slot/uploadExcel',
      {
        method: 'POST',
        body: formData,
      },
    )
    return response
  } catch (err) {
    console.error(err)
    throw err
  }
}

'Spring-boot' 카테고리의 다른 글

QueryDSL 적용기  (0) 2024.09.24
spring-boot 업로드 2  (1) 2024.06.13
Spring-boot Google Sheets API #2 연동  (0) 2024.04.25
Spring-boot Google Sheets API #1  (0) 2024.04.25
Spring-boot 로그 기록 남겨두기  (0) 2024.04.19