minio-worker/process-image.ts
2025-04-19 23:30:01 +03:00

86 lines
2.5 KiB
TypeScript

import sharp from "sharp";
import { getMinioClient } from "./minio";
import { lookup } from "mime-types";
export async function processImage(
bucket: string,
key: string
): Promise<boolean> {
const minio = getMinioClient();
// Get metadata
const stat = await minio.statObject(bucket, key);
const meta = stat as unknown as { metaData: Record<string, string> };
const mime = meta.metaData["content-type"] || lookup(key) || "";
// Skip if not an image
if (!mime.startsWith("image/")) {
console.log(`⏭️ Skipping non-image file: ${key}`);
return false;
}
// Skip if already processed
if (
meta.metaData["x-amz-meta-processed"] === "true" ||
meta.metaData["processed"] === "true"
) {
console.log(`♻️ Already processed: ${key}`);
return false;
}
// Read original image
const stream = await minio.getObject(bucket, key);
const chunks: Buffer[] = [];
for await (const chunk of stream) chunks.push(chunk);
const buffer = Buffer.concat(chunks);
const fileName = key.split("/").pop();
const filePath = key.substring(0, key.lastIndexOf("/"));
const processedMeta = {
"x-amz-meta-processed": "true",
};
// Helper function to write to MinIO
async function writeImage(path: string, buffer: Buffer, mimeType: string) {
await minio.putObject(bucket, path, buffer, buffer.length, {
"Content-Type": mimeType,
...processedMeta,
});
}
try {
// 🖼️ Create thumbnail
const thumb = await sharp(buffer).resize(200).toBuffer();
await writeImage(`${filePath}/thumbs/${fileName}`, thumb, mime);
// 📸 Optimized JPEG
const optimized = await sharp(buffer).jpeg({ quality: 80 }).toBuffer();
await writeImage(
`${filePath}/optimized/${fileName}`,
optimized,
"image/jpeg"
);
// 🌐 WebP variant
const webpName = fileName?.replace(/\.[^/.]+$/, ".webp");
const webp = await sharp(buffer).webp({ quality: 80 }).toBuffer();
await writeImage(`${filePath}/webp/${webpName}`, webp, "image/webp");
// (Optional: AVIF format - super modern)
// const avifName = fileName?.replace(/\.[^/.]+$/, ".avif");
// const avif = await sharp(buffer).avif({ quality: 50 }).toBuffer();
// await writeImage(`${filePath}/avif/${avifName}`, avif, "image/avif");
// 🔁 Re-upload original with metadata to mark as processed
await writeImage(key, buffer, mime);
console.log(`✅ Processed image: ${key}`);
return true;
} catch (err) {
console.error(`❌ Error processing image (${key}):`, err);
return false;
}
}