Spring boot: uploading and downloading file Azure Blob Store using rest API
If you have landed on this page means either your working with spring boot and trying to upload a file to Azure blob storage. so we know spring boot has gained popularity for its simple approach to bootstrap an application in very less time, Also Azure is gaining a lot of popularity these days as people are looking for an alternative.
So if you're wondering what is Azure Blob, it is another object store just like AWS S3 where you store and retrieve the file using API in pay as you go fashion and eliminating the scalability from your hands.
So now we shall see how to upload and download files from Azure Blob Store using spring boot. but before we need to do a couple of steps prior.
1) Pre Setup Create Storage Account On Azure
2) Fill the above form and click on review and create
3) Next step is to create a Container, a place where are files are store logically. in reality there will different physical locations for a single file.
4) Navigate to storage account > Access Keys and copy the connection string.
Now Create Spring boot application and add these dependencies to your pom.
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
the above dependency provides multipart file upload capability to the spring boot application.
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>12.6.0</version>
</dependency>
the Azure Client Storage is the java SDK provided by Azure themselves. using this we write the wrapper so we can reuse the required feature. So my completed pom looks like this
<?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.3.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>azureblob</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</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.6.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</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>
Add the connection String to your property file.
blob.connection-string=<your key from azure portal>
blob.container-name=<container-anme>
Create the Configuration Class returns the BlobClientBuilder Bean.
package com.example.azureblob;
import com.azure.storage.blob.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AzureStorageBlobClientConfig {
@Value("${blob.connection-string}")
String connectionString;
@Value("${blob.container-name}")
String containerName;
@Bean
public BlobClientBuilder getClient() {
BlobClientBuilder client = new BlobClientBuilder();
client.connectionString(connectionString);
client.containerName(containerName);
return client;
}
}
The soul purpose of this class is to make the client builder singleton and to auto-wire it where ever necessary. And also we can change the connection based on a different profile. the bean
Let's write the Adapter Service Class which implements file upload and download logic.
package com.example.azureblob;
import com.azure.storage.blob.BlobClientBuilder;
import com.azure.storage.blob.models.BlobProperties;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.UUID;
@Service
public class AzureBlobAdapter {
@Autowired
BlobClientBuilder client;
public String upload(MultipartFile file, String prefixName) {
if(file != null && file.getSize() > 0) {
try {
//implement your own file name logic.
String fileName = prefixName+ UUID.randomUUID().toString() +file.getOriginalFilename();
client.blobName(fileName).buildClient().upload(file.getInputStream(),file.getSize());
return fileName;
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
public byte[] getFile(String name) {
try {
File temp = new File("/temp/"+name);
BlobProperties properties = client.blobName(name).buildClient().downloadToFile(temp.getPath());
byte[] content = Files.readAllBytes(Paths.get(temp.getPath()));
temp.delete();
return content;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public boolean deleteFile(String name) {
try {
client.blobName(name).buildClient().delete();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
As we are familiar with the Adapter design pattern we write the middle layer to implement the required feature of the third party client by casting input and output to our fit.
Final Step lets create the controller and implement those.
this is the bonus section for those who are searching google for uploading the file using spring boot's rest controller. also downloading the file without leaving this page :-P
package com.example.azureblob;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@RestController
public class AzureBlobFileController {
@Autowired
AzureBlobAdapter azureAdapter;
@PostMapping(path = "/upload", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public Map<String, String> uploadFile(@RequestPart(value = "file", required = true) MultipartFile files) {
String name = azureAdapter.upload(files, "prefix");
Map<String, String> result = new HashMap<>();
result.put("key", name);
return result;
}
@GetMapping(path = "/download")
public ResponseEntity<ByteArrayResource> uploadFile(@RequestParam(value = "file") String file) throws IOException {
byte[] data = azureAdapter.getFile(file);
ByteArrayResource resource = new ByteArrayResource(data);
return ResponseEntity
.ok()
.contentLength(data.length)
.header("Content-type", "application/octet-stream")
.header("Content-disposition", "attachment; filename=\"" + file + "\"")
.body(resource);
}
}
I have implemented the file upload and download Rest Urls, which covers major use cases for the fellow developer, also Adapter provides delete feature. i hope that will be easy for anyone to map it to the Rest endpoint.