Search code examples
azureazure-storageazure-blob-storageazure-java-sdk

Copy Blob in Azure Blob Storage using Java v12 SDK


My Application is in a Kubernetes cluster and I'm using Java v12 SDK to interact with the Blob Storage. To authorize against Blob Storage I'm using Managed Identities.

My application needs to copy blobs within one container. I haven't found any particular recommendations or examples of how SDK should be used to do the copy.

I figured that the following approach works when I'm working with the emulator

copyBlobClient.copyFromUrl(sourceBlobClient.getBlobUrl());

However, when this gets executed in the cluster I get the following error

<Error>
   <Code>CannotVerifyCopySource</Code>
   <Message>The specified resource does not exist. RequestId: __ Time: __ </Message>
</Error> 

Message says "resource does not exist" but the blob is clearly there. My container has private access, though.

Now when I change the public access level to "Blob(anonymous read access for blobs only)" everything works as excepted. However, public access not acceptable to me.

Main question - what are the right ways to implement copy blob using Java v12 SDK.

What I could miss or misconfigured in my situation?

And the last is the error message itself. There is a part which says "CannotVerifyCopySource" which kind of helps you understand that there is something with access, but the message part is clearly misleading. Shouldn't it be more explicit about the error?


Solution

  • If you want to use Azure JAVA SDK to copy blob with Azure MSI, please refer to the following details

    • Copy blobs between storage accounts

    If you copy blobs between storage accounts with Azure MSI. We should do the following actions

    1. Assign Azure Storage Blob Data Reader to the MSI in the source container

    2. Assign Azure Storage Blob Data Contributor to the MSI in the dest container. Besides when we copy blob, we need write permissions to write content to blob

    3. Generate SAS token for the blob. If the souce blob is public, we can directly use source blob URL without sas token.

    For example

     try {
                BlobServiceClient blobServiceClient = new BlobServiceClientBuilder()
                        .endpoint("https://<>.blob.core.windows.net/" )
                        .credential(new DefaultAzureCredentialBuilder().build())
                        .buildClient();
                // get User Delegation Key
                OffsetDateTime delegationKeyStartTime = OffsetDateTime.now();
                OffsetDateTime delegationKeyExpiryTime = OffsetDateTime.now().plusDays(7);
                UserDelegationKey key =blobServiceClient.getUserDelegationKey(delegationKeyStartTime,delegationKeyExpiryTime);
    
                BlobContainerClient sourceContainerClient = blobServiceClient.getBlobContainerClient("test");
                BlobClient sourceBlob = sourceContainerClient.getBlobClient("test.mp3");
                // generate sas token
                OffsetDateTime expiryTime = OffsetDateTime.now().plusDays(1);
                BlobSasPermission permission = new BlobSasPermission().setReadPermission(true);
    
                BlobServiceSasSignatureValues myValues = new BlobServiceSasSignatureValues(expiryTime, permission)
                        .setStartTime(OffsetDateTime.now());
                String sas =sourceBlob.generateUserDelegationSas(myValues,key);
    
                // copy
                BlobServiceClient desServiceClient = new BlobServiceClientBuilder()
                        .endpoint("https://<>.blob.core.windows.net/" )
                        .credential(new DefaultAzureCredentialBuilder().build())
                        .buildClient();
                BlobContainerClient desContainerClient = blobServiceClient.getBlobContainerClient("test");
                String res =desContainerClient.getBlobClient("test.mp3")
                        .copyFromUrl(sourceBlob.getBlobUrl()+"?"+sas);
                System.out.println(res);
            } catch (Exception e) {
                e.printStackTrace();
            }
    

    enter image description here

    • Copy in the same account

    If you copy blobs in the same storage account with Azure MSI, I suggest you assign Storage Blob Data Contributor to the MSI in the storage account. Then we can do copy action with the method copyFromUrl.

    For example

    a. Assign Storage Blob Data Contributor to the MSI at the account level

    b. code

      try {
                BlobServiceClient blobServiceClient = new BlobServiceClientBuilder()
                        .endpoint("https://<>.blob.core.windows.net/" )
                        .credential(new DefaultAzureCredentialBuilder().build())
                        .buildClient();
    
                BlobContainerClient sourceContainerClient = blobServiceClient.getBlobContainerClient("test");
                BlobClient sourceBlob = sourceContainerClient.getBlobClient("test.mp3");
    
                BlobContainerClient desContainerClient = blobServiceClient.getBlobContainerClient("output");
                String res =desContainerClient.getBlobClient("test.mp3")
                        .copyFromUrl(sourceBlob.getBlobUrl());
                System.out.println(res);
            } catch (Exception e) {
                e.printStackTrace();
            }
    

    enter image description here

    For more details, please refer to here and here