I’m not sure if it will help, but here’s my function.
It works in dev with a 6MB file, but not in production due to the aforementioned limit.
import { IncomingForm, File as FormidableFile } from "formidable";
import { NextApiRequest, NextApiResponse } from "next";
import { storage } from "../../lib/db";
import chalk from "chalk";
import fs from "fs";
import sharp from "sharp";
const tempDir = "/tmp";
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir, { recursive: true });
}
function deleteTempDir(dirPath: string) {
fs.rm(dirPath, { recursive: true, force: true }, (err) => {
if (err) {
console.error(
chalk.yellow("Error deleting temporary image directory:"),
err
);
} else {
console.log(
chalk.green("Temporary image directory deleted successfully.")
);
}
});
}
export const config = {
api: {
bodyParser: false,
supportsResponseStreaming: true,
},
};
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === "POST") {
const form = new IncomingForm({
keepExtensions: true,
uploadDir: tempDir,
maxFileSize: 10 * 1024 * 1024,
});
form.parse(req, async (err, fields, files) => {
if (err) {
console.error("Formidable error:", err);
return res
.status(500)
.json({ message: "Error parsing the file" });
}
try {
const { userId, slug } = fields;
const imageFile = Array.isArray(files.imageFile)
? (files.imageFile[0] as unknown as FormidableFile)
: (files.imageFile as unknown as FormidableFile);
if (!imageFile) {
return res
.status(422)
.json({ message: "Missing image file!" });
}
if (!userId) {
return res.status(422).json({ message: "Missing userId!" });
}
if (!slug) {
return res.status(422).json({ message: "Missing slug!" });
}
const singleSlug = Array.isArray(slug) ? slug[0] : slug;
const imagePath = imageFile.filepath;
if (!fs.existsSync(imagePath)) {
return res
.status(500)
.json({ message: "Uploaded file not found" });
}
const metadata = await sharp(imageFile.filepath).metadata();
if (!metadata.width || !metadata.height || !metadata.format) {
return res
.status(422)
.json({ message: "No image metadata found!" });
}
console.log(chalk.cyan("Image height:"), metadata.height);
console.log(chalk.cyan("Image width:"), metadata.width);
console.log(
chalk.cyan("Original aspect ratio:"),
metadata.width / metadata.height
);
let useThisContentType = `image/${metadata.format}`;
let transformStream = sharp();
if (metadata.width > 992) {
console.log(
chalk.cyan("Resizing with original aspect ratio")
);
useThisContentType = "image/webp";
const originalAspectRatio =
metadata.width / metadata.height;
transformStream = transformStream.resize({
width: 992,
height: Math.round(992 / originalAspectRatio),
fit: sharp.fit.contain,
});
} else if (
metadata.width === metadata.height ||
metadata.height > metadata.width
) {
useThisContentType = "image/webp";
console.log(chalk.cyan("Resizing with new aspect ratio"));
const targetAspectRatio = 4 / 3;
transformStream = transformStream.resize({
width: 992,
height: Math.round(992 / targetAspectRatio),
fit: sharp.fit.cover,
position: sharp.strategy.entropy,
});
}
transformStream.on("error", (err) => {
console.error("Sharp error:", err);
res.status(500).json({
message: "Error processing the image",
});
});
const targetPath = //redacted
const bucketName = //redacted
const uploadStream = storage
.bucket(bucketName)
.file(targetPath)
.createWriteStream({
contentType: useThisContentType,
});
const readStream = fs.createReadStream(imageFile.filepath);
readStream.pipe(transformStream).pipe(uploadStream);
uploadStream.on("finish", () => {
console.log(
chalk.green("Processing and upload successful")
);
res.status(200).json({ imagePath: targetPath });
deleteTempDir(tempDir);
});
uploadStream.on("error", (err) => {
console.error("Upload error:", err);
deleteTempDir(tempDir);
res.status(500).json({
message: "Error uploading the image",
});
});
} catch (error: any) {
console.error("error", error);
let message = error.message;
if (message.includes("File too large")) {
message = "Max size for image file is 10 MB";
}
res.status(409).json({ message });
}
});
} else {
res.setHeader("Allow", "POST");
res.status(405).end("Method Not Allowed");
}
};
export default handler;