Skip to content
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

파일 업로드 기능 구현 완료 #97

Merged
merged 2 commits into from
Feb 21, 2024
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
1 change: 1 addition & 0 deletions src/main/java/balancetalk/global/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public enum ErrorCode {
NOT_FOUND_VOTE(NOT_FOUND, "해당 게시글에서 투표한 기록이 존재하지 않습니다."),
NOT_FOUND_BOOKMARK(NOT_FOUND, "해당 게시글에서 북마크한 기록이 존재하지 않습니다."),
NOT_FOUND_COMMENT(NOT_FOUND, "존재하지 않는 댓글입니다."),
NOT_FOUND_DIRECTORY(NOT_FOUND, "존재하지 않는 디렉토리입니다."),

// 409
ALREADY_VOTE(CONFLICT, "투표는 한 번만 가능합니다."),
Expand Down
62 changes: 62 additions & 0 deletions src/main/java/balancetalk/module/file/application/FileService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package balancetalk.module.file.application;

import balancetalk.global.exception.BalanceTalkException;
import balancetalk.module.file.domain.File;
import balancetalk.module.file.domain.FileRepository;
import balancetalk.module.file.domain.FileType;
import balancetalk.module.file.dto.FileDto;
import jakarta.servlet.ServletContext;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

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.Arrays;
import java.util.UUID;

import static balancetalk.global.exception.ErrorCode.NOT_FOUND_DIRECTORY;

@Service
@RequiredArgsConstructor
public class FileService {
private final FileRepository fileRepository;
private final ServletContext servletContext;

// 파일 업로드
@Transactional
public File uploadFile(MultipartFile file) throws IOException {
String uploadDir = "C:\\Users\\King\\Desktop"; // TODO : 경로 configuration 파일로 빼기
String originalFileName = file.getOriginalFilename();
String storedFileName = UUID.randomUUID().toString().replace("-", "") + "_" + originalFileName;
String path = Paths.get(uploadDir, storedFileName).toString();
String contentType = file.getContentType();
FileType fileType = convertMimeTypeToFileType(contentType);

FileDto fileDto = FileDto.of(file, storedFileName, path, fileType);
File saveFile = fileDto.toEntity();

Files.copy(file.getInputStream(), Paths.get(path), StandardCopyOption.REPLACE_EXISTING);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try / catch를 통해서 IoException을 처리해준다면 컨트롤러에서 익셉션을 throw하지 않아도 되지 않을까 싶어서 의견 여쭤봅니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분도 예외 처리 단계에서 개선할 예정입니다! 감사합니다!!

return fileRepository.save(saveFile);
}

private FileType convertMimeTypeToFileType(String mimeType) {
if (mimeType == null) {
throw new IllegalArgumentException("MIME 타입은 NULL이 될 수 없습니다.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exception들을 ErrorCode에 추가해서 사용하는건 어떨까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR 단위를 나누기 위해서 임시 구현한 단계입니다! 추후 예외 처리 이슈에서 구현하겠습니다~!

}

return Arrays.stream(FileType.values())
.filter(type -> type.getMimeType().equalsIgnoreCase(mimeType))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("지원하지 않는 파일 타입 : " + mimeType));
}
}
12 changes: 2 additions & 10 deletions src/main/java/balancetalk/module/file/domain/File.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
package balancetalk.module.file.domain;

import balancetalk.module.Notice;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
Expand All @@ -33,7 +25,7 @@ public class File {
@Column(nullable = false, length = 50)
private String uploadName; // 사용자가 업로드한 파일명

@Size(max = 50)
@Size(max = 100)
@Column(length = 50, unique = true)
private String storedName; // 서버 내부에서 관리하는 파일명

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package balancetalk.module.file.domain;

import org.springframework.data.jpa.repository.JpaRepository;

public interface FileRepository extends JpaRepository<File, Long> {
}
16 changes: 16 additions & 0 deletions src/main/java/balancetalk/module/file/dto/FileDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@
import balancetalk.module.file.domain.FileType;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import org.springframework.web.multipart.MultipartFile;

@Data
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class FileDto {
@Schema(description = "파일 id", example = "1")
private Long id;
@Schema(description = "사용자가 업로드한 파일명", example = "사진1")
private String uploadName;
@Schema(description = "서버에 저장되는 파일명", example = "d23d2dqwt1251asbds사진1")
private String storedName;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 필드만 스웨거 설정을 안하신 이유가 있을까요?.?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

발견 못한 필드네요! 감사합니다!!

@Schema(description = "업로드한 파일의 경로", example = "/...")
private String path;
@Schema(description = "업로드한 파일 확장자", example = "JPEG")
Expand All @@ -35,4 +41,14 @@ public static FileDto fromEntity(File file) {
.size(file.getSize())
.build();
}

public static FileDto of(MultipartFile file, String storedFileName, String path, FileType fileType) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정적 팩토리 메서드 말고 고려하신 다른 방법이 있으신가요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정적 팩터리 메서드 말고는 따로 생각나는 방법이 없었습니다..!

return FileDto.builder()
.uploadName(file.getOriginalFilename())
.storedName(storedFileName)
.path(path)
.type(fileType)
.size(file.getSize())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package balancetalk.module.file.presentation;

import balancetalk.module.file.application.FileService;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

@RestController
@RequiredArgsConstructor
@RequestMapping("/files")
public class FileController {
private final FileService fileService;

@ResponseStatus(HttpStatus.CREATED)
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
fileService.uploadFile(file);
return "파일이 업로드되었습니다.";
}
}