{"id":51123279,"url":"https://github.com/iamskyy666/file-upload-nodejs","last_synced_at":"2026-06-25T05:01:15.662Z","repository":{"id":366555530,"uuid":"1276647665","full_name":"iamskyy666/file-upload-nodejs","owner":"iamskyy666","description":"📂 All about file/image uploading using NodeJs/ExpressJs 🟢","archived":false,"fork":false,"pushed_at":"2026-06-22T09:36:58.000Z","size":498,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-22T11:20:45.487Z","etag":null,"topics":["backend","cloudinary","expressjs","file-upload","image-upload","multer","nodejs"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iamskyy666.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-22T07:02:15.000Z","updated_at":"2026-06-22T09:37:12.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/iamskyy666/file-upload-nodejs","commit_stats":null,"previous_names":["iamskyy666/file-upload-nodejs"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/iamskyy666/file-upload-nodejs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamskyy666%2Ffile-upload-nodejs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamskyy666%2Ffile-upload-nodejs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamskyy666%2Ffile-upload-nodejs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamskyy666%2Ffile-upload-nodejs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iamskyy666","download_url":"https://codeload.github.com/iamskyy666/file-upload-nodejs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamskyy666%2Ffile-upload-nodejs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34760219,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backend","cloudinary","expressjs","file-upload","image-upload","multer","nodejs"],"created_at":"2026-06-25T05:01:13.384Z","updated_at":"2026-06-25T05:01:15.557Z","avatar_url":"https://github.com/iamskyy666.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# File Uploading in Node.js and Express 📂\n\n---\n\n# 1. The Fundamental Question\n\nSuppose we have a form:\n\n```html\n\u003cform\u003e\n  \u003cinput type=\"text\" name=\"username\" /\u003e\n  \u003cinput type=\"file\" name=\"avatar\" /\u003e\n\u003c/form\u003e\n```\n\nand we choose:\n\n```text\nprofile.jpg\n```\n\nQuestion:\n\n**How does an image travel from our computer to the server?**\n\n---\n\n# 2. Why Can't We Send Files as JSON?\n\nNormally we send:\n\n```json\n{\n  \"username\": \"Skyy\",\n  \"email\": \"abc@gmail.com\"\n}\n```\n\nusing:\n\n```http\nContent-Type: application/json\n```\n\nThis works because:\n\n```text\nJSON = Text\n```\n\n---\n\nBut an image is:\n\n```text\nBinary Data\n```\n\nExample:\n\n```text\n01010010\n11101001\n00110110\n...\n```\n\nJSON isn't designed to efficiently transmit large binary files.\n\nTherefore:\n\n```text\napplication/json\n```\n\nis not appropriate.\n\n---\n\n# 3. The Solution: multipart/form-data\n\nBrowsers use:\n\n```http\nContent-Type: multipart/form-data\n```\n\nThis means:\n\n```text\nSplit request into multiple parts\n```\n\nExample:\n\n```text\nPart 1:\nusername=Skyy\n\nPart 2:\nfile=profile.jpg\n```\n\n---\n\nA request looks something like:\n\n```http\nPOST /upload\n\nContent-Type: multipart/form-data\n\n------Boundary\nContent-Disposition: form-data; name=\"username\"\n\nSkyy\n\n------Boundary\nContent-Disposition: form-data;\nname=\"avatar\";\nfilename=\"profile.jpg\"\n\n(binary image data)\n\n------Boundary--\n```\n\n---\n\n# Why the Boundary?\n\nImagine:\n\n```text\nusername\nimage\naddress\nresume\n```\n\nall inside one request.\n\nThe boundary acts like:\n\n```text\n---------- Separator ----------\n```\n\nso the server knows:\n\n```text\nWhere one field ends\nand another begins\n```\n\n---\n\n# Why Doesn't Express Parse Files Automatically?\n\nWe know:\n\n```js\napp.use(express.json());\n```\n\nThis parses:\n\n```text\napplication/json\n```\n\nAnd:\n\n```js\napp.use(express.urlencoded());\n```\n\nparses:\n\n```text\nx-www-form-urlencoded\n```\n\nBut:\n\n```text\nmultipart/form-data\n```\n\nis much more complicated.\n\nIt may contain:\n\n* Images\n* Videos\n* PDFs\n* ZIP files\n* Large binary streams\n\nExpress deliberately does not parse this.\n\n---\n\nTherefore we use:\n\n```text\nmulter\n```\n\nor\n\n```text\nformidable\n```\n\nor\n\n```text\nbusboy\n```\n\n---\n\n# What Does Multer Actually Do?\n\nMany tutorials say:\n\n```bash\nnpm install multer\n```\n\nand stop there.\n\nBut internally:\n\nMulter:\n\n```text\nReads incoming request stream\n↓\nSeparates boundaries\n↓\nExtracts files\n↓\nExtracts text fields\n↓\nStores files\n↓\nAttaches them to req.file\n```\n\n---\n\nExample:\n\n```js\nupload.single(\"avatar\")\n```\n\nThis middleware:\n\n```text\nReads multipart request\n↓\nFinds field named avatar\n↓\nSaves file\n↓\nAdds req.file\n```\n\n---\n\nThen:\n\n```js\nconsole.log(req.file)\n```\n\nmight print:\n\n```js\n{\n  fieldname: 'avatar',\n\n  originalname: 'profile.jpg',\n\n  mimetype: 'image/jpeg',\n\n  size: 50382,\n\n  filename:\n  '1745214142-profile.jpg',\n\n  path:\n  'uploads/profile.jpg'\n}\n```\n\n---\n\n# File Upload Middleware\n\nConsider:\n\n```js\napp.post(\n \"/upload\",\n upload.single(\"avatar\"),\n controller\n)\n```\n\nExecution:\n\n```text\nRequest arrives\n\n↓\n\nMulter middleware\n\n↓\n\nReads image\n\n↓\n\nStores image\n\n↓\n\nAdds req.file\n\n↓\n\nCalls controller\n\n↓\n\nController saves DB record\n```\n\n---\n\n# upload.single()\n\nMeans:\n\n```text\nAccept exactly one file\n```\n\n---\n\nExample:\n\n```html\n\u003cinput\ntype=\"file\"\nname=\"avatar\"\n/\u003e\n```\n\n---\n\nServer:\n\n```js\nupload.single(\"avatar\")\n```\n\n---\n\nThen:\n\n```js\nreq.file\n```\n\ncontains:\n\n```text\nOne file object\n```\n\n---\n\n# upload.array()\n\nSuppose:\n\n```html\n\u003cinput\nmultiple\nname=\"photos\"\n/\u003e\n```\n\n---\n\nServer:\n\n```js\nupload.array(\"photos\",5)\n```\n\n---\n\nNow:\n\n```js\nreq.files\n```\n\ncontains:\n\n```text\n[\n img1.jpg,\n img2.jpg,\n img3.jpg\n]\n```\n\nMaximum:\n\n```text\n5 files\n```\n\n---\n\n# upload.fields()\n\nSuppose:\n\n```html\navatar\nresume\ncoverImage\n```\n\nThree different files.\n\n---\n\nWe can do:\n\n```js\nupload.fields([\n { name: \"avatar\" },\n { name: \"resume\" },\n { name: \"coverImage\" }\n])\n```\n\n---\n\nResult:\n\n```js\nreq.files.avatar\n\nreq.files.resume\n\nreq.files.coverImage\n```\n\n---\n\n# Where Are Files Stored?\n\nThis is an important design decision.\n\nThere are generally three approaches.\n\n---\n\n# Option 1\n\nStore on local disk.\n\nExample:\n\n```text\nuploads/\n\nprofile.jpg\n\nresume.pdf\n```\n\n---\n\nMulter:\n\n```js\ndestination:\n\n\"./uploads\"\n```\n\n---\n\nAdvantages:\n\nSimple.\n\n---\n\nDisadvantages:\n\nIf server crashes:\n\n```text\nFiles disappear\n```\n\n---\n\nIf we run:\n\n```text\nServer A\nServer B\nServer C\n```\n\nFiles are inconsistent.\n\n---\n\n# Option 2\n\nStore inside database.\n\nExample:\n\n```text\nMongoDB\n\nimage:\n\u003cbinary data\u003e\n```\n\n---\n\nPossible.\n\nBut:\n\n```text\nDatabase size grows rapidly.\n```\n\nUsually not recommended for large images.\n\n---\n\n# Option 3 (Industry Standard)\n\nStore files in cloud storage.\n\nExamples:\n\n* [Amazon S3](https://aws.amazon.com/s3/?utm_source=chatgpt.com)\n* [Cloudinary](https://cloudinary.com/?utm_source=chatgpt.com)\n* [Firebase Storage](https://firebase.google.com/products/storage?utm_source=chatgpt.com)\n\n---\n\nThe flow:\n\n```text\nBrowser\n\n↓\n\nExpress\n\n↓\n\nCloud Storage\n\n↓\n\nReceive URL\n\n↓\n\nSave URL to MongoDB\n```\n\n---\n\nDatabase:\n\n```json\n{\n  \"username\":\"Skyy\",\n\n  \"avatar\":\n  \"https://...\"\n}\n```\n\nNot the image itself.\n\n---\n\n# Image Upload Flow in MERN\n\nA very common architecture:\n\n```text\nReact\n\n↓\n\nSelect Image\n\n↓\n\nFormData\n\n↓\n\nAxios POST\n\n↓\n\nExpress\n\n↓\n\nMulter\n\n↓\n\nCloudinary\n\n↓\n\nImage URL\n\n↓\n\nMongoDB\n\n↓\n\nReturn Response\n```\n\n---\n\n# Why Use FormData?\n\nSuppose:\n\n```html\n\u003cinput type=\"file\" /\u003e\n```\n\nWe get:\n\n```js\nconst file = e.target.files[0]\n```\n\n---\n\nThen:\n\n```js\nconst formData = new FormData();\n\nformData.append(\n \"avatar\",\n file\n);\n```\n\n---\n\nAxios:\n\n```js\naxios.post(\n \"/upload\",\n formData\n)\n```\n\nBrowser automatically sets:\n\n```http\nmultipart/form-data\n```\n\n---\n\n# req.body vs req.file\n\nSuppose:\n\n```html\n\u003cinput name=\"name\"\u003e\n\n\u003cinput\ntype=\"file\"\nname=\"avatar\"\u003e\n```\n\n---\n\nAfter multer:\n\n```js\nreq.body\n```\n\ncontains:\n\n```js\n{\n name:\"Skyy\"\n}\n```\n\n---\n\nwhile:\n\n```js\nreq.file\n```\n\ncontains:\n\n```js\n{\n originalname:\"profile.jpg\"\n}\n```\n\n---\n\n# File Size Limits\n\nNever trust users.\n\nSomeone may upload:\n\n```text\n50GB video\n```\n\n---\n\nMulter allows:\n\n```js\nlimits:{\n fileSize:\n 1024*1024*5\n}\n```\n\nMeaning:\n\n```text\n5MB max\n```\n\n---\n\n# File Type Validation\n\nSuppose attacker uploads:\n\n```text\nvirus.exe\n```\n\nrenamed as:\n\n```text\nimage.jpg\n```\n\nDangerous.\n\n---\n\nTherefore:\n\n```js\nfile.mimetype\n```\n\nis checked.\n\nAllowed:\n\n```text\nimage/png\n\nimage/jpeg\n\nimage/webp\n```\n\nRejected:\n\n```text\napplication/exe\n```\n\n---\n\n# Why Images Are Special\n\nImages are often:\n\n```text\nCompressed\n\nResized\n\nOptimized\n\nConverted\n```\n\nExample:\n\nUser uploads:\n\n```text\n15MB PNG\n```\n\nServer converts:\n\n```text\n600KB WEBP\n```\n\nSaving:\n\n* Storage\n* Bandwidth\n* Loading time\n\n---\n\n# Signed URLs\n\nModern systems often don't let:\n\n```text\nBrowser\n\n↓\n\nExpress\n\n↓\n\nS3\n```\n\nInstead:\n\n```text\nBrowser\n\n↓\n\nAsk Express\n\n↓\n\nGet Signed URL\n\n↓\n\nUpload directly to S3\n```\n\n---\n\nAdvantages:\n\n```text\nServer handles zero image data.\n\nLess CPU\n\nLess RAM\n\nBetter scalability\n```\n\n---\n\n# Memory Storage vs Disk Storage\n\nMulter offers:\n\n---\n\n## Disk Storage\n\n```text\nImage\n\n↓\n\nuploads/\n\n↓\n\nFile Path\n```\n\n---\n\n## Memory Storage\n\n```text\nImage\n\n↓\n\nRAM Buffer\n\n↓\n\nCloudinary\n\n↓\n\nDelete Buffer\n```\n\n---\n\nCloud uploads often use:\n\n```text\nMemory Storage\n```\n\nbecause:\n\n```text\nNo temporary file\n```\n\nis created.\n\n---\n\n# Complete Mental Model\n\nWhen we upload an image:\n\n```text\nUser selects file\n\n↓\n\nBrowser creates FormData\n\n↓\n\nmultipart/form-data request\n\n↓\n\nExpress receives stream\n\n↓\n\nMulter parses stream\n\n↓\n\nSeparates text and files\n\n↓\n\nStores file\n\n↓\n\nCreates req.file\n\n↓\n\nController runs\n\n↓\n\nUpload to Cloudinary/S3\n\n↓\n\nGet URL\n\n↓\n\nSave URL to MongoDB\n\n↓\n\nSend response\n```\n\n# Industry Standard Today\n\nFor modern MERN applications, the most common architecture is:\n\n```text\nReact\n↓\nFormData\n↓\nExpress\n↓\nMulter (Memory Storage)\n↓\nCloudinary / Amazon S3\n↓\nMongoDB stores URL only\n```\n\nThis architecture is scalable, inexpensive, cloud-friendly, and is the approach we will encounter in many professional Node.js and Express applications.\n\nLet's study file uploading from the **code level**, but still understand *why* each piece exists. We'll use the most common setup:\n\n```text\nReact / HTML Form\n        ↓\nmultipart/form-data\n        ↓\nExpress\n        ↓\nMulter\n        ↓\nLocal Storage or Cloudinary\n        ↓\nMongoDB stores URL\n```\n\n---\n\n# 1. Install Multer\n\nFirst:\n\n```bash\nnpm install multer\n```\n\nWhy?\n\nBecause:\n\n```text\nexpress.json()\n```\n\ncan parse:\n\n```text\napplication/json\n```\n\nand\n\n```text\nexpress.urlencoded()\n```\n\ncan parse:\n\n```text\napplication/x-www-form-urlencoded\n```\n\nBut:\n\n```text\nmultipart/form-data\n```\n\ncontains:\n\n* binary images\n* videos\n* files\n* text fields\n\nExpress cannot parse this by itself.\n\nMulter is the parser.\n\n---\n\n# 2. Create Upload Middleware\n\nExample:\n\n```js\nimport multer from \"multer\";\n```\n\nNow:\n\n```js\nconst storage = multer.diskStorage({\n  destination: function (req, file, cb) {\n    cb(null, \"./uploads\");\n  },\n\n  filename: function (req, file, cb) {\n    cb(\n      null,\n      Date.now() + \"-\" + file.originalname\n    );\n  },\n});\n```\n\n---\n\n## What's happening here?\n\n### destination\n\n```js\ndestination(req,file,cb)\n```\n\ndetermines:\n\n```text\nWhere should we store the file?\n```\n\nExample:\n\n```text\nuploads/\n```\n\n---\n\n### cb()\n\nMulter uses:\n\n```js\ncb(error, result)\n```\n\nExample:\n\n```js\ncb(null, \"./uploads\");\n```\n\nmeans:\n\n```text\nNo error\n\nStore file in:\n./uploads\n```\n\n---\n\n# filename\n\nSuppose two users upload:\n\n```text\navatar.jpg\n```\n\nIf we store:\n\n```text\nuploads/avatar.jpg\n```\n\nsecond file overwrites first.\n\nBad.\n\n---\n\nSo:\n\n```js\nDate.now()\n```\n\ncreates:\n\n```text\n172000000-avatar.jpg\n```\n\nUnique filename.\n\n---\n\n# 3. Create Upload Instance\n\n```js\nconst upload = multer({\n  storage,\n});\n```\n\nThis creates middleware.\n\n---\n\n# 4. upload.single()\n\nExample:\n\n```js\nrouter.post(\n  \"/upload\",\n  upload.single(\"avatar\"),\n  uploadController\n);\n```\n\nQuestion:\n\nWhy `\"avatar\"`?\n\nBecause:\n\n```html\n\u003cinput\ntype=\"file\"\nname=\"avatar\"\n/\u003e\n```\n\nThe field names must match.\n\n---\n\nWhen request arrives:\n\n```text\nupload.single(\"avatar\")\n\n↓\n\nFind field named avatar\n\n↓\n\nStore image\n\n↓\n\nAttach to req.file\n\n↓\n\nCall controller\n```\n\n---\n\n# 5. req.file\n\nSuppose:\n\n```text\nprofile.jpg\n```\n\nwas uploaded.\n\nThen:\n\n```js\nconsole.log(req.file)\n```\n\nprints:\n\n```js\n{\n fieldname: \"avatar\",\n\n originalname:\n \"profile.jpg\",\n\n encoding: \"7bit\",\n\n mimetype:\n \"image/jpeg\",\n\n destination:\n \"./uploads\",\n\n filename:\n \"17238932-profile.jpg\",\n\n path:\n \"uploads/17238932-profile.jpg\",\n\n size:\n 54000\n}\n```\n\n---\n\nLet's understand each field.\n\n---\n\n### originalname\n\n```text\nUser uploaded:\n\nprofile.jpg\n```\n\n---\n\n### filename\n\nStored as:\n\n```text\n17238932-profile.jpg\n```\n\n---\n\n### path\n\nWhere file physically exists:\n\n```text\nuploads/17238932-profile.jpg\n```\n\n---\n\n### mimetype\n\nVery important:\n\n```text\nimage/jpeg\n```\n\nor\n\n```text\nimage/png\n```\n\nWe can validate uploads using this.\n\n---\n\n# 6. req.body still works\n\nSuppose:\n\n```html\n\u003cinput\nname=\"username\"\n/\u003e\n\n\u003cinput\ntype=\"file\"\nname=\"avatar\"\n/\u003e\n```\n\nAfter multer:\n\n```js\nreq.body\n```\n\ncontains:\n\n```js\n{\n username:\"Skyy\"\n}\n```\n\nwhile:\n\n```js\nreq.file\n```\n\ncontains:\n\n```js\n{\n originalname:\n \"profile.jpg\"\n}\n```\n\n---\n\n# 7. upload.array()\n\nSuppose:\n\n```html\n\u003cinput\nmultiple\nname=\"photos\"\n/\u003e\n```\n\nUser uploads:\n\n```text\n1.jpg\n\n2.jpg\n\n3.jpg\n```\n\n---\n\nServer:\n\n```js\nupload.array(\"photos\",5)\n```\n\nMeaning:\n\n```text\nField:\n\nphotos\n\nMaximum:\n\n5 files\n```\n\n---\n\nResult:\n\n```js\nreq.files\n```\n\ncontains:\n\n```js\n[\n {...},\n\n {...},\n\n {...}\n]\n```\n\n---\n\n# 8. upload.fields()\n\nSuppose:\n\nOur form:\n\n```text\navatar\n\nresume\n\ncoverImage\n```\n\n---\n\nServer:\n\n```js\nupload.fields([\n {\n  name:\"avatar\",\n  maxCount:1\n },\n\n {\n  name:\"resume\",\n  maxCount:1\n },\n\n {\n  name:\"coverImage\",\n  maxCount:1\n }\n])\n```\n\n---\n\nResult:\n\n```js\nreq.files.avatar\n\nreq.files.resume\n\nreq.files.coverImage\n```\n\n---\n\n# 9. File Size Limits\n\nNever trust uploads.\n\nSomeone may upload:\n\n```text\n10GB movie\n```\n\nServer RAM dies.\n\n---\n\nMulter:\n\n```js\nconst upload = multer({\n\n storage,\n\n limits: {\n\n  fileSize:\n\n  5*1024*1024\n\n }\n\n});\n```\n\n---\n\nThis means:\n\n```text\n5MB max\n```\n\n---\n\nIf exceeded:\n\nMulter throws:\n\n```text\nLIMIT_FILE_SIZE\n```\n\n---\n\n# 10. File Type Validation\n\nSuppose attacker uploads:\n\n```text\nvirus.exe\n```\n\nrenamed:\n\n```text\ncat.jpg\n```\n\nDangerous.\n\n---\n\nWe use:\n\n```js\nfileFilter\n```\n\n---\n\nExample:\n\n```js\nconst upload = multer({\n\n storage,\n\n fileFilter:\n\n (req,file,cb)=\u003e{\n\n  if(\n\n   file.mimetype ===\n\n   \"image/jpeg\"\n\n  ){\n\n   cb(null,true);\n\n  }\n\n  else{\n\n   cb(\n\n    new Error(\n\n     \"Only images\"\n\n    )\n\n   );\n\n  }\n\n }\n\n})\n```\n\n---\n\nAllowed:\n\n```text\nimage/png\n\nimage/jpeg\n\nimage/webp\n```\n\nRejected:\n\n```text\napplication/exe\n```\n\n---\n\n# 11. Cloudinary Upload\n\nIn production:\n\nWe usually don't keep:\n\n```text\nuploads/\n```\n\nbecause:\n\n```text\nServer restarts\n\n↓\n\nFiles disappear\n```\n\n---\n\nInstead:\n\n```text\nExpress\n\n↓\n\nCloudinary\n\n↓\n\nImage URL\n```\n\n---\n\nCode:\n\n```js\nconst result =\n\nawait cloudinary.uploader.upload(\n\n req.file.path\n\n)\n```\n\n---\n\nCloudinary returns:\n\n```js\n{\n public_id:\n\n \"abc123\",\n\n secure_url:\n\n \"https://...\"\n}\n```\n\n---\n\nThen:\n\n```js\nawait User.create({\n\n avatar:\n\n result.secure_url\n\n})\n```\n\n---\n\nMongoDB stores:\n\n```json\n{\n \"avatar\":\n\n\"https://res.cloudinary...\"\n}\n```\n\n---\n\nNot:\n\n```text\nBinary image\n```\n\nOnly:\n\n```text\nURL\n```\n\n---\n\n# 12. Memory Storage\n\nInstead of:\n\n```text\nImage\n\n↓\n\nuploads folder\n\n↓\n\nCloudinary\n```\n\nwe can do:\n\n```text\nImage\n\n↓\n\nRAM Buffer\n\n↓\n\nCloudinary\n```\n\n---\n\nCode:\n\n```js\nconst storage =\n\nmulter.memoryStorage();\n```\n\n---\n\nThen:\n\n```js\nreq.file.buffer\n```\n\ncontains:\n\n```text\nRaw binary image\n```\n\ninside RAM.\n\n---\n\nVery common with:\n\n```text\nCloudinary\n\nAmazon S3\n\nFirebase Storage\n```\n\n---\n\n# 13. Serving Static Images\n\nSuppose:\n\n```text\nuploads/\n\nprofile.jpg\n```\n\nWe want:\n\n```text\nlocalhost:3000/profile.jpg\n```\n\n---\n\nExpress:\n\n```js\napp.use(\n\n express.static(\"uploads\")\n\n)\n```\n\n---\n\nNow:\n\n```text\nGET\n\n/profile.jpg\n```\n\nreturns:\n\n```text\nActual image\n```\n\n---\n\n# 14. React Side\n\nSuppose:\n\n```html\n\u003cinput type=\"file\"/\u003e\n```\n\n---\n\nWe get:\n\n```js\nconst file\n\n=\n\ne.target.files[0]\n```\n\n---\n\nCreate:\n\n```js\nconst formData\n\n=\n\nnew FormData()\n```\n\n---\n\nAppend:\n\n```js\nformData.append(\n\n \"avatar\",\n\n file\n\n)\n```\n\n---\n\nSend:\n\n```js\nawait axios.post(\n\n \"/upload\",\n\n formData\n\n)\n```\n\n---\n\nDo NOT do:\n\n```js\nheaders:{\n\n'Content-Type':\n\n'multipart/form-data'\n\n}\n```\n\nAxios and the browser automatically generate:\n\n```text\nmultipart/form-data\n\n+\n\nboundary\n```\n\nfor us.\n\n---\n\n# The Entire Request Lifecycle\n\n```text\nChoose image\n\n↓\n\nBrowser File Object\n\n↓\n\nFormData\n\n↓\n\nmultipart/form-data\n\n↓\n\nExpress\n\n↓\n\nMulter middleware\n\n↓\n\nreq.file\n\n↓\n\nCloudinary upload\n\n↓\n\nReceive secure_url\n\n↓\n\nStore URL in MongoDB\n\n↓\n\nReturn response\n\n↓\n\nReact displays image\n```\n\n# A Production MERN Upload Architecture\n\nMost professional MERN applications eventually evolve toward:\n\n```text\nReact\n\n↓\n\nFormData\n\n↓\n\nAxios\n\n↓\n\nExpress\n\n↓\n\nMulter Memory Storage\n\n↓\n\nCloudinary / S3\n\n↓\n\nMongoDB stores image URL only\n\n↓\n\nFrontend renders URL\n```\n\nThis is the architecture used in countless production applications because it avoids storing massive binary files in MongoDB, scales horizontally, and keeps our Node.js server lightweight and efficient.\n\n---\n\n# Multer Vs Express-fileupload 📁\n\nThis is an excellent question because many beginners see both **Multer** and **express-fileupload** and wonder why both exist.\n\nThe short answer is:\n\n\u003e We usually **don't need both**. They solve the same problem in different ways.\n\n---\n\n# The Problem Both Libraries Solve\n\nSuppose our frontend sends:\n\n```html\n\u003cinput type=\"file\" name=\"image\" /\u003e\n```\n\nThe browser sends:\n\n```http\nContent-Type: multipart/form-data\n```\n\nalong with the file bytes.\n\n---\n\nExpress can parse:\n\n```js\napp.use(express.json());\n```\n\nfor:\n\n```text\napplication/json\n```\n\nand:\n\n```js\napp.use(express.urlencoded({ extended: true }));\n```\n\nfor:\n\n```text\napplication/x-www-form-urlencoded\n```\n\nBut Express **cannot parse**:\n\n```text\nmultipart/form-data\n```\n\nwhich is used for:\n\n* Images\n* PDFs\n* Videos\n* ZIP files\n\nTherefore we need a middleware.\n\n---\n\n# Option 1: express-fileupload\n\nInstall:\n\n```bash\nnpm install express-fileupload\n```\n\nUse:\n\n```js\nimport fileUpload from \"express-fileupload\";\n\napp.use(fileUpload());\n```\n\nNow:\n\n```js\nreq.files\n```\n\nbecomes available automatically.\n\n---\n\nSuppose:\n\n```html\n\u003cinput type=\"file\" name=\"image\" /\u003e\n```\n\nThen:\n\n```js\nconsole.log(req.files);\n```\n\nmight show:\n\n```js\n{\n  image: {\n    name: \"cat.jpg\",\n\n    mimetype: \"image/jpeg\",\n\n    size: 45213,\n\n    data: \u003cBuffer ...\u003e\n  }\n}\n```\n\n---\n\n# Saving the File\n\nWith express-fileupload:\n\n```js\nawait req.files.image.mv(\n  \"./uploads/cat.jpg\"\n);\n```\n\nThe `.mv()` method means:\n\n```text\nMove uploaded file\nto another location.\n```\n\n---\n\n# Example\n\n```js\nconst uploadImage = async (req,res)=\u003e{\n\n  const image = req.files.image;\n\n  await image.mv(\n    \"./uploads/\" + image.name\n  );\n\n  res.send(\"Uploaded\");\n}\n```\n\nVery simple.\n\n---\n\n# Why Do Tutorials Use express-fileupload?\n\nBecause:\n\n```text\nEasy to learn\n```\n\nA beginner can upload files in:\n\n```text\n5 minutes\n```\n\nwithout learning:\n\n* Storage engines\n* Streams\n* Buffers\n* DiskStorage\n* MemoryStorage\n\n---\n\n# But There Are Limitations\n\nImagine users upload:\n\n```text\n50 MB\n\n100 MB\n\n500 MB videos\n```\n\n---\n\nexpress-fileupload often:\n\n```text\nLoads file into memory first.\n```\n\nMeaning:\n\n```text\nRequest\n\n↓\n\nRAM\n\n↓\n\nDisk\n```\n\nLarge files consume memory.\n\n---\n\nIf:\n\n```text\n100 users\n\n×\n\n100MB uploads\n```\n\nServer RAM can explode.\n\n---\n\n# Option 2: Multer\n\nMulter is more advanced.\n\nInstead of:\n\n```text\nWhole file into RAM\n```\n\nMulter can:\n\n```text\nReceive stream\n\n↓\n\nWrite directly to disk\n\n↓\n\nContinue receiving\n```\n\nThis is:\n\n```text\nStreaming\n```\n\nMuch more efficient.\n\n---\n\n# Multer Architecture\n\n```text\nBrowser\n\n↓\n\nmultipart request\n\n↓\n\nMulter\n\n↓\n\nDiskStorage\n\n↓\n\nuploads/\n\n↓\n\nController\n```\n\n---\n\nOr:\n\n```text\nBrowser\n\n↓\n\nMulter\n\n↓\n\nMemory Buffer\n\n↓\n\nCloudinary\n\n↓\n\nMongoDB URL\n```\n\n---\n\n# Why Production Apps Prefer Multer\n\nMulter gives us:\n\n### 1. Single File\n\n```js\nupload.single(\"avatar\")\n```\n\n---\n\n### 2. Multiple Files\n\n```js\nupload.array(\"images\",5)\n```\n\n---\n\n### 3. Different File Fields\n\n```js\nupload.fields([\n {name:\"avatar\"},\n {name:\"resume\"}\n])\n```\n\n---\n\n### 4. File Limits\n\n```js\nlimits:{\n\n fileSize:\n\n 5*1024*1024\n\n}\n```\n\n5MB maximum.\n\n---\n\n### 5. File Type Validation\n\n```js\nfileFilter\n```\n\nExample:\n\n```js\nif(\n file.mimetype===\"image/jpeg\"\n)\n```\n\nReject:\n\n```text\nvirus.exe\n```\n\nPretending to be:\n\n```text\ncat.jpg\n```\n\n---\n\n# Why express-fileupload Is Still Popular\n\nBecause this:\n\n```js\nconst image = req.files.image;\n\nawait image.mv(\"./uploads/\" + image.name);\n```\n\nis incredibly easy.\n\n---\n\nNo:\n\n```text\nstorage\n\nmemoryStorage\n\ndiskStorage\n\nfileFilter\n\nupload.single\n```\n\n---\n\nMany small tutorials and prototypes use it.\n\n---\n\n# Why Some E-commerce Courses Use express-fileupload\n\nBecause the instructor wants to teach:\n\n```text\nProducts\n\nOrders\n\nJWT\n\nMongoDB\n\nRedux\n```\n\nand not spend:\n\n```text\n2 hours\n```\n\nexplaining:\n\n```text\nStreams\n\nBuffers\n\nStorage Engines\n\nMultipart Parsing\n```\n\n---\n\n# express-fileupload vs Multer\n\n| Feature              | express-fileupload | Multer     |\n| -------------------- | ------------------ | ---------- |\n| Beginner Friendly    | ✅ Very             | Moderate   |\n| Single File          | ✅                  | ✅          |\n| Multiple Files       | ✅                  | ✅          |\n| Memory Efficient     | ❌                  | ✅          |\n| Streams              | ❌                  | ✅          |\n| Cloud Uploads        | Possible           | Excellent  |\n| Production Ready     | Small apps         | Large apps |\n| Fine-Grained Control | Limited            | Excellent  |\n\n---\n\n# Which One Should We Learn?\n\nSince we are learning:\n\n* Node.js\n* Express\n* MERN\n* Professional backend development\n\nI would recommend:\n\n```text\nMulter\n```\n\nbecause:\n\n```text\nMulter\n↓\n\nMemory Storage\n\n↓\n\nCloudinary\n\n↓\n\nMongoDB stores URL\n\n↓\n\nReact displays image\n```\n\nThis architecture is extremely common in professional MERN applications.\n\n---\n\n# Then Why Learn express-fileupload at All?\n\nBecause we may encounter older tutorials or codebases that use:\n\n```js\napp.use(fileUpload())\n```\n\nand:\n\n```js\nreq.files.image.mv(...)\n```\n\nUnderstanding it helps us read and maintain those projects, even if for new projects we would usually prefer Multer with cloud storage.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiamskyy666%2Ffile-upload-nodejs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiamskyy666%2Ffile-upload-nodejs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiamskyy666%2Ffile-upload-nodejs/lists"}