Uploading and downloading files are essential functionalities in web applications. This guide will walk you through creating a MERN (MongoDB, Express, React, Node.js) stack application to upload and download files. We will use Multer for handling file uploads and Axios for data fetching. Each component will be clearly explained with detailed code examples and modern CSS design.
Backend Setup
Step 1: Initialize the Project
First, create a new directory for your project and initialize it with npm.
mkdir mern-file-upload
cd mern-file-upload
npm init -y
Step 2: Install Dependencies
Install the necessary dependencies.
npm install express mongoose multer body-parser cors
npm install --save-dev nodemon
Step 3: Setup Server
Create server.js
in the root directory.
// Import dependencies
const express = require('express'); // Express framework
const mongoose = require('mongoose'); // Mongoose for MongoDB
const bodyParser = require('body-parser'); // Body parser for handling JSON data
const cors = require('cors'); // CORS for handling cross-origin requests
const fileRoutes = require('./routes/fileRoutes'); // Import routes
// Initialize Express app
const app = express();
// Middleware setup
app.use(bodyParser.json()); // Parse JSON data
app.use(cors()); // Enable CORS
// Database connection
mongoose.connect('mongodb://localhost:27017/fileuploads', {
useNewUrlParser: true, // Use new URL parser
useUnifiedTopology: true, // Use unified topology for MongoDB
});
// Routes setup
app.use('/api/files', fileRoutes); // Use the file routes
// Start the server
const PORT = process.env.PORT || 5000; // Define the port
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`); // Log the server status
});
Step 4: Create Models
Create a models
directory and add File.js
.
// Import Mongoose
const mongoose = require('mongoose');
// Define the File schema
const fileSchema = new mongoose.Schema({
filename: {
type: String,
required: true, // Filename is required
},
filepath: {
type: String,
required: true, // Filepath is required
},
mimetype: {
type: String,
required: true, // Mimetype is required
},
size: {
type: Number,
required: true, // File size is required
},
});
// Export the File model
module.exports = mongoose.model('File', fileSchema);
Step 5: Setup Controllers
Create a controllers
directory and add fileController.js
.
// Import dependencies
const File = require('../models/File'); // Import the File model
const multer = require('multer'); // Import Multer for file handling
const path = require('path'); // Import path module
// Setup Multer storage
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/'); // Define the destination folder for uploads
},
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${file.originalname}`); // Define the filename format
},
});
// Configure Multer with the storage settings
const upload = multer({ storage: storage });
// Handle file upload
const uploadFile = (req, res) => {
const { file } = req; // Extract the uploaded file from the request
if (!file) {
return res.status(400).json({ message: 'No file uploaded' }); // Handle error if no file is uploaded
}
// Create a new File document
const newFile = new File({
filename: file.originalname,
filepath: file.path,
mimetype: file.mimetype,
size: file.size,
});
// Save the file document to the database
newFile.save()
.then(() => res.status(201).json(newFile)) // Respond with the saved file
.catch((err) => res.status(500).json({ error: err.message })); // Handle errors
};
// Handle file download
const downloadFile = (req, res) => {
const { id } = req.params; // Extract the file ID from the request parameters
File.findById(id)
.then((file) => {
if (!file) {
return res.status(404).json({ message: 'File not found' }); // Handle error if file is not found
}
res.set({
'Content-Type': file.mimetype,
'Content-Disposition': `attachment; filename="${file.filename}"`,
});
res.sendFile(path.resolve(file.filepath)); // Send the file as a response
})
.catch((err) => res.status(500).json({ error: err.message })); // Handle errors
};
// Export the controller functions
module.exports = {
upload,
uploadFile,
downloadFile,
};
Step 6: Setup Routes
Create a routes
directory and add fileRoutes.js
.
// Import dependencies
const express = require('express'); // Import Express
const { upload, uploadFile, downloadFile } = require('../controllers/fileController'); // Import controller functions
// Initialize router
const router = express.Router();
// Define routes
router.post('/upload', upload.single('file'), uploadFile); // Route for uploading a file
router.get('/download/:id', downloadFile); // Route for downloading a file by ID
// Export the router
module.exports = router;
Step 7: Folder Structure
Ensure you have an uploads
directory in the root of your project.
mkdir uploads
Frontend Setup
Step 1: Create React App
In a new terminal, create a React application
npx create-react-app client
cd client
npm install axios
Step 2: Create Upload Component
In the src
directory, create a components
folder and add FileUpload.js
.
// Import dependencies
import React, { useState } from 'react'; // Import React and useState hook
import axios from 'axios'; // Import Axios for HTTP requests
import './FileUpload.css'; // Import CSS file for styling
// File upload component
const FileUpload = () => {
const [file, setFile] = useState(null); // State to store the selected file
const [message, setMessage] = useState(''); // State to store the message
// Handle file change
const handleFileChange = (e) => {
setFile(e.target.files[0]); // Set the selected file to the state
};
// Handle file upload
const handleUpload = async (e) => {
e.preventDefault(); // Prevent default form submission
const formData = new FormData(); // Create a new FormData object
formData.append('file', file); // Append the selected file to the FormData
try {
// Send the file to the server
const res = await axios.post('http://localhost:5000/api/files/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }, // Set the content type
});
setMessage('File uploaded successfully'); // Set success message
} catch (err) {
setMessage('Error uploading file'); // Set error message
}
};
return (
<div className="file-upload-container">
<h2>Upload File</h2>
<form onSubmit={handleUpload}>
<input type="file" onChange={handleFileChange} /> {/* File input */}
<button type="submit">Upload</button> {/* Upload button */}
</form>
{message && <p>{message}</p>} {/* Display message */}
</div>
);
};
export default FileUpload;
Step 3: Create Download Component
Add FileDownload.js
in the components
folder.
// Import dependencies
import React, { useState } from 'react'; // Import React and useState hook
import axios from 'axios'; // Import Axios for HTTP requests
import './FileDownload.css'; // Import CSS file for styling
// File download component
const FileDownload = () => {
const [fileId, setFileId] = useState(''); // State to store the file ID
const [message, setMessage] = useState(''); // State to store the message
// Handle file download
const handleDownload = async () => {
try {
const res = await axios.get(`http://localhost:5000/api/files/download/${fileId}`, {
responseType: 'blob', // Set response type to blob
});
const url = window.URL.createObjectURL(new Blob([res.data])); // Create a URL for the blob
window.location.href = url; // Navigate to the URL to trigger download
setMessage('File downloaded successfully'); // Set success message
} catch (err) {
setMessage('Error downloading file'); // Set error message
}
};
return (
<div className="file-download-container">
<h2>Download File</h2>
<input
type="text"
value={fileId}
onChange={(e) => setFileId(e.target.value)} // Update file ID state on change
placeholder="Enter File ID"
/>
<button onClick={handleDownload}>Download</button> {/* Download button */}
{message && <p>{message}</p>} {/* Display message */}
</div>
);
};
export default FileDownload;
Step 4: Modern CSS Design
Add FileUpload.css
and FileDownload.css
in the components
folder.
FileUpload.css
/* Styling for the file upload container */
.file-upload-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
border: 1px solid #ccc;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
background-color: #f9f9f9;
width: 300px;
margin: 50px auto;
}
.file-upload-container h2 {
margin-bottom: 20px;
}
.file-upload-container form {
display: flex;
flex-direction: column;
align-items: center;
}
.file-upload-container input[type="file"] {
margin-bottom: 10px;
}
.file-upload-container button {
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.file-upload-container button:hover {
background-color: #0056b3;
}
.file-upload-container p {
margin-top: 20px;
color: green;
}
FileDownload.css
/* Styling for the file download container */
.file-download-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
border: 1px solid #ccc;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
background-color: #f9f9f9;
width: 300px;
margin: 50px auto;
}
.file-download-container h2 {
margin-bottom: 20px;
}
.file-download-container input {
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.file-download-container button {
padding: 10px 20px;
background-color: #28a745;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.file-download-container button:hover {
background-color: #218838;
}
.file-download-container p {
margin-top: 20px;
color: green;
}
Step 5: Add Components to App
Modify App.js
to include the new components.
// Import dependencies
import React from 'react'; // Import React
import './App.css'; // Import global CSS
import FileUpload from './components/FileUpload'; // Import FileUpload component
import FileDownload from './components/FileDownload'; // Import FileDownload component
// Main App component
function App() {
return (
<div className="App">
<FileUpload /> {/* Render FileUpload component */}
<FileDownload /> {/* Render FileDownload component */}
</div>
);
}
export default App;
Step 6: App.css
Add some global styling in App.css
.
/* Global styling for the app */
.App {
text-align: center;
font-family: Arial, sans-serif;
}
Conclusion
In this comprehensive guide, we created a MERN stack application to upload and download files. We used Multer for file handling on the server side and Axios for making HTTP requests on the client side. Modern CSS was applied to give a polished look to our components. Each step was accompanied by detailed explanations and code comments to ensure clarity for beginners.
Welcome to DevTechTutor.com, your ultimate resource for mastering web development and technology! Whether you're a beginner eager to dive into coding or an experienced developer looking to sharpen your skills, DevTechTutor.com is here to guide you every step of the way. Our mission is to make learning web development accessible, engaging, and effective.