Merge branch 'be/refactor/image' into 'be/develop'

Refactor: 이미지 벌크 업로드

See merge request s11-s-project/S11P21S002!308
This commit is contained in:
정현조 2024-10-08 15:48:31 +09:00
commit f2b19941a8
4 changed files with 57 additions and 9 deletions

View File

@ -56,11 +56,10 @@ public class ImageController {
@Operation(summary = "이미지에 대한 Presigned ", description = "이미지에 대한 Presigned 주소를 받아옵니다.")
@SwaggerApiError({ErrorCode.BAD_REQUEST, ErrorCode.NOT_AUTHOR, ErrorCode.SERVER_ERROR})
public List<ImagePresignedUrlResponse> uploadFolderByPresignedImage(
@CurrentUser final Integer memberId,
@RequestBody final List<ImageMetaRequest> imageMetaList,
@PathVariable("project_id") final Integer projectId,
@PathVariable("folder_id") final Integer folderId) {
return imageService.uploadFolderByPresignedImage(memberId, imageMetaList, projectId, folderId);
return imageService.uploadFolderByPresignedImage(imageMetaList, projectId, folderId);
}
@GetMapping("/folders/{folder_id}/images/{image_id}")
@ -80,7 +79,6 @@ public class ImageController {
@Operation(summary = "이미지 폴더 이동", description = "이미지가 위치한 폴더를 변경합니다.")
@SwaggerApiError({ErrorCode.BAD_REQUEST, ErrorCode.NOT_AUTHOR, ErrorCode.SERVER_ERROR, ErrorCode.PARTICIPANT_EDITOR_UNAUTHORIZED})
public void moveFolderImage(
@CurrentUser final Integer memberId,
@PathVariable("folder_id") final Integer folderId,
@PathVariable("project_id") final Integer projectId,
@PathVariable("image_id") final Long imageId,
@ -97,7 +95,6 @@ public class ImageController {
@PathVariable("folder_id") final Integer folderId,
@PathVariable("project_id") final Integer projectId,
@PathVariable("image_id") final Long imageId) {
log.debug("project: {} , folder: {}, 삭제하려는 이미지: {}, 현재 로그인 중인 사용자 : {}", projectId, folderId, imageId);
imageService.deleteImage(projectId, folderId, imageId);
}

View File

@ -0,0 +1,41 @@
package com.worlabel.domain.image.repository;
import com.worlabel.domain.image.entity.Image;
import lombok.RequiredArgsConstructor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import java.util.List;
@Repository
@RequiredArgsConstructor
public class ImageBulkRepository {
private final JdbcTemplate jdbcTemplate;
@Transactional
public void saveAll(List<Image> imageList) {
String sql = "INSERT INTO project_image (image_extension,folder_id,image_key,status,image_title)" +
" values (?, ?, ?, ?, ?)";
int batchSize = 1000; // 배치 크기 설정
for (int i = 0; i < imageList.size(); i += batchSize) {
List<Image> batchList = imageList.subList(i, Math.min(i + batchSize, imageList.size()));
jdbcTemplate.batchUpdate(sql,
batchList,
batchList.size(),
(PreparedStatement ps, Image image) -> {
ps.setString(1, image.getExtension()); // image_extension (String)
ps.setInt(2, image.getFolder().getId()); // folder_id (Integer)
ps.setString(3, image.getImageKey()); // image_key (String)
ps.setString(4, image.getStatus().name()); // status (String or Enum)
ps.setString(5, image.getTitle()); // image_title (String)
});
}
}
}

View File

@ -2,6 +2,7 @@ package com.worlabel.domain.image.service;
import com.worlabel.domain.folder.entity.Folder;
import com.worlabel.domain.image.entity.Image;
import com.worlabel.domain.image.repository.ImageBulkRepository;
import com.worlabel.domain.image.repository.ImageRepository;
import com.worlabel.global.exception.CustomException;
import com.worlabel.global.exception.ErrorCode;
@ -25,6 +26,7 @@ public class ImageAsyncService {
private final S3UploadService s3UploadService;
private final ImageRepository imageRepository;
private final ImageBulkRepository imageBulkRepository;
private final ThreadPoolTaskExecutor imageUploadExecutor;
@Async("imageUploadExecutor")
@ -72,4 +74,10 @@ public class ImageAsyncService {
private String getExtension(String fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
@Transactional
@Async("imageUploadExecutor")
public void saveImages(final List<Image> imageList) {
imageBulkRepository.saveAll(imageList);
}
}

View File

@ -7,6 +7,7 @@ import com.worlabel.domain.folder.repository.FolderRepository;
import com.worlabel.domain.image.entity.Image;
import com.worlabel.domain.image.entity.LabelStatus;
import com.worlabel.domain.image.entity.dto.*;
import com.worlabel.domain.image.repository.ImageBulkRepository;
import com.worlabel.domain.image.repository.ImageRepository;
import com.worlabel.domain.participant.entity.PrivilegeType;
import com.worlabel.domain.project.entity.Project;
@ -22,6 +23,7 @@ import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@ -321,13 +323,12 @@ public class ImageService {
@Transactional
@CheckPrivilege(PrivilegeType.EDITOR)
public List<ImagePresignedUrlResponse> uploadFolderByPresignedImage(final Integer memberId,
final List<ImageMetaRequest> imageMetaList,
public List<ImagePresignedUrlResponse> uploadFolderByPresignedImage(final List<ImageMetaRequest> imageMetaList,
final Integer projectId,
final Integer folderId) {
Folder folder = getOrCreateFolder(folderId, projectId);
List<ImagePresignedUrlResponse> presignedUrls = new ArrayList<>();
List<Image> imageList = new ArrayList<>();
for (ImageMetaRequest meta : imageMetaList) {
// UUID 생성 이미지 Key 지정
String key = UUID.randomUUID().toString().substring(0,13);
@ -339,18 +340,19 @@ public class ImageService {
// DB에 이미지 메타데이터 저장
Image image = Image.of(fileName, s3UploadService.addBucketPrefix(key), extension, folder);
imageRepository.save(image);
imageList.add(image);
// Presigned URL과 함께 응답 데이터 생성
ImagePresignedUrlResponse response = ImagePresignedUrlResponse.of(meta.getId(), presignedUrl);
presignedUrls.add(response);
}
imageAsyncService.saveImages(imageList);
log.debug("이미지 개수 {}",presignedUrls.size());
return presignedUrls;
}
// 이미지 가져오면서 프로젝트 소속 여부를 확인
// 이미지 가져오면서 프로젝트 소속 여부를 확인
private Image getImageByIdAndFolderIdAndFolderProjectId(final Integer folderId, final Long imageId, final Integer projectId) {
return imageRepository.findByIdAndFolderIdAndFolderProjectId(imageId, folderId, projectId)
.orElseThrow(() -> new CustomException(ErrorCode.DATA_NOT_FOUND));