In this React tutorial, I will show you way to build React Hooks Multiple Files upload example using Axios and Multipart File for making HTTP requests, Bootstrap for progress bar and display list of files’ information (with download url).
More Practice:
– Typescript: React Multiple File Upload example (with Typescript)
– React.js Multiple Image Upload with Preview example
– React Hooks CRUD example with Axios and Web API
– React Custom Hook tutorial with example
– React Form Validation with Hooks example
– React Hooks: JWT Authentication (without Redux) example
– React + Redux: JWT Authentication example
Serverless:
– React Hooks + Firebase Realtime Database: CRUD App
– React Hooks + Firestore example: CRUD app
Contents
- React Hooks Multiple Files upload Overview
- Technology
- Web API for File Upload & Storage
- Project Structure
- Setup React Hooks Multiple File Upload Project
- Import Bootstrap to React Hooks Multiple File Upload App
- Initialize Axios for React HTTP Client
- Create Service for File Upload
- Create Page for Upload Multiple Files
- Add Files Upload Page to App Component
- Configure Port for React File Upload App
- Run the App
- Conclusion
- Further Reading
- Source Code
React Hooks Multiple Files upload Overview
We’re gonna create a React Multiple Files upload application using Hooks, in that user can:
- 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
When there is an error on uploading file:
Technology
- React 18/17/16
- Axios 0.27.2
- 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
Project Structure
After building the React project is done, the folder structure will look like this:
Let me explain it briefly.
– FileUploadService provides functions to save File and get Files using Axios.
– FilesUpload contains multiple files upload form, 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 Hooks Multiple File Upload Project
Open cmd at the folder you want to save Project folder, run command:
npx create-react-app react-hooks-multiple-file-upload
After the process is done. We create additional folders and files like the following tree:
public
src
components
FilesUpload.js
services
FileUploadService.js
App.css
App.js
index.js
package.json
Import Bootstrap to React Hooks Multiple File Upload App
Run command: npm install [email protected]
.
Or: yarn add [email protected]
Open src/App.js and modify the code inside it as following-
import React from "react";
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: npm install [email protected]
.
Or: yarn add [email protected]
.
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/FileUploadService.js
import http from "../http-common";
const upload = (file, onUploadProgress) => {
let formData = new FormData();
formData.append("file", file);
return http.post("/upload", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
onUploadProgress,
});
};
const getFiles = () => {
return http.get("/files");
};
const FileUploadService = {
upload,
getFiles,
};
export default FileUploadService;
– First we import Axios as http
from http-common.js.
– Inside upload()
function, 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.
Create Page for Upload Multiple Files
Let’s create a File Upload UI with Progress Bar, Card, Button and Message.
First we create a React template with React Hooks (useState
, useEffect
) and import FileUploadService
:
components/FileUpload.js
import React, { useState, useEffect, useRef } from "react";
import UploadService from "../services/FileUploadService";
const UploadFiles = () => {
return (
);
};
export default UploadFiles;
Then we define the state using React Hooks:
const UploadFiles = () => {
const [selectedFiles, setSelectedFiles] = useState(undefined);
const [progressInfos, setProgressInfos] = useState({ val: [] });
const [message, setMessage] = useState([]);
const [fileInfos, setFileInfos] = useState([]);
const progressInfosRef = useRef(null)
...
}
Next we define selectFiles()
function which helps us to get the selected Files from <input type="file" >
element later.
const UploadFiles = () => {
...
const selectFiles = (event) => {
setSelectedFiles(event.target.files);
setProgressInfos({ val: [] });
};
...
}
We use selectedFiles
array for accessing current selected Files. Every file has a corresponding progress Info (percentage, file name) and index in progressInfos
array.
const UploadFiles = () => {
...
const uploadFiles = () => {
const files = Array.from(selectedFiles);
let _progressInfos = files.map(file => ({ percentage: 0, fileName: file.name }));
progressInfosRef.current = {
val: _progressInfos,
}
const uploadPromises = files.map((file, i) => upload(i, file));
Promise.all(uploadPromises)
.then(() => UploadService.getFiles())
.then((files) => {
setFileInfos(files.data);
});
setMessage([]);
};
...
}
In the code above, for each file, we invoke upload()
function which call UploadService.upload()
method with an index (i
) parameter and return a Promise
.
After all the processes are 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.
So create following upload()
function like this:
const UploadFiles = () => {
...
const upload = (idx, file) => {
let _progressInfos = [...progressInfosRef.current.val];
return UploadService.upload(file, (event) => {
_progressInfos[idx].percentage = Math.round(
(100 * event.loaded) / event.total
);
setProgressInfos({ val: _progressInfos });
})
.then(() => {
setMessage((prevMessage) => ([
...prevMessage,
"Uploaded the file successfully: " + file.name,
]));
})
.catch(() => {
_progressInfos[idx].percentage = 0;
setProgressInfos({ val: _progressInfos });
setMessage((prevMessage) => ([
...prevMessage,
"Could not upload the file: " + file.name,
]));
});
};
...
}
The progress will be calculated basing on event.loaded
and event.total
and percentage value is assigned to specific item of progressInfos
by the index.
We also need to do this work in the Effect Hook useEffect()
method which serves the same purpose as componentDidMount()
:
const UploadFiles = () => {
...
useEffect(() => {
UploadService.getFiles().then((response) => {
setFileInfos(response.data);
});
}, []);
...
}
Now we return the Upload File UI. Add the following code inside return()
block:
const UploadFiles = () => {
...
return (
<div>
{progressInfos && progressInfos.val.length > 0 &&
progressInfos.val.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="row my-3">
<div className="col-8">
<label className="btn btn-default p-0">
<input type="file" multiple onChange={selectFiles} />
</label>
</div>
<div className="col-4">
<button
className="btn btn-success btn-sm"
disabled={!selectedFiles}
onClick={uploadFiles}
>
Upload
</button>
</div>
</div>
{message.length > 0 && (
<div className="alert alert-secondary" role="alert">
<ul>
{message.map((item, i) => {
return <li key={i}>{item}</li>;
})}
</ul>
</div>
)}
<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.
Don’t forget to export the function component:
const UploadFiles = () => {
...
}
export default UploadFiles;
You can simplify import statement with:
Absolute Import in React
Add Files Upload Page 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 FilesUpload from "./components/FilesUpload";
function App() {
return (
<div className="container" style={{ width: "600px" }}>
<div className="my-3">
<h3>bezkoder.com</h3>
<h4>React Hooks Multiple Files Upload</h4>
</div>
<FilesUpload />
</div>
);
}
export default App;
Configure Port for React File Upload App
Because most of HTTP Server use CORS configuration that accepts resource sharing restricted to some sites or ports, and if you use the Project in this Spring Boot Server or this Node.js Server, 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
Run this React File Upload Axios App with command: npm start
.
Or: yarn start
.
Open Browser with url http://localhost:8081/
and check the result.
Conclusion
Today we’re learned how to build an React.js application for multiple Files upload using Hooks, Axios, Bootstrap with Progress Bars. We also provide the ability to show list of files, upload progress percentage, and to download file from the server.
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
For multiple Images upload with preview, please visit:
React.js Multiple Image Upload with Preview example
Happy Learning! See you again.
Further Reading
- https://github.com/axios/axios
- React Hooks
- React Custom Hook tutorial with example
- Bootstrap 4 Progress
- React Hooks CRUD example with Axios and Web API
- React Hooks: JWT Authentication (without Redux) example
- React + Redux: JWT Authentication with Hooks
Fullstack:
– Spring Boot + React Redux example: Build a CRUD App
– React + Spring Boot + MySQL: CRUD example
– React + Spring Boot + PostgreSQL: CRUD example
– React + Spring Boot + MongoDB: CRUD example
– React + Node.js + Express + MySQL: CRUD example
– React Redux + Node.js + Express + MySQL: CRUD example
– React + Node.js + Express + PostgreSQL example
– React + Node.js + Express + MongoDB example
– React + Django + Rest Framework example
Serverless:
– React Hooks + Firebase Realtime Database: CRUD App
– React Hooks + Firestore example: CRUD app
Source Code
You can find the complete source code for this tutorial at Github.
Typescript version: React Multiple File Upload example (with Typescript)
Hello,
This is a great tutorial. I’m learning so much. But there’s a small mistake. At import FilesUpload from “./components/FilesUpload”, it’s actually import FileUpload from “./components/FileUpload”; without “s”. Not a big deal.
But I got an error when running.
When I ran “npm start”, react automatically popped-up my browser to localhost:3000. When I uploaded a file, I got the error -> https://i.postimg.cc/GmT3z3sR/Capture.png (localhost:8080 connection refused)
I’m confused over the port number and use of axios. Is axios at the backend that is accepting our file upload? If so, shouldn’t I be running a separate node js service together with this react app? Meaning I need to open another CLI and run node with axios as localhost:8080?
And why you mentioned I should go to localhost:8081 cos when I went there, I get “localhost connection refused” but I could see the file upload page at localhost:3000.
I hope you can reply me. I love your tutorial!
Hi, the backend doesn’t use axios. If you provide Rest APIs with Node, it uses ExpressJs. Please make sure that you’ve already run one of mentioned backend (from my tutorials).
bezkoder,
Thank you in advance for your tutorials!
Rob