Spring Boot 프로젝트에서 로컬 파일 시스템으로 이미지 업로드 구현하기

스카이 테이크아웃(苍穹外卖) 프로젝트에서 클라우드 스토리지 서비스를 사용하지 않고 로컬 파일 시스템에 이미지를 저장하는 방법을 정리합니다.

1. 업로드 디렉토리 생성

sky-server/src/main/resources 경로 아래에 upload 폴더를 생성합니다. 원하는 다른 위치에 생성해도 무방하며, 그때는 경로만 수정하면 됩니다.

2. WebMvcConfiguration에서 정적 리소스 핸들러 설정

WebMvcConfiguration 클래스의 addResourceHandlers 메서드를 오버라이드하여 업로드 디렉토리를 정적 리소스 경로로 등록합니다. 절대 경로를 사용해야 합니다.

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    // Swagger 문서 리소스
    registry.addResourceHandler("/doc.html")
            .addResourceLocations("classpath:/META-INF/resources/");
    registry.addResourceHandler("/webjars/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/");

    // 업로드된 파일을 서빙하기 위한 정적 리소스 매핑 (변경된 부분)
    registry.addResourceHandler("/static/**")
            .addResourceLocations("file:C:/Users/Abola/Desktop/cq/资料/资料/day01/后端初始工程/sky-take-out/sky-server/src/main/resources/upload/");
}

3. CommonController에서 파일 업로드 로직 작성

기존의 CommonController를 아래 코드로 대체합니다.

package com.sky.controller.admin;

import com.sky.constant.MessageConstant;
import com.sky.result.Result;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.UUID;

@RestController
@RequestMapping("/admin/common")
@Api(tags = "파일 업로드")
@Slf4j
public class CommonController {

    private static final String UPLOAD_DIR = "C:\\Users\\Abola\\Desktop\\cq\\资料\\资料\\day01\\后端初始工程\\sky-take-out\\sky-server\\src\\main\\resources\\upload\\";

    @PostMapping("/upload")
    public Result uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
        // 1. 파일 필수 검증
        if (file.isEmpty()) {
            return Result.error("업로드할 파일이 비어 있습니다.");
        }

        // 2. 업로드 디렉토리 존재 여부 확인 및 생성
        File uploadDir = new File(UPLOAD_DIR);
        if (!uploadDir.exists() || !uploadDir.isDirectory()) {
            boolean created = uploadDir.mkdirs();
            if (created) {
                log.info("업로드 디렉토리 생성 성공: {}", UPLOAD_DIR);
            } else {
                log.warn("업로드 디렉토리 생성 실패 또는 이미 존재: {}", UPLOAD_DIR);
            }
        }

        // 3. 원본 파일명 검증
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null || originalFilename.isBlank()) {
            return Result.error("파일명이 유효하지 않습니다.");
        }

        // 4. 파일 확장자 추출 및 허용된 형식 검사
        String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        if (!extension.equalsIgnoreCase(".png") 
            && !extension.equalsIgnoreCase(".jpg") 
            && !extension.equalsIgnoreCase(".jpeg")) {
            return Result.error("지원하지 않는 파일 형식입니다. (png, jpg, jpeg만 가능)");
        }

        // 5. UUID 기반 고유 파일명 생성 (경로 탐색 공격 방지를 위해 normalize 적용)
        String newFileName = UUID.randomUUID().toString() + extension;
        Path targetPath = Paths.get(UPLOAD_DIR).resolve(newFileName).normalize();

        // 6. 파일 저장
        try {
            Files.copy(file.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING);
            log.info("파일 업로드 성공: {}", newFileName);
        } catch (IOException e) {
            log.error("파일 업로드 실패: {}", newFileName, e);
            return Result.error(MessageConstant.UPLOAD_FAILED);
        }

        // 7. 접근 가능한 URL 반환
        String fileUrl = "http://localhost:8080/static/" + newFileName;
        return Result.success(fileUrl);
    }
}

테스트 결과

  • 이미지 업로드 후 페이지에서 즉시 미리보기가 가능합니다.
  • 클라우드 서비스(Aliyun OSS 등)를 사용하지 않아도 됩니다.
  • upload 폴더에 UUID로 생성된 파일이 저장되는 것을 확인할 수 있습니다.

태그: Spring Boot MultipartFile File Upload Static Resource Local Storage

6월 19일 21:40에 게시됨