In this React tutorial, I will show you way to build React Dropzone Multiple Files upload example using react-dropzone for drag & drop files, Axios and Multipart File for making HTTP requests, Bootstrap for progress bar and display list of files’ information (with download url).
More Practice:
– React File Upload/Download example with Spring Boot Rest Api
– React.js CRUD example to consume Web API
– React JWT Authentication & Authorization (without Redux) example
– React Redux: JWT Authentication & Authorization example
React Dropzone Multiple Files upload Overview
We’re gonna create a React Multiple Files upload application in that user can:
- drag and drop multiple files into a Dropzone
- see the upload process (percentage) of each file with progress bars
- view all uploaded files
- download link to file when clicking on the file name
Right after drag and drop files into the Dropzone:
Click on Upload button:
Technology
- React 17/16
- Axios 0.21.4
- react-dropzone 11.4.0
- Bootstrap 4
Web API for File Upload & Storage
Here are APIs that we will use Axios to make HTTP requests:
Methods | Urls | Actions |
---|---|---|
POST | /upload | upload a File |
GET | /files | get List of Files (name & url) |
GET | /files/[filename] | download a File |
You can find how to implement the Rest APIs Server at one of following posts:
– Node.js Express File Upload Rest API example
– Node.js Express File Upload to MongoDB example
– Node.js Express File Upload to Google Cloud Storage example
– Spring Boot Multipart File upload (to static folder) example
React Drag and Drop Multiple File Upload Application
After building the React.js project is done, the folder structure will look like this:
Let me explain it briefly.
– upload-files.service provides methods to save File and get Files using Axios.
– upload-files.component contains dropzone for drag-and-drop multiple files upload, progress bar, display of list files.
– App.js is the container that we embed all React components.
– http-common.js initializes Axios with HTTP base Url and headers.
– We configure port for our App in .env
Setup React Multiple Files Upload Project
Open cmd at the folder you want to save Project folder, run command:
npx create-react-app react-dropzone-multiple-files-upload
After the process is done. We create additional folders and files like the following tree:
public
src
components
upload-files.component.js
services
upload-files.service.js
App.css
App.js
index.js
package.json
Import Bootstrap to React Dropzone Multiple File Upload App
Run command: yarn add [email protected]
Or: npm install [email protected]
.
Open src/App.js and modify the code inside it as following-
import React from "react";
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
function App() {
return (
...
);
}
export default App;
Initialize Axios for React HTTP Client
Let’s install axios with command:
yarn add axios
or npm install axios
.
Under src folder, we create http-common.js file with following code:
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:8080",
headers: {
"Content-type": "application/json"
}
});
You can change the baseURL
that depends on REST APIs url that your Server configures.
Create Service for File Upload
This service will use Axios to send HTTP requests.
There are 2 functions:
upload(file)
: POST form data with a callback for tracking upload progressgetFiles()
: GET list of Files’ information
services/upload-files.service.js
import http from "../http-common";
class UploadFilesService {
upload(file, onUploadProgress) {
let formData = new FormData();
formData.append("file", file);
return http.post("/upload", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
onUploadProgress,
});
}
getFiles() {
return http.get("/files");
}
}
export default new UploadFilesService();
– First we import Axios as http
from http-common.js.
– Inside upload()
method, we use FormData
to store key-value pairs. It helps to build an object which corresponds to HTML form using append()
method.
– We pass onUploadProgress
to exposes progress events. This progress event are expensive (change detection for each event), so you should only use when you want to monitor it.
– We call Axios post()
to send an HTTP POST for uploading a File to Rest APIs Server and get()
method for HTTP GET request to retrieve all stored files.
Install react-dropzone
Add react-dropzone module into project with command:
– yarn add react-dropzone
– Or: npm install react-dropzone
Create Component for Drag and Drop Multiple File Upload
Let’s create a File Upload UI with Dropzone, Progress Bar, Card, Button and Message.
First we create a React component template, import react-dropzone
and UploadFilesService
:
components/upload-files.component.js
import React, { Component } from "react";
import Dropzone from "react-dropzone";
import UploadService from "../services/upload-files.service";
export default class UploadFiles extends Component {
constructor(props) {
}
render() {
}
}
Then we define the state inside constructor()
method:
export default class UploadFiles extends Component {
constructor(props) {
super(props);
...
this.state = {
selectedFiles: undefined,
progressInfos: [],
message: [],
fileInfos: [],
};
}
}
Next we define onDrop()
method which helps us to get the selected Files from <Dropzone>
element later.
export default class UploadFiles extends Component {
...
onDrop(files) {
if (files.length > 0) {
this.setState({ selectedFiles: files });
}
}
}
We use selectedFiles
array for accessing current selected Files. Every file has a corresponding progress Info (percentage, file name) and index in progressInfos
array.
export default class UploadFiles extends Component {
...
uploadFiles() {
const selectedFiles = this.state.selectedFiles;
let _progressInfos = [];
for (let i = 0; i < selectedFiles.length; i++) {
_progressInfos.push({ percentage: 0, fileName: selectedFiles[i].name });
}
this.setState(
{
progressInfos: _progressInfos,
message: [],
},
() => {
for (let i = 0; i < selectedFiles.length; i++) {
this.upload(i, selectedFiles[i]);
}
}
);
}
}
We call UploadService.upload()
method on each file
with a callback. So create following upload()
method:
export default class UploadFiles extends Component {
...
upload(idx, file) {
let _progressInfos = [...this.state.progressInfos];
UploadService.upload(file, (event) => {
_progressInfos[idx].percentage = Math.round(
(100 * event.loaded) / event.total
);
this.setState({
_progressInfos,
});
})
.then((response) => {
this.setState((prev) => {
let nextMessage = [
...prev.message,
"Uploaded the file successfully: " + file.name,
];
return {
message: nextMessage,
};
});
return UploadService.getFiles();
})
.then((files) => {
this.setState({
fileInfos: files.data,
});
})
.catch(() => {
_progressInfos[idx].percentage = 0;
this.setState((prev) => {
let nextMessage = [
...prev.message,
"Could not upload the file: " + file.name,
];
return {
progressInfos: _progressInfos,
message: nextMessage,
};
});
});
}
}
The progress will be calculated basing on event.loaded
and event.total
.
If the transmission is done, we call UploadService.getFiles()
to get the files’ information and assign the result to fileInfos
state, which is an array of {name, url}
objects.
We also need to do this work in componentDidMount()
method:
export default class UploadFiles extends Component {
...
componentDidMount() {
UploadService.getFiles().then((response) => {
this.setState({
fileInfos: response.data,
});
});
}
Now we implement the render function of the Drag and Drop File Upload UI with Dropzone
element. Add the following code inside render()
:
export default class UploadFiles extends Component {
...
render() {
const { selectedFiles, progressInfos, message, fileInfos } = this.state;
return (
<div>
{progressInfos &&
progressInfos.map((progressInfo, index) => (
<div className="mb-2" key={index}>
<span>{progressInfo.fileName}</span>
<div className="progress">
<div
className="progress-bar progress-bar-info"
role="progressbar"
aria-valuenow={progressInfo.percentage}
aria-valuemin="0"
aria-valuemax="100"
style={{ width: progressInfo.percentage + "%" }}
>
{progressInfo.percentage}%
</div>
</div>
</div>
))}
<div className="my-3">
<Dropzone onDrop={this.onDrop}>
{({ getRootProps, getInputProps }) => (
<section>
<div {...getRootProps({ className: "dropzone" })}>
<input {...getInputProps()} />
{selectedFiles &&
Array.isArray(selectedFiles) &&
selectedFiles.length ? (
<div className="selected-file">
{selectedFiles.length > 3
? `${selectedFiles.length} files`
: selectedFiles.map((file) => file.name).join(", ")}
</div>
) : (
`Drag and drop files here, or click to select files`
)}
</div>
<aside className="selected-file-wrapper">
<button
className="btn btn-success"
disabled={!selectedFiles}
onClick={this.uploadFiles}
>
Upload
</button>
</aside>
</section>
)}
</Dropzone>
</div>
{message.length > 0 && (
<div className="alert alert-secondary" role="alert">
<ul>
{message.map((item, i) => {
return <li key={i}>{item}</li>;
})}
</ul>
</div>
)}
{fileInfos.length > 0 && (
<div className="card">
<div className="card-header">List of Files</div>
<ul className="list-group list-group-flush">
{fileInfos &&
fileInfos.map((file, index) => (
<li className="list-group-item" key={index}>
<a href={file.url}>{file.name}</a>
</li>
))}
</ul>
</div>
)}
</div>
);
}
}
In the code above, we use Bootstrap Progress Bar:
.progress
as a wrapper- inner
.progress-bar
to indicate the progress .progress-bar
requiresstyle
to set the width by percentage.progress-bar
also requiresrole
and some aria attributes to make it accessible- label of the progress bar is the text within it
To display List of uploaded files, we iterate over fileInfos
array using map()
function. On each file item, we use file.url
as href
attribute and file.name
for showing text.
CSS style for Dropzone and File
Open App.css and add following styles:
.dropzone {
text-align: center;
padding: 30px;
border: 3px dashed #eeeeee;
background-color: #fafafa;
color: #bdbdbd;
cursor: pointer;
margin-bottom: 20px;
}
.selected-file-wrapper {
text-align: center;
}
.selected-file {
color: #000;
font-weight: bold;
}
Add Multiple Files Upload Component to App Component
Open App.js, import and embed the UploadFiles
Component tag.
import React from "react";
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
import UploadFiles from "./components/upload-files.component";
function App() {
return (
<div className="container" style={{ width: "600px" }}>
<div className="my-2">
<h3>bezkoder.com</h3>
<h4>React DropZone multiple Files upload</h4>
</div>
<UploadFiles />
</div>
);
}
export default App;
Configure Port for React Drag and Drop Multiple File Upload App
Because most of HTTP Server use CORS configuration that accepts resource sharing restricted to some sites or ports, you need to configure port for our App.
In project folder, create .env file with following content:
PORT=8081
So our app will run at port 8081.
Run the App
You can find how to implement the Rest APIs Server at one of following posts:
– Node.js Express File Upload Rest API example
– Node.js Express File Upload to MongoDB example
– Node.js Express File Upload to Google Cloud Storage example
– Spring Boot Multipart File upload (to static folder) example
Run this React Client: yarn start
or npm run start
.
Open Browser with url http://localhost:8081/
and check the result.
Further Reading
- https://github.com/axios/axios
- React Component
- Bootstrap 4 Progress
- react-dropzone
- React CRUD example to consume Web API
- React JWT Authentication (without Redux) example
- React + Redux: JWT Authentication example
– React Image Upload with Preview example
Conclusion
Today we’re learned how to build a React-Dropzone example for Multiple File upload using Axios and Bootstrap Progress Bar. We also provide the ability to show list of files, upload progress bar, and to download file from the server.
Happy Learning! See you again.
Source Code
The source code for the React Client is uploaded to Github.