Posted on Aug 30th 2018
File upload is a very important feature of web applications: it helps facilitate actions like setting a user’s profile picture, setting up dynamic galleries, remote file storage, and file sharing, among many other functionalities. In this article, we will be looking at file upload (more specifically, images), using React web framework, to a Node.js Express backend server.
Here is the application we are going to be building:
Configuring Our React Development Environment
We’re going to use Eclipse and CodeMix to develop our React application. To install CodeMix into your Eclipse environment, please follow the installation instructions on this page. So let’s get started with the React file uploads tutorial. Before proceeding, you have to make sure you have Node and npm (or Yarn) installed on your computer. If you don’t have them installed, you can download Node (which comes with npm) from the Node official website and follow the installation instructions.
Also, make sure that react-scripts is installed globally. This can be installed by executing the following command in the terminal:
npm install -g react-scripts
Now we can create our project using Eclipse IDE by navigating to File > New > Project. Select the React option under the CodeMix list of projects, name the application and click Next to proceed (make sure you’re connected to the Internet to get the newest React template and folder structure for the application).
Creating a React project in CodeMix
Once the process is completed, we can now install the required dependencies for our project using `Ctrl+Shift+P` to open a command terminal. Once the terminal is opened, execute the command below to install all the required packages:
Afterwards, we will pull in some modules we would use to build the application, using the command below:
npm install --save express cors multer axios
Development
Before we get started with coding, let’s go over a few of the technologies we will be using:
- Express: it is a Node.js module which simplifies the creation of a node server.
- Cors: it is a Node.js module that provides a middleware to handle cross-origin resource sharing.
- Multer: it is a Node.js middleware for handling “multipart/form-data”, which is primarily used for uploading files.
- Axios: it is a Promise-based HTTP client which we will use to communicate with the Node server.
Let’s move on to the development of the application. First, we would create the server that would have just one route, which would accept the files submitted, save them and return a path to the files. We do this by creating a file at the root of our project folder called `server.js`, which contains the server setup, Multer configuration and the sole route of the application.
This file should look like this:
const express = require('express');
const multer = require('multer');
const cors = require('cors');
const app = express();
app.use(express.static('public'))
var storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'public/images/uploads')
},
filename: (req, file, cb) => {
cb(null, Date.now() + '-' + file.originalname)
}
});
const upload = multer({ storage })
app.use(cors());
app.post('/upload', upload.single('image'), (req, res) => {
if (req.file)
res.json({
imageUrl: `images/uploads/${req.file.filename}`
});
else
res.status("409").json("No Files to Upload.");
});
const PORT = 5000;
app.listen(PORT);
console.log('api runnging on port: ' + PORT);
The first section of this code loads all modules we would be using in setting up our Express backend. It also instructs Express to load all files in the public directory in root directory of the project (that is, it exposes them to be rendered through call to the file path from the root URL).
Next, we set up the configuration for the multer middleware, telling multer how and where to store received files. In our case, we are saving them in the `public/images/uploads` directory. Then we instantiate a multer object, passing to it the configuration which we created.
Afterwards, we create the route to which the image will be posted (This is done after we enabled the cors middleware.). On the route definition, we pass the multer middleware which expects a single file upload. We then return the file path as response data to the caller or return an error if no file is found.
We then run the server on the given port. To run the server, we have to include an entry in the `package.json` file in the scripts array.
"server": "node server.js"
This will enable us to run the server from the terminal, using the following command in the project directory. After that, we can proceed to build our React application to use the server we created.
npm run server
We proceed by creating a file `app.js` which would contain our application’s main component.
Given the template provided by CodeMix, we would have an `index.js` which is the entry point the application. Here we will import our App component and render it.
The `index.js` should look like this:
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
Now we can proceed to implement the React part of this application. The `app.js` file should contain the code below:
import React, { Component } from 'react';
import axios from 'axios';
const BASE_URL = 'http://localhost:5000/';
class App extends Component {
constructor(props) {
super(props);
this.state = {
images: [],
imageUrls: [],
message: ''
}
}
selectImages = (event) => {
let images = []
for (var i = 0; i < event.target.files.length; i++) {
images[i] = event.target.files.item(i);
}
images = images.filter(image => image.name.match(/\.(jpg|jpeg|png|gif)$/))
let message = `${images.length} valid image(s) selected`
this.setState({ images, message })
}
uploadImages = () => {
const uploaders = this.state.images.map(image => {
const data = new FormData();
data.append("image", image, image.name);
// Make an AJAX upload request using Axios
return axios.post(BASE_URL + 'upload', data)
.then(response => {
this.setState({
imageUrls: [ response.data.imageUrl, ...this.state.imageUrls ]
});
})
});
// Once all the files are uploaded
axios.all(uploaders).then(() => {
console.log('done');
}).catch(err => alert(err.message));
}
render() {
return (
<div>
<br/>
<div className="col-sm-12">
<h1>Image Uploader</h1><hr/>
<div className="col-sm-4">
<input className="form-control " type="file"
onChange={this.selectImages} multiple/>
</div>
<p className="text-info">{this.state.message}</p>
<br/><br/><br/>
<div className="col-sm-4">
<button className="btn btn-primary" value="Submit"
onClick={this.uploadImages}>Submit</button>
</div>
</div>
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><hr/><br/>
<div className="row col-lg-12">
{
this.state.imageUrls.map((url, i) => (
<div className="col-lg-2" key={i}>
<img src={BASE_URL + url} className="img-rounded img-responsive"
alt="not available"/><br/>
</div>
))
}
</div>
</div>
);
}
}
export default App;
In the constructor, we initialize the state object of the component. Next, we define the `selectImages` method which is triggered by the `onChange` event of the file input field. Here we loop through the `FileList` object received from the input field, validating and checking to make sure that the files are images (using a property on each file called “name” in a regular expression match check) and adding the result to the images property on the state object.
Next, we define the `uploadImages` which is triggered by the `onClick` event on the submit button. This method loops through the images array in the component state (creating a form containing the next image to be uploaded), sends them as post request using axios to the upload URL and, finally, saves each file URL received to the imageUrls array in the state. We then handle the event of completion of all uploads using the `axios.all` method on the array of promises returned to the `uploaders` handle.
Finally, we render each of the images uploaded, using the array of file paths in the `imageUrls` object in the state object.
Conclusion
In this article, we discussed how to create a Node Express backend that accepts file inputs, stores the file, and returns the path to the file for rendering. We also covered interaction with a server which we created using the React framework. The method used performs adequately for both single and multiple file (image) uploads.
We also took advantage of the various features presented to us by CodeMix, including application scaffolding, syntax highlighting, and code snippets throughout the development of this application.
The code for this application can be downloaded here.
On a remote dev team? Try CodeTogether—it’s free!
- Live share IDEs & coding sessions
- See changes in real time
- Cross-IDE support for VS Code, IntelliJ & Eclipse
- Guests join from Browser or IDE
- End-to-end source encryption
- www.codetogether.com