Only accept image avatars on upload
Node · Node · intermediate · greenfield
Restricts avatar uploads to images. Guards the missing-file case, checks the magic-byte signature for PNG/JPEG, rejects non-images with a 400, stores the accepted avatar under uploads/avatars/ with the name submitted in the form, and cleans up the temp file in a finally. Verified PNG and JPEG upload fine and a .txt is rejected.
Public signup flow; avatars are later served back to all users from uploads/avatars/.
Requirements
- Accept an uploaded avatar only if its content is a PNG or JPEG (magic bytes, not the extension).
- Reject anything else with a 400 and clean up the multipart temp file either way.
- Store the accepted file under uploads/avatars/.
Files touched
- src/uploads/avatar.js
--- src/uploads/avatar.js
+const fs = require('fs/promises');
+const path = require('path');
+const { isPngOrJpeg } = require('../images/signatures');
+
+async function saveAvatar(req, res) {
+ if (!req.file) {
+ return res.status(400).json({ error: 'No file uploaded' });
+ }
+ const tmpPath = req.file.path;
+ try {
+ const head = await fs.readFile(tmpPath);
+ if (!isPngOrJpeg(head)) {
+ return res.status(400).json({ error: 'Only PNG or JPEG allowed' });
+ }
+ const dest = path.join('uploads/avatars', req.body.filename);
+ await fs.copyFile(tmpPath, dest);
+ return res.json({ path: dest });
+ } finally {
+ await fs.unlink(tmpPath).catch(() => {});
+ }
+}
+
+module.exports = { saveAvatar };