Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 12 additions & 17 deletions src/main/java/com/back/domain/file/controller/FileController.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ public ResponseEntity<RsData<FileUploadResponseDto>> uploadFile(
) {
FileUploadResponseDto res = fileService.uploadFile(
req.getMultipartFile(),
req.getEntityType(),
req.getEntityId(),
user.getUserId()
);

Expand All @@ -39,44 +37,41 @@ public ResponseEntity<RsData<FileUploadResponseDto>> uploadFile(
.body(RsData.success("파일 업로드 성공", res));
}

@GetMapping(value = "/read")
@GetMapping(value = "/read/{attachmentId}")
public ResponseEntity<RsData<FileReadResponseDto>> getFile(
@RequestParam("entityType") @NotBlank(message = "entityType은 필수입니다.") EntityType entityType,
@RequestParam("entityId") @NotBlank(message = "entityId는 필수입니다.") Long entityId
@PathVariable("attachmentId") Long attachmentId
) {
FileReadResponseDto res = fileService.getFile(entityType, entityId);
FileReadResponseDto res = fileService.getFile(attachmentId);

return ResponseEntity
.status(HttpStatus.OK)
.body(RsData.success("파일 조회 성공", res));
}

@PutMapping(value = "/update", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<RsData<Void>> updateFile(
@PutMapping(value = "/update/{attachmentId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<RsData<FileUpdateResponseDto>> updateFile(
@PathVariable("attachmentId") Long attachmentId,
@ModelAttribute @Valid FileUpdateRequestDto req,
@AuthenticationPrincipal CustomUserDetails user
) {
fileService.updateFile(
FileUpdateResponseDto res = fileService.updateFile(
attachmentId,
req.getMultipartFile(),
req.getEntityType(),
req.getEntityId(),
user.getUserId()
);

return ResponseEntity
.status(HttpStatus.OK)
.body(RsData.success("파일 업데이트 성공"));
.body(RsData.success("파일 업데이트 성공", res));
}

@DeleteMapping(value = "/delete")
@DeleteMapping(value = "/delete/{attachmentId}")
public ResponseEntity<RsData<Void>> deleteFile(
@RequestParam("entityType") @NotBlank(message = "entityType은 필수입니다.") EntityType entityType,
@RequestParam("entityId") @NotBlank(message = "entityId는 필수입니다.") Long entityId,
@PathVariable("attachmentId") Long attachmentId,
@AuthenticationPrincipal CustomUserDetails user
) {
fileService.deleteFile(
entityType,
entityId,
attachmentId,
user.getUserId()
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

@Data
public class FileReadResponseDto {
private String imageUrl;
private String publicURL;

public FileReadResponseDto(String imageUrl) {
this.imageUrl = imageUrl;
public FileReadResponseDto(String publicURL) {
this.publicURL = publicURL;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,4 @@
public class FileUpdateRequestDto {
@NotNull(message = "파일 입력은 필수입니다.")
private MultipartFile multipartFile;

private EntityType entityType;

private Long entityId;
}
12 changes: 12 additions & 0 deletions src/main/java/com/back/domain/file/dto/FileUpdateResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.back.domain.file.dto;

import lombok.Data;

@Data
public class FileUpdateResponseDto {
private String publicURL;

public FileUpdateResponseDto(String publicURL) {
this.publicURL = publicURL;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,4 @@
public class FileUploadRequestDto {
@NotNull(message = "파일 입력은 필수입니다.")
private MultipartFile multipartFile;

private EntityType entityType;

private Long entityId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

@Data
public class FileUploadResponseDto {
private String imageUrl;
private Long attachmentId;
private String publicURL;

public FileUploadResponseDto(String imageUrl) {
this.imageUrl = imageUrl;
public FileUploadResponseDto(Long attachmentId, String publicURL) {
this.attachmentId = attachmentId;
this.publicURL = publicURL;
}
}
22 changes: 9 additions & 13 deletions src/main/java/com/back/domain/file/entity/FileAttachment.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class FileAttachment extends BaseEntity {

private String originalName;

private String filePath;
private String publicURL;

private long fileSize;

Expand All @@ -36,25 +36,21 @@ public FileAttachment(
String storedName,
MultipartFile multipartFile,
User user,
EntityType entityType,
Long entityId,
String filePath
String publicURL
) {
this.storedName = storedName;
originalName = multipartFile.getOriginalFilename();
this.filePath = filePath;
fileSize = multipartFile.getSize();
this.originalName = multipartFile.getOriginalFilename();
this.publicURL = publicURL;
this.fileSize = multipartFile.getSize();
this.contentType = multipartFile.getContentType();
this.user = user;

attachmentMappings.add(new AttachmentMapping(this ,entityType, entityId));
}

public void update(String storedName, MultipartFile multipartFile, String filePath) {
public void update(String storedName, MultipartFile multipartFile, String publicURL) {
this.storedName = storedName;
originalName = multipartFile.getOriginalFilename();
this.filePath = filePath;
fileSize = multipartFile.getSize();
this.originalName = multipartFile.getOriginalFilename();
this.publicURL = publicURL;
this.fileSize = multipartFile.getSize();
this.contentType = multipartFile.getContentType();
}
}
72 changes: 27 additions & 45 deletions src/main/java/com/back/domain/file/service/FileService.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.back.domain.file.dto.FileReadResponseDto;
import com.back.domain.file.dto.FileUpdateResponseDto;
import com.back.domain.file.dto.FileUploadResponseDto;
import com.back.domain.file.entity.AttachmentMapping;
import com.back.domain.file.entity.EntityType;
Expand All @@ -20,6 +21,8 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
Expand All @@ -34,14 +37,10 @@ public class FileService {
private final AmazonS3 amazonS3;
private final FileAttachmentRepository fileAttachmentRepository;
private final UserRepository userRepository;
private final AttachmentMappingRepository attachmentMappingRepository;
private final EntityValidator entityValidator;

@Transactional
public FileUploadResponseDto uploadFile(
MultipartFile multipartFile,
EntityType entityType,
Long entityId,
Long userId
) {
User user = userRepository.findById(userId)
Expand All @@ -53,46 +52,44 @@ public FileUploadResponseDto uploadFile(
String storedFileName = createFileName(multipartFile.getOriginalFilename());

// S3의 저장된 파일의 PublicURL
String filePath = s3Upload(storedFileName, multipartFile);
String publicURL = s3Upload(storedFileName, multipartFile);

// FileAttachment 정보 저장
fileAttachmentRepository.save(
FileAttachment fileAttachment = fileAttachmentRepository.save(
new FileAttachment(
storedFileName,
multipartFile,
user,
entityType,
entityId,
filePath
publicURL
)
);

return new FileUploadResponseDto(filePath);
return new FileUploadResponseDto(fileAttachment.getId(), publicURL);
}


@Transactional(readOnly = true)
public FileReadResponseDto getFile(
EntityType entityType,
Long entityId
) {
FileAttachment fileAttachment = getFileAttachmentOrThrow(entityType, entityId);
public FileReadResponseDto getFile(Long attachmentId) {
FileAttachment fileAttachment = fileAttachmentRepository.findById(attachmentId)
.orElseThrow(() ->
new CustomException(ErrorCode.FILE_NOT_FOUND)
);

String filePath = fileAttachment.getFilePath();
String publicURL = fileAttachment.getPublicURL();

return new FileReadResponseDto(filePath);
return new FileReadResponseDto(publicURL);
}

@Transactional
public void updateFile(
public FileUpdateResponseDto updateFile(
Long attachmentId,
MultipartFile multipartFile,
EntityType entityType,
Long entityId,
Long userId
) {
entityValidator.validate(entityType, entityId);

FileAttachment fileAttachment = getFileAttachmentOrThrow(entityType, entityId);
FileAttachment fileAttachment = fileAttachmentRepository.findById(attachmentId)
.orElseThrow(() ->
new CustomException(ErrorCode.FILE_NOT_FOUND)
);

checkAccessPermission(fileAttachment, userId);

Expand All @@ -102,26 +99,22 @@ public void updateFile(
// S3에 새롭게 저장할 파일 이름
String newStoredName = createFileName(multipartFile.getOriginalFilename());

String filePath = s3Upload(newStoredName, multipartFile);
String publicURL = s3Upload(newStoredName, multipartFile);

s3Delete(oldStoredName);

// fileAttachment 정보 업데이트
fileAttachment.update(newStoredName, multipartFile, filePath);
fileAttachment.update(newStoredName, multipartFile, publicURL);
return new FileUpdateResponseDto(publicURL);
}

@Transactional
public void deleteFile(EntityType entityType, Long entityId, Long userId) {
entityValidator.validate(entityType, entityId);

AttachmentMapping attachmentMapping = attachmentMappingRepository
.findByEntityTypeAndEntityId(entityType, entityId)
public void deleteFile(Long attachmentId, Long userId) {
FileAttachment fileAttachment = fileAttachmentRepository.findById(attachmentId)
.orElseThrow(() ->
new CustomException(ErrorCode.ATTACHMENT_MAPPING_NOT_FOUND)
new CustomException(ErrorCode.FILE_NOT_FOUND)
);

FileAttachment fileAttachment = attachmentMapping.getFileAttachment();

checkAccessPermission(fileAttachment, userId);

s3Delete(fileAttachment.getStoredName());
Expand Down Expand Up @@ -173,19 +166,8 @@ private String createFileName(String fileName) {

// 파일 접근 권한 체크
private void checkAccessPermission(FileAttachment fileAttachment, Long userId) {
if (fileAttachment.getUser().getId() != userId) {
if (!fileAttachment.getUser().getId().equals(userId)) {
throw new CustomException(ErrorCode.FILE_ACCESS_DENIED);
}
}

// AttachmentMapping -> fileAttachment 추출
private FileAttachment getFileAttachmentOrThrow(EntityType entityType, Long entityId) {
AttachmentMapping attachmentMapping = attachmentMappingRepository
.findByEntityTypeAndEntityId(entityType, entityId)
.orElseThrow(() ->
new CustomException(ErrorCode.ATTACHMENT_MAPPING_NOT_FOUND)
);

return attachmentMapping.getFileAttachment();
}
}
1 change: 1 addition & 0 deletions src/main/java/com/back/global/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ public enum ErrorCode {
FILE_UPLOAD_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "FILE_001", "파일 업로드에 실패했습니다."),
ATTACHMENT_MAPPING_NOT_FOUND(HttpStatus.NOT_FOUND, "FILE_002", "매핑된 파일 정보를 찾을 수 없습니다."),
FILE_ACCESS_DENIED(HttpStatus.FORBIDDEN, "FILE_003", "파일을 접근할 권한이 없습니다."),
FILE_NOT_FOUND(HttpStatus.NOT_FOUND, "FILE_004", "파일 정보를 찾을 수 없습니다."),

// ======================== 토큰 관련 ========================
INVALID_EMAIL_TOKEN(HttpStatus.UNAUTHORIZED, "TOKEN_001", "유효하지 않은 이메일 인증 토큰입니다."),
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/back/global/security/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins(
"http://localhost:3000", // Catfe 프론트 개발 서버
"https://www.catfe.com" // Catfe 프론트 운영 서버
"https://www.catfe.site" // Catfe 프론트 운영 서버
)
.allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")
.allowedHeaders("*")
Expand Down
Loading