File uploads is an essential part of software development. It has revolutionized the way we work, socialize and learn as humans. From building social media apps, to video streaming apps, to online learning platforms. The list goes on and on.
File upload is applied (but not limited to) the following areas:
- picture uploads (as seen in social media platforms)
- multimedia uploads
Objective
By the end of this article, you'll be able to implement file uploads using the MERN stack and multer. A big step in building the next big thing.
Prerequisites
To follow through this tutorial, I assume you have basic knowledge of the following technologies:
- mongoDB
- express.js
- react.js
- react hooks
- node.js
Folder structure
Our application will have a backend and a frontend. The structure of your app is entirely up to you!
Frontend
In the frontend, create a simple form. The form is to have encType
attribute set to "multipart/form-data"
. Here's a sample code.
import React, {useState} from 'react';
function App () {
const [uploadedFile, setUploadedFile] = useState ('');
const [fileTitle, setFileTitle] = useState ('');
function handleFormSubmittion (e) {
e.preventDefault ();
let form = document.getElementById ('form');
let formData = new FormData (form);
// do something
console.log("Form submitted")
}
function handleFileTitle (e) {
setFileTitle (e.target.value);
}
function handleUploadedFile (e) {
setUploadedFile (e.target.value);
}
return (
<React.Fragment>
<h1>File upload</h1>
<form
encType="multipart/form-data"
onSubmit={handleFormSubmittion}
id="form"
>
<input
type="file"
name="uploadedFile"
value={uploadedFile}
onChange={handleUploadedFile}
required
/>
<br />
<br />
<label>File title:</label><br />
<input
type="text"
placeholder="Enter file title"
name="fileTitle"
value={fileTitle}
onChange={handleFileTitle}
required
/>
<br />
<br />
<button type="submit">Submit Form</button>
</form>
</React.Fragment>
);
}
export default App;
Backend
To get setup the backend, install the following dependencies:
- express
- cors
- body-parser
- multer
Multer is responsible for handling multipart forms. It adds a body
object and a file
or files
object to the request
object. The body
object contains the values of the text fields of the form, the file
or files
object contains the files uploaded via the form.
To get started, we create a storage engine. The storage engine is responsible for the following:
- file destination
- file renaming
There are two options available: destination
and filename
. They are both functions that determine where the file should be stored. Destination is used to determine where the uploaded files should be stored. This can also be given as a string (e.g. '/tmp/uploads'). If no destination is given, the operating system's default directory for temporary files is used. Filename is used to determine what the file should be named inside the folder. If no filename is given, each file will be given a random name that doesn't include any file extension.
Here's the code
// storage engine for multer
const storageEngine = multer.diskStorage ({
destination: './public/uploads/',
filename: function (req, file, callback) {
callback (
null,
file.fieldname + '-' + Date.now () + path.extname (file.originalname)
);
},
});
Next, we create a file filter (optional).It lets us filter out unwanted files. For this tutorial, we will expect only images.
Here's the code.
// file filter for multer
const fileFilter = (req, file, callback) => {
let pattern = /jpg|png|svg/; // reqex
if (pattern.test (path.extname (file.originalname))) {
callback (null, true);
} else {
callback ('Error: not a valid file');
}
};
Then, we initialize multer.
Here's the code.
// initialize multer
const upload = multer ({
storage: storageEngine,
fileFilter
});
Finally, we create the routes for our application.
Multer. provides a single
method for acessing single files. It takes a string as an argument.
NOTE: It is important that you use the name field value from the form in your upload function. This tells multer which field on the request it should look for the files in. If these fields aren't the same in the HTML form and on your server, your upload will fail.
Here's the code:
// routing
app.post ('/upload', upload.single ('uploadedFile'), (req, res) => {
console.log(req.file)
res.json (req.file).status (200);
});
Our final backend code should look like this:
const express = require ('express');
const bodyParser = require ('body-parser');
const cors = require ('cors');
const path = require ('path');
const multer = require ('multer');
// constants
const app = express ();
const PORT = process.env.PORT || 5000;
// middleware
app.use (bodyParser.json ());
app.use (bodyParser.urlencoded ({extended: true}));
app.use (cors ());
// storage engine for multer
const storageEngine = multer.diskStorage ({
destination: './public/uploads/',
filename: function (req, file, callback) {
callback (
null,
file.fieldname + '-' + Date.now () + path.extname (file.originalname)
);
},
});
// file filter for multer
const fileFilter = (req, file, callback) => {
let pattern = /jpg|png|svg/; // reqex
if (pattern.test (path.extname (file.originalname))) {
callback (null, true);
} else {
callback ('Error: not a valid file');
}
};
// initialize multer
const upload = multer ({
storage: storageEngine,
fileFilter: fileFilter,
});
// routing
app.post ('/upload', upload.single ('uploadedFile'), (req, res) => {
res.json (req.file).status (200);
});
app.listen (PORT, () => console.log (`Server running on port: ${PORT}`));
Connecting your Frontend to your Backend
To connect your frontend and your backend, do the following:
- install axios
- make a post request to the backend
Here's what your handleFormSubmittion
method should look like.
function handleFormSubmittion (e) {
e.preventDefault ();
let form = document.getElementById ('form');
let formData = new FormData (form);
// new line added
axios.post ('http://localhost:5000/upload', formData);
}
To confirm your app is working correctly, upload a picture. All uploads will be stored in backend/public/uploads
.
Conclusion
File uploads are essential when building modern web apps. I tried my best to make this article as simple as possible. You can re-read this article if the concepts were not clear. Your feedbacks are essential. It helps me put out better content.
Stay safe:)