Skip to content

Instantly share code, notes, and snippets.

@sina-byn
Last active June 26, 2024 15:04
Show Gist options
  • Save sina-byn/d99028438812718cb60236cc45357eec to your computer and use it in GitHub Desktop.
Save sina-byn/d99028438812718cb60236cc45357eec to your computer and use it in GitHub Desktop.
express image optimization middleware
// code for https://medium.com/@sina-byn/code-an-express-middleware-to-optimize-your-images-c9225308e4ba
const { createReadStream } = require('fs');
const express = require('express');
const fs = require('fs/promises');
const sharp = require('sharp');
const path = require('path');
// * utils
const getContentType = fileName => {
const ext = path.extname(fileName).slice(1);
let contentType;
switch (ext) {
case 'jpg':
case 'jfif':
case 'jpeg':
contentType = 'image/jpeg';
break;
case 'png':
contentType = 'image/png';
break;
case 'webp':
contentType = 'image/webp';
break;
case 'svg':
contentType = 'image/svg+xml';
break;
default:
contentType = 'application/octet-stream';
}
return contentType;
};
const app = express();
app.get('*', async (req, res, next) => {
const storagePath = path.join(__dirname, 'public');
const fileName = req.params[0] ?? '';
const filePath = path.join(storagePath, fileName);
try {
const stats = await fs.stat(filePath);
if (!stats.isFile()) return next();
const contentType = getContentType(fileName);
res.setHeader('Content-Type', contentType);
if (['image/svg+xml', 'application/octet-stream'].includes(contentType)) {
const readStream = createReadStream(filePath);
readStream.pipe(res);
readStream.on('error', next);
return;
}
const image = sharp(filePath);
const metadata = await image.metadata();
const aspectRatio = metadata.width / metadata.height;
const quality = Math.trunc(+(req.query.q ?? 100));
let width = Math.trunc(+(req.query.w ?? 0));
let height = Math.trunc(+(req.query.h ?? 0));
if (width && !height) {
height = Math.round(width * (1 / aspectRatio));
} else if (height && !width) {
width = Math.round(height * aspectRatio);
} else {
width = metadata.width;
height = metadata.height;
}
const stream = image
.resize({ width, height })
.jpeg({ quality, progressive: true, force: false })
.webp({ quality, progressive: true, force: false })
.png({ quality, progressive: true, force: false });
stream.pipe(res);
stream.on('error', next);
} catch (err) {
console.error(err);
res.status(404).send('File not found');
}
});
app.listen(8080, () => {
console.log('server running at http://localhost:8080');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment