This commit is contained in:
root
2025-12-09 22:38:32 +05:00
parent 734ebb9d40
commit b0b9f18ef5
@@ -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
}
}
}