Skip to content
BackendExpressJsNodeJs

9. Express File Upload Multer Tutorial | Image PDF Hindi 2026

April 25, 2026 21 min read

नमस्ते दोस्तों! 🙏
स्वागत है The Easy Master पर!

क्या तुमने कभी सोचा है – Express.js app mein user profile picture, PDF, ya document kaise upload karein?

Jab users image upload karte hain (WhatsApp, Instagram, Gmail), toh backend ko files receive karni padti हैं। Node.js by default files handle nahi karta – iske liye Multer middleware use karte hain।

Express file upload Multer Hindi में समझना बहुत जरूरी है क्योंकि:

  • Profile pictures, documents, PDFs – sab upload karne padte hain
  • Every real-world app mein file upload feature hota है
  • Multer is the most popular file upload library (2M+ weekly downloads)
  • Interview mein pakka file upload questions puche jayenge

Aaj kya seekhoge?

TopicKya Seekhega?
Multer Kya Hai?Introduction aur setup
Single File UploadEk file upload
Multiple Files UploadEk saath multiple files
File ValidationType, size, extension check
Storage OptionsDiskStorage, MemoryStorage
Error HandlingUpload errors handle karna
Image ProcessingSharp se resize/compress
Real ProjectProfile picture upload API

Kya tumhe pata hai?
Multer multipart/form-data format handle करता है – jo HTML forms mein file upload के लिए use होता है!

तो चलिए शुरू करते हैं – Express file upload Multer Hindi सीखने का सफर! 🚀

Table of Contents

1. File Upload क्या Hai? – Introduction

File upload client se server tak files bhejne की process है।

Without Multer (Hard Way):

Code
// ❌ Raw Node.js – very complex!
const http = require('http');
const fs = require('fs');
const formidable = require('formidable'); // alternative

// Lots of manual parsing...

With Multer (Easy Way):

Code
// ✅ Multer – simple & clean!
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });

app.post('/upload', upload.single('image'), (req, res) => {
  res.json({ file: req.file });
});

Express file upload Multer Hindi में हम complete setup सीखेंगे।

2. Multer Setup – Installation aur Configuration

Step 1: Install Multer

Code
npm install multer

Step 2: Basic Setup

Code
// server.js
const express = require('express');
const multer = require('multer');
const path = require('path');

const app = express();

// Configure storage
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/'); // Where to save files
  },
  filename: (req, file, cb) => {
    // Create unique filename: timestamp-originalname
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    cb(null, uniqueSuffix + path.extname(file.originalname));
  }
});

// File filter (optional)
const fileFilter = (req, file, cb) => {
  // Accept images only
  if (file.mimetype.startsWith('image/')) {
    cb(null, true);
  } else {
    cb(new Error('Only images are allowed!'), false);
  }
};

// Create multer instance
const upload = multer({
  storage: storage,
  limits: {
    fileSize: 5 * 1024 * 1024 // 5MB limit
  },
  fileFilter: fileFilter
});

// Create uploads folder if not exists
const fs = require('fs');
if (!fs.existsSync('uploads')) {
  fs.mkdirSync('uploads');
}

// Routes
app.post('/upload', upload.single('image'), (req, res) => {
  res.json({
    message: 'File uploaded successfully!',
    file: req.file
  });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Step 3: HTML Form for Testing

Code
<!-- public/upload.html -->
<!DOCTYPE html>
<html>
<head>
    <title>File Upload</title>
    <style>
        body { font-family: Arial; padding: 50px; }
        .container { max-width: 500px; margin: auto; }
        input, button { margin: 10px 0; padding: 10px; }
        button { background: #61DAFB; border: none; cursor: pointer; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Upload File</h1>
        <form action="/upload" method="POST" enctype="multipart/form-data">
            <input type="file" name="image" accept="image/*" required>
            <br>
            <button type="submit">Upload</button>
        </form>
    </div>
</body>
</html>

3. Single File Upload – Ek File Upload Karna

Basic Single File Upload:

Code
// server.js
const express = require('express');
const multer = require('multer');
const path = require('path');

const app = express();

// Simple config (no custom filename)
const upload = multer({ dest: 'uploads/' });

// Single file upload - field name = 'file'
app.post('/upload', upload.single('file'), (req, res) => {
  // req.file contains uploaded file info
  // req.body contains text fields
  
  console.log('File:', req.file);
  console.log('Body:', req.body);
  
  res.json({
    success: true,
    message: 'File uploaded successfully!',
    file: {
      originalName: req.file.originalname,
      size: req.file.size,
      path: req.file.path,
      mimetype: req.file.mimetype
    }
  });
});

app.listen(3000);

Complete Single Upload with Custom Config:

Code
// config/multer.js
const multer = require('multer');
const path = require('path');

// Custom storage configuration
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    let folder = 'uploads/';
    
    // Different folders for different file types
    if (file.mimetype.startsWith('image/')) {
      folder = 'uploads/images/';
    } else if (file.mimetype === 'application/pdf') {
      folder = 'uploads/pdfs/';
    } else {
      folder = 'uploads/documents/';
    }
    
    cb(null, folder);
  },
  filename: (req, file, cb) => {
    // Create unique filename
    const timestamp = Date.now();
    const random = Math.round(Math.random() * 1E9);
    const ext = path.extname(file.originalname);
    const basename = path.basename(file.originalname, ext);
    
    // Sanitize filename (remove spaces, special chars)
    const sanitized = basename.replace(/[^a-zA-Z0-9]/g, '_');
    const filename = `${timestamp}-${random}-${sanitized}${ext}`;
    
    cb(null, filename);
  }
});

// File filter
const fileFilter = (req, file, cb) => {
  const allowedTypes = [
    'image/jpeg',
    'image/png',
    'image/gif',
    'image/webp',
    'application/pdf',
    'application/msword',
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
  ];
  
  if (allowedTypes.includes(file.mimetype)) {
    cb(null, true);
  } else {
    cb(new Error(`File type ${file.mimetype} is not supported`), false);
  }
};

// Multer instance
const upload = multer({
  storage: storage,
  limits: {
    fileSize: 10 * 1024 * 1024 // 10MB
  },
  fileFilter: fileFilter
});

module.exports = upload;
Code
// routes/upload.js
const express = require('express');
const router = express.Router();
const upload = require('../config/multer');
const fs = require('fs');
const path = require('path');

// Ensure upload directories exist
const createDir = (dir) => {
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
};

createDir('uploads/images');
createDir('uploads/pdfs');
createDir('uploads/documents');

// Single file upload
router.post('/file', upload.single('file'), (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).json({ error: 'No file uploaded' });
    }
    
    res.status(201).json({
      success: true,
      message: 'File uploaded successfully',
      data: {
        originalName: req.file.originalname,
        filename: req.file.filename,
        size: req.file.size,
        mimetype: req.file.mimetype,
        path: req.file.path,
        url: `/uploads/${path.basename(req.file.path)}`
      }
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// File with additional form data
router.post('/profile', upload.single('avatar'), (req, res) => {
  const { name, email } = req.body;
  const avatar = req.file;
  
  if (!avatar) {
    return res.status(400).json({ error: 'Avatar is required' });
  }
  
  res.json({
    success: true,
    message: 'Profile updated',
    data: {
      user: { name, email },
      avatar: {
        filename: avatar.filename,
        url: `/uploads/${avatar.filename}`
      }
    }
  });
});

module.exports = router;

4. Multiple Files Upload – Ek Saath Multiple Files

Upload Multiple Files (Same field):

Code
// Multiple files with same field name
router.post('/multiple', upload.array('files', 5), (req, res) => {
  // req.files is array of files
  // Max 5 files
  
  if (!req.files || req.files.length === 0) {
    return res.status(400).json({ error: 'No files uploaded' });
  }
  
  const files = req.files.map(file => ({
    originalName: file.originalname,
    filename: file.filename,
    size: file.size,
    mimetype: file.mimetype,
    url: `/uploads/${file.filename}`
  }));
  
  res.json({
    success: true,
    message: `${files.length} files uploaded successfully`,
    data: { files }
  });
});

Upload Multiple Files (Different fields):

Code
// Different fields for different files
router.post('/documents', upload.fields([
  { name: 'avatar', maxCount: 1 },
  { name: 'resume', maxCount: 1 },
  { name: 'certificates', maxCount: 5 }
]), (req, res) => {
  // req.files is object: { avatar: [...], resume: [...], certificates: [...] }
  
  const response = {
    avatar: req.files.avatar ? req.files.avatar[0] : null,
    resume: req.files.resume ? req.files.resume[0] : null,
    certificates: req.files.certificates || []
  };
  
  res.json({
    success: true,
    message: 'Documents uploaded successfully',
    data: response
  });
});

HTML Form for Multiple Files:

Code
<!-- public/multiple-upload.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Multiple File Upload</title>
</head>
<body>
    <h1>Upload Multiple Files</h1>
    
    <!-- Multiple files same field -->
    <form action="/upload/multiple" method="POST" enctype="multipart/form-data">
        <h3>Upload Multiple Images</h3>
        <input type="file" name="files" multiple accept="image/*">
        <button type="submit">Upload</button>
    </form>
    
    <!-- Different fields -->
    <form action="/upload/documents" method="POST" enctype="multipart/form-data">
        <h3>Upload Documents</h3>
        <input type="file" name="avatar" accept="image/*" required>
        <input type="file" name="resume" accept=".pdf,.doc,.docx" required>
        <input type="file" name="certificates" multiple accept=".pdf">
        <button type="submit">Upload</button>
    </form>
</body>
</html>

5. File Validation – Type, Size, Extension Check

Complete Validation Middleware:

Code
// middleware/uploadValidation.js
const multer = require('multer');
const path = require('path');

// File type validation
const fileFilter = (req, file, cb) => {
  const allowedImageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
  const allowedDocTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
  
  const isImage = allowedImageTypes.includes(file.mimetype);
  const isDocument = allowedDocTypes.includes(file.mimetype);
  
  if (req.query.type === 'image' && isImage) {
    cb(null, true);
  } else if (req.query.type === 'document' && isDocument) {
    cb(null, true);
  } else if (!req.query.type && (isImage || isDocument)) {
    cb(null, true);
  } else {
    cb(new Error(`Invalid file type. Allowed: ${req.query.type === 'image' ? 'JPEG, PNG, GIF, WEBP' : 'PDF, DOC, DOCX'}`), false);
  }
};

// Custom validation function
const validateFile = (req, res, next) => {
  if (!req.file && !req.files) {
    return res.status(400).json({ error: 'No file uploaded' });
  }
  
  // Check file size (already handled by multer limits)
  
  // Check for viruses (can integrate ClamAV)
  
  next();
};

module.exports = { fileFilter, validateFile };

Size Validation:

Code
// config/multer.js
const multer = require('multer');

// Size limits for different file types
const sizeLimits = {
  image: 5 * 1024 * 1024,    // 5MB
  document: 10 * 1024 * 1024, // 10MB
  video: 50 * 1024 * 1024,    // 50MB
  default: 5 * 1024 * 1024     // 5MB
};

const getSizeLimit = (req) => {
  const type = req.query.type || 'default';
  return sizeLimits[type] || sizeLimits.default;
};

const upload = multer({
  storage: storage,
  limits: {
    fileSize: (req, file, cb) => {
      const limit = getSizeLimit(req);
      cb(null, limit);
    }
  },
  fileFilter: fileFilter
});

Extension Validation:

Code
// utils/fileValidation.js
const path = require('path');

const allowedExtensions = {
  images: ['.jpg', '.jpeg', '.png', '.gif', '.webp'],
  documents: ['.pdf', '.doc', '.docx', '.txt'],
  archives: ['.zip', '.rar', '.7z']
};

const validateExtension = (filename, type = 'images') => {
  const ext = path.extname(filename).toLowerCase();
  return allowedExtensions[type].includes(ext);
};

const getFileType = (mimetype) => {
  if (mimetype.startsWith('image/')) return 'images';
  if (mimetype === 'application/pdf') return 'documents';
  if (mimetype.includes('word')) return 'documents';
  if (mimetype.includes('zip')) return 'archives';
  return 'other';
};

module.exports = { validateExtension, getFileType, allowedExtensions };

6. Storage Options – DiskStorage vs MemoryStorage

DiskStorage (Save to Disk):

Code
const multer = require('multer');
const path = require('path');

const diskStorage = multer.diskStorage({
  destination: (req, file, cb) => {
    let dest = 'uploads/';
    
    // Dynamic destination based on file type
    if (file.mimetype.startsWith('image/')) {
      dest = 'uploads/images/';
    } else if (file.mimetype === 'application/pdf') {
      dest = 'uploads/pdfs/';
    }
    
    cb(null, dest);
  },
  filename: (req, file, cb) => {
    // Custom filename
    const unique = Date.now() + '-' + Math.round(Math.random() * 1E9);
    const ext = path.extname(file.originalname);
    cb(null, unique + ext);
  }
});

const uploadDisk = multer({ storage: diskStorage });

MemoryStorage (Keep in Memory – for Cloud Upload):

Code
const multer = require('multer');

// Store file in memory (buffer)
const memoryStorage = multer.memoryStorage();

const uploadMemory = multer({ 
  storage: memoryStorage,
  limits: { fileSize: 5 * 1024 * 1024 }
});

// Upload to cloud (AWS S3, Cloudinary, etc.)
router.post('/upload-cloud', uploadMemory.single('image'), async (req, res) => {
  try {
    const file = req.file;
    
    // Upload to Cloudinary
    const cloudinary = require('cloudinary').v2;
    const result = await cloudinary.uploader.upload_stream(
      { folder: 'myapp' },
      (error, result) => {
        if (error) throw error;
        res.json({ url: result.secure_url });
      }
    ).end(file.buffer);
    
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

DiskStorage vs MemoryStorage:

FeatureDiskStorageMemoryStorage
StorageSaves to diskKeeps in RAM (buffer)
Use CaseLocal development, direct servingCloud upload, processing
Memory UsageLowHigh (files in RAM)
SpeedFastFaster (no disk write)
PersistenceYes (files saved)No (need to save manually)
Best forDirect file servingCloud upload, image processing

7. Error Handling – Upload Errors Handle Karna

Multer Error Types:

Code
// server.js
const multer = require('multer');

const upload = multer({
  dest: 'uploads/',
  limits: {
    fileSize: 5 * 1024 * 1024 // 5MB
  },
  fileFilter: (req, file, cb) => {
    if (file.mimetype.startsWith('image/')) {
      cb(null, true);
    } else {
      cb(new Error('Only images are allowed!'), false);
    }
  }
});

// Error handling middleware for multer
app.post('/upload', (req, res) => {
  upload.single('image')(req, res, (err) => {
    if (err instanceof multer.MulterError) {
      // Multer-specific errors
      if (err.code === 'FILE_TOO_LARGE') {
        return res.status(400).json({ 
          error: 'File too large! Maximum size is 5MB' 
        });
      }
      if (err.code === 'LIMIT_FILE_COUNT') {
        return res.status(400).json({ 
          error: 'Too many files!' 
        });
      }
      if (err.code === 'LIMIT_UNEXPECTED_FILE') {
        return res.status(400).json({ 
          error: 'Unexpected field name!' 
        });
      }
      return res.status(400).json({ error: err.message });
    }
    
    if (err) {
      // Custom errors from fileFilter
      return res.status(400).json({ error: err.message });
    }
    
    if (!req.file) {
      return res.status(400).json({ error: 'No file uploaded!' });
    }
    
    res.json({ success: true, file: req.file });
  });
});

Global Error Handler:

Code
// middleware/errorHandler.js
const errorHandler = (err, req, res, next) => {
  // Multer errors
  if (err instanceof multer.MulterError) {
    const errors = {
      'LIMIT_FILE_SIZE': 'File too large. Maximum size is 5MB.',
      'LIMIT_FILE_COUNT': 'Too many files.',
      'LIMIT_UNEXPECTED_FILE': 'Unexpected field name.',
      'LIMIT_PART_COUNT': 'Too many parts.',
      'LIMIT_FIELD_KEY': 'Field name too long.',
      'LIMIT_FIELD_VALUE': 'Field value too long.',
      'LIMIT_FIELD_COUNT': 'Too many fields.',
      'MIME_TYPE_NOT_SUPPORTED': 'File type not supported.'
    };
    
    return res.status(400).json({
      success: false,
      error: errors[err.code] || err.message,
      code: err.code
    });
  }
  
  // Custom validation errors
  if (err.message === 'Only images are allowed!') {
    return res.status(400).json({
      success: false,
      error: err.message
    });
  }
  
  // Default error
  res.status(500).json({
    success: false,
    error: 'Internal server error'
  });
};

module.exports = errorHandler;

8. Image Processing – Sharp Se Resize/Compress

Install Sharp:

Code
npm install sharp

Image Processing Middleware:

Code
// middleware/imageProcessor.js
const sharp = require('sharp');
const path = require('path');
const fs = require('fs');

const processImage = async (req, res, next) => {
  if (!req.file) {
    return next();
  }
  
  try {
    const filePath = req.file.path;
    const ext = path.extname(req.file.originalname);
    const dir = path.dirname(filePath);
    const basename = path.basename(filePath, ext);
    
    // Create different sizes
    const sizes = {
      thumbnail: 150,
      small: 300,
      medium: 600,
      large: 1200
    };
    
    for (const [size, width] of Object.entries(sizes)) {
      const outputPath = path.join(dir, `${basename}-${size}${ext}`);
      
      await sharp(filePath)
        .resize(width, null, { // width, height=auto
          withoutEnlargement: true,
          fit: 'cover'
        })
        .jpeg({ quality: 80 })
        .toFile(outputPath);
    }
    
    // Compress original
    const compressedPath = path.join(dir, `${basename}-compressed${ext}`);
    await sharp(filePath)
      .jpeg({ quality: 70 })
      .png({ quality: 70 })
      .toFile(compressedPath);
    
    req.processedImages = {
      original: req.file.path,
      compressed: compressedPath,
      thumbnail: path.join(dir, `${basename}-thumbnail${ext}`),
      small: path.join(dir, `${basename}-small${ext}`),
      medium: path.join(dir, `${basename}-medium${ext}`),
      large: path.join(dir, `${basename}-large${ext}`)
    };
    
    next();
  } catch (error) {
    console.error('Image processing error:', error);
    next(error);
  }
};

module.exports = processImage;

Using Image Processor:

Code
// routes/image.js
const express = require('express');
const router = express.Router();
const multer = require('multer');
const upload = multer({ dest: 'uploads/temp/' });
const processImage = require('../middleware/imageProcessor');

router.post('/upload', upload.single('image'), processImage, (req, res) => {
  res.json({
    success: true,
    message: 'Image uploaded and processed successfully',
    data: {
      original: `/uploads/${path.basename(req.processedImages.original)}`,
      compressed: `/uploads/${path.basename(req.processedImages.compressed)}`,
      thumbnail: `/uploads/${path.basename(req.processedImages.thumbnail)}`,
      small: `/uploads/${path.basename(req.processedImages.small)}`,
      medium: `/uploads/${path.basename(req.processedImages.medium)}`,
      large: `/uploads/${path.basename(req.processedImages.large)}`
    }
  });
});

module.exports = router;

9. File Download – Uploaded Files Serve Karna

Serve Static Files:

Code
// server.js
const express = require('express');
const path = require('path');

// Serve uploaded files statically
app.use('/uploads', express.static('uploads'));

// Now files can be accessed at:
// http://localhost:3000/uploads/filename.jpg

Download Endpoint:

Code
// routes/download.js
const express = require('express');
const router = express.Router();
const path = require('path');
const fs = require('fs');

// Download file by filename
router.get('/file/:filename', (req, res) => {
  const filename = req.params.filename;
  const filepath = path.join(__dirname, '../uploads', filename);
  
  // Check if file exists
  if (!fs.existsSync(filepath)) {
    return res.status(404).json({ error: 'File not found' });
  }
  
  // Download with original name (from database)
  res.download(filepath, filename);
});

// Download with custom name
router.get('/download/:filename', (req, res) => {
  const filename = req.params.filename;
  const filepath = path.join(__dirname, '../uploads', filename);
  
  res.download(filepath, 'custom-name-' + filename, (err) => {
    if (err) {
      console.error('Download error:', err);
      res.status(500).json({ error: 'Download failed' });
    }
  });
});

// Get file info
router.get('/info/:filename', (req, res) => {
  const filename = req.params.filename;
  const filepath = path.join(__dirname, '../uploads', filename);
  
  if (!fs.existsSync(filepath)) {
    return res.status(404).json({ error: 'File not found' });
  }
  
  const stats = fs.statSync(filepath);
  
  res.json({
    filename: filename,
    size: stats.size,
    created: stats.birthtime,
    modified: stats.mtime,
    url: `/uploads/${filename}`
  });
});

module.exports = router;

10. Real-world Project – Profile Picture Upload API

Complete Profile Picture Upload System:

Code
// config/multer.js
const multer = require('multer');
const path = require('path');
const fs = require('fs');

// Ensure upload directory exists
const uploadDir = 'uploads/avatars';
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir, { recursive: true });
}

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, uploadDir);
  },
  filename: (req, res, cb) => {
    const userId = req.user?.id || 'anonymous';
    const timestamp = Date.now();
    const ext = path.extname(file.originalname);
    cb(null, `user-${userId}-${timestamp}${ext}`);
  }
});

const fileFilter = (req, file, cb) => {
  const allowed = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
  
  if (allowed.includes(file.mimetype)) {
    cb(null, true);
  } else {
    cb(new Error('Only JPEG, PNG, GIF, and WEBP images are allowed'), false);
  }
};

const upload = multer({
  storage: storage,
  limits: {
    fileSize: 2 * 1024 * 1024 // 2MB
  },
  fileFilter: fileFilter
});

module.exports = upload;
Code
// middleware/avatarProcessor.js
const sharp = require('sharp');
const path = require('path');
const fs = require('fs');

const processAvatar = async (req, res, next) => {
  if (!req.file) {
    return next();
  }
  
  try {
    const filePath = req.file.path;
    const dir = path.dirname(filePath);
    const basename = path.basename(filePath, path.extname(filePath));
    const ext = '.jpg'; // Convert all to JPG for consistency
    
    // Create optimized avatar
    const optimizedPath = path.join(dir, `${basename}-optimized${ext}`);
    
    await sharp(filePath)
      .resize(400, 400, {
        fit: 'cover',
        position: 'center'
      })
      .jpeg({ quality: 85 })
      .toFile(optimizedPath);
    
    // Create thumbnail
    const thumbnailPath = path.join(dir, `${basename}-thumb${ext}`);
    
    await sharp(filePath)
      .resize(100, 100, {
        fit: 'cover',
        position: 'center'
      })
      .jpeg({ quality: 70 })
      .toFile(thumbnailPath);
    
    // Delete original file
    fs.unlinkSync(filePath);
    
    req.avatar = {
      original: req.file.originalname,
      optimized: `/uploads/avatars/${path.basename(optimizedPath)}`,
      thumbnail: `/uploads/avatars/${path.basename(thumbnailPath)}`
    };
    
    next();
  } catch (error) {
    console.error('Avatar processing error:', error);
    next(error);
  }
};

module.exports = processAvatar;
Code
// routes/profile.js
const express = require('express');
const router = express.Router();
const upload = require('../config/multer');
const processAvatar = require('../middleware/avatarProcessor');
const { authenticate } = require('../middleware/auth');

// Update profile picture
router.post('/avatar', 
  authenticate,
  upload.single('avatar'),
  processAvatar,
  async (req, res) => {
    try {
      // Update user in database
      const user = await User.findByIdAndUpdate(
        req.user.id,
        {
          avatar: req.avatar.optimized,
          avatarThumbnail: req.avatar.thumbnail
        },
        { new: true }
      );
      
      // Delete old avatar files if they exist
      if (user.oldAvatar) {
        const oldPath = path.join(__dirname, '..', user.oldAvatar);
        if (fs.existsSync(oldPath)) fs.unlinkSync(oldPath);
      }
      
      res.json({
        success: true,
        message: 'Profile picture updated successfully',
        data: {
          avatar: req.avatar.optimized,
          thumbnail: req.avatar.thumbnail
        }
      });
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  }
);

// Get user profile with avatar
router.get('/me', authenticate, async (req, res) => {
  const user = await User.findById(req.user.id).select('-password');
  
  res.json({
    success: true,
    data: {
      id: user.id,
      name: user.name,
      email: user.email,
      avatar: user.avatar ? `${req.protocol}://${req.get('host')}${user.avatar}` : null,
      avatarThumbnail: user.avatarThumbnail ? `${req.protocol}://${req.get('host')}${user.avatarThumbnail}` : null
    }
  });
});

module.exports = router;

11. Common Mistakes + Solutions

Mistake 1: Missing enctype in HTML form

Code
<!-- ❌ File won't upload -->
<form action="/upload" method="POST">

<!-- ✅ Correct enctype -->
<form action="/upload" method="POST" enctype="multipart/form-data">

Mistake 2: Wrong field name

Code
// Client side
<input type="file" name="myImage">

// ❌ Wrong field name in multer
upload.single('image')

// ✅ Correct field name
upload.single('myImage')

Mistake 3: Not creating upload directory

Code
// ❌ Error: ENOENT: no such file or directory
const upload = multer({ dest: 'uploads/' });

// ✅ Create directory first
const fs = require('fs');
if (!fs.existsSync('uploads')) {
  fs.mkdirSync('uploads');
}

Mistake 4: Forgetting to handle file size error

Code
// ❌ User gets cryptic error
const upload = multer({ dest: 'uploads/' });

// ✅ Handle multer errors
upload.single('file')(req, res, (err) => {
  if (err instanceof multer.MulterError) {
    if (err.code === 'FILE_TOO_LARGE') {
      return res.status(400).json({ error: 'File too large!' });
    }
  }
});

Mistake 5: Not validating file type

Code
// ❌ Any file can be uploaded
const upload = multer({ dest: 'uploads/' });

// ✅ Validate file type
const fileFilter = (req, file, cb) => {
  if (file.mimetype.startsWith('image/')) {
    cb(null, true);
  } else {
    cb(new Error('Only images allowed'), false);
  }
};

12. Quick Cheat Sheet

Installation:

Code
npm install multer

Basic Setup:

Code
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });

Single File:

Code
upload.single('fieldname')
// Access: req.file

Multiple Files (same field):

Code
upload.array('fieldname', maxCount)
// Access: req.files (array)

Multiple Files (different fields):

Code
upload.fields([
  { name: 'avatar', maxCount: 1 },
  { name: 'gallery', maxCount: 5 }
])
// Access: req.files.avatar, req.files.gallery

Disk Storage:

Code
const storage = multer.diskStorage({
  destination: (req, file, cb) => cb(null, 'uploads/'),
  filename: (req, file, cb) => cb(null, Date.now() + '-' + file.originalname)
});

File Filter:

Code
const fileFilter = (req, file, cb) => {
  if (file.mimetype.startsWith('image/')) {
    cb(null, true);
  } else {
    cb(new Error('Only images!'), false);
  }
};

Limits:

Code
limits: {
  fileSize: 5 * 1024 * 1024, // 5MB
  files: 5,
  fields: 10
}

13. FAQ

Q1: Express file upload Multer Hindi में सबसे important concept kya hai?
upload.single() – ye single file upload handle करता है। File req.file में आती है।

Q2: Multer kya hai?
Multer Node.js middleware hai jo multipart/form-data (file uploads) handle करता है।

Q3: diskStorage vs memoryStorage – kya use karein?
DiskStorage – files save karne के लिए (profile pictures, documents)। MemoryStorage – cloud upload या image processing के लिए (buffer में चाहिए)।

Q4: File size limit kaise set karein?
limits: { fileSize: 5 * 1024 * 1024 } – 5MB limit।

Q5: File type validation kaise karein?
fileFilter function use करो – mimetype check करो।

Q6: Multiple files upload kaise karein?
upload.array('files', 5) – same field name से multiple files।

Q7: Different fields से files kaise upload karein?
upload.fields([{ name: 'avatar' }, { name: 'resume' }])

Q8: Uploaded files serve kaise karein?
app.use('/uploads', express.static('uploads'))

Q9: Image compress/resize kaise karein?
sharp library use करो – upload के बाद process करो।

Q10: Multer errors handle kaise karein?
upload.single()(req, res, (err) => {}) – callback में error handle करो।

14. Conclusion

बहुत बढ़िया दोस्तों! आज हमने Express file upload Multer Hindi को पूरी detail में समझा।

Quick Recap:

ConceptKey Takeaway
MulterFile upload middleware
Single Fileupload.single('field')
Multiple Filesupload.array() or upload.fields()
ValidationfileFilter for type, limits for size
StorageDiskStorage (save) vs MemoryStorage (buffer)
ProcessingSharp for image resize/compress

Mera personal experience:

File upload pehle complex lagta था – raw Node.js में बहुत code likhna padta था। Multer ने सब easy कर दिया। अब 5 lines में file upload working है। Image processing ke liye Sharp use करो – quality maintain रहती है।

Tum bhi ye steps follow karo:

  1. ✅ Multer install करो
  2. ✅ Basic single file upload implement करो
  3. ✅ File validation add करो (type, size)
  4. ✅ Multiple files upload try करो
  5. ✅ Sharp से image processing add करो

अब तुम्हारी बारी है!

नीचे comment में बताओ:

  1. तुमने कभी file upload implement kiya है?
  2. कौन सा validation sabse important लगा?
  3. अगला topic क्या चाहिए? (MongoDB with Mongoose? JWT Authentication? WebSockets?)

The Easy Master पर बने रहो। Happy File Uploading! 🚀📁

Resources

Additional Resources

TheEasyMaster

Author at The Easy Master.

Related posts

Leave a Reply

Your email address will not be published. Required fields are marked *