Azure Blob Storage + Spring Boot + React - File Upload, Download, and Delete Example

Hello everyone, Hope you are doing well, today we will learn how to upload, download, and delete the file with Azure Blob Storage, Spring Boot, and React. You could download the source code from our GitHub repository.


Technologies used

Backend:

  • Spring Boot 2.7.1
  • Java 17

Frontend:

  • React 17.0.2
  • Axios 0.27.2
  • Bootstrap 4.6.0
  • react-dropzone 11.3.4

Cloud Service:

  • Azure Blob Storage

A little bit of Background

Azure Blob Storage

Azure Blob storage is Microsoft's object storage solution for the cloud. Blob storage is optimized for storing massive amounts of unstructured data. Unstructured data is data that doesn't adhere to a particular data model or definition, such as text or binary data.

Spring Boot

Spring Boot makes it easy to create stand-alone, production-grade Spring-based Applications that you can "just run". 


React

React is a JavaScript library for building user interfaces.


Axios

Axios is the promise-based HTTP client for the browser.


react-dropzone

Simple React hook to create an HTML5-compliant drag'n'drop zone for files.


Let's begin,

Sign in to Azure Portal and create a resource group

Sign in to Azure portal https://portal.azure.com/#home and find "Resource groups" like below.



Then, create a resource group like the one below.


Here the resource group name is "Knowledgefactory". Create a resource group by clicking the "Review + create " button.



Create an Azure Storage Account and blob container using Azure Portal

Sign in to Azure portal https://portal.azure.com/#home and search for "Storage accounts" like below.

You will be taken to a page like the below image
Then click on the "Create storage account" button.

Create a storage account,
Enter/Select Resource groupStorage account name, etc... Then click on the "Review + create" button.

You will be taken to a page like the below image,
Then click on the "Create" button.

Now, You can see "Deployment is in progress" like the below image.

Once deployment is completed you can see the "Your deployment is complete" page like the below image.

Then, Create Blob Container, like the below image.

You will be taken to a page like the below image,

Then click on the "Connection String"
Copy "Connection string" and keep it safe for future purposes.



We will build two projects: 

1. Backend: spring-azure-storage-blob
2. Frontend: react-drag-drop-file-upload-download


Backend 

Final Project Directory:



Pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.1</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>spring-azure-storage-blob</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-azure-storage-blob</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>12.13.0</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>


<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>



application.yaml

azure:
storage:
container:
name: <Container name>
connection:
string: <Connection string>


Azure Blob Configuration

package com.knf.dev.demo.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobServiceClient;
import com.azure.storage.blob.BlobServiceClientBuilder;

@Configuration
public class AzureBlobConfig {

@Value("${azure.storage.connection.string}")
private String connectionString;

@Value("${azure.storage.container.name}")
private String containerName;

@Bean
public BlobServiceClient clobServiceClient() {

BlobServiceClient blobServiceClient =
new BlobServiceClientBuilder()
.connectionString(connectionString)
.buildClient();

return blobServiceClient;

}

@Bean
public BlobContainerClient blobContainerClient() {

BlobContainerClient blobContainerClient =
clobServiceClient()
.getBlobContainerClient(containerName);

return blobContainerClient;

}
}


Azure Blob Service

package com.knf.dev.demo.service;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import com.azure.core.http.rest.PagedIterable;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobServiceClient;
import com.azure.storage.blob.models.BlobItem;

@Component
public class AzureBlobService {

@Autowired
BlobServiceClient blobServiceClient;

@Autowired
BlobContainerClient blobContainerClient;

public String upload(MultipartFile multipartFile)
throws IOException {

// Todo UUID
BlobClient blob = blobContainerClient
.getBlobClient(multipartFile.getOriginalFilename());
blob.upload(multipartFile.getInputStream(),
multipartFile.getSize(), true);

return multipartFile.getOriginalFilename();
}

public byte[] getFile(String fileName)
throws URISyntaxException {

BlobClient blob = blobContainerClient.getBlobClient(fileName);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
blob.download(outputStream);
final byte[] bytes = outputStream.toByteArray();
return bytes;

}

public List<String> listBlobs() {

PagedIterable<BlobItem> items = blobContainerClient.listBlobs();
List<String> names = new ArrayList<String>();
for (BlobItem item : items) {
names.add(item.getName());
}
return names;

}

public Boolean deleteBlob(String blobName) {

BlobClient blob = blobContainerClient.getBlobClient(blobName);
blob.delete();
return true;
}

}


Create a Controller

package com.knf.dev.demo.controller;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.knf.dev.demo.service.AzureBlobService;

@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
public class AzureController {

@Autowired
private AzureBlobService azureBlobService;

@PostMapping("/upload")
public ResponseEntity<String> upload
(@RequestParam("file") MultipartFile file)
throws IOException {

String fileName = azureBlobService.upload(file);
return ResponseEntity.ok(fileName);
}

@GetMapping("/files")
public ResponseEntity<List<String>> getAllBlobs() {

List<String> items = azureBlobService.listBlobs();
return ResponseEntity.ok(items);
}

@DeleteMapping("/delete/{name}")
public ResponseEntity<Boolean> delete
(@PathVariable("name") String name) {

azureBlobService.deleteBlob(name);
return ResponseEntity.ok().build();
}

@GetMapping(path = "/download/{name}")
public ResponseEntity<Resource> getFile
(@PathVariable("name") String name)
throws URISyntaxException {

ByteArrayResource resource =
new ByteArrayResource(azureBlobService
.getFile(name));

HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" +
name + "\"");

return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.headers(headers).body(resource);
}
}


Spring Boot Main Driver

package com.knf.dev.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}



Frontend

Frontend Project Directory:


package.json

{
"name": "react-drag-drop-file-upload-download",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"axios": "^0.27.2",
"bootstrap": "^4.6.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-dropzone": "^11.3.4",
"react-scripts": "4.0.3",
"web-vitals": "^1.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"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/UploadFilesService.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");
}

deleteFile(name){
return http.delete("/delete/"+name);
}
}

export default new UploadFilesService();



/components/UploadFilesComponent.js

import React, { Component } from "react";
import Dropzone from "react-dropzone";
import UploadService from "../services/UploadFilesService";

export default class UploadFilesComponent
extends Component {

constructor(props) {
super(props);
this.upload = this.upload.bind(this);
this.onDrop = this.onDrop.bind(this);
this.deleteFile = this.deleteFile.bind(this);

this.state = {
selectedFiles: undefined,
currentFile: undefined,
progress: 0,
message: "",
fileInfos: [],
};
}

deleteFile(name){
UploadService.deleteFile(name).then( res => {
this. componentDidMount();
});
}

componentDidMount() {
UploadService.getFiles().then((response) => {
this.setState({
fileInfos: response.data,
});
});
}

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: "Could not upload the file!",
currentFile: undefined,
});
});

this.setState({
selectedFiles: undefined,
});
}

onDrop(files) {
if (files.length > 0) {
this.setState({ selectedFiles: files });
}
}

render() {
const { selectedFiles, currentFile,
progress, message, fileInfos } = this.state;

return (
<div>
<Dropzone onDrop={this.onDrop} multiple={false}>
{({ getRootProps, getInputProps }) => (
<section>
<div {...getRootProps({ className: "dropzone" })}>
<input {...getInputProps()} />
{selectedFiles && selectedFiles[0].name ? (
<div className="selected-file">
{selectedFiles && selectedFiles[0].name}
</div>
) : (
<h4>Drag and drop file here, or click to
select file</h4>
)}
</div>
<aside className="selected-file-wrapper">
<button
className="btn btn-secondary btn-lg btn-block"
disabled={!selectedFiles}
onClick={this.upload}>
Upload
</button>
</aside>
</section>
)}
</Dropzone>
<br></br>
{currentFile && (
<div className="progress mb-3">
<div className
="progress-bar progress-bar-info progress-bar-striped"
role="progressbar"
aria-valuenow={progress}
aria-valuemin="0"
aria-valuemax="100"
style={{ width: progress + "%" }}
>
{progress}%
</div>
</div>
)}
<div className="alert alert-light" role="alert">
{message}
</div>

{fileInfos.length > 0 && (
<div className="card">
<table class="table">
<thead>
<tr>
<th scope="col">File Name</th>
<th scope="col">Action</th>
<th scope="col">Action</th>
</tr>
</thead>
{fileInfos.map((file) => (
<>
<tbody>
<tr>
<td>{file}</td>
<td><a href={`http://localhost:8080/download/${file}`}
class="btn btn-success">Download</a></td>
<td><button onClick={ () => this.deleteFile(file)}
type="button" class="btn btn-danger">
Delete</button></td>
</tr>
</tbody>
</>
))}
</table>
</div>
)}
</div>
);
}
}



App.js

import React from "react";
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
import UploadFiles from "./components/UploadFilesComponent";

function App() {
return (

<div>
<nav class="navbar navbar-dark bg-dark">
<div class="btn-group mx-auto">
<h4 class="text-white">Azure Blob Storage
+ Spring Boot + React - File Upload
, Download, & Delete</h4>
</div>
</nav><br></br>
<div class="container">
<UploadFiles />
</div></div>
);
}

export default App;



App.css

.dropzone {
text-align: center;
padding: 40px;
border: 4px 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;
}



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;
}


/public/index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable
JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

Download the complete source code - click here

                                        

Local Setup and Run the application

Step1: Download or clone the source code from GitHub to the 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

From the browser call the endpoint http://localhost:3000/


More related topics,

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Spring Boot + Mockito simple application with 100% code coverage

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete