스카이 테이크아웃(苍穹外卖) 프로젝트에서 클라우드 스토리지 서비스를 사용하지 않고 로컬 파일 시스템에 이미지를 저장하는 방법을 정리합니다.
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로 생성된 파일이 저장되는 것을 확인할 수 있습니다.