minio-worker/process-image.ts
2025-04-26 19:44:51 +03:00

96 lines
2.6 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();
// Fetch 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) || "";
if (!mime.startsWith("image/")) {
console.log(`⏭️ Skipping non-image file: ${key}`);
return false;
}
if (
meta.metaData["x-amz-meta-processed"] === "true" ||
meta.metaData["processed"] === "true"
) {
console.log(`♻️ Already processed: ${key}`);
return false;
}
const originalStream = await minio.getObject(bucket, key);
const fileName = key.split("/").pop()!;
const filePath = key.substring(0, key.lastIndexOf("/"));
const processedMeta = {
"x-amz-meta-processed": "true",
};
// Helper to upload from a stream
async function uploadFromStream(
targetPath: string,
mimeType: string,
transformStream: NodeJS.ReadableStream
) {
const chunks: Buffer[] = [];
for await (const chunk of transformStream) {
chunks.push(chunk as Buffer);
}
const finalBuffer = Buffer.concat(chunks);
await minio.putObject(bucket, targetPath, finalBuffer, finalBuffer.length, {
"Content-Type": mimeType,
...processedMeta,
});
}
try {
// 🖼️ Thumbnail (resize to 200px width)
await uploadFromStream(
`${filePath}/thumbs/${fileName}`,
mime,
originalStream.pipe(sharp().resize(200))
);
// Re-fetch original again for each variant (streams are one-time-use)
const optimizedStream = await minio.getObject(bucket, key);
// 📸 Optimized JPEG
await uploadFromStream(
`${filePath}/optimized/${fileName}`,
"image/jpeg",
optimizedStream.pipe(sharp().jpeg({ quality: 80 }))
);
const webpStream = await minio.getObject(bucket, key);
// 🌐 WebP version
const webpName = fileName.replace(/\.[^/.]+$/, ".webp");
await uploadFromStream(
`${filePath}/webp/${webpName}`,
"image/webp",
webpStream.pipe(sharp().webp({ quality: 80 }))
);
const finalOriginalStream = await minio.getObject(bucket, key);
// 🔁 Re-upload the original with updated metadata to mark it processed
await uploadFromStream(key, mime, finalOriginalStream);
console.log(`✅ Processed image: ${key}`);
return true;
} catch (err) {
console.error(`❌ Error processing image (${key}):`, err);
return false;
}
}