In this tutorial, I will show you way to build Material UI Image Upload example with Preview to Rest API. The React App uses Axios and Multipart File for making HTTP requests, Material UI for progress bar and other UI components. You also have a display list of images’ information (with download url).
More Practice:
– React Material UI examples with a CRUD Application
– React JWT Authentication (without Redux) example
– React + Redux: JWT Authentication example
– Form Validation: React Hook Form & Material UI example
Contents
- Overview
- Technology
- Rest API for File Upload & Storage
- React App for Material UI upload Images
- Setup Material UI Image Upload Project
- Import Material UI to React App
- Initialize Axios for React HTTP Client
- Create Service for Image Upload
- Create Material UI Component for Upload Images
- Add Image Upload Component to App Component
- Configure Port for Material UI Image Upload App
- Run the App
- Further Reading
- Conclusion
- Source Code
Overview
We’re gonna create a React Material UI Image upload application in that user can:
- display the preview of image before uploading
- see the upload process (percentage) with progress bar
- view all uploaded images
- link to download the image when clicking on the file name
Technology
- React 16/17
- Axios 0.21.1
- Material UI 4.11.3
Rest API for File Upload & Storage
Here is the API that our React App will work with:
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
Or: Spring Boot Multipart File upload (to database) example
React App for Material UI upload Images
After building the React project is done, the folder structure will look like this:
Let me explain it briefly.
– upload-images.service provides methods to upload Image and get Files using Axios.
– upload-files.component contains Material UI upload form, progress bar, display of list images with download url.
– App.js is the container that we embed all React components.
– We customize styles in App.css.
– http-common.js initializes Axios with HTTP base Url and headers.
– We configure port for our App in .env
Setup Material UI Image Upload Project
Open cmd at the folder you want to save Project folder, run command:
npx create-react-app material-ui-image-upload
After the process is done. We create additional folders and files like the following tree:
public
src
components
upload-images.component.js
services
upload-files.service.js
App.css
App.js
index.js
package.json
Import Material UI to React App
Run command: npm install @material-ui/core
Or: yarn add @material-ui/core
Initialize Axios for React HTTP Client
Let’s install axios with command: 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 Image 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.
Create Material UI Component for Upload Images
Let’s create a Image Upload UI with Material UI Progress Bar, Box, Button, Typography and ListItem.
First we create a React component template and import UploadFilesService
:
components/upload-images.component.js
import React, { Component } from "react";
import UploadService from "../services/upload-files.service";
export default class UploadImages 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 = {
currentFile: undefined,
previewImage: undefined,
progress: 0,
message: "",
isError: false,
imageInfos: [],
};
}
}
Next we define selectFile()
method which helps us to get the selected Files from <input type="file" >
element later.
export default class UploadImages extends Component {
...
selectFile(event) {
this.setState({
currentFile: event.target.files[0],
previewImage: URL.createObjectURL(event.target.files[0]),
progress: 0,
message: ""
});
}
We use event.target.files[0]
for accessing current File as the first Item.
previewImage
We’re gonna use URL.createObjectURL()
static method to get the image preview URL as previewImage
. This method produces a DOMString
including a URL describing the object provided in the parameter. The URL life is tied to the document in the window on which it was created.
We call UploadService.upload()
method on the currentFile
with a callback. So create following upload()
method:
export default class UploadImages extends Component {
...
upload() {
this.setState({
progress: 0
});
UploadService.upload(this.state.currentFile, (event) => {
this.setState({
progress: Math.round((100 * event.loaded) / event.total),
});
})
.then((response) => {
this.setState({
message: response.data.message,
isError: false
});
return UploadService.getFiles();
})
.then((files) => {
this.setState({
imageInfos: files.data,
});
})
.catch((err) => {
this.setState({
progress: 0,
message: "Could not upload the image!",
currentFile: undefined,
isError: true
});
});
}
The progress will be calculated basing on event.loaded
and event.total
.
If the transmission is done, we call UploadService.getFiles()
to get the images’ information and assign the result to imageInfos
state, which is an array of {name, url}
objects.
We also need to do this work in componentDidMount()
method:
export default class UploadImages extends Component {
...
componentDidMount() {
UploadService.getFiles().then((response) => {
this.setState({
imageInfos: response.data,
});
});
}
}
Now we implement the render function of the Upload Image UI. Add the following code inside render()
:
...
import LinearProgress from '@material-ui/core/LinearProgress';
import { Box, Typography, Button, ListItem, withStyles } from '@material-ui/core';
const BorderLinearProgress = withStyles((theme) => ({
root: {
height: 15,
borderRadius: 5,
},
colorPrimary: {
backgroundColor: "#EEEEEE",
},
bar: {
borderRadius: 5,
backgroundColor: '#1a90ff',
},
}))(LinearProgress);
export default class UploadImages extends Component {
...
render() {
const {
currentFile,
previewImage,
progress,
message,
imageInfos,
isError
} = this.state;
return (
<div className="mg20">
<label htmlFor="btn-upload">
<input
id="btn-upload"
name="btn-upload"
style={{ display: 'none' }}
type="file"
accept="image/*"
onChange={this.selectFile} />
<Button
className="btn-choose"
variant="outlined"
component="span" >
Choose Image
</Button>
</label>
<div className="file-name">
{currentFile ? currentFile.name : null}
</div>
<Button
className="btn-upload"
color="primary"
variant="contained"
component="span"
disabled={!currentFile}
onClick={this.upload}>
Upload
</Button>
{currentFile && (
<Box className="my20" display="flex" alignItems="center">
<Box width="100%" mr={1}>
<BorderLinearProgress variant="determinate" value={progress} />
</Box>
<Box minWidth={35}>
<Typography variant="body2" color="textSecondary">{`${progress}%`}</Typography>
</Box>
</Box>)
}
{previewImage && (
<div>
<img className="preview my20" src={previewImage} alt="" />
</div>
)}
{message && (
<Typography variant="subtitle2" className={`upload-message ${isError ? "error" : ""}`}>
{message}
</Typography>
)}
<Typography variant="h6" className="list-header">
List of Images
</Typography>
<ul className="list-group">
{imageInfos &&
imageInfos.map((image, index) => (
<ListItem
divider
key={index}>
<img src={image.url} alt={image.name} height="80px" className="mr20" />
<a href={image.url}>{image.name}</a>
</ListItem>
))}
</ul>
</div >
);
}
}
In the code above, we use Material UI Progress Bar – Linear:
<LinearProgress variant="determinate" value={progress} />
variant
could be"determinate"
or"buffer"
to indicate progress bar style without/withbuffer
valuevalue
to specify the progress by percentageTypography
on the right to display progress value
const BorderLinearProgress = withStyles((theme) => ({
root: { ... },
colorPrimary: { ... },
bar: { ... },
}))(LinearProgress);
We also need to call withStyles()
with styles (root, colorPrimary, bar)
as input parameter to return BorderLinearProgress
(higher-order component).
To display List of uploaded images, we iterate over imageInfos
array using map()
function. On each file item, we use image.url
as href
attribute and image.name
for showing text. The data will be displayed with Material UI ListItem
.
You can simplify import statement with:
Absolute Import in React
Add Image Upload Component to App Component
Open App.js, import and embed the UploadImages
Component tag.
import React from "react";
import "./App.css";
import { Typography } from "@material-ui/core";
import UploadImages from "./components/upload-images.component";
function App() {
return (
<div className="container">
<div className="mg20">
<Typography variant="h5">bezkoder.com</Typography>
<Typography variant="h6">Material UI Image Upload with Preview</Typography>
</div>
<UploadImages />
</div>
);
}
export default App;
Configure Port for Material UI Image Upload App
Because most of HTTP Server use CORS configuration that accepts resource sharing retricted to some sites or ports, and if you use the Project in this post or this post for making 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
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
Or: Spring Boot Multipart File upload (to database) example
Run this React – Material UI Image Upload with Preview application: npm start
.
Open Browser with url http://localhost:8081/
and check the result.
Further Reading
Conclusion
Today we’re learned how to build an example for Image upload (preview) using Material UI with React and Axios. We also provide the ability to show list of images, upload progress bar, and to download file from the server.
Happy Learning! See you again.
More Practice:
- React Material UI examples with a CRUD Application
- React JWT Authentication (without Redux) example
- React + Redux: JWT Authentication example
- Form Validation: React Hook Form & Material UI example
Source Code
The source code for the React Client is uploaded to Github.