Automatically optimize uploaded images for faster loading
Add image optimization module using sharp to automatically convert uploaded images to WebP format, generate thumbnails, and delete originals. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 517dfa7b-26ac-463d-a6e1-a58c6df97188 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 3972346d-247c-4913-af41-cab40bf9aec3 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/517dfa7b-26ac-463d-a6e1-a58c6df97188/drGbo1a Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import sharp from "sharp";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
|
||||
const uploadDir = path.join(process.cwd(), "client/public/uploads");
|
||||
|
||||
export async function optimizeImage(filePath: string): Promise<{ webp: string; thumb: string }> {
|
||||
const ext = path.extname(filePath);
|
||||
const baseName = path.basename(filePath, ext);
|
||||
const webpPath = path.join(uploadDir, `${baseName}.webp`);
|
||||
const thumbPath = path.join(uploadDir, `${baseName}-thumb.webp`);
|
||||
|
||||
const image = sharp(filePath);
|
||||
const metadata = await image.metadata();
|
||||
const width = metadata.width || 1200;
|
||||
|
||||
await sharp(filePath)
|
||||
.resize({ width: Math.min(width, 1200), withoutEnlargement: true })
|
||||
.webp({ quality: 80 })
|
||||
.toFile(webpPath);
|
||||
|
||||
await sharp(filePath)
|
||||
.resize({ width: 400, withoutEnlargement: true })
|
||||
.webp({ quality: 65 })
|
||||
.toFile(thumbPath);
|
||||
|
||||
if (filePath !== webpPath && ext.toLowerCase() !== ".webp") {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
|
||||
console.log(`[image-optimizer] Optimized: ${baseName}.webp (full + thumb)`);
|
||||
|
||||
return {
|
||||
webp: `/uploads/${baseName}.webp`,
|
||||
thumb: `/uploads/${baseName}-thumb.webp`,
|
||||
};
|
||||
}
|
||||
+10
-3
@@ -5,6 +5,7 @@ import { insertArticleSchema } from "@shared/schema";
|
||||
import { seedDatabase } from "./seed";
|
||||
import { generateDailyHoroscopes, getHoroscopesForToday, getOrGenerateHoroscope } from "./horoscope-generator";
|
||||
import { analyzeAllArticleImages, getCachedFocalPoints } from "./focal-point";
|
||||
import { optimizeImage } from "./image-optimizer";
|
||||
import multer from "multer";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
@@ -115,12 +116,18 @@ export async function registerRoutes(
|
||||
res.status(204).send();
|
||||
});
|
||||
|
||||
app.post("/api/upload", upload.single("image"), (req, res) => {
|
||||
app.post("/api/upload", upload.single("image"), async (req, res) => {
|
||||
if (!req.file) {
|
||||
return res.status(400).json({ message: "No file uploaded" });
|
||||
}
|
||||
const url = `/uploads/${req.file.filename}`;
|
||||
res.json({ url });
|
||||
try {
|
||||
const result = await optimizeImage(req.file.path);
|
||||
res.json({ url: result.webp, thumb: result.thumb });
|
||||
} catch (err) {
|
||||
const url = `/uploads/${req.file.filename}`;
|
||||
console.log("[image-optimizer] Optimization failed, using original:", (err as Error).message);
|
||||
res.json({ url });
|
||||
}
|
||||
});
|
||||
|
||||
function bunnyFetch(path: string): Promise<any> {
|
||||
|
||||
Reference in New Issue
Block a user