백엔드 파일 받기 (Day 60)

코딩기록·2025년 1월 2일

[ 📚 기본 초기세팅 ]

🔹 1. application 파일에 파일 용량 설정

spring:

  servlet:
    multipart:
      max-file-size: 10MB # 파일 1개 당 최대 용량
      max-request-size: 100MB # 한 번에 업로드할 수 있는 파일의 총 용량

🔹 2. 클라이언트가 어떤 url로 요청하면(e.g. htt://...../images/1) 어디에 저장되어 있는 파일을 클라이언트에 전달할지를 설정

// 클래스 구현 내용 : 사용자가 http://..../path로 요청을 하면,
//                       어떤 위치에 있는 정적 리소르를 제공해주세요!

// 1. @Configuration 을 붙여서 Bean 자동 생성 + Configuration 하는 파일임을 알림
@Configuration
@RequiredArgsConstructor
public class WebResourceConfig implements WebMvcConfigurer {
  
  private final FileUploadConfig fileUploadConfig;

  // 클라이언트에 path에 통신 시 제공할 정적 리소스의 위치 매핑
  @Override
  // 2. addResourceHandlers(ResourceHandler Registry registry) 메소드 오버라이드
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
      // 2-1. ResourceHandlerRegistory.addResourceHandler("path") : 아래 주소로 클라이언트가 요청을 보내면
      //  -> http://localhost:9000/uuploaders/loopy.jpg로 요청하면
      registry.addResourceHandler("/uploads/**")
              // ResourceHandlerRegistry.addResourceLocation("경로") : 이 위치에 있는 정작 리소트를 제공해라.
              // 실제 로컬에 저장된 C:Users/user/spring/upload/loopy.jpg를 꺼내 주겠다.
              .addResourceLocations("file:" + fileUploadConfig.getLocation());
  }
}

🔹 어플리케이션 실행되자마자 로컬에 이미지 저장할 폴더를 생성

// 클라이언트가 파일업로드 시, 저장될 폴더를 지정하고 어플리케이션에 실행 시에 그 폴더를 생성해주는 이벤트
@Getter @Setter
@Configuration // spring에서 자동 bean 생성
public class FileUploadConfig {

  // @Value : 필드 값을 ()와 같이 주입
  @Value("${file.upload.location}")
  private String location; // 파일을 저장할 루트 디렉토리 지정

  // @PostConsutrct : 서버가 실행되자마자 아래 메소드를 실행해라.
  // 아래 메소드 내용 : 폴더 생성
  @PostConstruct   
  public void init() {
      File directory = new File(location);
      if (!directory.exists()) {
          directory.mkdirs();
      }
  }





[ 📚 클라이언트가 보낸 파일을 저장하고, api에 매핑 하는 법 ]

  • @RequestParam, MutlpartFile, MultipartFile. transferTo()

    // 1. 클라이언트가 File을 POST하는 경우, (평상시에는 @RequestParam은 쿼리 파라미터를 받지만)
    // @RequestParam으로 클라리언트가 Json Body에 보낸 File을 받음
    // 2. 이것을 아래에서 Spring framework에서 제공하는 MultipartFile이라는 객체로 받음
    // 3. MultipartFile.transferTo(파일 업로드 위치) 로 파일을 전송할 수 있음

        @PostMapping("/upload-multi")
      public ResponseEntity<?> uploadMultiFile(
              // 1. 클라이언트가 File을 POST하는 경우, (평상시네는 @RequestParam은 쿼리 파라미터를 받지만)
              //   @RequestParam으로 클라리언트가 Json Body에 보낸 File을 받음
              // 2. 이것을 아래에서 Spring framework에서 제공하는 MultipartFile이라는 객체로 받음
              // 3. MultipartFile.transferTo(파일 업로드 위치) 로 파일을 전송할 수 있음
              @RequestParam("profile") List<MultipartFile> fileList
              ) {
          for (MultipartFile file : fileList) {
              log.info("uploaded file name: {}", file.getOriginalFilename());
              log.info("uploaded file type: {}", file.getContentType());
    
              // 저장 파일 경로 생성
              String rootDir = fileUploadConfig.getLocation();
              String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
              File uploadedLocation = new File(rootDir + fileName);
    
              // 파일 저장
              try {
                  // MultipartFile.transferTo(업로드할 경로)
                  file.transferTo(uploadedLocation);
              } catch (IOException e) {
                  return ResponseEntity.internalServerError().body("파일 저장 실패!");
              }
          }
          return ResponseEntity.ok().body("multiple upload success!");
    
      }
    }
    





[ 📚 클라이언트가 파일 + JSON을 한 번에 보냈을 때 저장하고, 받는 법]

  • @ RequestPart("key 이름") MultipartFile images
  • @ RequestPart("key 이름") RequestEntity jsontext

📍 1. APIController에서 파일 받아서, userService에 처리 요청

@RestController
@RequestMapping("/api/posts")
@Slf4j
@RequiredArgsConstructor
public class PostController {

    private final PostService postService;

    // 피드 생성 요청
    @PostMapping
    public ResponseEntity<?> createFeed(
            // 피드 내용, 작성자 이름 JSON { "writer": "", "content": "" } -> 검증
            @RequestPart("feed") @Valid PostCreate postCreate
            // 이미지 파일 목록 multipart-file
            , @RequestPart("images") List<MultipartFile> images
    ) {

        images.forEach(image -> {
            log.info("uploaded image file name - {}", image.getOriginalFilename());
        });

        postCreate.setImages(images);
        log.info("feed create request: POST - {}", postCreate);

        // 이미지와 JSON을 서비스클래스로 전송
        Long postId = postService.createFeed(postCreate);

        // 응답 메시지 생성
        Map<String, Object> response = Map.of(
                "id", postId
                , "message", "save success"
        );

        return ResponseEntity
                .ok()
                .body(response);
    }
}

📍 2-1. @Service에서 DTO를 Entity로 변환해주면서 유효성 검증

📍 2-2. @Service에서 FileUploadUtil에 파일 서버에 저장 요청

    🔹보통은 DB에 바로 저장 요청하지만, 이 경우 DB에는 이미지 자체를 담는 것이 아니라 이미지 저장 경로만 담으므로 먼저 파일을 서버에 요청해야 함

📍 2-3. @Service에서 DB에 파일 url 저장 요청


@Service
@Slf4j
@RequiredArgsConstructor
public class PostService {

    private final PostRepository postRepository; // db에 피드내용 저장, 이미지저장
    private final FileUploadUtil fileUploadUtil; // 로컬서버에 이미지 저장

    // 피드 생성 DB에 가기 전 후 중간처리
    public Long createFeed(PostCreate postCreate) {

        // entity 변환
        Post post = postCreate.toEntity();

        // 피드게시물을 posts테이블에 insert
        postRepository.saveFeed(post);

        // 이미지 관련 처리를 모두 수행
        Long postId = post.getId();

        processImages(postCreate.getImages(), postId);

        // 컨트롤러에게 결과 반환
        return postId;
    }

📍 3. FileUpload에서 파일 저장 및 API와 파일 매핑 요청

  • 파일 저장 : MultipartFile 객체의 transferTo(new File("저장경로"))
  • 파일과 API 매핑 : 위에 기본세팅에서 만든 resourceHandler로 매핑됨
@Slf4j
@RequiredArgsConstructor
@Component
public class FileUploadUtil {

    private final FileUploadConfig fileUploadConfig;

    // 하나의 파일을 로컬 저장 폴더에 저장하고 그 업로드 경로를 리턴
    public String saveFile(MultipartFile file) {
        if (file.isEmpty()) {
            // ... 예외처리
        }


        // 원본 파일명 불러오기
        String originalFilename = file.getOriginalFilename();
        // 파일명 랜덤으로 바꾸기
        String newFileName = UUID.randomUUID() + "_" + originalFilename;

        try {
            // 저장할 절대경로 생성
            String uploadPath = fileUploadConfig.getLocation() + newFileName;

            log.debug("Attempting to save file to : {}", uploadPath);

            // 실제 파일 저장
            file.transferTo(new File(uploadPath));
            // server url : WebRESOURCEconfig에서 바꿔놓은 url
        } catch (Exception e) {
            // ... 예외처리
            log.error("Failed to save file: {}", newFileName, e);
        }
        return "/uploads/" + newFileName;
    }

}

0개의 댓글