Using the new Azure SDK for Java to upload images asynchronously, using Spring Reactor

Using the new Azure SDK for Java to upload images asynchronously, using Spring Reactor

Using the new Azure SDK for Java to upload images asynchronously, using Spring Reactor

The new Azure SDK for Java

This blog post uses the upcoming Azure SDK for Java: at the time of this writing, this is still a preview release, but many people have already started using it.

To be more precise, we are talking here of the Azure SDK for Java (August 2019 Preview), also seen as "1.0.0-preview.2" on Maven Central.

This new release is important as it uses some new, modern API guidelines, and also because its asynchronous features are powered by Spring Reactor.

Those new reactive APIs are interesting as they deliver better scalability, and work very well with Spring. Unfortunately, they come at the cost of using an API that is more complex to understand, and more complex to debug: this is why we are doing this blog post!

The problem with uploading data

Uploading data takes time, and usually need a good connection: if you're working with mobile devices, this can definitely be an issue! If we use a thread-based model, this means that sending several files is going to block several threads, and is not going to be very scalable. Or you could put all files in a queue that you would manage yourself: this is probably quite complex to code, and this will prevent you from uploading those files in parallel, so performance won't be very good. This is where Spring Reactor comes into play!

The idea here is to upload that data in an asynchronous, non-blocking way, so we can upload many files in parallel without blocking the system.

Spring Reactor and Spring Webflux

Please note that the example here works with Spring Webflux: this is the reactive version of Spring, and as this works in a totally different way than the classical Spring MVC (which is thread-based), this code won't work with Spring MVC. This is one of the issues when doing reactive programming: it's not really possible to mix reactive code and thread-based code, so all your project will need be made and thought with a reactive API. In my opinion, this is best suited in a microservice architecture, where you will code some specific services with a reactive API, and some with a traditional thread-based API. Reactive microservices will probably be a bit more complex to develop and debug, but will provide better scalability, start-up time, memory consumption, so they will be used for some specific, resource-intensive tasks.

Creating a Storage Account

In the the Azure portal, create a new storage account:

Once it is created, go to that storage account and select "Shared access signature", or SAS. A SAS is a signature that allows to access some resources, for a limited period of time: it is perfect for uploading data on a specific location, without compromising security.

After clicking on "Generate SAS and connection string", copy the last generated text field, named "Blob service SAS URL". This is the one we will use with the Azure SDK for Java.

Add the Azure SDK for Java to the pom.xml

As the Azure SDK for Java preview is on Maven Central, this is just a matter of adding the dependency to the project's pom.xml:

<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-storage-blob</artifactId>
    <version>12.0.0-preview.2</version>
</dependency>

Using the new asynchronous API

Let's first have a look at the final code, which is available on https://github.com/jdubois/jhipster-azure-blob-storage/blob/master/src/main/java/com/example/demo/PictureResource.java:

@RestController
@RequestMapping("/api")
public class PictureResource {

Logger log = LoggerFactory.getLogger(PictureResource.class);

@Value("${AZURE_BLOB_ENDPOINT}")
private String endpoint;

@PostMapping("/picture")
public void uploadPicture() throws IOException {
    log.debug("Configuring storage client");
    BlobServiceAsyncClient client =  new BlobServiceClientBuilder()
        .endpoint(endpoint)
        .buildAsyncClient();

    client.createContainer("pictures")
        .doOnError((e) -&gt; {
            log.info("Container already exists");
        })
        .flatMap(
            (clientResponse) -&gt; {
                log.info("Uploading picture");
                return clientResponse
                    .value()
                    .getBlockBlobAsyncClient("picture.png")
                    .uploadFromFile("src/main/resources/image.png");
            })
        .subscribe();
}

}

WARNING This API only works when using Spring Reactive, so please note you need to use this in a Spring Webflux project, and not a Spring MVC project.

Authentication is done using the "Blob service SAS URL" that we copied above, and which is provided using the AZURE_BLOB_ENDPOINT environment variable in this example: please note that the SAS is included in the URL, so there is no need to authenticate elsewhere (there is a credentials() method in the API, that might be misleading, but which is useless in our case). This URL should thus be stored securely, and not commited with your code.

Sending the image uses the Spring Reactor API:

  • We create a specific pictures container to store some data
  • We then use Spring Reactor's API to upload a picture
  • And we finish by using the subscribe() method, which makes our code run asynchronously

As a result, this method will return very quickly, and then the container will be created and the image uploaded, in another thread. This can make debugging harder, but allows our application to accept many more requests, and process them asynchronously.

Improving the reactive code

This tip was provided by Christophe Bornet, many thanks to him!

The previous code is what we usually see in projects, but that can be improved, in order to let Spring Webflux handle the .subscribe() part: this will preserve the backpressure between Spring Webflux and the Azure SDK.

The change can be seen in this commit, where we replace the .subscribe() by a .then() and we return a Mono<Void> instead of not returning anything. It will be Spring Webflux's responsibility to handle that Mono and call .subscribe().

The resulting code is the following:

    @PostMapping("/picture")
    public Mono<Void> uploadPicture() throws IOException {
        log.debug("Configuring storage client");
        BlobServiceAsyncClient client =  new BlobServiceClientBuilder()
            .endpoint(endpoint)
            .buildAsyncClient();

    return client.createContainer("pictures")
        .doOnError((e) -&gt; {
            log.info("Container already exists");
        })
        .flatMap(
            (clientResponse) -&gt; {
                log.info("Uploading picture");
                return clientResponse
                    .value()
                    .getBlockBlobAsyncClient("picture.png")
                    .uploadFromFile("src/main/resources/image.png");
            })
        .then();
}

It's a bit more advanced usage of the reactive APIs, but the result should be worth the trouble.

Conclusion and feedback

We are currently lacking documentation and samples on this new asynchronous API in Azure SDK for Java. I believe that it is very important in some specific scenarios like the one we have here, as typically you should not upload or download data in the current thread if you want a scalable application.

This SDK is still in preview, so if you have feedback on this API, please comment on this post!

For example, the current API allows you to create a container (and this will fail if a container already exist) or get an existing container (and this will fail if it does not exist yet). Do you think there should be an option to have something like getOrCreateContainer("name"), that will automatically create a container if it is requested?

Originally published by  Julien Dubois at dev.to

=====================================================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

☞ Java Programming Masterclass for Software Developers

☞ JSP, Servlets and JDBC for Beginners: Build a Database App

☞ Java 8 New Features In Simple Way

☞ Java In-Depth: Become a Complete Java Engineer!

☞ Java for Absolute Beginners

java azure

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

How to Install OpenJDK 11 on CentOS 8

What is OpenJDK? OpenJDk or Open Java Development Kit is a free, open-source framework of the Java Platform, Standard Edition (or Java SE).

Java Core (2020) | Java for beginners | Brush up your Java Skills | Get Hired

In this video we will learn about all the major concepts that come under Java Core . The course is very carefully designed and is made with keeping simplicit...

Java EE on Azure with WebLogic and Linux Virtual Machines

Microsoft wants to help you run Java EE on Azure. This blog post announces the availability of four Azure marketplace offers jointly developed with Oracle. These offers let you easily run WebLogic Server on Microsoft Azure Linux Virtual machines in a variety of ready-to-use configurations.

Getting Started With Azure Event Grid Viewer

In the article, we will go to the next step to create a subscription and use webhook event handlers to view those logs in our Azure web application.

Java NIO FileSystem APIs and the new Azure SDK for Java

The Azure SDK for Java recently released a preview of a custom implementation of Java’s FileSystem APIs (the azure-storage-blob-nio package on Maven), enabling developers to access Azure Blob Storage through a familiar file system interface. By adding this new dependency, you can easily instruct the JVM to point all file system operations to Azure Blob Storage rather than the local system.