Merge branch 'be/feat/217-image' into 'be/develop'

Feat: 이미지 저장시에 Json 파일도 저장하는 로직 추가 S11P21S002-217

See merge request s11-s-project/S11P21S002!131
This commit is contained in:
홍창기 2024-09-23 14:51:01 +09:00
commit a337b8cde2
5 changed files with 72 additions and 31 deletions

View File

@ -33,9 +33,9 @@ public class ImageController {
@SwaggerApiError({ErrorCode.BAD_REQUEST, ErrorCode.NOT_AUTHOR, ErrorCode.SERVER_ERROR}) @SwaggerApiError({ErrorCode.BAD_REQUEST, ErrorCode.NOT_AUTHOR, ErrorCode.SERVER_ERROR})
public void uploadImage( public void uploadImage(
@CurrentUser final Integer memberId, @CurrentUser final Integer memberId,
@PathVariable("folder_id") final Integer folderId,
@PathVariable("project_id") final Integer projectId, @PathVariable("project_id") final Integer projectId,
@Parameter(name = "폴더에 추가 할 이미지 리스트", description = "MultiPartFile을 imageList로 추가해준다.", example = "") @RequestBody final List<MultipartFile> imageList) { @PathVariable("folder_id") final Integer folderId,
@Parameter(name = "폴더에 추가 할 이미지 리스트", description = "MultiPartFile을 imageList로 추가해준다.", example = "") @RequestPart final List<MultipartFile> imageList) {
imageService.uploadImageList(imageList, folderId, projectId, memberId); imageService.uploadImageList(imageList, folderId, projectId, memberId);
} }
@ -45,8 +45,9 @@ public class ImageController {
@SwaggerApiError({ErrorCode.BAD_REQUEST, ErrorCode.NOT_AUTHOR, ErrorCode.SERVER_ERROR}) @SwaggerApiError({ErrorCode.BAD_REQUEST, ErrorCode.NOT_AUTHOR, ErrorCode.SERVER_ERROR})
public void uploadFolder( public void uploadFolder(
@Parameter(name = "폴더", description = "MultiPartFile을 폴더나 zip으로 추가해준다.", example = "") @RequestPart final MultipartFile folderZip, @Parameter(name = "폴더", description = "MultiPartFile을 폴더나 zip으로 추가해준다.", example = "") @RequestPart final MultipartFile folderZip,
@PathVariable("project_id") Integer projectId) throws IOException { @PathVariable("project_id") final Integer projectId,
imageService.uploadFolderWithImages(folderZip, projectId); @PathVariable("folder_id") final Integer folderId) throws IOException {
imageService.uploadFolderWithImages(folderZip, projectId, folderId);
} }
@GetMapping("/{image_id}") @GetMapping("/{image_id}")

View File

@ -40,12 +40,6 @@ public class Image extends BaseEntity {
@Column(name = "image_extenstion", nullable = false, length = 10) @Column(name = "image_extenstion", nullable = false, length = 10)
private String extension; private String extension;
/**
* 이미지 순서
*/
@Column(name = "image_order", nullable = false)
private Integer order;
/** /**
* 이미지 레이블링 상태 * 이미지 레이블링 상태
*/ */
@ -61,16 +55,15 @@ public class Image extends BaseEntity {
@JsonIgnore @JsonIgnore
private Folder folder; private Folder folder;
private Image(final String imageTitle, final String imageKey, final String extension, final Integer order, final Folder folder) { private Image(final String imageTitle, final String imageKey, final String extension, final Folder folder) {
this.title = imageTitle; this.title = imageTitle;
this.imageKey = imageKey; this.imageKey = imageKey;
this.extension = extension; this.extension = extension;
this.order = order;
this.folder = folder; this.folder = folder;
} }
public static Image of(final String imageTitle, final String imageKey,final String extension, final Integer order, final Folder folder) { public static Image of(final String imageTitle, final String imageKey, final String extension, final Folder folder) {
return new Image(imageTitle, imageKey, extension, order, folder); return new Image(imageTitle, imageKey, extension, folder);
} }
public void moveFolder(final Folder moveFolder) { public void moveFolder(final Folder moveFolder) {

View File

@ -10,15 +10,19 @@ import java.util.Optional;
public interface ImageRepository extends JpaRepository<Image, Long> { public interface ImageRepository extends JpaRepository<Image, Long> {
// todo N + 1 발생할듯
@Query("select i from Image i " + @Query("select i from Image i " +
"where i.folder.project.id = :projectId") "join fetch i.folder f " +
"join fetch f.project p " +
"where p.id = :projectId")
List<Image> findImagesByProjectId(@Param("projectId") Integer projectId); List<Image> findImagesByProjectId(@Param("projectId") Integer projectId);
Optional<Image> findByIdAndFolderIdAndFolderProjectId(Long imageId, Integer folderId, Integer projectId); Optional<Image> findByIdAndFolderIdAndFolderProjectId(Long imageId, Integer folderId, Integer projectId);
@Query("SELECT i FROM Image i " + @Query("SELECT i FROM Image i " +
"JOIN FETCH i.folder f " +
"JOIN FETCH f.project p " +
"WHERE i.id = :imageId " + "WHERE i.id = :imageId " +
"AND i.folder.project.id = :projectId ") "AND p.id = :projectId")
Optional<Image> findByIdAndProjectId(@Param("imageId") Long imageId, @Param("projectId") Integer projectId); Optional<Image> findByIdAndProjectId(@Param("imageId") Long imageId, @Param("projectId") Integer projectId);
} }

View File

@ -40,19 +40,20 @@ public class ImageService {
private final FolderRepository folderRepository; private final FolderRepository folderRepository;
private final ProjectRepository projectRepository; private final ProjectRepository projectRepository;
private static int orderCount = 0;
/** /**
* 이미지 리스트 업로드 * 이미지 리스트 업로드
*/ */
@CheckPrivilege(value = PrivilegeType.EDITOR) @CheckPrivilege(value = PrivilegeType.EDITOR)
public void uploadImageList(final List<MultipartFile> imageList, final Integer folderId, final Integer projectId, final Integer memberId) { public void uploadImageList(final List<MultipartFile> imageList, final Integer folderId, final Integer projectId, final Integer memberId) {
Folder folder = getFolder(folderId); Folder folder = null;
for (int order = 0; order < imageList.size(); order++) { if (folderId != 0) {
MultipartFile file = imageList.get(order); folder = getFolder(folderId);
}
for (MultipartFile file : imageList) {
String extension = getExtension(file); String extension = getExtension(file);
String imageKey = s3UploadService.upload(file, extension, projectId); String imageKey = s3UploadService.upload(file, extension, projectId);
Image image = Image.of(file.getOriginalFilename(), imageKey, extension, order, folder); Image image = Image.of(file.getOriginalFilename(), imageKey, extension, folder);
imageRepository.save(image); imageRepository.save(image);
} }
} }
@ -116,15 +117,15 @@ public class ImageService {
save(imageId, labelRequest.getData()); save(imageId, labelRequest.getData());
} }
public void uploadFolderWithImages(MultipartFile folderOrZip, Integer projectId) throws IOException { public void uploadFolderWithImages(MultipartFile folderOrZip, Integer projectId, Integer folderId) throws IOException {
orderCount = 0;
// 프로젝트 정보 가져오기 // 프로젝트 정보 가져오기
Project project = projectRepository.findById(projectId) Project project = projectRepository.findById(projectId)
.orElseThrow(() -> new CustomException(ErrorCode.PROJECT_NOT_FOUND)); .orElseThrow(() -> new CustomException(ErrorCode.PROJECT_NOT_FOUND));
// 부모 폴더가 최상위인지 확인
Folder parentFolder = null; Folder parentFolder = null;
if (folderId != 0) {
parentFolder = getFolder(folderId);
}
// 파일이 zip 파일인지 확인 // 파일이 zip 파일인지 확인
String originalFilename = folderOrZip.getOriginalFilename(); String originalFilename = folderOrZip.getOriginalFilename();
@ -166,7 +167,7 @@ public class ImageService {
// InputStream으로 S3 업로드 // InputStream으로 S3 업로드
String imageKey = s3UploadService.uploadFromInputStream(inputStream, extension, project.getId(), file.getName()); String imageKey = s3UploadService.uploadFromInputStream(inputStream, extension, project.getId(), file.getName());
Image image = Image.of(file.getName(), imageKey, extension, orderCount++, currentFolder); Image image = Image.of(file.getName(), imageKey, extension, currentFolder);
imageRepository.save(image); imageRepository.save(image);
} catch (IOException e) { } catch (IOException e) {
throw new CustomException(ErrorCode.FAIL_TO_CREATE_FILE); throw new CustomException(ErrorCode.FAIL_TO_CREATE_FILE);

View File

@ -12,6 +12,8 @@ import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*; import java.io.*;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
@ -131,6 +133,12 @@ public class S3UploadService {
PutObjectRequest putRequest = new PutObjectRequest(bucket, s3FileName, byteArrayInputStream, metadata); PutObjectRequest putRequest = new PutObjectRequest(bucket, s3FileName, byteArrayInputStream, metadata);
amazonS3.putObject(putRequest); // S3 파일 업로드 amazonS3.putObject(putRequest); // S3 파일 업로드
BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(bytes));
int imageWidth = bufferedImage.getWidth();
int imageHeight = bufferedImage.getHeight();
uploadJsonWithDimensions(s3Key, imageWidth, imageHeight);
} catch (Exception e) { } catch (Exception e) {
log.error("", e); log.error("", e);
throw new CustomException(ErrorCode.FAIL_TO_CREATE_FILE); throw new CustomException(ErrorCode.FAIL_TO_CREATE_FILE);
@ -174,9 +182,11 @@ public class S3UploadService {
public String uploadFromInputStream(InputStream inputStream, String extension, Integer projectId, String fileName) { public String uploadFromInputStream(InputStream inputStream, String extension, Integer projectId, String fileName) {
try { try {
// UUID로 S3 파일 이름 생성
String s3Key = getS3FileName(projectId); String s3Key = getS3FileName(projectId);
String s3FileName = s3Key + "." + extension; String s3FileName = s3Key + "." + extension;
// 이미지 파일 업로드
byte[] bytes = IOUtils.toByteArray(inputStream); byte[] bytes = IOUtils.toByteArray(inputStream);
ObjectMetadata metadata = new ObjectMetadata(); ObjectMetadata metadata = new ObjectMetadata();
@ -185,12 +195,44 @@ public class S3UploadService {
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes)) { try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes)) {
PutObjectRequest putRequest = new PutObjectRequest(bucket, s3FileName, byteArrayInputStream, metadata); PutObjectRequest putRequest = new PutObjectRequest(bucket, s3FileName, byteArrayInputStream, metadata);
amazonS3.putObject(putRequest); amazonS3.putObject(putRequest); // 이미지 업로드
} }
// 이미지 높이와 너비 추출
BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(bytes));
int imageWidth = bufferedImage.getWidth();
int imageHeight = bufferedImage.getHeight();
// 너비와 높이를 포함한 JSON 업로드
uploadJsonWithDimensions(s3Key, imageWidth, imageHeight); // JSON 파일 업로드 메서드 호출
return url + "/" + s3Key; return url + "/" + s3Key;
} catch (IOException e) { } catch (IOException e) {
throw new CustomException(ErrorCode.FAIL_TO_CREATE_FILE); throw new CustomException(ErrorCode.FAIL_TO_CREATE_FILE);
} }
} }
// 이미지의 너비와 높이를 포함하는 JSON 파일 업로드
private void uploadJsonWithDimensions(String s3Key, int width, int height) {
try {
// JSON 파일 이름 생성 (s3Key + ".json")
String jsonFileName = s3Key + ".json";
// 이미지의 너비와 높이를 포함한 JSON 데이터 생성
String jsonContent = "{\n\"imageHeight\": " + height + ",\n\"imageWidth\": " + width + "\n}";
byte[] jsonBytes = jsonContent.getBytes(StandardCharsets.UTF_8);
ObjectMetadata jsonMetadata = new ObjectMetadata();
jsonMetadata.setContentType("application/json");
jsonMetadata.setContentLength(jsonBytes.length);
try (ByteArrayInputStream jsonInputStream = new ByteArrayInputStream(jsonBytes)) {
PutObjectRequest jsonPutRequest = new PutObjectRequest(bucket, jsonFileName, jsonInputStream, jsonMetadata);
amazonS3.putObject(jsonPutRequest); // JSON 파일 업로드
}
} catch (Exception e) {
throw new CustomException(ErrorCode.FAIL_TO_CREATE_FILE);
}
}
} }