Spring Boot + ReactJS: File Upload & Download Example
Hello everyone, today we will learn how to upload and download the file with Spring Boot and React. You could download the source code from our GitHub repository.
Backend:
- Spring Boot 2.7.0
- Java 17
Frontend:
- React 17.0.1
- Axios 0.27.2
- Bootstrap 4.4.1
User Interface:
Backend Project Directory:
Frontend Project Directory:
We will build two projects:
1. Backend: springboot-fileupload-filedownload
2. Frontend: react-file-upload-download
Project 1: springboot-file-upload-download
pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.0</version> <!-- lookup parent from repository --> </parent> <groupId>com.knowledgefactory</groupId> <artifactId>springboot-file-upload-download</artifactId> <packaging>jar</packaging> <version>0.0.1-SNAPSHOT</version> <name>springboot-file-upload-download</name>
<properties> <java.version>17</java.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- spring mvc, rest --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <description>springboot-file-upload-download</description></project>
UploadDownloadService.java
@Servicepublic class UploadDownloadService { private static final String path = "/home/user/Desktop/files";
public List<String> uploadFile(MultipartFile file) throws Exception {
// Save file on system if (!file.getOriginalFilename().isEmpty()) {
BufferedOutputStream outputStream = new BufferedOutputStream( new FileOutputStream(new File(path, file.getOriginalFilename())));
outputStream.write(file.getBytes()); outputStream.flush(); outputStream.close();
} else { throw new Exception(); }
List<String> list = new ArrayList<String>(); File files = new File(path); String[] fileList = files.list(); for (String name : fileList) { list.add(name); }
return list;
}
public List<String> getListofFiles() throws Exception {
List<String> list = new ArrayList<String>(); File files = new File(path); String[] fileList = ((File) files).list(); for (String name : fileList) { list.add(name); }
return list;
}}
FileController.java
@CrossOrigin(origins = "*", maxAge = 3600)@RestControllerpublic class FileController {
private static final String path = "/home/user/Desktop/files/";
@Autowired UploadDownloadService service;
@PostMapping("/upload") public ResponseEntity<List<String>> fileUpload (@RequestParam("file") MultipartFile file) throws Exception {
return new ResponseEntity<>(service.uploadFile(file), HttpStatus.OK);
}
@GetMapping(path = "/download/{name}") public ResponseEntity<Resource> download (@PathVariable("name") String name) throws IOException {
File file = new File(path + name); Path path = Paths.get(file.getAbsolutePath()); ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(path));
return ResponseEntity.ok().headers(this.headers(name)) .contentLength(file.length()) .contentType(MediaType .parseMediaType("application/octet-stream")) .body(resource); }
@GetMapping("/files") public ResponseEntity<List<String>> getListOfFiles() throws Exception {
return new ResponseEntity<>(service.getListofFiles(), HttpStatus.OK);
}
private HttpHeaders headers(String name) {
HttpHeaders header = new HttpHeaders(); header.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + name); header.add("Cache-Control", "no-cache, no-store, must-revalidate"); header.add("Pragma", "no-cache"); header.add("Expires", "0"); return header;
}}
application.properties
spring.servlet.multipart.enabled=true# Threshold after which files are written to disk.spring.servlet.multipart.file-size-threshold=2KB# Max file size.spring.servlet.multipart.max-file-size=200MB# Max Request Sizespring.servlet.multipart.max-request-size=215MB
Spring Boot Driver
@SpringBootApplicationpublic class KnowledgefactorydemoApplication {
public static void main(String[] args) {
SpringApplication .run(KnowledgefactorydemoApplication.class, args); }}
Project 2: react-file-upload-download
package.json
{ "name": "react-file-upload-download", "version": "0.1.0", "private": true, "dependencies": { "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", "axios": "^0.27.2", "bootstrap": "^4.4.1", "react": "^17.0.1", "react-dom": "^17.0.1", "react-scripts": "3.4.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }}
common.js
import axios from "axios";
export default axios.create({ baseURL: "http://localhost:8080", headers: { "Content-type": "application/json" }});
/services/file.service.js
import http from "../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();
/components/file.component.js
import React, { Component } from "react";import UploadService from "../services/file.service";
export default class UploadFiles extends Component { constructor(props) { super(props); this.selectFile = this.selectFile.bind(this); this.upload = this.upload.bind(this);
this.state = { selectedFiles: undefined, currentFile: undefined, progress: 0, message: "", textValue: "Choose File", fileInfos: [], }; }
componentDidMount() { UploadService.getFiles().then((response) => { this.setState({ fileInfos: response.data, }); }); }
selectFile(event) { this.setState({ selectedFiles: event.target.files, }); }
upload() { let currentFile = this.state.selectedFiles[0];
this.setState({ progress: 0, currentFile: currentFile, });
UploadService.upload(currentFile, (event) => { this.setState({ progress: Math.round((100 * event.loaded) / event.total), }); }) .then((response) => { this.setState({ message: response.data.message, }); return UploadService.getFiles(); }) .then((files) => { this.setState({ fileInfos: files.data }); }) .catch(() => { this.setState({ progress: 0, message: "Failed to upload the file!", currentFile: undefined, }); });
this.setState({ selectedFiles: undefined, }); }
render() { const { selectedFiles, currentFile, progress, message, fileInfos, } = this.state;
return ( <div> <div class="form-group"> <input class="form-control" name="file" onChange={this.selectFile} type="file" /> </div> <div class="form-group"> <button class="btn btn-dark" disabled={!selectedFiles} onClick={this.upload} type="submit">Upload</button> </div>
{currentFile && ( <div className="progress"> <div className="progress-bar bg-success" role="progressbar" aria-valuenow={progress} aria-valuemin="0" aria-valuemax="100" style={{ width: progress + "%" }} > {progress}% </div> </div> )} <div className="alert alert-light" role="alert"> <p class="text-danger">{message}</p> </div>
<div className="card"> <div class="btn-group mx-auto"> <h4 class="card-header ">Download the file </h4> </div> <ul className="list-group"> {fileInfos && fileInfos.map((file) => (
<a href={`http://localhost:8080/download/${file}`} class="list-group-item list-group-item-action "> <li>{file}</li></a>
))} </ul> </div> </div> ); }}
App.js
import React from "react";import "./App.css";import "bootstrap/dist/css/bootstrap.min.css";
import UploadFiles from "./components/file.component";
function App() { return (
<div> <nav class="navbar navbar-dark bg-dark"> <div class="btn-group mx-auto"> <h2 class="text-white">Spring Boot + React JS: File Upload & Download example</h2> </div> </nav><br></br> <div class="container"> <UploadFiles /> </div></div> );}
export default App;
index.js
import React from 'react';import ReactDOM from 'react-dom';import './index.css';import App from './App';import * as serviceWorker from './serviceWorker';
ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root'));
serviceWorker.unregister();
index.css
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;}
code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;}
Download the complete source code - click here
Local Setup and Run the application
Step1: Download or clone the source code from GitHub to a local machine - Click here
Backend
Step 2: mvn clean install
Step 3: Run the Spring Boot application - mvn spring-boot:run
Frontend
Step 4: npm install
Step 5: npm start