In this section, we list the main changes you need to be aware of when converting your Storage Blob SDK library from Version 8 to Version 12.
For more info of the motivation behind this major change, please refer to this guide.
Java Development Kit (JDK) with version 8 or above
Our core synchronous classes have been replaced, as well as new asynchronous counterparts added.
Core V8 classes | Equivalent V12 Class | NEW Asynchronous clients |
---|---|---|
CloudStorageAccount | BlobServiceClientBuilder | BlobServiceClientBuilder |
CloudBlobClient | BlobServiceClient | BlobServiceAsyncClient |
CloudBlobContainer | ContainerClient | ContainerAsyncClient |
CloudBlob | BlobClient | BlobAsyncClient |
CloudBlockBlob | BlockBlobClient | BlockBlobAsyncClient |
CloudAppendBlob | AppendBlobClient | AppendBlobAsyncClient |
CloudPageBlob | PageBlobClient | PageBlobAsyncClient |
Dependency for Blob service:
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>12.0.0</version>
</dependency>
In V8, core classes were built from CloudStorageAccount
which authenticated with connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.parse("<connection-string>");
CloudBlobContainer blobContainer = storageAccount.createCloudBlobClient().getContainerReference("<container-name>");
blobContainer.create();
CloudBlockBlob blockBlob = blobContainer.getBlockBlobReference("<blob-name>");
In V12, we have moved to a builder pattern. To replicate the above V8 snippet:
// To get the BlobClient, we can use builder to initialize.
final BlobClient blobClient = new BlobClientBuilder()
.endpoint("https://" + "<account-name>" + ".blob.core.windows.net")
.containerName("mycontainer")
.blobName("myimage.jpg")
.credential(new StorageSharedKeyCredential("<account-name>", "<account-key>"))
.buildClient();
// We can initialize BlockBlobClient from blobClient.
final BlockBlobClient blockBlobClient = blobClient.getBlockBlobClient();
SAS token generation has moved from helper classes to the core clients themselves. See section SAS Token to learn how to use them as a credential. Not all fields being set in these samples are necessary. At minimum, only an expiry time and permission set are necessary to create a SAS.
In V8, to generate the SAS token, you needed to use SharedAccessSignatureHelper
to fluently build out all it's options.
You also needed to provide ServiceClient
which includes SharedKeyCredential
to sign the token with.
// Build the client
CloudStorageAccount storageAccount = CloudStorageAccount.parse("<connection-string>");
CloudBlobContainer blobContainer = storageAccount.createCloudBlobClient().getContainerReference("<container-name>");
blobContainer.create();
CloudBlockBlob blockBlob = blobContainer.getBlockBlobReference("<blob-name>");
// Initialize the properties
String identifier = "identifier";
EnumSet<SharedAccessBlobPermissions> permissions = EnumSet.of(
SharedAccessBlobPermissions.READ,
SharedAccessBlobPermissions.WRITE,
SharedAccessBlobPermissions.CREATE,
SharedAccessBlobPermissions.DELETE,
SharedAccessBlobPermissions.ADD);
IPRange ipR = new IPRange("0.0.0.0", "255.255.255.255");
Calendar calendar = Calendar.getInstance();
Date startDate = calendar.getTime();
calendar.add(Calendar.DAY_OF_YEAR, 1);
Date expiryDate = calendar.getTime();
SharedAccessBlobHeaders headers = new SharedAccessBlobHeaders();
headers.setCacheControl("cache");
headers.setContentDisposition("disposition");
headers.setContentEncoding("encoding");
headers.setContentLanguage("language");
headers.setContentType("type");
SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy();
policy.setPermissions(permissions);
policy.setSharedAccessStartTime(startDate);
policy.setSharedAccessExpiryTime(expiryDate);
// Build the token
String sasToken = blockBlob.generateSharedAccessSignature(policy, headers, identifier, ipR, SharedAccessProtocols.HTTPS_ONLY);
In V12, SAS tokens are generated off the client to the resource you wish to generate the SAS for.
The resource path handled automatically because of this, and the SharedKeyCredential
authenticating the client is also used automatically to sign the SAS. See building a client for how to get an authenticated client.
// specify token properties
String identifier = "identifier";
BlobSASPermission permissions = new BlobSASPermission()
.withRead(true)
.withCreate(true)
.withDelete(true)
.withWrite(true); // We can also choose BlobContainerSasPermission, BlobServiceSasQueryParameters based on the object level we want to grant access.
OffsetDateTime startTime = OffsetDateTime.now().minusDays(1);
OffsetDateTime expiryTime = OffsetDateTime.now().plusDays(1);
SasIpRange ipRange = new SasIpRange()
.setIpMax("0.0.0.0")
.setIpMax("255.255.255.255");
SasProtocol sasProtocol = SasProtocol.HTTPS_HTTP;
String cacheControl = "cache";
String contentDisposition = "disposition";
String contentEncoding = "encoding";
String contentLanguage = "language";
String contentType = "type";
String version = BlobServiceVersion.V2019_02_02.getVersion();
// build the token
BlobServiceSasSignatureValues sasSignatureValues = new BlobServiceSasSignatureValues(version, sasProtocol, startTime, expiryTime, permissions.toString(), ipRange, identifier, cacheControl, contentDisposition, contentEncoding, contentLanguage, contentType);
BlobServiceSasQueryParameters sasQueryParameters = sasSignatureValues.generateSasQueryParameters(new StorageSharedKeyCredential("<account-name>", "<account-key"));
V8 CloudStorageAccount
can take connection strings as credentials.
CloudStorageAccount storageAccount = CloudStorageAccount.parse("<connection-string>");
In V12, our builders accept connection strings, no calls to endpoint()
or credential()
required. However, connection strings generally only point at the account, so you WILL need to specify container and blob names as necessary.
BlobContainerClient client = new BlobContainerClientBuilder()
.connectionString("${connection-string}")
.containerName("mycontainer")
.buildClient();
Shared key credentials can be used directly to authenticate your client. You need to fetch the ${accountName}
and ${accountKey}
from Azure Portal.
Learn More from README
In V8, you used the credential to build the CloudStorageAccount
.
// Initialize the credentials
StorageCredentialsAccountAndKey credential = new StorageCredentialsAccountAndKey("${accountName}", "${accountKey}");
// CloudStorageAccount take the credential.
CloudStorageAccount storageAccount = new CloudStorageAccount(credential);
Since the builder in V12 manages pipeline generation, you can just hand the StorageSharedKeyCredential
to the builder.
// Initialize the credentials
StorageSharedKeyCredential credential = new StorageSharedKeyCredential("${accountName}", "${accountKey}");
// How service object take in the credentials.
BlobServiceClient client = new BlobServiceClientBuilder()
.endpoint("https://" + "<account-name>" + ".blob.core.windows.net")
.credential(credential)
.buildClient();
A URL with a SAS token looks like the following: https://${accountName}.blob.core.windows.net/?${sasToken}
.
Refer the documentation for more info.
See section Generate SAS token for how to generate the token programmatically.
In V8, you had to sasToken string, which can paste from portal or generate from session above .
// Here is how the blob object take in the sas token generated in above section [Generate SAS token](#generate-sas-token).
StorageCredentialsSharedAccessSignature credential = new StorageCredentialsSharedAccessSignature("sasToken");
CloudStorageAccount storageAccount = new CloudStorageAccount(credential);
In V12, SAS tokens can be managed just like any other credential.
// How service object take in the credentials.
BlobServiceClient client = new BlobServiceClientBuilder()
.endpoint("https://${accountName}.blob.core.windows.net")
.sasToken("${sasToken}")
.buildClient();
However, if you already have the full URL with SAS token attached, you can use that too:
BlobServiceClient client = new BlobServiceClientBuilder()
.endpoint("https://${accountName}.blob.core.windows.net/?${sasToken}")
.buildClient();
In V8, we only provide many APIs overloads with the same return type.
public BlobContainerProperties getProperties()
In V12, we provide at least one minimum and one maximum for most of the operations.
Minimum overload returns someType of T
directly. Maximum overload returns response of someType Response<T>
which includes the information of the headers, the request, status code, and the type value.
Minimum overload in async:
public BlobContainerProperties getProperties()
Maximum overload in async:
public Response<BlobContainerProperties> getPropertiesWithResponse(String leaseId, Duration timeout, Context context)
Listing or Paging API returned ResultSegment<SomeResponseType>
in V8 which provided the continuation token and result list in ResultSegment
.
List all blobs V8:
private CloudBlobContainer container;
private List<ListBlobItem> retrieveBlobsForCurrentPage(ResultContinuation pageToken, List<ListBlobItem> results) throws Exception {
if (pageToken == null) {
return results;
}
results.addAll(container.listBlobsSegmented(null, false, EnumSet.noneOf(BlobListingDetails.class), null, pageToken, null, null).getResults());
return results;
}
public void run() throws Exception{
// Build CloudBlobContainer
CloudStorageAccount storageAccount = CloudStorageAccount.parse("<connection-string>");
CloudBlobClient blobClient = storageAccount.createCloudBlobClient();
container = blobClient.getContainerReference("<container-name>");
// Initialize empty blob list
ResultSegment<ListBlobItem> results = container.listBlobsSegmented();
// Start from first page
retrieveBlobsForCurrentPage(results.getContinuationToken(), results.getResults());
}
V12 provides two pagination classes: PagedIterable<T>
for sync and PageFlux<T>
for async.
These allow you to consume listing operations by individual item or by response pages. The latter is needed to access general information in each HTTP response.
List all blobs using sync API V12, by page:
private BlobContainerClient containerClient;
private List<BlobItem> retrieveBlobs(String pageToken, List<BlobItem> results) {
if (pageToken == null) {
return results;
}
containerClient.listBlobs().streamByPage(pageToken).forEach(
response -> {
results.addAll((Collection) response.getValue());
retrieveBlobs(response.getContinuationToken(), results);
}
);
return results;
}
public void run() {
// Build ContainerClient
containerClient = new BlobContainerClientBuilder().credential(new StorageSharedKeyCredential("<account-name>", "<account-key>")).buildClient();
// Initialize empty blob list
List<BlobItem> results = new ArrayList<>();
// Start from first page
containerClient.listBlobs(null, Duration.ofSeconds(30))
.streamByPage()
.forEach(response ->
{
results.addAll((Collection) response.getValue());
if (response.getContinuationToken() != null) {
results.addAll((Collection) retrieveBlobs(response.getContinuationToken(), results));
}
});
}
In v8 and prior versions, there existed two classes: BlobInputStream
and BlobOutputStream
,
which simplified uploads and downloads by providing streams developers were used to.
Since Java's InputStream
and OutputStream
are synchronous interfaces, they were not included in V10/V11.
With the inclusion of synchronous clients in V12, customers who may have already migrated from v8 to V10/V11 will find these familiar classes, but they will be brand new to those who started with V10/V11. Note that these classes do NOT give you access to the HTTP requests/responses.
Get an InputStream to download a blob in V12.
final BlobClient blobClient = new BlobClientBuilder()
.endpoint("https://${accountName}.blob.core.windows.net")
.credential(new StorageSharedKeyCredential("<account-name>", "<account-key>"))
.buildClient();
InputStream blobInputStream = blobClient.openInputStream();
// insert your method of choice to read from an InputStream
Get an OutputStream to upload a blob in V12.
final BlockBlobClient blobClient = new BlobClientBuilder()
.endpoint("https://${accountName}.blob.core.windows.net")
.credential(new StorageSharedKeyCredential("<account-name>", "<account-key>"))
.buildClient().getBlockBlobClient();
OutputStream blobOutputStream = blobClient.getBlobOutputStream();
// insert your method of choice to write to an OutputStream