关于Formidable文件类型校验代码位置及提前终止非图片文件上传的技术咨询
Problem 1: Correct Placement for Formidable File Type Validation Code
The issue here is how you're passing the filter option to Formidable. You wrapped it in a separate options object and added that as a nested property inside the Formidable config—Formidable doesn't recognize that nested options key, so your filter never runs.
Here are two straightforward fixes:
Option 1: Inline the filter directly in the main config
app.post("/api/upload", async (req, res, next) => { const form = formidable({ multiples: true, uploadDir: "./uploads", maxFileSize: 1024 * 1024, // Place the filter directly in the top-level configuration filter: function ({ name, originalFilename, mimetype }) { // Only keep image files return mimetype && mimetype.includes("image"); }, }); // Don't forget to parse the request to trigger the upload flow form.parse(req, (err, fields, files) => { if (err) { next(err); return; } // Handle successful upload response here res.json({ success: true, files }); }); });
Option 2: Merge your filter options with the main config
If you prefer to keep the filter in a separate object, use the spread operator (...) to merge its properties into the Formidable config:
app.post("/api/upload", async (req, res, next) => { const filterOptions = { filter: function ({ name, originalFilename, mimetype }) { return mimetype && mimetype.includes("image"); }, }; const form = formidable({ multiples: true, uploadDir: "./uploads", maxFileSize: 1024 * 1024, ...filterOptions, // Merge the filter into the main config }); // Rest of your parse and upload handling logic... });
Problem 2: Stop Non-Image Files Before They Upload
Your current file.destroy() approach works after the file has already been saved to disk—which is why you see files being uploaded first then deleted. The proper fix is to use the filter function from Problem 1: it runs before Formidable starts writing the file to your server, so non-image files are rejected immediately without ever touching your disk.
Here's a complete, improved example:
const fs = require("fs").promises; // Use promise-based fs for cleaner async code const path = require("path"); const formidable = require("formidable"); app.post("/api/upload", async (req, res, next) => { const form = formidable({ multiples: true, uploadDir: "./uploads", filter: ({ name, originalFilename, mimetype }) => { // Reject non-image files before any upload begins return mimetype && mimetype.includes("image"); }, }); // Handle valid image files only form.on("file", async (field, file) => { // Generate a unique filename to avoid overwrites (fixed your original index issue) const fileExtension = path.extname(file.originalFilename); const uniqueFileName = `avatar-${Date.now()}-${Math.random().toString(36).slice(2)}${fileExtension}`; const newFilePath = path.join(form.uploadDir, uniqueFileName); try { await fs.rename(file.filepath, newFilePath); console.log(`Successfully processed: ${file.originalFilename}`); } catch (renameErr) { console.error("Failed to rename file:", renameErr); next(renameErr); } }); // Handle rejected files or other upload errors form.on("error", (err) => { if (err.message.includes("File rejected by filter")) { console.log("Rejected a non-image file upload attempt"); res.status(400).send("Error: Only image files are allowed"); } else { console.error("Upload error:", err); next(err); } }); // Parse the request to initiate the upload process form.parse(req, (parseErr, fields, files) => { if (parseErr) { next(parseErr); return; } res.status(200).json({ success: true, files: Object.values(files) }); }); });
Quick note: Your original uploadDirIndex logic could cause duplicate filenames if multiple uploads happen simultaneously. The example above uses Date.now() plus a random string to ensure unique file names every time.
内容的提问来源于stack exchange,提问作者robokonk

