새로 시작한 프로젝트에서 동영상 업로드를 구현할 일이 생겨서 구현하는 중에 에러 메시지를 뱉어내지 않고 Unkown Exception이 자꾸 떨어지는 것을 확인했다. 그리고 게시물을 4~5개 정도 서칭하고 지피팅을 30분째 하고 있을 때 로그 레벨을 Debug 까지 낮추고 나서야
Resolved [org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size exceeded]
라는 에러가 발생하고 있는 것을 알게 되었다.
그리고 그 에러 검색을 해보고 나서야 이걸 해결하는 방법에 대해서 알게 되었다.
해결 방법은 application.yaml
파일에 max file size를 설정해주는 것이다.
spring:
servlet:
multipart:
enabled: true
max-file-size: 10000MB #10GB
max-request-size: 10000MB # 10GB
file-size-threshold:
@Service
@Slf4j
public class VideoService {
private static final long MAX_FILE_SIZE = 1024L * 1024L * 1024L * 10L; // 10GB
public String upload(MultipartFile file) {
checkFileTypeOrThrow(file);
checkFileSizeOrThrow(file);
String fileName = UUID.randomUUID() + ".mp4";
//
String directoryPath = getDirectoryPath();
Path filePath = Paths.get(directoryPath, fileName);
try (OutputStream os = Files.newOutputStream(filePath)) {
os.write(file.getBytes());
return fileName;
} catch (IOException e) {
log.error("file upload failed", e);
throw new ApiException(VideoStatus.FILE_UPLOAD_FAILED);
}
}
/**
* 파일을 저장할 디렉토리 경로를 가져오고 없다면 생성한다.
*/
private String getDirectoryPath() {
String dierctory = "src/main/resources/static/video";
Path directtoryPath = Paths.get(dierctory);
if (!Files.exists(directtoryPath)) {
try {
Files.createDirectories(directtoryPath);
} catch (IOException e) {
log.error("file upload failed", e);
throw new ApiException(VideoStatus.FILE_UPLOAD_FAILED);
}
}
return directtoryPath.toString();
}
/**
* 파일 타입이 MP4인지 확인
*/
private void checkFileTypeOrThrow(MultipartFile file) {
String contentType = file.getContentType();
if (!contentType.equals("video/mp4")) {
throw new ApiException(VideoStatus.INVALID_FILETYPE);
}
}
/**
* 파일 사이즈가 10GB 이상인지 확인
*/
private void checkFileSizeOrThrow(MultipartFile file) {
long fileSize = file.getSize();
if (fileSize >= MAX_FILE_SIZE) {
throw new ApiException(VideoStatus.FILE_SIZE_EXCEEDED);
}
}
}
@RestControllerWithEnvelopPattern
@RequestMapping("/open-api/video")
@RequiredArgsConstructor
public class VideoApiController {
private final VideoService videoService;
@PostMapping("/upload")
public String saveFile(@RequestParam("file") MultipartFile file) {
String fileName = videoService.upload(file);
return fileName;
}
}
@Getter
@AllArgsConstructor
public enum VideoStatus implements ApiStatusIfs {
INVALID_FILETYPE(4400, "지원하지 않는 파일 형식입니다."),
FILE_SIZE_EXCEEDED(4401, "파일 크기가 최대 크기를 초과하였습니다."),
FILE_UPLOAD_FAILED(4502, "파일 업로드에 실패하였습니다."),
EMPTY_FILE(4403, "파일이 비어있습니다."),
;
private final Integer statusCode;
private final String message;
}
VideoController 👉 VideoService로 이어지는 코드이다. VideoService에서는 해당하는 파일이 .mp4 파일인지 확인하고 용량을 확인해서 10GB를 넘는지 확인한다.
그리고 이 검증 구간을 통과하고 나면 getDirectoryPath() 메서드를 통해서 project root를 기준으로 src/main/resources/static/video 폴더가 있는지 확인하고 없다면 생성한 후 그 폴더에 random한 UUID로 파일이름을 지정해서 저장한다.