Skip to content

Commit

Permalink
Merge pull request #19 from gyeongnam-gyeongmae/dev/feat/save-image
Browse files Browse the repository at this point in the history
[#9] 이미지 저장 구현
  • Loading branch information
haroya01 authored Sep 22, 2023
2 parents dc09aaf + ce67112 commit a624b23
Show file tree
Hide file tree
Showing 14 changed files with 368 additions and 47 deletions.
5 changes: 4 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ dependencies {
annotationProcessor("org.projectlombok:lombok")
testAnnotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
implementation("org.springframework.boot:spring-boot-configuration-processor")

annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
implementation("org.springframework.cloud:spring-cloud-aws-context:2.2.6.RELEASE")


implementation("com.querydsl:querydsl-jpa:${queryDslVersion}")
annotationProcessor("com.querydsl:querydsl-apt:${queryDslVersion}")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package megabrain.gyeongnamgyeongmae.domain.auctionItem.dto;

import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.List;

import lombok.*;
import megabrain.gyeongnamgyeongmae.domain.auctionItem.domain.entity.AuctionItem;
import megabrain.gyeongnamgyeongmae.domain.auctionItem.domain.entity.AuctionItemStatus;
import megabrain.gyeongnamgyeongmae.domain.auctionItem.domain.entity.AuctionStatus;
Expand All @@ -14,6 +13,7 @@
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Setter
public class AuctionItemResponse {

private Long id;
Expand All @@ -37,6 +37,8 @@ public class AuctionItemResponse {
private Long likeCount;
private Long viewCount;

private List<String> images;

public static AuctionItemResponse of(AuctionItem auctionItem) {
return AuctionItemResponse.builder()
.id(auctionItem.getId())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package megabrain.gyeongnamgyeongmae.domain.auctionItem.service.Item;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import megabrain.gyeongnamgyeongmae.domain.auctionItem.domain.entity.AuctionItem;
import megabrain.gyeongnamgyeongmae.domain.auctionItem.domain.entity.AuctionItemLike;
Expand All @@ -15,6 +18,8 @@
import megabrain.gyeongnamgyeongmae.domain.category.domain.entity.Category;
import megabrain.gyeongnamgyeongmae.domain.category.domain.repository.CategoryRepository;
import megabrain.gyeongnamgyeongmae.domain.category.service.CategoryService;
import megabrain.gyeongnamgyeongmae.domain.image.domain.entity.Image;
import megabrain.gyeongnamgyeongmae.domain.image.domain.repository.ImageRepository;
import megabrain.gyeongnamgyeongmae.domain.user.domain.entity.User;
import megabrain.gyeongnamgyeongmae.domain.user.domain.repository.UserRepository;
import megabrain.gyeongnamgyeongmae.domain.user.service.UserService;
Expand All @@ -31,6 +36,7 @@ public class AuctionItemServiceImpl implements AuctionItemService {
private final UserRepository userRepository;
private final CategoryRepository categoryRepository;
private final UserService userService;
private final ImageRepository imageRepository;

@Override
@Transactional
Expand All @@ -52,9 +58,19 @@ public void createAuctionItem(CreateAuctionItemRequest createAuctionItemRequest)
@Transactional
public AuctionItemResponse findAuctionItemById(Long id) {
AuctionItem auctionItem = auctionItemRepository.findById(id).orElseThrow(RuntimeException::new);
List<Image> images = imageRepository.findImageByAuctionItemId(id);
auctionItem.checkShowAuctionItem(auctionItem);
updateAuctionItemViewCount(auctionItem);
return AuctionItemResponse.of(auctionItem);
AuctionItemResponse auctionItemResponse = AuctionItemResponse.of(auctionItem);
if (!images.isEmpty()){
List<String> imageUrls = images.stream().map(this::makeImageUrl).collect(Collectors.toList());
auctionItemResponse.setImages(imageUrls);
}
return auctionItemResponse;
}

private String makeImageUrl(Image image){
return image.getImageUrl();
}

@Override
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package megabrain.gyeongnamgyeongmae.domain.image.Controller;


import io.swagger.v3.oas.annotations.Operation;

import lombok.RequiredArgsConstructor;
import megabrain.gyeongnamgyeongmae.domain.image.Service.ImageService;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

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


@RequiredArgsConstructor
@RestController
@RequestMapping("api/{from}/{id}/images/")
public class ImageController {

private final ImageService imageService;

@PostMapping(value = "/upload", consumes = {
MediaType.MULTIPART_FORM_DATA_VALUE
})
@Operation(summary = "이미지", description = "이미지 업로드")
public ResponseEntity<HttpStatus> uploadImage(
@RequestPart("file") List<MultipartFile> files, @PathVariable String from, @PathVariable Long id) throws IOException {
imageService.uploadImage(files, from, id);

return new ResponseEntity<>(HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package megabrain.gyeongnamgyeongmae.domain.image.Service;

import com.amazonaws.SdkClientException;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;


@Service
public class AwsS3Service {

private final AmazonS3 amazonS3;

@Autowired
public AwsS3Service(AmazonS3 amazonS3) {
this.amazonS3 = amazonS3;
}

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

public String upload(MultipartFile file, String filename) throws IOException {
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentType(file.getContentType());
objectMetadata.setContentLength(file.getSize());

try {
amazonS3.putObject(new PutObjectRequest(bucket, filename, file.getInputStream(), objectMetadata)
.withCannedAcl(CannedAccessControlList.PublicRead));
} catch (SdkClientException e) {
e.printStackTrace();
System.out.println("업로드 실패: " + e.getMessage());
}

return amazonS3.getUrl(bucket, filename).toString();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package megabrain.gyeongnamgyeongmae.domain.image.Service;


import org.springframework.web.multipart.MultipartFile;

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

public interface ImageService {

void uploadImage(List<MultipartFile> images, String from, Long id) throws IOException;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package megabrain.gyeongnamgyeongmae.domain.image.Service;

import lombok.RequiredArgsConstructor;
import megabrain.gyeongnamgyeongmae.domain.auctionItem.domain.entity.AuctionItem;
import megabrain.gyeongnamgyeongmae.domain.auctionItem.domain.entity.Comment;
import megabrain.gyeongnamgyeongmae.domain.auctionItem.domain.repostiory.AuctionItemCommentRepository;
import megabrain.gyeongnamgyeongmae.domain.auctionItem.domain.repostiory.AuctionItemRepository;
import megabrain.gyeongnamgyeongmae.domain.image.domain.repository.ImageRepository;
import megabrain.gyeongnamgyeongmae.domain.image.dto.FileType;
import megabrain.gyeongnamgyeongmae.domain.image.domain.entity.Image;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class ImageServiceImpl implements ImageService {

private final ImageRepository imageRepository;
private final AwsS3Service awsS3Service;
private final AuctionItemRepository auctionItemRepository;
private final AuctionItemCommentRepository commentRepository;

@Override
public void uploadImage(List<MultipartFile> images, String from, Long id) throws IOException {
String whereFrom = checkImageUploadFind(from);
AuctionItem auctionItem = null;
Comment comment = null;
if (whereFrom.equals("AuctionItem")) {
auctionItem = checkIsRealIdAuctionItem(id);
}
if (whereFrom.equals("Comment")) {
comment = checkIsRealIdComment(id);
}
upload(images, whereFrom, auctionItem, comment);
}

private AuctionItem checkIsRealIdAuctionItem(Long id) {
return auctionItemRepository.findById(id).orElseThrow(() -> new RuntimeException("존재하지 않는 경매"));
}

private Comment checkIsRealIdComment(Long id) {
return commentRepository.findById(id).orElseThrow(() -> new RuntimeException("존재하지 않는 댓글"));
}

private void upload(List<MultipartFile> images, String from, AuctionItem auctionItem, Comment comment) throws IOException {
for (MultipartFile file : images) {
String originalFilename = file.getOriginalFilename();

if (originalFilename == null) {
throw new RuntimeException("파일 확장자 없음");
}

String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);

if (!FileType.isValid(fileExtension)) {
throw new RuntimeException("이미지 파일 아님");
}

String fileName = uploadFileName(from, fileExtension);
awsS3Service.upload(file, fileName);

String fileShow = "https://d231cnlxdxmjew.cloudfront.net/" + fileName;
Image image = Image.builder()
.imageFrom(from)
.name(originalFilename)
.url(fileShow)
.build();
image.setAuctionItem(auctionItem);
image.setComment(comment);
imageRepository.save(image);
}
}

private String uploadFileName(String from, String fileExtension) {
return from + "/" + (UUID.randomUUID().toString().replace("-", "") + "." + fileExtension);

}

private String checkImageUploadFind(String from) {
if (from.equals("Profile")) {
return "Profile";
}
if (from.equals("AuctionItem")) {
return "AuctionItem";
}
if (from.equals("Comment")) {
return "Comment";
}
throw new RuntimeException("이미지 파라미터 잘못");
}

}




Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package megabrain.gyeongnamgyeongmae.domain.image.domain.entity;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import megabrain.gyeongnamgyeongmae.domain.auctionItem.domain.entity.AuctionItem;
import megabrain.gyeongnamgyeongmae.domain.auctionItem.domain.entity.Comment;
import javax.persistence.*;

@NoArgsConstructor
@Entity
@Table(name = "Image")
@Getter
@Setter
public class Image {

@Id
@Column(name = "image_url")
private String imageUrl;

@Column(name = "image_name")
private String imageName;

@Column(name = "image_from")
private String imageFrom;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "auction_id")
private AuctionItem auctionItem;

@Column(name = "removed")
private boolean removed;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name ="comment_id")
private Comment comment;

@Builder
public Image(String name, String url, String imageFrom) {
this.imageName = name;
this.imageUrl = url;
this.imageFrom = imageFrom;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package megabrain.gyeongnamgyeongmae.domain.image.domain.repository;

import megabrain.gyeongnamgyeongmae.domain.image.domain.entity.Image;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;

@Repository
public interface ImageRepository extends JpaRepository<Image, Long> {

@Query(value = "SELECT DISTINCT * FROM Image where auction_id = :id", nativeQuery = true)
List<Image> findImageByAuctionItemId(Long id);
}
Loading

0 comments on commit a624b23

Please sign in to comment.