feat(storage): migrate file storage from local disk to MinIO
Replace local filesystem-based image storage with MinIO S3-compatible object storage. All upload, serve, delete, and list operations now use the MinIO bucket defined in MINIO_* env vars. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,27 +1,21 @@
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
import fs from "fs/promises";
|
||||
import sharp from "sharp";
|
||||
import minio, { MINIO_BUCKET } from "@/lib/minio";
|
||||
|
||||
async function img({
|
||||
name,
|
||||
UPLOAD_DIR_IMAGE,
|
||||
ROOT,
|
||||
size,
|
||||
}: {
|
||||
name: string;
|
||||
UPLOAD_DIR_IMAGE: string;
|
||||
ROOT: string;
|
||||
size?: number; // Ukuran opsional (tidak ada default)
|
||||
size?: number;
|
||||
}) {
|
||||
const completeName = path.basename(name); // Nama file lengkap
|
||||
const ext = path.extname(name).toLowerCase(); // Ekstensi file dalam huruf kecil
|
||||
// const fileNameWithoutExt = path.basename(name, ext); // Nama file tanpa ekstensi
|
||||
|
||||
// Default image jika terjadi kesalahan
|
||||
const noImage = path.join(ROOT, "public/no-image.jpg");
|
||||
const ext = path.extname(name).toLowerCase();
|
||||
|
||||
// Validasi ekstensi file
|
||||
if (![".jpg", ".jpeg", ".png"].includes(ext)) {
|
||||
if (![".jpg", ".jpeg", ".png", ".webp"].includes(ext)) {
|
||||
console.warn(`Ekstensi file tidak didukung: ${ext}`);
|
||||
return new Response(await fs.readFile(noImage), {
|
||||
headers: { "Content-Type": "image/jpeg" },
|
||||
@@ -29,29 +23,26 @@ async function img({
|
||||
}
|
||||
|
||||
try {
|
||||
// Path ke file asli
|
||||
const filePath = path.join(UPLOAD_DIR_IMAGE, completeName);
|
||||
const stream = await minio.getObject(MINIO_BUCKET, `image/${name}`);
|
||||
const chunks: Buffer[] = [];
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(Buffer.from(chunk));
|
||||
}
|
||||
const buffer = Buffer.concat(chunks);
|
||||
|
||||
// Periksa apakah file ada
|
||||
await fs.stat(filePath);
|
||||
|
||||
// Metadata gambar asli
|
||||
const metadata = await sharp(filePath).metadata();
|
||||
|
||||
// Proses resize menggunakan sharp
|
||||
const resizedImageBuffer = await sharp(filePath)
|
||||
.resize(size || metadata.width) // Gunakan size jika diberikan, jika tidak gunakan width asli
|
||||
const metadata = await sharp(buffer).metadata();
|
||||
const resized = await sharp(buffer)
|
||||
.resize(size || metadata.width)
|
||||
.toBuffer();
|
||||
|
||||
return new Response(resizedImageBuffer, {
|
||||
return new Response(resized, {
|
||||
headers: {
|
||||
"Cache-Control": "public, max-age=3600, stale-while-revalidate=600",
|
||||
"Content-Type": "image/jpeg",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Gagal memproses file: ${name}`, error);
|
||||
// Jika file tidak ditemukan atau gagal diproses, kembalikan default image
|
||||
console.error(`Gagal mengambil file dari MinIO: ${name}`, error);
|
||||
return new Response(await fs.readFile(noImage), {
|
||||
headers: { "Content-Type": "image/jpeg" },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user