4

Looking for help on how to copy a file from an Azure Blob Storage to a File Share when using an Azure Function Blob Trigger (v3)

I see lots and lots of articles and SO questions on copying from File Share to Blob, but nothing in reverse, and trying to reverse the code samples I've found isn't working out too well

I have come up with a solution, but it's not ideal as I believe it is first downloading the file into memory then uploading it to the File Share:


        [FunctionName("MyBlobTrigger")]
        public async void Run([BlobTrigger("uploads/{name}", Connection = "UploadStorageAccount")]Stream myBlob, string name, ILogger log, CancellationToken cancellationToken)
        {

            ShareClient share = new ShareClient(storageConnection, fileShareName);

            ShareDirectoryClient directory = share.GetRootDirectoryClient();
            ShareFileClient fileShare = directory.GetFileClient(name);

            try
            {
                using (Stream stream = myBlob)
                {
                    fileShare.Create(stream.Length);
                    await fileShare.UploadRangeAsync(new Azure.HttpRange(0, stream.Length), stream);
                }

            }
         }

So this does work, though with these issues:

  1. I think it's downloading first to memory then uploading, would prefer to just transfer the file only within the Azure service, it's possible to have some very large files
  2. I would also prefer to use CloudBlockBlob instead of Stream for my blob, main reason being that at the end of the function I need to delete the file, I can easily do this if using CloudBlockBlob. Problem is I haven't been able to figure out how to do the copy when using it

Any ideas?

EDIT - Final code used from accepted answer:

    [FunctionName("MyBlobTrigger")]
    public async void Run([BlobTrigger("uploads/{name}", Connection = "UploadStorageAccount")]CloudBlockBlob myBlob, string name, ILogger log, CancellationToken cancellationToken)
    {

        ShareClient share = new ShareClient(storageConnection, fileShareName);

        ShareDirectoryClient directory = share.GetRootDirectoryClient();
        ShareFileClient fileShare = directory.GetFileClient(name);

        try
        {

            SharedAccessBlobPolicy sasConstraints = new SharedAccessBlobPolicy
            {
                SharedAccessExpiryTime = DateTime.UtcNow.AddHours(1),
                Permissions = SharedAccessBlobPermissions.Read
            };
            var sasToken = myBlob.GetSharedAccessSignature(sasConstraints);
            var blobSasUrl = $"{myBlob.Uri.AbsoluteUri}{sasToken}";

            fileShare.Create(myBlob.Properties.Length);
            await fileShare.StartCopyAsync(new Uri(blobSasUrl));
4
  • Based on the documentation here, you should be able to provide an object of type CloudBlockBlob as input instead of stream. Please try with that. Commented Sep 17, 2021 at 2:29
  • Is there any reason you are required to use Azure Functions? If you're looking for a straight point to point copy, then Azure Data Factory is a much better fit. You can use the built-in Copy Data Tool or Copy Activity (more info here) to set up a pipeline with various triggers (scheduled, tumbling window, storage, or custom - more info here) to achieve this very easily. Commented Sep 17, 2021 at 3:01
  • @MattStannett Yes, I need to run it as a trigger for when a file is uploaded to the Blob, there is some processing that happens before and after the copy operation which I haven't shown, then the file is finally deleted from blob at the end, I'm not able to do this processing with Data Factory Commented Sep 17, 2021 at 13:52
  • @GauravMantri Yes I know I can use CloudBlockBlob as the trigger input, that isn't my issue as I would prefer to use it, the issue is then how do I copy the file from blob to file share using CloudBlockBlob? So far I can only do it using stream Commented Sep 17, 2021 at 13:54

1 Answer 1

3

To copy the contents of a blob to a file share file, you don't really need to download it first. You can simply make use of Azure Storage's async server-side copy feature.

Essentially you would create a SAS URL for the blob with at least read permission and then use that as a source URL for file copy operation.

I have added some pseudo-code below to show how it can be done.

[FunctionName("MyBlobTrigger")]
public async void Run([BlobTrigger("uploads/{name}", Connection = "UploadStorageAccount")]CloudBlockBlob myBlob, string name, ILogger log, CancellationToken cancellationToken)
{

    //Step 1: Get shared access signature for the blob.
    //var sasToken = myBlob.GetSharedAccessSignature();
    
    //Step 2: Get SAS URL for the blob.
    //var blobSasUrl = $"{myBlob.Uri.AbsoluteUri}{sasToken}";

    ShareClient share = new ShareClient(storageConnection, fileShareName);

    ShareDirectoryClient directory = share.GetRootDirectoryClient();
    ShareFileClient fileShare = directory.GetFileClient(name);

    ShareClient share = new ShareClient(storageConnection, fileShareName);

    ShareDirectoryClient directory = share.GetRootDirectoryClient();
    ShareFileClient fileShare = directory.GetFileClient(name);

    try
    {
        //Step 3: Create empty file based on blob's content length
        //fileShare.Create(myBlob.Properties.Length);
        //Step 4: Copy blob's contents to storage file using async file copy.
        //await fileShare.StartCopyAsync(new Uri(blobSasUrl));
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks, I needed to add an access policy for GetSharedAccessSignature(), I gave it Read, List, Write, Create - just to cover all my bases, but I'm getting an auth exception on the copy: "Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature."
My code was just a pseudo code. For copying, you just need read permission. From the error, it looks like an issue with your SAS token. Can you edit your question and 1) include your complete code and 2) include what your SAS token looks like?
Just spotted the issue, the ? isn't needed when constructing the blobSasUrl as it is already part of the sas token, so I had ??, removed the extra and she's good, thanks!! Marking as answered
Thanks for spotting the issue! I have edited my answer to remove that extra ?.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.