-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/chung step2 #6
Changes from 6 commits
63bb89b
b4c3d5a
5d81365
70ffaa3
89e5678
af8ff26
7732bf6
539d0d2
576e60b
d5b94bb
4149f7a
99fb8e0
4986865
962485c
15ad8be
a0fce68
bc03dbf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.c4cometrue.mystorage.dto; | ||
|
||
import jakarta.validation.constraints.NotNull; | ||
|
||
public record FolderContentsRequest( | ||
Long folderId, | ||
@NotNull(message = "사용자 id는 널이 될 수 없습니다") long userId | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 왜 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 폴더 id 가 null 일 시 최상단 부모를 의미하게 했습니다! 그리고 userId 가 null 이 될 수 없게 했기 때문에 @NotNull 과 long 을 썼습니다 |
||
) { | ||
public static FolderContentsRequest of(Long folderId, long userId) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 DTO를 생성하는 곳에서 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://tecoble.techcourse.co.kr/post/2020-05-26-static-factory-method/ |
||
return new FolderContentsRequest(folderId, userId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.c4cometrue.mystorage.dto; | ||
|
||
import jakarta.validation.constraints.NotNull; | ||
|
||
public record FolderCreateRequest(@NotNull long userId, @NotNull String userFolderName, Long parentId) { | ||
public static FolderCreateRequest of(long userId, String userFolderName, long parentId) { | ||
return new FolderCreateRequest(userId, userFolderName, parentId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.c4cometrue.mystorage.dto; | ||
|
||
import jakarta.validation.constraints.NotNull; | ||
|
||
public record FolderNameChangeRequest( | ||
@NotNull(message = "유저id는 널이 될 수 없습니다") long userId, | ||
Long folderId, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 폴더의 이름을 바꾸는 경우에도 Null일 수 있나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 아닙니다! 수정하겠습니다! |
||
String folderName | ||
) { | ||
public static FolderNameChangeRequest of( | ||
long userId, | ||
Long folderId, | ||
String folderName) { | ||
return new FolderNameChangeRequest(userId, folderId, folderName); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.c4cometrue.mystorage.dto; | ||
|
||
import com.c4cometrue.mystorage.file.FileMetadata; | ||
import com.c4cometrue.mystorage.folder.FolderMetadata; | ||
import com.c4cometrue.mystorage.meta.MetadataType; | ||
|
||
public record MetaResponse( | ||
Long id, | ||
String metadataOriginalName, | ||
Long parentId, | ||
MetadataType type | ||
) { | ||
public static MetaResponse from(FileMetadata metadata) { | ||
return new MetaResponse( | ||
metadata.getId(), | ||
metadata.getOriginalFileName(), | ||
metadata.getParentId(), | ||
metadata.getMetadataType() | ||
); | ||
} | ||
|
||
public static MetaResponse from(FolderMetadata metadata) { | ||
return new MetaResponse( | ||
metadata.getId(), | ||
metadata.getOriginalFolderName(), | ||
metadata.getParentId(), | ||
metadata.getMetadataType() | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,14 +3,12 @@ | |
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.bind.annotation.DeleteMapping; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.ResponseStatus; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import com.c4cometrue.mystorage.dto.FileDeleteRequest; | ||
import com.c4cometrue.mystorage.dto.FileDownloadRequest; | ||
import com.c4cometrue.mystorage.dto.FileUploadRequest; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
|
@@ -20,12 +18,6 @@ | |
public class FileController { | ||
private final FileService fileService; | ||
|
||
@PostMapping | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Metadata 컨트롤러로 인해 파일 업로드를 이동하셨군요.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 파일과 폴더 서비스에 의존성이 생기는 게 자연스러운가 생각해서 |
||
@ResponseStatus(HttpStatus.CREATED) | ||
public void uploadFile(FileUploadRequest fileUploadRequest) { | ||
fileService.uploadFile(fileUploadRequest.multipartFile(), fileUploadRequest.userId()); | ||
} | ||
|
||
@DeleteMapping | ||
@ResponseStatus(HttpStatus.NO_CONTENT) | ||
public void deleteFile(FileDeleteRequest request) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
package com.c4cometrue.mystorage.file; | ||
|
||
import java.util.List; | ||
|
||
import org.springframework.stereotype.Service; | ||
|
||
import com.c4cometrue.mystorage.exception.ErrorCode; | ||
|
@@ -16,26 +18,38 @@ public void deleteBy(Long fileId) { | |
fileRepository.deleteById(fileId); | ||
} | ||
|
||
public Metadata findBy(Long fileId, Long userId) { | ||
private void existBy(Long fileId) { | ||
if (!fileRepository.existsById(fileId)) { | ||
throw ErrorCode.CANNOT_FOUND_FILE.serviceException("fileId : {}", fileId); | ||
} | ||
} | ||
|
||
public FileMetadata findBy(Long fileId, Long userId) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. existBy랑 매칭이 좀 어색해보이네요.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 악성 유저가 다른 사람의 파일에 접근하는 것을 막고자 의도 했습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. delete의 경우 FileService 에서 유저가 해당 파일 소유주인지 확인을 한 다음 오기 때문에 별도로 userId 는 추가 하지 않았습니다! |
||
return fileRepository.findByIdAndUploaderId(fileId, userId) | ||
.orElseThrow(() -> ErrorCode.CANNOT_FOUND_FILE.serviceException("fileId : {}, userId : {}", fileId, | ||
userId)); | ||
} | ||
|
||
public void persist(Metadata metadata, Long userId) { | ||
duplicateBy(metadata.getOriginalFileName(), userId); | ||
fileRepository.save(metadata); | ||
public void persist(FileMetadata fileMetadata, Long userId, Long parentId) { | ||
validateFileOwnershipBy(parentId, userId); | ||
duplicateBy(fileMetadata.getOriginalFileName(), userId, parentId); | ||
fileRepository.save(fileMetadata); | ||
} | ||
|
||
public void existBy(Long fileId) { | ||
if (!fileRepository.existsById(fileId)) { | ||
throw ErrorCode.CANNOT_FOUND_FILE.serviceException("fileId : {}", fileId); | ||
private void duplicateBy(String fileName, Long userId, Long parentId) { | ||
if (fileRepository.checkDuplicateFileName(parentId, userId, fileName)) { | ||
throw ErrorCode.DUPLICATE_FILE_NAME.serviceException("fileName : {}", fileName); | ||
} | ||
} | ||
|
||
public void duplicateBy(String fileName, Long userId) { | ||
if (fileRepository.checkDuplicateFileName(fileName, userId)) { | ||
throw ErrorCode.DUPLICATE_FILE_NAME.serviceException("fileName : {}", fileName); | ||
public List<FileMetadata> findChildBy(Long parentId, Long userId) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fileId는 Unique 하게 만들었으면서, folderId는 Unique하게 만들지 않은 이유가 있나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좀더 설명 부탁드려도 되나요!? |
||
validateFileOwnershipBy(parentId, userId); | ||
return fileRepository.findByParentIdAndUploaderId(parentId, userId); | ||
} | ||
|
||
private void validateFileOwnershipBy(Long folderId, Long userId) { | ||
if (folderId != null && !fileRepository.existsByIdAndUploaderId(folderId, userId)) { | ||
throw ErrorCode.UNAUTHORIZED_FILE_ACCESS.serviceException(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
package com.c4cometrue.mystorage.file; | ||
|
||
public interface FileReader { | ||
Metadata findBy(Long fileId, Long userId); | ||
import java.util.List; | ||
|
||
void existBy(Long fileId); | ||
public interface FileReader { | ||
FileMetadata findBy(Long fileId, Long userId); | ||
|
||
void duplicateBy(String fileName, Long userId); | ||
List<FileMetadata> findChildBy(Long parentId, Long userId); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,26 @@ | ||
package com.c4cometrue.mystorage.file; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.data.jpa.repository.Query; | ||
|
||
public interface FileRepository extends JpaRepository<Metadata, Long> { | ||
Optional<Metadata> findByIdAndUploaderId(Long id, Long uploaderId); | ||
public interface FileRepository extends JpaRepository<FileMetadata, Long> { | ||
Optional<FileMetadata> findByIdAndUploaderId(Long id, Long uploaderId); | ||
|
||
@Query("SELECT CASE WHEN COUNT(m) > 0 " | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JDK 17에서는 """ 으로 Text Block 생성이 가능합니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 감사합니다 |
||
+ "THEN TRUE " | ||
+ "ELSE FALSE END " | ||
+ "FROM Metadata m " | ||
+ "WHERE m.uploaderId = :uploaderId " | ||
+ "FROM FileMetadata m " | ||
+ "WHERE " | ||
+ "(m.parentId = :parentId OR (m.parentId IS NULL AND :parentId IS NULL)) " | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (parent_id, uploader_id, original_file_name) 의 인덱스가 필요하겠네요. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 추가하겠습니다! |
||
+ "AND m.uploaderId = :uploaderId " | ||
+ "AND m.originalFileName = :fileName ") | ||
boolean checkDuplicateFileName(String fileName, Long uploaderId); | ||
boolean checkDuplicateFileName(Long parentId, Long uploaderId, String fileName); | ||
|
||
List<FileMetadata> findByParentIdAndUploaderId(Long parentId, Long userId); | ||
|
||
boolean existsByIdAndUploaderId(Long parentId, Long userId); | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
|
||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.List; | ||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Service; | ||
|
@@ -15,39 +16,46 @@ | |
@Service | ||
@RequiredArgsConstructor | ||
public class FileService { | ||
private final FileDataAccessService fileDataAccessService; | ||
|
||
@Value("${file.storage-path}") | ||
private String storagePath; | ||
private final FileReader fileReader; | ||
private final FileWriter fileWriter; | ||
|
||
@Value("${file.buffer}") | ||
private int bufferSize; | ||
|
||
@Transactional | ||
public void uploadFile(MultipartFile file, Long userId) { | ||
public void uploadFile(MultipartFile file, Long userId, Long parentId, String basePath) { | ||
String originalFileName = file.getOriginalFilename(); | ||
String storedFileName = Metadata.storedName(); | ||
Path path = Paths.get(storagePath, storedFileName); | ||
Metadata metadata = Metadata.of(originalFileName, storedFileName, path.toString(), userId); | ||
|
||
fileDataAccessService.persist(metadata, userId); | ||
String storedFileName = FileMetadata.storedName(); | ||
Path path = Paths.get(basePath, storedFileName); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 헐 저도 |
||
FileMetadata fileMetadata = FileMetadata.builder() | ||
.originalFileName(originalFileName) | ||
.storedFileName(storedFileName) | ||
.filePath(path.toString()) | ||
.uploaderId(userId) | ||
.parentId(parentId) | ||
.build(); | ||
|
||
fileWriter.persist(fileMetadata, userId, parentId); | ||
FileUtil.uploadFile(file, path, bufferSize); | ||
} | ||
|
||
public void downloadFile(Long fileId, String userPath, Long userId) { | ||
Metadata metadata = fileDataAccessService.findBy(fileId, userId); | ||
Path originalPath = Paths.get(metadata.getFilePath()); | ||
Path userDesignatedPath = Paths.get(userPath).resolve(metadata.getOriginalFileName()).normalize(); | ||
FileMetadata fileMetadata = fileReader.findBy(fileId, userId); | ||
Path originalPath = Paths.get(fileMetadata.getFilePath()); | ||
Path userDesignatedPath = Paths.get(userPath).resolve(fileMetadata.getOriginalFileName()).normalize(); | ||
|
||
FileUtil.download(originalPath, userDesignatedPath, bufferSize); | ||
} | ||
|
||
@Transactional | ||
public void deleteFile(Long fileId, Long userId) { | ||
Metadata metadata = fileDataAccessService.findBy(fileId, userId); | ||
fileDataAccessService.deleteBy(fileId); | ||
Path path = Paths.get(metadata.getFilePath()); | ||
FileMetadata fileMetadata = fileReader.findBy(fileId, userId); | ||
fileWriter.deleteBy(fileId); | ||
Path path = Paths.get(fileMetadata.getFilePath()); | ||
|
||
FileUtil.delete(path); | ||
} | ||
|
||
public List<FileMetadata> findChildBy(Long parentId, Long userId) { | ||
return fileReader.findChildBy(parentId, userId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
package com.c4cometrue.mystorage.file; | ||
|
||
public interface FileWriter { | ||
void persist(Metadata metadata, Long userId); | ||
void persist(FileMetadata fileMetadata, Long userId, Long parentId); | ||
|
||
void deleteBy(Long fileId); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@NotNull
로 바꾸셨군요! 그런데 저는 테스트해보면 다른 입력값은MethodArgumentNotValidException
가 발생하는데 Multipartfile은 파일을 담지 않고 보내면ConstraintViolationException
가 발생하더라구요. 예외 핸들러에서 한번에 처리해줄 수 없는데 청천님은 어떻게 하려고 하셨나요..There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
예외에 많이 미진해서 하은님 코드 보며 많이 배우고 있습니다!