How to upload and download a file with Azure Blob Storage and Spring Boot
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.

Hello, I'm Srivastava, in this post we will see how to upload a file to Azure Blob Storage and download the file from the same.
What is 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.
Why use Blob Storage?
Blob storage is designed for:
- Serving images or documents directly to a browser.
- Storing files for distributed access.
- Streaming video and audio.
- Writing to log files.
- Storing data for backup and restore, disaster recovery, and archiving.
- Storing data for analysis by an on-premises or Azure-hosted service.
Blob Storage Resources
Blob storage offers three types of resources:
- The storage account
- A container in the storage account
- A blob in a container
The following diagram shows the relationship between these resources.
Storage Accounts
A storage account provides a unique namespace in Azure for your data. Every object that you store in Azure Storage has an address that includes your unique account name. The combination of the account name and the Azure Storage blob endpoint forms the base address for the objects in your storage account.
For example, if your storage account is named mystorageaccount, then the default endpoint for Blob storage is:
http://mystorageaccount.blob.core.windows.net
Follow the instructions provided here to create storage account.
The details we need from the storage account are: Account Name, Account Key, Blob Endpoint
Containers
A container organizes a set of blobs, similar to a directory in a file system. A storage account can include an unlimited number of containers, and a container can store an unlimited number of blobs.
Note: The container name must be lowercase.
Blobs
Azure Storage supports three types of blobs:
- Block blobs store text and binary data. Block blobs are made up of blocks of data that can be managed individually. Block blobs can store up to about 190.7 TiB.
- Append blobs are made up of blocks like block blobs, but are optimized for append operations. Append blobs are ideal for scenarios such as logging data from virtual machines.
- Page blobs store random access files up to 8 TiB in size. Page blobs store virtual hard drive (VHD) files and serve as disks for Azure virtual machines.
Implementation
Creating the Project
Spring Boot provides Spring Initializr, a quick start generator for Spring Projects. Head over to https://start.spring.io to access this web tool. Include web,azure storage, lombok, devtools and configuration processor dependencies and click generate. A zip file will be downloaded which contains your project related settings and a pom.xml to get dependencies. You can unzip it and import it into your favourite IDE.
(OR)
Some IDEs such as IntelliJ, STS will allow you to create project right from it that has Spring Initializr.
If you want to manually include the storage dependency, the following dependency should be added to your pom.xml.
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>azure-spring-boot-starter-storage</artifactId>
<version>3.13.0</version>
</dependency>
Making Changes
Once you boostrapped the project, open application.properties and add the following properties to it
azure.storage.account-name=youraccountname
azure.storage.account-key=youraccountkey
azure.storage.blob-endpoint=https://youraccountname.blob.core.windows.net
file.upload-dir=./uploads
I gave uploads directory as my file storage location which would be under our project root path. This would directory will be created when we start our app (of course we will configure it). You can give your own path to be uploaded.
Create few packages inside your main package (e.g. com.fileapi) such as controller
, config
, property
, and service
packages.
Add the following code in property package.
FileStorageProperties.java
package com.fileapi.property;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Getter
@Setter
@ConfigurationProperties(prefix = "file")
public class FileStorageProperties {
private String uploadDir;
}
We have used @ConfigurationProperties(prefix = "file")
annotation so that it binds all the properties with prefix file
to the fields in our class on application startup. If you want to create an additional file property in properties file, all you need to do is to just add a field in our class.
Enabling Configuration Properties
To make @ConfigurationProperties
feature work, you need to add @EnableConfigurationProperties
to any configuration class. We will add it in our main class. So open src/main/java/com/fileapi/SpringBootAzureFileApiApplication.java
and add the @EnableConfigurationProperties
annoation to it like this:
package com.fileapi;
import com.fileapi.property.FileStorageProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties({FileStorageProperties.class})
public class SpringBootAzureFileApiApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootAzureFileApiApplication.class, args);
}
}
Now create a file named AzureBlobStorageConfig.java and add the following code to it
package com.fileapi.config;
import com.azure.storage.blob.BlobServiceClient;
import com.azure.storage.blob.BlobServiceClientBuilder;
import com.azure.storage.common.StorageSharedKeyCredential;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Locale;
@Configuration
public class AzureBlobStorageConfig {
@Value("${azure.storage.account-name}")
private String accountName;
@Value("${azure.storage.account-key}")
private String accountKey;
@Bean
public BlobServiceClient getBlobServiceClient() {
return new BlobServiceClientBuilder()
.endpoint(String.format(Locale.ROOT, "https://%s.blob.core.windows.net", accountName))
.credential(new StorageSharedKeyCredential(accountName, accountKey))
.buildClient();
}
}
We created the configuration to get the BlobServiceClient bean. It uses the storage account name and account key.
Service for upload and download
Create a file FileService.java in service package and add the following code to it.
package com.fileapi.service;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobServiceClient;
import com.azure.storage.blob.specialized.BlockBlobClient;
import com.fileapi.property.FileStorageProperties;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@Log4j2
@Service
public class FileService {
private final Path fileStorageLocation;
private final BlobServiceClient blobServiceClient;
@Autowired
public FileService(@NonNull FileStorageProperties fileStorageProperties, BlobServiceClient blobServiceClient) {
// get the path of the upload directory
fileStorageLocation = Path.of(fileStorageProperties.getUploadDir());
this.blobServiceClient = blobServiceClient;
try {
// creates directory/directories, if directory already exists, it will not throw exception
Files.createDirectories(fileStorageLocation);
} catch (IOException e) {
log.error("Could not create the directory where the uploaded files will be stored.", e);
}
}
public Path getFileStorageLocation() {
try {
Files.createDirectories(this.fileStorageLocation);
} catch (IOException e) {
log.error("Could not create the directory where the uploaded file will be stored.", e);
}
return fileStorageLocation;
}
public Boolean uploadAndDownloadFile(@NonNull MultipartFile file, String containerName) {
boolean isSuccess = true;
BlobContainerClient blobContainerClient = getBlobContainerClient(containerName);
String filename = file.getOriginalFilename();
BlockBlobClient blockBlobClient = blobContainerClient.getBlobClient(filename).getBlockBlobClient();
try {
// delete file if already exists in that container
if (blockBlobClient.exists()) {
blockBlobClient.delete();
}
// upload file to azure blob storage
blockBlobClient.upload(new BufferedInputStream(file.getInputStream()), file.getSize(), true);
String tempFilePath = fileStorageLocation + "/" + filename;
Files.deleteIfExists(Paths.get(tempFilePath));
// download file from azure blob storage to a file
blockBlobClient.downloadToFile(new File(tempFilePath).getPath());
} catch (IOException e) {
isSuccess = false;
log.error("Error while processing file {}", e.getLocalizedMessage());
}
return isSuccess;
}
private @NonNull BlobContainerClient getBlobContainerClient(@NonNull String containerName) {
// create container if not exists
BlobContainerClient blobContainerClient = blobServiceClient.getBlobContainerClient(containerName);
if (!blobContainerClient.exists()) {
blobContainerClient.create();
}
return blobContainerClient;
}
}
In the above service there are a couple of important things going on. In the getBlobContainerClient()
method, we are creating the container if not exists. Remember we created bean to get BlobServiceClient
and now we have autorwired in the service. And we are building BlockBlobClient
passing file to it. If the same file exists in the container, we are deleting it. Finally we upload the file to azure blob storage. As we already have the reference to the to the uploaded file, we are calling the method downloadToFile()
and storing it in our file storage location.
Controller for REST API
Create a file FileController.java in controller package and add the following code to it.
package com.fileapi.controller;
import com.fileapi.service.FileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.nio.file.Files;
import java.nio.file.Paths;
@RestController
@RequestMapping("/api/v1/file")
public class FileController {
private final FileService fileService;
@Autowired
public FileController(FileService fileService) {
this.fileService = fileService;
}
@PostMapping(value = "/", produces = {MediaType.IMAGE_PNG_VALUE, MediaType.IMAGE_JPEG_VALUE})
public ResponseEntity<?> uploadAndDownload(@RequestParam("file") MultipartFile file) {
try {
if (fileService.uploadAndDownloadFile(file, "files")) {
final ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(Paths.get(fileService
.getFileStorageLocation() + "/" + file.getOriginalFilename())));
return ResponseEntity.status(HttpStatus.OK).contentLength(resource.contentLength()).body(resource);
}
return ResponseEntity.ok("Error while processing file");
} catch (Exception e) {
return ResponseEntity.ok("Error while processing file");
}
}
}
For the purpose of this example, I have used only images (png and jpeg) to upload and download. I have given the container name as "files".
Testing the API
You can also find the source code at github.
Thanks for reading!