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
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'org.postgresql:postgresql'

// S3
implementation(platform("software.amazon.awssdk:bom:2.25.66"))
implementation("software.amazon.awssdk:s3")
implementation("software.amazon.awssdk:url-connection-client")

// JWT
implementation("io.jsonwebtoken:jjwt-api:0.13.0")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.13.0")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.team.moodit.api.controller.v1;

import com.team.moodit.support.auth.ApiUser;
import com.team.moodit.support.file.FileUploader;
import com.team.moodit.support.file.UploadResult;
import com.team.moodit.support.response.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequiredArgsConstructor
public class FIleController {
private final FileUploader fileUploader;

@PostMapping("/v1/files/upload")
public ApiResponse<UploadResult> uploadFile(
ApiUser apiUser,
@RequestPart MultipartFile file
) {
UploadResult result = fileUploader.upload(apiUser.getId(), file);
return ApiResponse.success(result);
}
}
36 changes: 36 additions & 0 deletions src/main/java/com/team/moodit/storage/db/s3/S3Uploader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.team.moodit.storage.db.s3;

import com.team.moodit.support.error.ApiException;
import com.team.moodit.support.error.ErrorType;
import java.io.IOException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;

@Slf4j
@Component
@RequiredArgsConstructor
public class S3Uploader {
private final S3Client s3Client;

@Value("${storage.s3.bucket}")
private String bucket;

public String uploadFile(MultipartFile file, String objectKey) throws IOException {
s3Client.putObject(
PutObjectRequest.builder()
.bucket(bucket)
.key(objectKey)
.contentType(file.getContentType())
.build(),
RequestBody.fromInputStream(file.getInputStream(), file.getSize())
);

return objectKey;
}
}
52 changes: 52 additions & 0 deletions src/main/java/com/team/moodit/storage/db/s3/config/S3Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.team.moodit.storage.db.s3.config;

import java.net.URI;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.S3Configuration;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;

@Configuration
public class S3Config {
@Value("${storage.s3.endpoint}") private String endpoint;
@Value("${storage.s3.access-key}") private String accessKey;
@Value("${storage.s3.secret-key}") private String secretKey;

public StaticCredentialsProvider staticCredentialsProvider() {
return StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey));
}

public S3Configuration s3Configuration() {
return S3Configuration.builder()
.pathStyleAccessEnabled(true)
.build();
}

@Bean
public S3Client s3Client() {
return S3Client.builder()
.httpClient(UrlConnectionHttpClient.builder().build())
.credentialsProvider(staticCredentialsProvider())
.region(Region.AP_NORTHEAST_2)
.endpointOverride(URI.create(endpoint))
.serviceConfiguration(s3Configuration())
.overrideConfiguration(ClientOverrideConfiguration.builder().build())
.build();
}

@Bean
public S3Presigner s3Presigner() {
return S3Presigner.builder()
.credentialsProvider(staticCredentialsProvider())
.region(Region.AP_NORTHEAST_2)
.endpointOverride(URI.create(endpoint))
.build();
}
}
5 changes: 4 additions & 1 deletion src/main/java/com/team/moodit/support/error/ErrorType.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ public enum ErrorType {

// Auth
INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다.", LogLevel.INFO),
EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 토큰 입니다.", LogLevel.INFO);
EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 토큰 입니다.", LogLevel.INFO),

// File
FILE_UPLOADING_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "파일 업로드 중 알 수 없는 오류가 발생했습니다. 잠시 후 다시 시도해주세요.", LogLevel.ERROR);

private final HttpStatus status;
private final String message;
Expand Down
43 changes: 43 additions & 0 deletions src/main/java/com/team/moodit/support/file/FileUploader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.team.moodit.support.file;

import com.team.moodit.storage.db.s3.S3Uploader;
import com.team.moodit.support.error.ApiException;
import com.team.moodit.support.error.ErrorType;
import java.io.IOException;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@Component
@RequiredArgsConstructor
public class FileUploader {
private final S3Uploader s3Uploader;

@Value("${storage.s3.endpoint}") private String endpoint;
@Value("${storage.s3.bucket}") private String bucket;

public UploadResult upload(Long userId, MultipartFile file) {
try {
String objectKey = uploadToS3(file);

// TODO: FileEntity 등 저장 로직 추가

return new UploadResult(
1L,
endpoint + "/" + bucket + "/" + objectKey
);
} catch (IOException e) {
log.error("[FileUploader] userId: {}, filename: {}. size: {} bytes, message: {}", userId, file.getOriginalFilename(), file.getSize(), e.getMessage(), e);
throw new ApiException(ErrorType.FILE_UPLOADING_FAILED);
}
}

private String uploadToS3(MultipartFile file) throws IOException {
String objectKey = UUID.randomUUID() + "_" + file.getOriginalFilename();
return s3Uploader.uploadFile(file, objectKey);
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/team/moodit/support/file/UploadResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.team.moodit.support.file;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class UploadResult {
private Long id;
private String fileUrl;
}
1 change: 1 addition & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ spring:
config:
import:
- db-core.yml
- db-s3.yml
- client-kakao.yml

jwt:
Expand Down
12 changes: 12 additions & 0 deletions src/main/resources/db-s3.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB

storage:
s3:
endpoint: http://14.6.152.212:9000
bucket: moodit-dev
access-key: admin
secret-key: qlqjstongue@74