쇼핑몰 주제로 팀프로젝트를 진행 중인데 그 중 상품 등록, 상품 조회,상품 수정 을 맡았다
상품등록 중 상품 이미지를 s3를 이용하여 이미지를 업로드 시키고, DB에는 s3 객체 url을 남겨서 상품 조회할 때는 이미지 객체 url을 프론트쪽으로 넘기기로 했다
처음해보는 이미지 관리였어서 어려움이 있었는데 기록으로 남겨놓으면 좋을 것 같다
내가 이해했던 방향은
client에서 첨부파일을 첨부하면 spring에서 제공하고 있는 MultipatFile 인터페이스를 이용하여 파일을 업로드 한다
(MultipartFile -> 파일의 이름,크기, 내용과 같은 업로드된 파일의 내용에 엑세스하는 방법을 제공)
MultipartFile을 객체로 변환
스트림 데이터로 변환된 객체를 s3에 업로드
s3에 저장된 객체 url DB imagePath에 저장
이런 과정으로 이해하고 로직을 짰다
처음엔...s3 업로드 과정 자체가 이해가 안가서 여러가지 구글에 나와있는 방법을 따라해봤지만, 사진하나 업로드과정과 여러 사진을 동시에 업로드 방식이 많이 달라서...더 헤맸었다
그러다, 한 글을 보고 감을 잡고 s3업로드 사진을 업로드하여 쇼핑몰 프로젝트에 상품을 등록할 수 있게 기능구현을 완료하였다
만약 이 글이 나처럼 헤매고 있는 분이 있다면, 작음 도움이 되길 바란다
포스트맨을 통해서 상품등록을 위해 데이터를 나눠보내야한다
1) 사진을 제외한 상품등록에 관련 내용은 application/json 형식으로 데이터를 보내게 된다
2) 이미지 파일은 multipart/form-data 형식으로 multipartFile로 받아야하기 때문에 따로 보내준다
@PostMapping(consumes = {"multipart/form-data"} )
public ResponseEntity<Map<String, String>> registerProducts(@RequestPart(name = "file") List<MultipartFile> multipartFilelist,
@RequestPart (value = "data") ProductDTO productDto) throws IOException{
System.out.println("productRequestDto " + productDto);
return adminService.resisterProducts(multipartFilelist, productDto);
}
AdminService
public ResponseEntity<Map<String, String>> resisterProducts(List<MultipartFile> multipartFilelist, ProductDTO productDTO) throws IOException {
// DTO -> Entity로 변환
ProductEntity productEntity = ProductEntity.builder()
.productName(productDTO.getProductName())
.productPrice(productDTO.getProductPrice())
.category(productDTO.getCategory())
.productContents(productDTO.getProductContents())
.productSize(productDTO.getProductSize())
.saleStatus(productDTO.getSaleStatus())
.build();
//상품 db 저장
Long id = adminProductRepository.save(productEntity)
.getId();
// stock dto -> entity 로 변환
StockEntity stockEntity = StockEntity.builder()
.productEntity(productEntity)
.stockAmount(productDTO.getStockAmount())
.sellAmount(productDTO.getSellAmount())
.build();
//재고 db 저장
stockRepository.save(stockEntity);
//이미지 db 저장
if (multipartFilelist != null) {
s3Service.upload(multipartFilelist, "static", productEntity);
}
//상품이 제대로 등록 되었는 지 확인
Optional<ProductEntity> findId = adminProductRepository.findById(id);
Map<String, String> map = new HashMap<>();
if (findId.isPresent()) {
map.put("message", "상품이 성공적으로 등록되었습니다.");
} else {
map.put("message", "상품 등록이 실패하였습니다.");
}
return ResponseEntity.status(200)
.body(map);
}
S3service
@Slf4j
@Component
@RequiredArgsConstructor
@Service
public class S3Service {
private final AmazonS3Client amazonS3Client;
private final ImageFileRepository imageFileRepository;
@Value("${cloud.aws.s3.product-bucket}")
private String productBucket;
//s3 올릴 이미지 객체 url로 변환 , DB 에 url 저장
public void upload(List<MultipartFile> multipartFilelist, String dirName , ProductEntity product) throws IOException {
int imageNum = 1; // 이미지 번호 초기값
for (MultipartFile multipartFile : multipartFilelist){
if (multipartFile != null){
File uploadFile = convert(multipartFile)
.orElseThrow(() -> new IllegalArgumentException("error: MultipartFile -> File convert fail"));
ProductImageEntity productImageEntity = new ProductImageEntity(upload(uploadFile, dirName), product); //url 정보 저장
//이미지 번호 설정 1~ 이미지 갯수 만큼 ++
productImageEntity.setImageNum(imageNum++);
imageFileRepository.save(productImageEntity);
}
}
}
// S3로 파일 업로드하기
private String upload(File uploadFile, String dirName) {
String fileName = dirName + "/" + UUID.randomUUID(); // S3에 저장된 파일 이름
String uploadImageUrl = putS3(uploadFile, fileName); // s3로 업로드
removeNewFile(uploadFile);
return uploadImageUrl;
}
// S3로 업로드
private String putS3(File uploadFile, String fileName) {
amazonS3Client.putObject(new PutObjectRequest(productBucket, fileName, uploadFile).withCannedAcl(CannedAccessControlList.PublicRead));
return amazonS3Client.getUrl(productBucket, fileName).toString();
}
// 로컬에 저장된 이미지 지우기
private void removeNewFile(File targetFile) {
if (targetFile.delete()) {
log.info("Local:File delete success");
return;
}
log.info("Local: File delete fail");
}
//이미지 변환과정
private Optional<File> convert(MultipartFile multipartFile) throws IOException {
// File convertFile = new File(System.getProperty("user.dir") + "/" + multipartFile.getOriginalFilename());
File convertFile = new File(multipartFile.getOriginalFilename());
// 바로 위에서 지정한 경로에 File이 생성됨 (경로가 잘못되었다면 생성 불가능)
if (convertFile.createNewFile()) {
try (FileOutputStream fos = new FileOutputStream(convertFile)) { // FileOutputStream 데이터를 파일에 바이트 스트림으로 저장하기 위함
fos.write(multipartFile.getBytes());
}
return Optional.of(convertFile);
}
return Optional.empty();
}
//s3 이미지 삭제
public void deleteFile(String filename) {
System.out.println("delete filename = " + filename);
amazonS3Client.deleteObject(new DeleteObjectRequest(productBucket, filename));
System.out.println(String.format("[%s] deletion complete", filename));
}
}