├── .gitignore ├── middleware ├── filesPayloadExists.js ├── fileExtLimiter.js └── fileSizeLimiter.js ├── package.json ├── app.js ├── README.md └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | files 4 | -------------------------------------------------------------------------------- /middleware/filesPayloadExists.js: -------------------------------------------------------------------------------- 1 | const filesPayloadExists = (req, res, next) => { 2 | if (!req.files) return res.status(400).json({ status: "error", message: "Missing files" }) 3 | 4 | next() 5 | } 6 | 7 | module.exports = filesPayloadExists -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node_file_uploader", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node app.js", 8 | "dev": "nodemon app.js", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "express": "^4.18.1", 16 | "express-fileupload": "^1.4.0" 17 | }, 18 | "devDependencies": { 19 | "nodemon": "^2.0.16" 20 | } 21 | } -------------------------------------------------------------------------------- /middleware/fileExtLimiter.js: -------------------------------------------------------------------------------- 1 | const path = require("path") 2 | 3 | const fileExtLimiter = (allowedExtArray) => { 4 | return (req, res, next) => { 5 | const files = req.files 6 | 7 | const fileExtensions = [] 8 | Object.keys(files).forEach(key => { 9 | fileExtensions.push(path.extname(files[key].name)) 10 | }) 11 | 12 | // Are the file extension allowed? 13 | const allowed = fileExtensions.every(ext => allowedExtArray.includes(ext)) 14 | 15 | if (!allowed) { 16 | const message = `Upload failed. Only ${allowedExtArray.toString()} files allowed.`.replaceAll(",", ", "); 17 | 18 | return res.status(422).json({ status: "error", message }); 19 | } 20 | 21 | next() 22 | } 23 | } 24 | 25 | module.exports = fileExtLimiter -------------------------------------------------------------------------------- /middleware/fileSizeLimiter.js: -------------------------------------------------------------------------------- 1 | const MB = 5; // 5 MB 2 | const FILE_SIZE_LIMIT = MB * 1024 * 1024; 3 | 4 | const fileSizeLimiter = (req, res, next) => { 5 | const files = req.files 6 | 7 | const filesOverLimit = [] 8 | // Which files are over the limit? 9 | Object.keys(files).forEach(key => { 10 | if (files[key].size > FILE_SIZE_LIMIT) { 11 | filesOverLimit.push(files[key].name) 12 | } 13 | }) 14 | 15 | if (filesOverLimit.length) { 16 | const properVerb = filesOverLimit.length > 1 ? 'are' : 'is'; 17 | 18 | const sentence = `Upload failed. ${filesOverLimit.toString()} ${properVerb} over the file size limit of ${MB} MB.`.replaceAll(",", ", "); 19 | 20 | const message = filesOverLimit.length < 3 21 | ? sentence.replace(",", " and") 22 | : sentence.replace(/,(?=[^,]*$)/, " and"); 23 | 24 | return res.status(413).json({ status: "error", message }); 25 | 26 | } 27 | 28 | next() 29 | } 30 | 31 | module.exports = fileSizeLimiter -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const fileUpload = require("express-fileupload"); 3 | const path = require("path"); 4 | 5 | const filesPayloadExists = require('./middleware/filesPayloadExists'); 6 | const fileExtLimiter = require('./middleware/fileExtLimiter'); 7 | const fileSizeLimiter = require('./middleware/fileSizeLimiter'); 8 | 9 | const PORT = process.env.PORT || 3500; 10 | 11 | const app = express(); 12 | 13 | app.get("/", (req, res) => { 14 | res.sendFile(path.join(__dirname, "index.html")); 15 | }); 16 | 17 | app.post('/upload', 18 | fileUpload({ createParentPath: true }), 19 | filesPayloadExists, 20 | fileExtLimiter(['.png', '.jpg', '.jpeg']), 21 | fileSizeLimiter, 22 | (req, res) => { 23 | const files = req.files 24 | console.log(files) 25 | 26 | Object.keys(files).forEach(key => { 27 | const filepath = path.join(__dirname, 'files', files[key].name) 28 | files[key].mv(filepath, (err) => { 29 | if (err) return res.status(500).json({ status: "error", message: err }) 30 | }) 31 | }) 32 | 33 | return res.json({ status: 'success', message: Object.keys(files).toString() }) 34 | } 35 | ) 36 | 37 | 38 | 39 | 40 | app.listen(PORT, () => console.log(`Server running on port ${PORT}`)); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # "Node.js File Upload API with Express Tutorial" 2 | 3 | --- 4 | 5 | ### Author Links 6 | 7 | 👋 Hello, I'm Dave Gray. 8 | 9 | ✅ [Check out my YouTube Channel with all of my tutorials](https://www.youtube.com/DaveGrayTeachesCode). 10 | 11 | 🚩 [Subscribe to my channel](https://bit.ly/3nGHmNn) 12 | 13 | ☕ [Buy Me A Coffee](https://buymeacoffee.com/DaveGray) 14 | 15 | 🚀 Follow Me: 16 | 17 | - [Twitter](https://twitter.com/yesdavidgray) 18 | - [LinkedIn](https://www.linkedin.com/in/davidagray/) 19 | - [Blog](https://yesdavidgray.com) 20 | - [Reddit](https://www.reddit.com/user/DaveOnEleven) 21 | 22 | --- 23 | 24 | ### Description 25 | 26 | 📺 [YouTube Video](https://youtu.be/4pmkQjsKJ-U) for this repository. 27 | 28 | --- 29 | 30 | ### 🎓 Academic Honesty 31 | 32 | **DO NOT COPY FOR AN ASSIGNMENT** - Avoid plagiargism and adhere to the spirit of this [Academic Honesty Policy](https://www.freecodecamp.org/news/academic-honesty-policy/). 33 | 34 | --- 35 | 36 | ### 📚 Tutorial References 37 | 38 | - 🔗 [NPM express-fileupload](https://www.npmjs.com/package/express-fileupload) 39 | 40 | 41 | ### 📚 Node.js & Express References 42 | 43 | - 🔗 [Node.js Official site and docs](https://nodejs.org/) 44 | - 🔗 [Express Official site and docs](https://expressjs.com/) 45 | - 🔗 [Node Jobs](https://www.ziprecruiter.com/candidate/search?search=node&location=) 46 | 47 | ### ⚙ VS Code Extensions I Use: 48 | 49 | - 🔗 [ES7 React JS Snippets Extension](https://marketplace.visualstudio.com/items?itemName=dsznajder.es7-react-js-snippets) 50 | - 🔗 [vscode-icons VS Code Extension](https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons) 51 | - 🔗 [Github Themes VS Code Extension](https://marketplace.visualstudio.com/items?itemName=GitHub.github-vscode-theme) 52 | - 🔗 [Multi Cursor Case Preserve Extension](https://marketplace.visualstudio.com/items?itemName=Cardinal90.multi-cursor-case-preserve) -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |