-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #46 from jupyter471/weekly
상품 이미지 등록 및 수정 기능 #30
- Loading branch information
Showing
13 changed files
with
302 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
src/main/java/com/helpmeCookies/global/config/S3Config.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.helpmeCookies.global.config; | ||
|
||
import com.amazonaws.auth.AWSCredentials; | ||
import com.amazonaws.auth.AWSStaticCredentialsProvider; | ||
import com.amazonaws.auth.BasicAWSCredentials; | ||
import com.amazonaws.services.s3.AmazonS3; | ||
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 AmazonS3 amazonS3() { | ||
AWSCredentials credentials = new BasicAWSCredentials(accessKey,secretKey); | ||
|
||
return AmazonS3ClientBuilder | ||
.standard() | ||
.withCredentials(new AWSStaticCredentialsProvider(credentials)) | ||
.withRegion(region) | ||
.build(); | ||
} | ||
} |
72 changes: 72 additions & 0 deletions
72
src/main/java/com/helpmeCookies/global/utils/AwsS3FileUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package com.helpmeCookies.global.utils; | ||
|
||
import com.amazonaws.services.s3.AmazonS3; | ||
import com.amazonaws.services.s3.model.CannedAccessControlList; | ||
import com.amazonaws.services.s3.model.ObjectMetadata; | ||
import com.amazonaws.services.s3.model.PutObjectRequest; | ||
import com.helpmeCookies.product.dto.FileUploadResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.multipart.MultipartFile; | ||
import org.springframework.web.server.ResponseStatusException; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.UUID; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class AwsS3FileUtils { | ||
private final AmazonS3 amazonS3; | ||
|
||
@Value("${cloud.aws.s3.bucket}") | ||
private String bucket; | ||
|
||
//다중파일 업로드후 url 반환 | ||
public List<FileUploadResponse> uploadMultiImages(List<MultipartFile> multipartFiles) { | ||
List<FileUploadResponse> fileList = new ArrayList<>(); | ||
|
||
multipartFiles.forEach(file -> { | ||
String fileName = createFileName(file.getOriginalFilename()); //파일 이름 난수화 | ||
ObjectMetadata objectMetadata = new ObjectMetadata(); | ||
objectMetadata.setContentLength(file.getSize()); | ||
objectMetadata.setContentType(file.getContentType()); | ||
|
||
try (InputStream inputStream = file.getInputStream()) { | ||
amazonS3.putObject(new PutObjectRequest(bucket, fileName, inputStream, objectMetadata) | ||
.withCannedAcl(CannedAccessControlList.PublicRead)); | ||
} catch (IOException e) { | ||
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "파일 업로드 실패" + fileName); | ||
} | ||
|
||
fileList.add(new FileUploadResponse(amazonS3.getUrl(bucket,fileName).toString(),fileName)); | ||
}); | ||
|
||
return fileList; | ||
} | ||
|
||
public String createFileName(String fileName) { | ||
return UUID.randomUUID().toString().concat(getFileExtension(fileName)); | ||
} | ||
|
||
//TODO error handler 필요 | ||
public String getFileExtension(String fileName) { | ||
try { | ||
String extension = fileName.substring(fileName.lastIndexOf(".")).toLowerCase(); | ||
//이미지 파일 확장자 목록 | ||
List<String> allowedExtensions = Arrays.asList(".jpg", ".jpeg", ".png"); | ||
|
||
if (!allowedExtensions.contains(extension)) { | ||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "이미지 파일만 업로드가 가능합니다. 지원되지 않는 형식의 파일" + fileName); | ||
} | ||
return extension; | ||
} catch (StringIndexOutOfBoundsException e) { | ||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,"잘못된 형식의 파일" + fileName + "입니다."); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
src/main/java/com/helpmeCookies/product/dto/FileUploadResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.helpmeCookies.product.dto; | ||
|
||
import com.helpmeCookies.product.entity.ProductImage; | ||
|
||
public record FileUploadResponse( | ||
String photoUrl, | ||
String uuid | ||
) { | ||
public ProductImage toEntity(Long productId) { | ||
return ProductImage.builder() | ||
.productId(productId) | ||
.photoUrl(photoUrl) | ||
.uuid(uuid) | ||
.build(); | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
src/main/java/com/helpmeCookies/product/dto/ProductImageResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.helpmeCookies.product.dto; | ||
|
||
import java.util.List; | ||
|
||
public record ProductImageResponse( | ||
List<String> urls | ||
) { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
src/main/java/com/helpmeCookies/product/repository/ProductImageRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.helpmeCookies.product.repository; | ||
|
||
import com.helpmeCookies.product.entity.ProductImage; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
import java.util.List; | ||
|
||
@Repository | ||
public interface ProductImageRepository extends JpaRepository<ProductImage,Long> { | ||
List<ProductImage> findAllByProductId(Long productId); | ||
void deleteAllByProductId(Long productId); | ||
} |
35 changes: 35 additions & 0 deletions
35
src/main/java/com/helpmeCookies/product/service/ProductImageService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package com.helpmeCookies.product.service; | ||
|
||
import com.helpmeCookies.global.utils.AwsS3FileUtils; | ||
import com.helpmeCookies.product.dto.FileUploadResponse; | ||
import com.helpmeCookies.product.repository.ProductImageRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
import java.io.IOException; | ||
import java.util.List; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class ProductImageService { | ||
private final AwsS3FileUtils awsS3FileUtils; | ||
private final ProductImageRepository productImageRepository; | ||
|
||
@Transactional | ||
public List<FileUploadResponse> uploadMultiFiles(Long productId, List<MultipartFile> files) throws IOException { | ||
List<FileUploadResponse> uploadResponses = awsS3FileUtils.uploadMultiImages(files); | ||
uploadResponses.forEach(response -> | ||
productImageRepository.save(response.toEntity(productId))); | ||
return uploadResponses; | ||
} | ||
|
||
@Transactional | ||
public void editImages(Long productId, List<MultipartFile> files) throws IOException { | ||
//우선은 전부 삭제하고 다시 업로드 | ||
//추후에 개선 예정 | ||
productImageRepository.deleteAllByProductId(productId); | ||
uploadMultiFiles(productId, files); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
src/test/java/com/helpmeCookies/product/service/ProductImageServiceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package com.helpmeCookies.product.service; | ||
|
||
import com.helpmeCookies.global.utils.AwsS3FileUtils; | ||
import com.helpmeCookies.product.dto.FileUploadResponse; | ||
import com.helpmeCookies.product.entity.Category; | ||
import com.helpmeCookies.product.entity.Product; | ||
import com.helpmeCookies.product.repository.ProductImageRepository; | ||
import com.helpmeCookies.product.repository.ProductRepository; | ||
import org.junit.jupiter.api.AfterEach; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.boot.test.mock.mockito.MockBean; | ||
import org.springframework.mock.web.MockMultipartFile; | ||
import org.springframework.test.context.ActiveProfiles; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
import static org.junit.jupiter.api.Assertions.*; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.BDDMockito.given; | ||
import static org.mockito.Mockito.when; | ||
|
||
@SpringBootTest | ||
@ActiveProfiles("test") | ||
class ProductImageServiceTest { | ||
@MockBean | ||
private AwsS3FileUtils awsS3FileUtils; | ||
|
||
private ProductImageService productImageService; | ||
@MockBean | ||
private ProductRepository productRepository; | ||
@MockBean | ||
private ProductImageRepository productImageRepository; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
productImageService = new ProductImageService(awsS3FileUtils, productImageRepository); | ||
} | ||
|
||
@AfterEach | ||
void tearDown() { | ||
} | ||
|
||
@Test | ||
@DisplayName("상품 이미지 S3 서버에 업로드") | ||
void uploadMultiFiles() throws IOException { | ||
given(productRepository.findById(any())) | ||
.willReturn(Optional.of(new Product("더미",Category.CERAMIC,"100",10000L,"테스트항목","테스트 주소", | ||
null,null))); | ||
MockMultipartFile file1 = new MockMultipartFile("test1","img1.jpg","image/jpeg","image content".getBytes()); | ||
MockMultipartFile file2 = new MockMultipartFile("test2","img2.jpg","image/jpeg","image content".getBytes()); | ||
List<MultipartFile> files = Arrays.asList(file1,file2); | ||
|
||
List<FileUploadResponse> expected = new ArrayList<>(); | ||
expected.add(new FileUploadResponse("url1","1111")); | ||
expected.add(new FileUploadResponse("url2","2222")); | ||
when(awsS3FileUtils.uploadMultiImages(files)).thenReturn(expected); | ||
|
||
List<FileUploadResponse> actual = productImageService.uploadMultiFiles(1L,files); | ||
assertEquals(2,actual.size(), "배열의 크기는 2여야함"); | ||
} | ||
} |
Oops, something went wrong.