Skip to content

Commit

Permalink
Merge pull request #37 from Kernel360/kdh/modify-file-entity
Browse files Browse the repository at this point in the history
Update: 파일, 링크 관련 엔티티 수정
  • Loading branch information
kdh10806 authored Jan 6, 2025
2 parents ef80ded + 8874ed0 commit 6a46120
Show file tree
Hide file tree
Showing 14 changed files with 632 additions and 384 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ public enum ErrorCode {

// 4000번대 코드 : 파일 s3 버킷 업/다운로드 관련
S3_UPLOAD_FAIL_ERROR(4000, HttpStatus.INTERNAL_SERVER_ERROR, "S3 파일 업로드에 실패했습니다."),
FILE_NOT_EXIST_ERROR(4001, HttpStatus.BAD_REQUEST, "빈 파일이거나 존재하지 않는 파일입니다."),
INVALID_FILE_NAME_ERROR(4002, HttpStatus.BAD_REQUEST, "파일명이 비었거나 유효하지 않습니다."),
INVALID_FILE_CATEGORY_ERROR(4003, HttpStatus.BAD_REQUEST, "파일 카테고리가 유효하지 않습니다."),
FORMAT_NOT_PERMITTED_ERROR(4004, HttpStatus.BAD_REQUEST, "유효하지 않은 파일 타입입니다."),
MIME_NOT_PERMITTED_ERROR(4005, HttpStatus.BAD_REQUEST, "유효하지 않은 MIME 타입입니다."),
FILE_SIZE_EXCEED_ERROR(4006, HttpStatus.BAD_REQUEST, "파일 사이즈가 초과되었습니다."),


// 5000번대 코드 : 서버 내부 오류 관련
INTERNAL_SERVER_ERROR(5001, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류가 발생했습니다."),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.seveneleven.devlens.global.util.file;

import com.seveneleven.devlens.global.exception.BusinessException;
import com.seveneleven.devlens.global.response.ErrorCode;
import com.seveneleven.devlens.global.util.file.constant.FileCategory;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class FileValidator {

private static final long MAX_FILE_SIZE = 20 * 1024 * 1024; // 20MB 제한
Expand All @@ -17,19 +18,19 @@ public class FileValidator {
* @param fileCategoryName 파일 카테고리명
*/
public static void validateFile(MultipartFile file, String fileCategoryName) {
if (file == null || file.isEmpty()) {
throw new IllegalArgumentException("File cannot be null or empty.");
if (ObjectUtils.isEmpty(file)) {
throw new BusinessException(ErrorCode.FILE_NOT_EXIST_ERROR);
}

String fileName = file.getOriginalFilename();
if (fileName == null || fileName.isBlank()) {
throw new IllegalArgumentException("Filename cannot be null or empty.");
if (StringUtils.isBlank(fileName)) {
throw new BusinessException(ErrorCode.INVALID_FILE_NAME_ERROR);
}

FileCategory fileCategory = validateFileCategory(fileCategoryName); // 파일 카테고리 검증
validateFileExtension(fileName, fileCategory); // 파일 확장자 검증
validateMimeType(file.getContentType(), fileCategory); // MIME 타입 검증
//validateFileSize(file.getSize()); // 파일 크기 검증
validateFileSize(file.getSize()); // 파일 크기 검증
}

/**
Expand All @@ -39,7 +40,7 @@ private static FileCategory validateFileCategory(String fileCategoryName) {
try{
return FileCategory.valueOf(fileCategoryName);
} catch (IllegalArgumentException e){
throw new IllegalArgumentException("Invalid file category: " + fileCategoryName);
throw new BusinessException(ErrorCode.INVALID_FILE_CATEGORY_ERROR);
}
}

Expand All @@ -49,7 +50,7 @@ private static FileCategory validateFileCategory(String fileCategoryName) {
private static void validateFileExtension(String fileName, FileCategory fileCategory) {
String fileExtension = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
if (!fileCategory.getAllowedExtensions().contains(fileExtension)) {
throw new IllegalArgumentException("Invalid file extension for category: " + fileCategory.name());
throw new BusinessException(ErrorCode.FORMAT_NOT_PERMITTED_ERROR);
}
}

Expand All @@ -58,7 +59,7 @@ private static void validateFileExtension(String fileName, FileCategory fileCate
*/
private static void validateMimeType(String mimeType, FileCategory fileCategory) {
if (!fileCategory.getAllowedMimeTypes().contains(mimeType)) {
throw new IllegalArgumentException("Invalid file type for category: " + fileCategory.name());
throw new BusinessException(ErrorCode.MIME_NOT_PERMITTED_ERROR);
}
}

Expand All @@ -67,7 +68,7 @@ private static void validateMimeType(String mimeType, FileCategory fileCategory)
*/
private static void validateFileSize(long fileSize) {
if (fileSize > MAX_FILE_SIZE) {
throw new IllegalArgumentException("File size exceeds limit of 20MB.");
throw new BusinessException(ErrorCode.FILE_SIZE_EXCEED_ERROR);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,81 +1,81 @@
package com.seveneleven.devlens.global.util.file.Service;

import com.seveneleven.devlens.global.exception.BusinessException;
import com.seveneleven.devlens.global.response.APIResponse;
import com.seveneleven.devlens.global.response.ErrorCode;
import com.seveneleven.devlens.global.util.file.FileValidator;
import com.seveneleven.devlens.global.util.file.dto.FileMetadataDto;
import com.seveneleven.devlens.global.util.file.entity.FileMetadata;
import com.seveneleven.devlens.global.util.file.repository.FileMetadataRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
public class FileService {
private final FileMetadataRepository fileMetadataRepository;
private final S3ClientService s3ClientService;

/**
* 1. 파일 업로드
* @param file 업로드할 파일
* @param uploaderId 업로더 id
* @param fileCategory 파일 카테고리
* @param referenceId 파일 참조 ID
* @return FileMetadataDto 업로드한 파일 메타데이터
*/
@Transactional
public APIResponse uploadFile(MultipartFile file, Long uploaderId, String fileCategory, Long referenceId) throws Exception {
//1. 파일 검증
FileValidator.validateFile(file, fileCategory);

//2. 고유 파일 이름(UUID) 및 S3 키 생성
//파일명이 없거나 비어있으면 Unknown-File로 설정
String originalFilename = (file.getOriginalFilename() != null && !file.getOriginalFilename().isBlank())
? file.getOriginalFilename() : "Unknown-File";
//UUID 생성
String uniqueFileName = s3ClientService.generateUniqueFileName(originalFilename);
//S3 키 생성
String s3Key = s3ClientService.generateS3Key(uploaderId, fileCategory, uniqueFileName);

//3. S3 업로드 및 FileMetadata 데이터 생성
String filePath = null;
try{
//S3 업로드
filePath = s3ClientService.uploadFile(file, s3Key);

//업로드하는 파일 메타데이터 생성
FileMetadata metadata = FileMetadata.builder()
.fileCategory(fileCategory)
.fileDisplayTitle(file.getOriginalFilename())
.fileTitle(uniqueFileName)
.writtenBy(uploaderId) //TODO) Audit
.writtenAt(LocalDateTime.now()) //TODO) Audit
.contentType(file.getContentType())
.fileFormat(file.getOriginalFilename().substring(originalFilename.lastIndexOf('.') + 1))
.fileSize(file.getSize() / 1024.0) // KB
.filePath(filePath)
.referenceId(referenceId) // 필요 시 설정
.createdBy(1L) //TODO) Audit
.createdAt(LocalDateTime.now()) //TODO) Audit
.build();

//FileMetaData 저장
FileMetadata savedMetadata = fileMetadataRepository.save(metadata);

//DTO로 변환 후 반환
return APIResponse.create(FileMetadataDto.toDto(savedMetadata));

} catch (Exception e){
//저장 실패시 S3에서 삭제
s3ClientService.deleteFile(s3Key);

throw new BusinessException(e.getMessage(), ErrorCode.S3_UPLOAD_FAIL_ERROR);
}
}

}
//package com.seveneleven.devlens.global.util.file.Service;
//
//import com.seveneleven.devlens.global.exception.BusinessException;
//import com.seveneleven.devlens.global.response.APIResponse;
//import com.seveneleven.devlens.global.response.ErrorCode;
//import com.seveneleven.devlens.global.util.file.FileValidator;
//import com.seveneleven.devlens.global.util.file.dto.FileMetadataDto;
//import com.seveneleven.devlens.global.util.file.entity.FileMetadata;
//import com.seveneleven.devlens.global.util.file.repository.FileMetadataRepository;
//import lombok.RequiredArgsConstructor;
//import org.springframework.stereotype.Service;
//import org.springframework.transaction.annotation.Transactional;
//import org.springframework.web.multipart.MultipartFile;
//
//import java.time.LocalDateTime;
//
//@Service
//@RequiredArgsConstructor
//public class FileService {
// private final FileMetadataRepository fileMetadataRepository;
// private final S3ClientService s3ClientService;
//
// /**
// * 1. 파일 업로드
// * @param file 업로드할 파일
// * @param uploaderId 업로더 id
// * @param fileCategory 파일 카테고리
// * @param referenceId 파일 참조 ID
// * @return FileMetadataDto 업로드한 파일 메타데이터
// */
// @Transactional
// public APIResponse uploadFile(MultipartFile file, Long uploaderId, String fileCategory, Long referenceId) throws Exception {
// //1. 파일 검증
// FileValidator.validateFile(file, fileCategory);
//
// //2. 고유 파일 이름(UUID) 및 S3 키 생성
// //파일명이 없거나 비어있으면 Unknown-File로 설정
// String originalFilename = (file.getOriginalFilename() != null && !file.getOriginalFilename().isBlank())
// ? file.getOriginalFilename() : "Unknown-File";
// //UUID 생성
// String uniqueFileName = s3ClientService.generateUniqueFileName(originalFilename);
// //S3 키 생성
// String s3Key = s3ClientService.generateS3Key(uploaderId, fileCategory, uniqueFileName);
//
// //3. S3 업로드 및 FileMetadata 데이터 생성
// String filePath = null;
// try{
// //S3 업로드
// filePath = s3ClientService.uploadFile(file, s3Key);
//
// //업로드하는 파일 메타데이터 생성
// FileMetadata metadata = FileMetadata.builder()
// .fileCategory(fileCategory)
// .fileDisplayTitle(file.getOriginalFilename())
// .fileTitle(uniqueFileName)
// .writtenBy(uploaderId) //TODO) Audit
// .writtenAt(LocalDateTime.now()) //TODO) Audit
// .contentType(file.getContentType())
// .fileFormat(file.getOriginalFilename().substring(originalFilename.lastIndexOf('.') + 1))
// .fileSize(file.getSize() / 1024.0) // KB
// .filePath(filePath)
// .referenceId(referenceId) // 필요 시 설정
// .createdBy(1L) //TODO) Audit
// .createdAt(LocalDateTime.now()) //TODO) Audit
// .build();
//
// //FileMetaData 저장
// FileMetadata savedMetadata = fileMetadataRepository.save(metadata);
//
// //DTO로 변환 후 반환
// return APIResponse.create(FileMetadataDto.toDto(savedMetadata));
//
// } catch (Exception e){
// //저장 실패시 S3에서 삭제
// s3ClientService.deleteFile(s3Key);
//
// throw new BusinessException(e.getMessage(), ErrorCode.S3_UPLOAD_FAIL_ERROR);
// }
// }
//
//}
Loading

0 comments on commit 6a46120

Please sign in to comment.