Skip to content

Commit

Permalink
Merge pull request #13 from Kusitms-28th-Meetup-D/feature/10-S3
Browse files Browse the repository at this point in the history
S3 연동 및 이미지 업로드
  • Loading branch information
emilywin825 authored Oct 27, 2023
2 parents eb25975 + 11e5df7 commit 3bb3666
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 5 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ dependencies {

// mongoDB
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'

//S3
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
}

tasks.named('test') {
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/com/kusithm/meetupd/common/config/S3Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.kusithm.meetupd.common.config;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class S3Config {

@Value("${cloud.aws.credentials.access-key}")
private String accessKey;

@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;

@Value("${cloud.aws.region.static}")
private String region;

@Bean
public AmazonS3Client amazonS3Client() {
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

return (AmazonS3Client) AmazonS3ClientBuilder
.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class SecurityConfig {
private final TokenProvider tokenProvider;

// TODO api 추가될 때 white list url 확인해서 추가하기.
private static final String[] WHITE_LIST_URL = {"/api/health","/api/auth/register","/api/auth/login", "/api/auth/reissue", "/"};
private static final String[] WHITE_LIST_URL = {"/api/health","/api/auth/register","/api/auth/login", "/api/auth/reissue", "/","/api/s3/upload"};

@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/com/kusithm/meetupd/common/error/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ public enum ErrorCode {
* 400 Bad Request
*/


/**
* 401 Unauthorized
*/
Expand Down Expand Up @@ -47,8 +46,8 @@ public enum ErrorCode {
/**
* 500 INTERNAL SERVER ERROR
*/
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다.");

INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."),
FILE_UPLOAD_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "S3 이미지 업로드에 실패");

private final HttpStatus status;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.kusithm.meetupd.common.error;

public class FileUploadFailedException extends ApplicationException {

public FileUploadFailedException(ErrorCode error) {
super(error);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.kusithm.meetupd.domain.fileUpload.controller;

import com.kusithm.meetupd.domain.fileUpload.dto.response.FileUploadResponse;
import com.kusithm.meetupd.domain.fileUpload.service.AwsS3Service;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@RequiredArgsConstructor
@RestController
@RequestMapping("/api/s3")
public class S3FileUploadController {

private final AwsS3Service awsS3Service;

@PostMapping("/upload")
public FileUploadResponse uploadFile(
@RequestPart(value = "file") List<MultipartFile> images) {
return awsS3Service.uploadFile(images);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.kusithm.meetupd.domain.fileUpload.dto.response;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Builder
@Getter
public class FileUploadResponse {
List<String> fileUrls = new ArrayList<>();

public FileUploadResponse (List<String> fileUrls){
this.fileUrls=fileUrls;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.kusithm.meetupd.domain.fileUpload.entity;

import jakarta.persistence.*;
import lombok.*;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
@Entity(name = "Image")
public class Image {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "image_id")
private Long id;

@Column(name = "image_url", nullable = false)
private String imageUrl;

public Image(String url){
this.imageUrl=url;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.kusithm.meetupd.domain.fileUpload.mysql;


import com.kusithm.meetupd.domain.fileUpload.entity.Image;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ImageRepository extends JpaRepository<Image, Long> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.kusithm.meetupd.domain.fileUpload.service;

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 com.amazonaws.services.s3.model.PutObjectResult;
import com.kusithm.meetupd.common.error.ErrorCode;
import com.kusithm.meetupd.common.error.FileUploadFailedException;
import com.kusithm.meetupd.domain.fileUpload.dto.response.FileUploadResponse;
import com.kusithm.meetupd.domain.fileUpload.entity.Image;
import com.kusithm.meetupd.domain.fileUpload.mysql.ImageRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import static com.kusithm.meetupd.common.error.ErrorCode.FILE_UPLOAD_FAILED;

@Slf4j
@RequiredArgsConstructor
@Service
public class AwsS3Service {
private final AmazonS3Client amazonS3Client;

@Value("${cloud.aws.s3.bucket}")
private String bucketName;
private final ImageRepository imageRepository;

public FileUploadResponse uploadFile(List<MultipartFile> images) {

List<String> fileUrls = new ArrayList<>();
for (MultipartFile image : images) {
String fileName = image.getOriginalFilename();

ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentType(image.getContentType());

try (InputStream inputStream = image.getInputStream()) {
amazonS3Client.putObject(new PutObjectRequest(bucketName, fileName, inputStream, objectMetadata)
.withCannedAcl(CannedAccessControlList.PublicRead));

String imagePath = amazonS3Client.getUrl(bucketName, fileName).toString(); // 접근가능한 URL 가져오기
fileUrls.add(imagePath);
imageRepository.save(new Image(imagePath));

} catch (IOException e) {
throw new FileUploadFailedException(FILE_UPLOAD_FAILED);
}
}

return new FileUploadResponse(fileUrls);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ private Sample saveText(String text) {
sampleRepository.save(sample);
return sample;
}
}
}

0 comments on commit 3bb3666

Please sign in to comment.