DEV Community

ASHDEEP SINGH
ASHDEEP SINGH

Posted on

Intro to Dapp III

Hello World !

This week in learning blockchain , I explored ways to deal with images in MERN stack. Here is what i learnt this week.

Broadly there are only two ways to deal with images in MERN , either use own file system (i.e, the system hosting the website / server PC) or do go through various 3rd party APIs and use them for storing images.

1) File system storage
Using images on file system usually consists of adding a new type of module which gives you the option of dealing with images , the one used here was multer.
A basic setup with multer would look like this :
Backend

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

const app = express();

// Set storage engine for uploaded files
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/'); // Files will be stored in the 'uploads' folder
  },
  filename: function (req, file, cb) {
    cb(null, Date.now() + path.extname(file.originalname)); // Use timestamp + file extension as filename
  }
});

// Initialize multer with the storage configuration
const upload = multer({ storage: storage });

// Define a POST route for uploading files
app.post('/upload', upload.single('file'), (req, res) => {
  try {
    res.status(200).json({ message: 'File uploaded successfully', file: req.file });
  } catch (err) {
    res.status(500).send(err);
  }
});

// Start server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

frontend

import React, { useState } from 'react';
import axios from 'axios';

const FileUpload = () => {
  const [file, setFile] = useState(null);
  const [message, setMessage] = useState('');

  // Handle file selection
  const handleFileChange = (e) => {
    setFile(e.target.files[0]);
  };

  // Handle form submission
  const handleSubmit = async (e) => {
    e.preventDefault();
    const formData = new FormData();
    formData.append('file', file);

    try {
      const res = await axios.post('http://localhost:5000/upload', formData, {
        headers: { 'Content-Type': 'multipart/form-data' }
      });
      setMessage(res.data.message);
    } catch (err) {
      console.error(err);
      setMessage('File upload failed');
    }
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input type="file" onChange={handleFileChange} />
        <button type="submit">Upload</button>
      </form>
      {message && <p>{message}</p>}
    </div>
  );
};

export default FileUpload;
Enter fullscreen mode Exit fullscreen mode

2) Use 3rd party APIs
We can also use 3rd party APIs , here cloudinary to store images , here's how it works , you signin on their platform , grab the Cloud name, API Key, and API Secret then go and use their endpoint to hit with ur data , which if worked properly should return image URL. Store this image URL wherever you wish to and use this one in future when ever needed.

Here is how the code looks like :
frontend (uploading images directly from frontend)

import React, { useState } from 'react';

const FileUpload = () => {
  const [file, setFile] = useState(null);
  const [fileUrl, setFileUrl] = useState('');
  const [message, setMessage] = useState('');

  // Handle file selection
  const handleFileChange = (e) => {
    setFile(e.target.files[0]);
  };

  // Handle file upload to Cloudinary
  const handleUpload = async (e) => {
    e.preventDefault();

    if (!file) {
      setMessage('Please select a file first');
      return;
    }

    // Create FormData object and append the file
    const formData = new FormData();
    formData.append('file', file);
    formData.append('upload_preset', 'your_unsigned_preset'); // Use your unsigned upload preset here

    try {
      const res = await fetch(`https://api.cloudinary.com/v1_1/your_cloud_name/image/upload`, {
        method: 'POST',
        body: formData,
      });

      const data = await res.json();
      setFileUrl(data.secure_url);  // The URL of the uploaded file
      setMessage('File uploaded successfully!');
    } catch (error) {
      console.error('Error uploading file:', error);
      setMessage('Failed to upload file.');
    }
  };

  return (
    <div>
      <form onSubmit={handleUpload}>
        <input type="file" onChange={handleFileChange} />
        <button type="submit">Upload to Cloudinary</button>
      </form>
      {message && <p>{message}</p>}
      {fileUrl && (
        <div>
          <p>Uploaded File:</p>
          <img src={fileUrl} alt="Uploaded file" style={{ width: '300px' }} />
        </div>
      )}
    </div>
  );
};

export default FileUpload;
Enter fullscreen mode Exit fullscreen mode

Now for the final comparision , one must know when to use multer and when to use cloudinary , and also when to use both.

1. When to Use Multer

Multer is a middleware for handling file uploads in Node.js applications. It helps in processing file uploads by storing them in the server's filesystem or temporarily holding them for further processing.
Use Multer when:

File Upload to Local Storage:

  • When you want to store files (like images, PDFs, videos) directly on the server’s filesystem.
  • Good for small-scale applications or use cases where storing files locally makes sense.

Pre-processing Files:

  • You need to process the file (e.g., resize images, compress them) before uploading to a storage service.
  • If you need to parse the file in-memory (e.g., CSV files) and extract data.

Temporary Storage:

  • If you want to temporarily store files on the server and later move them to cloud storage (such as Amazon S3 or Cloudinary) after performing some validation or transformations.

Backend Processing of Files:

  • When you need to validate or manipulate files directly in your backend server (e.g., checking file size, format, or type before uploading).

When Multer might be enough:

  • Small Projects: When the project is small, like a local app or website, and there are no scaling requirements.
  • Controlled Environment: If you're deploying the app to an environment where you can control the storage (such as private servers, small VPS, or cloud VM).

Example Use Case for Multer:

  • A small company internal tool where users upload documents, and you store them in the local server for retrieval by other employees.

2. When to Use Cloudinary

Cloudinary is a cloud-based service for managing and serving media assets like images, videos, and documents. It also offers powerful transformation features (resizing, cropping, optimizing, etc.) on-the-fly.
Use Cloudinary when:

Cloud-Based Storage:

  • You don’t want to handle or manage file storage locally or on the server. Cloudinary manages file storage for you on the cloud, ensuring scalability and security.
  • This is especially useful when scaling, as Cloudinary’s CDN can quickly serve assets worldwide.

Media Optimization:

  • You want to optimize images, videos, or other media for better performance (e.g., reducing file size, applying lazy loading, or serving different image resolutions depending on device screen size).
  • Cloudinary automatically optimizes images for web performance, SEO, and mobile responsiveness, without you having to do manual processing.

Transformations:

  • When you need to apply image/video transformations (cropping, resizing, effects, overlays, etc.) dynamically via URL manipulation, without the need to store multiple versions of the same media.
  • This is useful in scenarios like user profile picture uploads where you need different image sizes and formats for thumbnails, social sharing, and profile display.

Direct Uploads from Frontend:

  • If you want to bypass the backend entirely and allow users to upload files directly from the frontend to Cloudinary.
  • This reduces server load and complexity, making your architecture leaner and faster.

Global Delivery via CDN:

  • When you want your media files to be served globally via Cloudinary’s CDN for faster delivery and better performance for users in different regions.

Security and Compliance:

  • Cloudinary provides media security features such as signed URLs and access control, ensuring media files are secure.
  • Useful for applications where compliance with security protocols or privacy laws (GDPR, HIPAA, etc.) is necessary.

Example Use Case for Cloudinary:

  • A social media platform where users upload profile pictures, and you need to optimize and deliver these pictures to users globally in different formats (like thumbnails, high-resolution, etc.), while handling transformations like cropping, resizing, and watermarking dynamically.

3. When to Use Both (Multer + Cloudinary)

In some cases, you might want to use both Multer and Cloudinary together. This is common in situations where you need to process a file server-side before sending it to Cloudinary or need control over initial file validation or transformation.
Use Both When:

Pre-Processing Before Upload:

  • You need to validate or process files (e.g., resize, compress) using Multer and a library like sharp before uploading to Cloudinary for further processing or storage.
  • This is useful when Cloudinary’s transformations don’t cover specific preprocessing requirements or when you want to validate the file size, format, or content before uploading.

Multi-step Workflow:

  • If you want to store files temporarily on your server (with Multer) and later move them to Cloudinary after manual approval or other processing tasks.

Example Use Case for Multer + Cloudinary:

  • A photography website where users upload high-resolution photos, and the server first compresses or resizes them using Multer. Then, once optimized, the server uploads the final images to Cloudinary for long-term storage and dynamic delivery.

My 2 cents :

  • use 3rd party if making a project for hobby / resume as it shows we can use it as well. (atleast that's what i think)
  • Multer is needed when working on inhouse projects and thus one should know multer too.
  • Incase multer gives error check console , in my case it was folder not being present , so make sure to deal with addresses correctly.
  • If using 3rd party for image uploads we can directly upload image from frontend , although controversial but hey if you really wanna use 3rd party integration why not leverage it's direct accessibility.
  • Remember if using 3rd party for directly uploading from frontend , the ways might differ as when doing it from backend as now we will not have the Cloud name, API Key, and API Secret which are present in backend , thus some other form of that API might be required (Unsigned Upload Preset in our case , which gives settings for uploading images without the Cloud name, API Key, and API Secret).
  • Using backend and API is same as using mutler , we first use mutler to bring images to backend then use 3rd party API to upload it to service provider.

That's all for today folks , see ya next time.
Stay tuned for more.

Top comments (0)