Skip to content

Latest commit

 

History

History
315 lines (268 loc) · 14.1 KB

File metadata and controls

315 lines (268 loc) · 14.1 KB

Storage Blob Service SDK Migration Guide from 8.x to 12.x

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.

Prerequisites

Java Development Kit (JDK) with version 8 or above

Converting Core Classes

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

Updated Maven dependency

Dependency for Blob service:

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

Build Client

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(); 

Generate SAS token

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"));

Credentials

Connection string

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 credential

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();

SASToken

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();

Minimum Overload APIs and Maxmum Overload APIs:

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)

Pagination:

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

BlobInputStream and BlobOutputStream

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

Miscellaneous

  • Examples for how to approximate v8 StorageEvent behavior can be found here.
  • Examples for how to approximate v8 LocationMode behavior can be found here.