Prerequisites
Before we start, ensure you have the following:
- Java Development Kit (JDK) installed
- Apache Maven installed
- Node.js and npm installed
- An IDE (such as IntelliJ IDEA, Eclipse, or VS Code) installed
Step 1: Setting Up the Spring Boot Project
1.1 Create a Spring Boot Project
-
Open Spring Initializr:
- Go to Spring Initializr in your web browser.
-
Configure Project Metadata:
- Project: Maven Project
- Language: Java
- Spring Boot: Select the latest version of Spring Boot 3
- Group: com.example
- Artifact: spring-boot-react-file
- Name: spring-boot-react-file
- Description: Full Stack Application with Spring Boot and React for File Upload and Download
- Package Name: com.example.springbootreactfile
- Packaging: Jar
- Java Version: 17 (or your preferred version)
- Click
Next
.
-
Select Dependencies:
- On the
Dependencies
screen, select the dependencies you need:- Spring Web
- Spring Boot DevTools
- Click
Next
.
- On the
-
Generate the Project:
- Click
Generate
to download the project zip file. - Extract the zip file to your desired location.
- Click
-
Open the Project in Your IDE:
- Open your IDE and import the project as a Maven project.
1.2 Project Structure
After importing the project, you will see the following structure in your IDE:
spring-boot-react-file
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── springbootreactfile
│ │ │ ├── SpringBootReactFileApplication.java
│ │ │ ├── controller
│ │ │ └── service
│ ├── main
│ │ └── resources
│ │ ├── application.properties
│ └── test
│ └── java
│ └── com
│ └── example
│ └── springbootreactfile
│ └── SpringBootReactFileApplicationTests.java
└── pom.xml
Step 2: Creating the Backend
2.1 Create the File Service
This service will handle storing the uploaded files on the server and retrieving them.
In the service
package, create a new Java class named FileService
:
package com.example.springbootreactfile.service;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
@Service
public class FileService {
private final Path fileStorageLocation = Paths.get("uploads").toAbsolutePath().normalize();
public FileService() {
try {
Files.createDirectories(this.fileStorageLocation);
} catch (Exception ex) {
throw new RuntimeException("Could not create the directory where the uploaded files will be stored.", ex);
}
}
public String storeFile(MultipartFile file) {
// Normalize file name
String fileName = UUID.randomUUID().toString() + "_" + file.getOriginalFilename();
try {
// Copy file to the target location (Replacing existing file with the same name)
Path targetLocation = this.fileStorageLocation.resolve(fileName);
Files.copy(file.getInputStream(), targetLocation);
return fileName;
} catch (IOException ex) {
throw new RuntimeException("Could not store file " + fileName + ". Please try again!", ex);
}
}
public Resource loadFileAsResource(String fileName) {
try {
Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
Resource resource = new UrlResource(filePath.toUri());
if (resource.exists()) {
return resource;
} else {
throw new RuntimeException("File not found " + fileName);
}
} catch (Exception ex) {
throw new RuntimeException("File not found " + fileName, ex);
}
}
}
Explanation:
- Path Initialization: The
fileStorageLocation
is initialized to the "uploads" directory in the project root. This is where the files will be stored. - Directory Creation: In the constructor, we create the "uploads" directory if it doesn't exist.
- File Storage Method: The
storeFile
method stores the uploaded file with a unique name by appending a UUID to avoid conflicts. It then saves the file in thefileStorageLocation
directory. - Load File Method: The
loadFileAsResource
method loads a file as a resource by its name. If the file exists, it returns the resource; otherwise, it throws an exception.
2.2 Create the File Controller
This controller will expose the REST API endpoints to handle file uploads and downloads.
In the controller
package, create a new Java class named FileController
:
package com.example.springbootreactfile.controller;
import com.example.springbootreactfile.service.FileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/api/files")
public class FileController {
private final FileService fileService;
@Autowired
public FileController(FileService fileService) {
this.fileService = fileService;
}
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
String fileName = fileService.storeFile(file);
return ResponseEntity.ok("File uploaded successfully: " + fileName);
}
@GetMapping("/download/{fileName:.+}")
public ResponseEntity<Resource> downloadFile(@PathVariable String fileName) {
Resource resource = fileService.loadFileAsResource(fileName);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
}
}
Explanation:
- File Upload Endpoint: The
/upload
endpoint handlesPOST
requests to upload files. It receives aMultipartFile
and stores it using theFileService
. - File Download Endpoint: The
/download/{fileName}
endpoint handlesGET
requests to download files. It receives the file name as a path variable and loads the file as a resource using theFileService
. - Response: The upload endpoint returns a success message with the stored file name. The download endpoint returns the file as an attachment in the response.
2.3 Update application.properties
Ensure the application.properties
file is set up correctly to handle file uploads:
# File upload properties
spring.servlet.multipart.max-file-size=2MB
spring.servlet.multipart.max-request-size=2MB
Step 3: Creating the Frontend with React
3.1 Set Up React Project
-
Open a terminal and navigate to your workspace directory.
-
Create a new React project using Create React App:
npx create-react-app react-frontend
-
Navigate to the project directory:
cd react-frontend
3.2 Install Axios
Install Axios to make HTTP requests:
npm install axios
3.3 Install Bootstrap
Install Bootstrap for styling:
npm install bootstrap
3.4 Create Components
Create the necessary components for the file upload and download functionalities.
3.4.1 Create FileService.js
Create a new file FileService.js
in the src
directory to handle API requests:
import axios from 'axios';
const API_BASE_URL = "http://localhost:8080/api/files";
class FileService {
uploadFile(file) {
const formData = new FormData();
formData.append("file", file);
return axios.post(`${API_BASE_URL}/upload`, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
}
downloadFile(fileName) {
return axios.get(`${API_BASE_URL}/download/${fileName}`, {
responseType: 'blob',
});
}
}
export default new FileService();
Explanation:
-
API Base URL: The base URL for the API endpoints.
-
uploadFile Method: This method takes a file as input, creates a
FormData
object, and sends aPOST
request to the backend API to upload the file. -
downloadFile Method: This method takes a file name as input and sends a
GET
request to the backend API to download the file.
3.4.2 Create FileUploadComponent.js
Create a new file FileUploadComponent.js
in the src/components
directory:
import React, { useState } from 'react';
import FileService from '../FileService';
import 'bootstrap/dist/css/bootstrap.min.css';
const FileUploadComponent = () => {
const [selectedFile, setSelectedFile] = useState(null);
const [message, setMessage] = useState('');
const handleFileChange = (e) => {
setSelectedFile(e.target.files[0]);
};
const handleFileUpload = async (e) => {
e.preventDefault();
try {
const response = await FileService.uploadFile(selectedFile);
setMessage(response.data);
} catch (error) {
setMessage('File upload failed!');
}
};
return (
<div className="container mt-5">
<div className="row justify-content-center">
<div className="col-md-6">
<div className="card">
<div className="card-header">File Upload</div>
<div className="card-body">
{message && <div className="alert alert-info">{message}</div>}
<form onSubmit={handleFileUpload}>
<div className="form-group">
<label>Choose file</label>
<input
type="file"
className="form-control"
onChange={handleFileChange}
/>
</div>
<button type="submit" className="btn btn-primary mt-3">Upload</button>
</form>
</div>
</div>
</div>
</div>
</div>
);
};
export default FileUploadComponent;
Explanation:
- File Selection: The
handleFileChange
method sets the selected file to the component's state when a file is chosen. - File Upload: The
handleFileUpload
method sends the selected file to the backend API usingFileService
and handles the response or error messages. - UI: The component renders a form with a file input and a submit button. It displays messages based on the file upload result.
3.4.3 Create FileDownloadComponent.js
Create a new file FileDownloadComponent.js
in the src/components
directory:
import React, { useState } from 'react';
import FileService from '../FileService';
import 'bootstrap/dist/css/bootstrap.min.css';
const FileDownloadComponent = () => {
const [fileName, setFileName] = useState('');
const [message, setMessage] = useState('');
const handleFileChange = (e) => {
setFileName(e.target.value);
};
const handleFileDownload = async (e) => {
e.preventDefault();
try {
const response = await FileService.downloadFile(fileName);
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
setMessage('File downloaded successfully!');
} catch (error) {
setMessage('File download failed!');
}
};
return (
<div className="container mt-5">
<div className="row justify-content-center">
<div className="col-md-6">
<div className="card">
<div className="card-header">File Download</div>
<div className="card-body">
{message && <div className="alert alert-info">{message}</div>}
<form onSubmit={handleFileDownload}>
<div className="form-group">
<label>Enter file name</label>
<input
type="text"
className="form-control"
value={fileName}
onChange={handleFileChange}
/>
</div>
<button type="submit" className="btn btn-primary mt-3">Download</button>
</form>
</div>
</div>
</div>
</div>
</div>
);
};
export default FileDownloadComponent;
Explanation:
- File Name Input: The
handleFileChange
method sets the file name to the component's state when a file name is entered. - File Download: The
handleFileDownload
method sends the file name to the backend API usingFileService
and handles the response or error messages. If the download is successful, it creates a link to download the file and triggers a click event to download it. - UI: The component renders a form with an input for the file name and a submit button. It displays messages based on the file download result.
3.4.4 Create App.js
Modify the App.js
file to include the file upload and download components:
import React from 'react';
import FileUploadComponent from './components/FileUploadComponent';
import FileDownloadComponent from './components/FileDownloadComponent';
import 'bootstrap/dist/css/bootstrap.min.css';
const App = () => {
return (
<div className="container">
<FileUploadComponent />
<FileDownloadComponent />
</div>
);
};
export default App;
3.4.5 Update index.js
Ensure the index.js
file is set up correctly:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import 'bootstrap/dist/css/bootstrap.min.css';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Step 4: Running the Application
4.1 Run the Spring Boot Application
- Open the
SpringBootReactFileApplication
class in thesrc/main/java/com/example/springbootreactfile
directory. - Click the green
Run
button in your IDE or use the terminal to run the application:./mvnw spring-boot:run
4.2 Run the React Application
-
Open a terminal and navigate to the
react-frontend
directory. -
Start the React application:
npm start
-
Open your web browser and navigate to
http://localhost:3000
.
You can now use the file upload and download functionalities provided by the React frontend and Spring Boot backend.
Conclusion
In this tutorial, we created a full-stack application using Spring Boot for the backend and React (with functional components and hooks) for the frontend. We implemented both file upload and download functionalities and created simple forms for uploading and downloading files with React. This setup provides a solid foundation for developing more complex full-stack applications with file upload and download capabilities.
Comments
Post a Comment