How to set up file upload with React and Node
Mục lục bài viết
How to set up file upload with React and Node
File upload is a pretty simple but widely used operation on any app or site. It involves submitting the file and copying it to the remote location.
let’s begin
The file upload app should have two components:
- Front end: lets user select file.
- Backend (API): that the front end can send a file to.
If you’re using a service to store files such as Amazon or Firebase, you’ll get an API endpoint to hit with the file. The server will store, compress, replicate and do everything else you want to do with simple settings.
Since I do not have an existing API, I’ll create one locally using Node and then hit that API from the front-end. That’s the master plan!
Creating a backend
If you already have an existing server with an endpoint to send the post request with a file, you may skip to the React portion (front-end). The backend API code is hosted here on GitHub. It runs on port 8000 by default. The endpoint to upload is /upload
.
For the tutorial, I’ll be using express-generator to create a basic Express server.
If you do not have express-generator, install it with npm install express-generator --global
.
Then create an Express app with express-generator file-upload-api
.
I’m calling the backend app file-upload-api
.
It creates a folder with Express, but it won’t install the node_modules
.
cd file-upload-api
npm install
This will create thenode_modules
directory and install all modules from thepackage.json
.
File upload needs more packages: npm install cors express-fileupload
.
cors
is to access the backend from another server that will run on a different port or a different server.
express-file-upload
is a handy package to handle uploaded files in express. We use it as middleware with express to move files from local disk to server.
I’ll set up the basic express server on post request on the route /upload
. I have not changed anything unnecessary from the express-generator package, as all we need is a basic server to handle our requests.
app.post('/upload', (req, res, next) => {
let uploadFile = req.files.file
const fileName = req.files.file.name
uploadFile.mv(
`${__dirname}/public/files/${fileName}`,
function (err) {
if (err) {
return res.status(500).send(err)
}res.json({
file: `public/${req.files.file.name}`,
})
},
)
})
On hitting /upload
with a POST request, the file is copied to /public/files/fileName.ext
. Since I’ll be sending the file appended to the file key from the front-end, the file will be accessible at req.files.file
. The filename will be available at req.files.file.name
. If you need more details, log the file or refer to the documentation.
Creating the frontend on React
Now that we have the API endpoint to hit with the file, let’s work on an interface that will allow the user to upload the file to the server. The code is available here on GitHub.
As always, I start with create-react-app
.
If you don’t havecreate-react-app
, npm install create-react-app:
create-react-app file-upload-react
We need the file upload field, this is an input type:
<input type="file"/>
Handling the selected files
The uploading needs a plan. There are a few different ways to handle it.
Here’s mine:
- Get the file: it’s in the
event.target.files[0]
in the input. - Set the state on file selection, using onClick on the input.
- Set another state
loaded
to0
. We’ll use this to show upload progress. - Send API request on click of the button, using onClick in the button.
It would also be possible to send the file when the file changes, but it’s not a good idea. Since the button and selected file deal with the exact same file, it’s a good idea to use state to handle the file.
Initialize the state
this.state = { selectedFile: null, loaded: 0, }
Add the onClick handlers to the App for selecting the file and submitting the file.
return (
<div className="App">
<input type="file" name="" id="" onChange={this.handleselectedFile} />
<button onClick={this.handleUpload}>Upload</button>
<div> {Math.round(this.state.loaded,2) } %</div>
</div>
)
- handleSelectedFile: sets state to selected file and resets upload %.
- handleUpload: sends file to server on clicking
Upload
button.
handleSelectedFile
just sets the state selectedFile
to the selected file.
handleselectedFile = event => {
this.setState({
selectedFile: event.target.files[0],
loaded: 0,
})
}
handleUpload
will upload to the API. For this purpose, I’ll use the lightweight axios library.
npm install axios
.
import axios from 'axios'
We send a POST request which has 3 components: endpoint, file, and another object argument for progress.
handleUpload = () => {
const data = new FormData()
data.append('file', this.state.selectedFile, this.state.selectedFile.name)axios
.post(endpoint, data, {
onUploadProgress: ProgressEvent => {
this.setState({
loaded: (ProgressEvent.loaded / ProgressEvent.total*100),
})
},
})
.then(res => {
console.log(res.statusText)
})}
Create a new FormData called data. It is a default JavaScript object. Append the data field with file and name.
I have defined endpoint
in top of the file as http://localhost:8000
.
data
is the appended file to be sent.
The third argument is for progress reporting, and it requests the progressEvent. You can refer to axios example here for more details on progressEvent.
ProgressEvent has loaded
and total
values. We use them to calculate percentage and display in the div
below the button.
The response status is logged after the promise is resolved.
The pitfalls and improvements
Uploading using React and Node has no pitfalls, but this demo does! As it was just a demo, errors weren’t handled well. There’s no way to cancel the upload; if you refresh the front-end while uploading, the server will crash.
The server doesn’t check for script files, not does it sanitize them.
So this is how to handle uploads on React with a Node as a backend. You can also use React to do the same with API to remote server and data storage solutions.