Complete the migration of all gallery images to Cloudinary
Implement image compression using Sharp to handle uploads exceeding 10MB, update Cloudinary URL generation, and finalize the gallery migration process. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 23852c00-4779-460a-9e0c-d09fee4b6c92 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: ecf3cabf-0fca-4959-8ab7-e18c7b222cf7 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/23852c00-4779-460a-9e0c-d09fee4b6c92/ncMMRQ9 Replit-Helium-Checkpoint-Created: true
This commit is contained in:
+44
-9
@@ -1,4 +1,5 @@
|
||||
import { v2 as cloudinary } from "cloudinary";
|
||||
import sharp from "sharp";
|
||||
|
||||
cloudinary.config({
|
||||
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
|
||||
@@ -7,6 +8,7 @@ cloudinary.config({
|
||||
});
|
||||
|
||||
const GALLERY_FOLDER = "folx-gallery";
|
||||
const MAX_UPLOAD_SIZE = 9 * 1024 * 1024;
|
||||
|
||||
export interface CloudinaryGalleryImage {
|
||||
publicId: string;
|
||||
@@ -19,10 +21,8 @@ export interface CloudinaryGalleryImage {
|
||||
}
|
||||
|
||||
function buildUrl(publicId: string, transforms: string): string {
|
||||
return cloudinary.url(publicId, {
|
||||
transformation: transforms,
|
||||
secure: true,
|
||||
});
|
||||
const cloudName = process.env.CLOUDINARY_CLOUD_NAME || "djqxt0pf3";
|
||||
return `https://res.cloudinary.com/${cloudName}/image/upload/${transforms}/${publicId}`;
|
||||
}
|
||||
|
||||
export function generateImageUrls(publicId: string): { thumb: string; large: string; mobile: string; full: string } {
|
||||
@@ -34,6 +34,31 @@ export function generateImageUrls(publicId: string): { thumb: string; large: str
|
||||
};
|
||||
}
|
||||
|
||||
async function downloadAndCompress(imageUrl: string): Promise<Buffer> {
|
||||
const resp = await fetch(imageUrl);
|
||||
if (!resp.ok) throw new Error(`Download failed: ${resp.status}`);
|
||||
const arrayBuffer = await resp.arrayBuffer();
|
||||
const buffer = Buffer.from(arrayBuffer);
|
||||
|
||||
if (buffer.length <= MAX_UPLOAD_SIZE) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const compressed = await sharp(buffer)
|
||||
.resize({ width: 4000, withoutEnlargement: true })
|
||||
.jpeg({ quality: 85 })
|
||||
.toBuffer();
|
||||
|
||||
if (compressed.length > MAX_UPLOAD_SIZE) {
|
||||
return sharp(buffer)
|
||||
.resize({ width: 3000, withoutEnlargement: true })
|
||||
.jpeg({ quality: 75 })
|
||||
.toBuffer();
|
||||
}
|
||||
|
||||
return compressed;
|
||||
}
|
||||
|
||||
export async function uploadToCloudinary(imageUrl: string, fileName: string): Promise<string | null> {
|
||||
const publicId = `${GALLERY_FOLDER}/${fileName.replace(/\.\w+$/, "").replace(/[^a-zA-Z0-9_-]/g, "_")}`;
|
||||
|
||||
@@ -45,12 +70,22 @@ export async function uploadToCloudinary(imageUrl: string, fileName: string): Pr
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
const result = await cloudinary.uploader.upload(imageUrl, {
|
||||
public_id: publicId,
|
||||
overwrite: false,
|
||||
resource_type: "image",
|
||||
const buffer = await downloadAndCompress(imageUrl);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const uploadStream = cloudinary.uploader.upload_stream(
|
||||
{
|
||||
public_id: publicId,
|
||||
overwrite: false,
|
||||
resource_type: "image",
|
||||
},
|
||||
(error, result) => {
|
||||
if (error) reject(error);
|
||||
else resolve(result!.public_id);
|
||||
}
|
||||
);
|
||||
uploadStream.end(buffer);
|
||||
});
|
||||
return result.public_id;
|
||||
} catch (err: any) {
|
||||
console.error(`Cloudinary upload failed for ${fileName}:`, err.message);
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user