Refactor: 이미지 벌크 업로드
This commit is contained in:
parent
6a32b071ff
commit
402620130b
@ -56,12 +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) {
|
||||
log.debug("requestImageList {}", imageMetaList);
|
||||
return imageService.uploadFolderByPresignedImage(memberId, imageMetaList, projectId, folderId);
|
||||
return imageService.uploadFolderByPresignedImage(imageMetaList, projectId, folderId);
|
||||
}
|
||||
|
||||
@GetMapping("/folders/{folder_id}/images/{image_id}")
|
||||
@ -81,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,
|
||||
@ -98,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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
@ -336,21 +337,22 @@ public class ImageService {
|
||||
|
||||
// Presigned URL 생성
|
||||
String presignedUrl = s3UploadService.generatePresignedUrl(key, extension);
|
||||
log.debug("presignedUrl {}", presignedUrl);
|
||||
|
||||
// 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));
|
||||
|
Loading…
Reference in New Issue
Block a user