Skip to content
34 changes: 10 additions & 24 deletions src/main/java/umc/th/juinjang/controller/ImageController.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -19,49 +18,36 @@
import umc.th.juinjang.apiPayload.ApiResponse;
import umc.th.juinjang.apiPayload.code.status.SuccessStatus;
import umc.th.juinjang.model.dto.image.ImageDeleteRequestDTO;
import umc.th.juinjang.model.dto.image.ImageListResponseDTO;
import umc.th.juinjang.model.dto.image.ImagesGetResponse;
import umc.th.juinjang.service.image.ImageCommandService;
import umc.th.juinjang.service.image.ImageQueryService;
import umc.th.juinjang.service.limjang.LimjangCommandService;

@RestController
@RequestMapping("/api/limjang/image")
@RequiredArgsConstructor
@Validated
public class ImageController {

private final LimjangCommandService limjangCommandService;
private final ImageCommandService imageCommandService;
private final ImageQueryService imageQueryService;

@CrossOrigin
@Operation(summary = "사진 생성 API", description = "사진 업로드 api입니다.")
@PostMapping(value = "", consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponse uploadImages(
@RequestParam(name = "limjangId") Long limjangId, @RequestPart(name = "images") List<MultipartFile> images)
{
imageCommandService.uploadImages(limjangId ,images);
return ApiResponse.onSuccess(SuccessStatus.IMAGE_UPDATE);
}
@Operation(summary = "사진 생성 API", description = "사진 업로드 api입니다.")
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponse uploadImages(@RequestParam(name = "limjangId") Long limjangId, @RequestPart(name = "images") List<MultipartFile> images) {
imageCommandService.createImages(limjangId, images);
return ApiResponse.onSuccess(SuccessStatus.IMAGE_UPDATE);
}

@CrossOrigin
@Operation(summary = "사진 조회 API", description = "사진을 조회하는 api입니다.")
@GetMapping(value = "{limjangId}")
public ApiResponse<ImageListResponseDTO.ImagesListDTO> uploadImages(
@PathVariable(name = "limjangId") @Valid Long limjangId)
{
@GetMapping(value = "/{limjangId}")
public ApiResponse<ImagesGetResponse> uploadImages(@PathVariable(name = "limjangId") @Valid Long limjangId) {
return ApiResponse.onSuccess(imageQueryService.getImageList(limjangId));
}

@CrossOrigin
@Operation(summary = "이미지 선택 삭제", description = "이미지 게시글을 여러 개 선택해서 삭제하는 api입니다.")
@PostMapping("/delete")
public ApiResponse deleteImage(@RequestBody @Valid ImageDeleteRequestDTO.DeleteDto deleteIds
){

public ApiResponse deleteImage(@RequestBody @Valid ImageDeleteRequestDTO.DeleteDto deleteIds) {
imageCommandService.deleteImages(deleteIds);
return ApiResponse.onSuccess(SuccessStatus.IMAGE_DELETE);
}

}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package umc.th.juinjang.model.dto.image;

import java.util.List;
import umc.th.juinjang.model.entity.Image;

public record ImagesGetResponse (List<ImageResponse> images) {
record ImageResponse(Long imageId, String imageUrl) {
static ImageResponse of(Long imageId, String imageUrl) {
return new ImageResponse(imageId, imageUrl);
}
}

public static ImagesGetResponse of(List<Image> images) {
return new ImagesGetResponse(images.stream().map(it -> ImageResponse.of(it.getImageId(), it.getImageUrl())).toList());
}
}


6 changes: 6 additions & 0 deletions src/main/java/umc/th/juinjang/model/entity/Image.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,10 @@ public class Image extends BaseEntity {
@Column(nullable = false)
private String imageUrl;

public static Image create(String imageUrl, Limjang limjang) {
return Image.builder()
.imageUrl(imageUrl)
.limjangId(limjang)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,8 @@ public interface LimjangRepository extends JpaRepository<Limjang, Long>, Limjang
Optional<Limjang> findByLimjangIdAndMemberIdWithLimjangPriceAndDeletedIsFalse(@Param("id") Long id, @Param("member") Member member);

@Query("SELECT l FROM Limjang l join fetch l.limjangPrice left join fetch l.report WHERE l.limjangId = :id AND l.memberId = :member AND l.deleted = false")
Optional<Limjang> findByLimjangIdAndDeletedIsFalse(@Param("id") Long id, @Param("member") Member member);
Optional<Limjang> findByLimjangIdAndMemberAndDeletedIsFalse(@Param("id") Long id, @Param("member") Member member);

@Query("SELECT l FROM Limjang l WHERE l.limjangId = :id AND l.deleted = false")
Optional<Limjang> findByLimjangIdAndDeletedIsFalse(@Param("id") Long id);
}
50 changes: 30 additions & 20 deletions src/main/java/umc/th/juinjang/service/external/S3Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,41 @@
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import java.io.File;
import java.io.IOException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Optional;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.beans.factory.annotation.Value;
import umc.th.juinjang.apiPayload.code.status.ErrorStatus;
import umc.th.juinjang.apiPayload.exception.handler.S3Handler;

@Slf4j
@RequiredArgsConstructor // final 멤버변수가 있으면 생성자 항목에 포함시킴
@Component
@RequiredArgsConstructor
@Service
public class S3Service {

private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final AmazonS3Client amazonS3Client;

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

// MultipartFile을 전달받아 File로 전환한 후 S3에 업로드
public String upload(MultipartFile multipartFile, String dirName) throws IOException {
File uploadFile = convert(multipartFile)
.orElseThrow(() -> new IllegalArgumentException("MultipartFile -> File 전환 실패"));
return upload(uploadFile, dirName, multipartFile.getInputStream(), multipartFile.getSize(), multipartFile.getContentType());
public String upload(MultipartFile multipartFile, String dirName) {
File uploadFile = convert(multipartFile).orElseThrow(() -> new S3Handler(ErrorStatus.IMAGE_EMPTY));

try {
return upload(uploadFile, dirName, multipartFile.getInputStream(), multipartFile.getSize(), multipartFile.getContentType());
} catch (Exception e) {
logger.error("파일 업로드 중 error 발생");
throw new S3Handler(ErrorStatus._INTERNAL_SERVER_ERROR);
}
}

private String upload(File uploadFile,String dirName, InputStream inputStream, Long fileSize, String contentType) {
Expand Down Expand Up @@ -72,16 +76,22 @@ private void removeNewFile(File targetFile) {
}
}

private Optional<File> convert(MultipartFile file) throws IOException {
String originalFilename = file.getOriginalFilename();
String safeFilename = originalFilename.replaceAll("[^a-zA-Z0-9.-]", "_");
File convertFile = new File(safeFilename);

if(convertFile.createNewFile()) {
try (FileOutputStream fos = new FileOutputStream(convertFile)) {
fos.write(file.getBytes());
private Optional<File> convert(MultipartFile file) {
try {
String originalFilename = file.getOriginalFilename();
String safeFilename = originalFilename.replaceAll("[^a-zA-Z0-9.-]", "_");
File convertFile = new File(safeFilename);

if (convertFile.createNewFile()) {
try (FileOutputStream fos = new FileOutputStream(convertFile)) {
fos.write(file.getBytes());
}
return Optional.of(convertFile);
} else {
return Optional.empty();
}
return Optional.of(convertFile);
} catch (Exception e) {
logger.error("파일 변환 중 error 발생" +e);
}
return Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import umc.th.juinjang.model.dto.image.ImageDeleteRequestDTO;

public interface ImageCommandService {
void uploadImages(Long limjangId, List<MultipartFile> images);
void createImages(long limjangId, List<MultipartFile> images);

void deleteImages(ImageDeleteRequestDTO.DeleteDto ids);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,73 +1,49 @@
package umc.th.juinjang.service.image;

import java.io.IOException;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import umc.th.juinjang.apiPayload.code.status.ErrorStatus;
import umc.th.juinjang.apiPayload.exception.handler.LimjangHandler;
import umc.th.juinjang.converter.image.ImageUploadConverter;
import umc.th.juinjang.model.dto.image.ImageDeleteRequestDTO;
import umc.th.juinjang.model.entity.Image;
import umc.th.juinjang.model.entity.Limjang;
import umc.th.juinjang.repository.image.ImageRepository;
import umc.th.juinjang.repository.limjang.LimjangRepository;
import umc.th.juinjang.service.external.S3Service;

@Slf4j
@Service
@RequiredArgsConstructor
public class ImageCommandServiceImpl implements ImageCommandService {

private final ImageRepository imageRepository;
private final LimjangRepository limjangRepository;
private final S3Service s3Service;
private final String DIR_NAME = "image";

@Override
@Transactional
public void uploadImages(Long limjangId, List<MultipartFile> images) {

Limjang limjang = limjangRepository.findById(limjangId)
.orElseThrow(()-> new LimjangHandler(ErrorStatus.LIMJANG_NOTFOUND_ERROR));

images.forEach(it -> {
try {
if (!it.isEmpty()) {
String storedFileName = s3Service.upload(it, "image");
Image image = ImageUploadConverter.toImageDto(storedFileName, limjang);
limjang.saveImages(image);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
public void createImages(final long limjangId, final List<MultipartFile> files) {
Limjang limjang = getLimjangById(limjangId);
for (MultipartFile file : files) {
String imageUrl = s3Service.upload(file, DIR_NAME);
imageRepository.save(Image.create(imageUrl, limjang));
}
}

private Limjang getLimjangById(final long limjangId) {
return limjangRepository.findById(limjangId).orElseThrow(()-> new LimjangHandler(ErrorStatus.LIMJANG_NOTFOUND_ERROR));
}

@Override
@Transactional
public void deleteImages(ImageDeleteRequestDTO.DeleteDto ids
) { //이미지 id로 삭제한다...!
List<Long> deleteIds = ids.getImageIdList();

try {

//s3에서 삭제
List<Image> imageList = imageRepository.findAllById(deleteIds);

imageList.forEach(image -> {
s3Service.deleteFile(image.getImageUrl());
imageRepository.deleteById(image.getImageId());
});
} catch (DataIntegrityViolationException e) {
throw new LimjangHandler(ErrorStatus.IMAGE_DELETE_NOT_COMPLETE);
} catch (EmptyResultDataAccessException e) {
throw new LimjangHandler(ErrorStatus.IMAGE_DELETE_NOT_FOUND);
}
public void deleteImages(final ImageDeleteRequestDTO.DeleteDto ids) {
List<Image> images = imageRepository.findAllById(ids.getImageIdList());
for (Image image : images) {
s3Service.deleteFile(image.getImageUrl());
imageRepository.deleteById(image.getImageId());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package umc.th.juinjang.service.image;

import umc.th.juinjang.model.dto.image.ImageListResponseDTO;
import umc.th.juinjang.model.dto.image.ImagesGetResponse;

public interface ImageQueryService {
ImageListResponseDTO.ImagesListDTO getImageList(Long limjangId);
ImagesGetResponse getImageList(long limjangId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
import org.springframework.transaction.annotation.Transactional;
import umc.th.juinjang.apiPayload.code.status.ErrorStatus;
import umc.th.juinjang.apiPayload.exception.handler.LimjangHandler;
import umc.th.juinjang.converter.image.ImageListConverter;
import umc.th.juinjang.model.dto.image.ImageListResponseDTO;
import umc.th.juinjang.model.dto.image.ImagesGetResponse;
import umc.th.juinjang.model.entity.Image;
import umc.th.juinjang.model.entity.Limjang;
import umc.th.juinjang.repository.image.ImageRepository;
Expand All @@ -24,13 +23,13 @@ public class ImageQueryServiceImpl implements ImageQueryService {

@Override
@Transactional(readOnly = true)
public ImageListResponseDTO.ImagesListDTO getImageList(Long limjangId) {
Limjang findLimjang = limjangRepository.findById(limjangId)
.orElseThrow(() -> new LimjangHandler(ErrorStatus.LIMJANG_NOTFOUND_ERROR));

List<Image> imageList = imageRepository.findImagesByLimjangId(findLimjang);

return ImageListConverter.toImageListDto(imageList);
public ImagesGetResponse getImageList(final long limjangId) {
Limjang limjang = getLimjang(limjangId);
List<Image> images = imageRepository.findImagesByLimjangId(limjang);
return ImagesGetResponse.of(images);
}

private Limjang getLimjang(final long limjangId) {
return limjangRepository.findByLimjangIdAndDeletedIsFalse(limjangId).orElseThrow(() -> new LimjangHandler(ErrorStatus.LIMJANG_NOTFOUND_ERROR));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public LimjangDetailGetResponse getDetail(long id, Member member) {
}

private Limjang getByIdAndMember(Long id, Member member) {
return limjangRepository.findByLimjangIdAndDeletedIsFalse(id, member).orElseThrow(() -> new LimjangHandler(ErrorStatus.LIMJANG_NOTFOUND_ERROR));
return limjangRepository.findByLimjangIdAndMemberAndDeletedIsFalse(id, member).orElseThrow(() -> new LimjangHandler(ErrorStatus.LIMJANG_NOTFOUND_ERROR));
}

@Override
Expand Down