Skip to content

Commit

Permalink
codesquad-issue-team-05#80 feat : 파일 업로드 api 구현, 예외처리 완료
Browse files Browse the repository at this point in the history
  • Loading branch information
DOEKYONG committed Aug 14, 2023
1 parent 3ec9f59 commit bec54b4
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 2 deletions.
3 changes: 3 additions & 0 deletions be/issue/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ dependencies {
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
// 패스워드 암호화
implementation group: 'org.mindrot', name: 'jbcrypt', version: '0.4'
// OAuth 서버 요청
implementation 'org.springframework.boot:spring-boot-starter-webflux'
// 파일 업로드
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.time.DateTimeException;

import org.springframework.http.HttpStatus;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
Expand Down Expand Up @@ -55,8 +56,12 @@ public enum ErrorCode implements StatusCode {
DUPLICATE_OBJECT_FOUND(HttpStatus.BAD_REQUEST, "중복된 항목 선택입니다."),
NOT_EXIST_ISSUE(HttpStatus.BAD_REQUEST, "존재하지 않는 이슈입니다."),
ALREADY_DELETED_ISSUE(HttpStatus.BAD_REQUEST, "이미 삭제된 이슈입니다."),
NOT_FOUND_ISSUES(HttpStatus.BAD_REQUEST, "이슈를 찾을 수 없습니다.");
NOT_FOUND_ISSUES(HttpStatus.BAD_REQUEST, "이슈를 찾을 수 없습니다."),

// -- [File] -- //
EMPTY_FILE_EXCEPTION(HttpStatus.BAD_REQUEST, "파일을 선택해 주세요."),
FAILED_UPLOAD_FILE(HttpStatus.BAD_REQUEST, "파일 업로드에 실패했습니다."),
MAX_FILE_SIZE(HttpStatus.BAD_REQUEST, "업로드 할 수 있는 최대 크기는 20MB 입니다.");

private HttpStatus status;
private String message;
Expand Down Expand Up @@ -96,6 +101,9 @@ public static StatusCode from(RuntimeException e) {
if (e instanceof DateTimeException) {
return ErrorCode.NOT_FOUND_DATE;
}
if (e instanceof MaxUploadSizeExceededException) {
return ErrorCode.MAX_FILE_SIZE;
}
return ErrorCode.ILLEGAL_ARGUMENT_EXCEPTION;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import codesquad.issueTracker.global.common.ApiResponse;
import io.jsonwebtoken.ExpiredJwtException;

@RestControllerAdvice
public class GlobalExceptionHandler {
Expand Down Expand Up @@ -47,5 +47,13 @@ public ResponseEntity<ApiResponse<String>> handleDateTimeException(DateTimeExcep
.body(ApiResponse.fail(statusCode.getStatus(), statusCode.getMessage()));
}

@ExceptionHandler(MaxUploadSizeExceededException.class)
protected ResponseEntity<ApiResponse<String>> handleMaxUploadSizeExceededException(
MaxUploadSizeExceededException e) {
StatusCode statusCode = ErrorCode.from(e);
return ResponseEntity.status(statusCode.getStatus())
.body(ApiResponse.fail(statusCode.getStatus(), statusCode.getMessage()));
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import codesquad.issueTracker.global.common.ApiResponse;
import codesquad.issueTracker.issue.dto.IssueFileResponseDto;
import codesquad.issueTracker.issue.dto.IssueLabelResponseDto;
import codesquad.issueTracker.issue.dto.IssueMilestoneResponseDto;
import codesquad.issueTracker.issue.dto.IssueOptionResponseDto;
Expand Down Expand Up @@ -129,4 +132,10 @@ public ApiResponse<String> patchMilestone(@PathVariable Long id, @RequestBody Mo
issueService.modifyMilestone(id, request);
return ApiResponse.success(SUCCESS.getStatus(), SUCCESS.getMessage());
}

@PostMapping("/upload")
public ApiResponse<IssueFileResponseDto> uploadFile(@RequestPart(value = "file") MultipartFile multipartFile) {
IssueFileResponseDto response = issueService.uploadImg(multipartFile);
return ApiResponse.success(SUCCESS.getStatus(), response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package codesquad.issueTracker.issue.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class IssueFileResponseDto {
private String imgUrl;
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
package codesquad.issueTracker.issue.service;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;

import codesquad.issueTracker.global.common.Status;
import codesquad.issueTracker.global.exception.CustomException;
import codesquad.issueTracker.global.exception.ErrorCode;
import codesquad.issueTracker.issue.domain.Issue;
import codesquad.issueTracker.issue.dto.IssueFileResponseDto;
import codesquad.issueTracker.issue.dto.IssueLabelResponseDto;
import codesquad.issueTracker.issue.dto.IssueMilestoneResponseDto;
import codesquad.issueTracker.issue.dto.IssueOptionResponseDto;
Expand Down Expand Up @@ -43,10 +53,13 @@
@Transactional(readOnly = true)
public class IssueService {

@Value("${cloud.aws.s3.bucket}")
private String bucketName;
private final IssueRepository issueRepository;
private final LabelService labelService;
private final UserService userService;
private final MilestoneService milestoneService;
private final AmazonS3Client amazonS3Client;

@Transactional
public Long save(IssueWriteRequestDto request, Long id) {
Expand Down Expand Up @@ -239,4 +252,27 @@ public Long modifyMilestone(Long id, ModifyIssueMilestoneDto request) {
}
return issueRepository.updateMilestone(id, milestoneId);
}

public IssueFileResponseDto uploadImg(MultipartFile multipartFile) {
validateFileExists(multipartFile);
String fileName = multipartFile.getOriginalFilename();
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentType(multipartFile.getContentType());

try (InputStream inputStream = multipartFile.getInputStream()) {
amazonS3Client.putObject(new PutObjectRequest(bucketName, fileName, inputStream, objectMetadata)
.withCannedAcl(CannedAccessControlList.PublicRead));
} catch (IOException e) {
throw new CustomException(ErrorCode.FAILED_UPLOAD_FILE);
}
String url = amazonS3Client.getUrl(bucketName, fileName).toString();
IssueFileResponseDto response = new IssueFileResponseDto(url);
return response;
}

private void validateFileExists(MultipartFile multipartFile) {
if (multipartFile.isEmpty()) {
throw new CustomException(ErrorCode.EMPTY_FILE_EXCEPTION);
}
}
}

0 comments on commit bec54b4

Please sign in to comment.