From b0b9f18ef57cca9e5498fc067fe27283e30c3b56 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 9 Dec 2025 22:38:32 +0500 Subject: [PATCH] . --- .../service/ImageStorageServiceImpl.java | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 src/main/java/kz/konturai/parser/service/ImageStorageServiceImpl.java diff --git a/src/main/java/kz/konturai/parser/service/ImageStorageServiceImpl.java b/src/main/java/kz/konturai/parser/service/ImageStorageServiceImpl.java new file mode 100644 index 0000000..c49d9ea --- /dev/null +++ b/src/main/java/kz/konturai/parser/service/ImageStorageServiceImpl.java @@ -0,0 +1,111 @@ +package kz.konturai.parser.service; + +import io.minio.GetPresignedObjectUrlArgs; +import io.minio.MinioClient; +import io.minio.PutObjectArgs; +import io.minio.http.Method; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.ByteArrayInputStream; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * MinIO-based implementation of ImageStorageService. + * + * Uploads images to MinIO storage and returns presigned URLs that are publicly + * accessible for a limited time (7 days). These URLs can be used by Instagram + * API. + */ +@Service +public class ImageStorageServiceImpl implements ImageStorageService { + + private static final Logger logger = LoggerFactory.getLogger(ImageStorageServiceImpl.class); + private static final int PRESIGNED_URL_EXPIRY_DAYS = 7; + + private final MinioClient minioClient; + private final String bucketName; + + public ImageStorageServiceImpl( + @Value("${minio.endpoint}") String endpoint, + @Value("${minio.access-key}") String accessKey, + @Value("${minio.secret-key}") String secretKey, + @Value("${minio.bucket-name}") String bucketName) { + this.minioClient = MinioClient.builder() + .endpoint(endpoint) + .credentials(accessKey, secretKey) + .build(); + this.bucketName = bucketName; + } + + @Override + public String uploadImage(byte[] imageBytes, String filename) { + try { + // Generate a unique object name with timestamp and UUID to avoid collisions + String objectName = generateObjectName(filename); + + // Determine content type based on filename extension + String contentType = determineContentType(filename); + + // Upload the image to MinIO + minioClient.putObject(PutObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .stream(new ByteArrayInputStream(imageBytes), imageBytes.length, -1) + .contentType(contentType) + .build()); + + logger.info("Successfully uploaded image to MinIO: {}", objectName); + + // Generate a presigned URL that's valid for 7 days + String presignedUrl = minioClient.getPresignedObjectUrl( + GetPresignedObjectUrlArgs.builder() + .method(Method.GET) + .bucket(bucketName) + .object(objectName) + .expiry(PRESIGNED_URL_EXPIRY_DAYS, TimeUnit.DAYS) + .build()); + + logger.info("Generated presigned URL for image: {}", objectName); + return presignedUrl; + + } catch (Exception e) { + logger.error("Failed to upload image to MinIO and generate presigned URL: {}", e.getMessage(), e); + throw new RuntimeException("Failed to upload image to storage: " + e.getMessage(), e); + } + } + + /** + * Generates a unique object name for the image. + * Format: instagram/{timestamp}-{uuid}-{original-filename} + */ + private String generateObjectName(String filename) { + String timestamp = String.valueOf(System.currentTimeMillis()); + String uuid = UUID.randomUUID().toString().substring(0, 8); + String sanitizedFilename = filename != null ? filename.replaceAll("[^a-zA-Z0-9._-]", "_") : "image.jpg"; + return String.format("instagram/%s-%s-%s", timestamp, uuid, sanitizedFilename); + } + + /** + * Determines the content type based on the filename extension. + */ + private String determineContentType(String filename) { + if (filename == null) { + return "image/jpeg"; + } + + String lowerFilename = filename.toLowerCase(); + if (lowerFilename.endsWith(".png")) { + return "image/png"; + } else if (lowerFilename.endsWith(".gif")) { + return "image/gif"; + } else if (lowerFilename.endsWith(".webp")) { + return "image/webp"; + } else { + return "image/jpeg"; // Default to JPEG + } + } +}