Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to download a file to browser from Azure Blob Storage

I'm already successfully listing available files, but I needed to know how I could pass that file down to the browser for a user to download without necessarily saving it to the server

Here is how I get the list of files

var azureConnectionString = CloudConfigurationManager.GetSetting("AzureBackupStorageConnectString");
var containerName = ConfigurationManager.AppSettings["FmAzureBackupStorageContainer"];
if (azureConnectionString == null || containerName == null)
    return null;

CloudStorageAccount backupStorageAccount = CloudStorageAccount.Parse(azureConnectionString);
var backupBlobClient = backupStorageAccount.CreateCloudBlobClient();
var container = backupBlobClient.GetContainerReference(containerName); 
var blobs = container.ListBlobs(useFlatBlobListing: true);
var downloads = blobs.Select(blob => blob.Uri.Segments.Last()).ToList();
like image 537
stackoverfloweth Avatar asked May 26 '15 19:05

stackoverfloweth


People also ask

How do I access Blob storage from my browser?

If you are trying to access the blob you need to specify the container name and the blob name. Suppose, you have a blob by name "MyBlob" present in "mycontainer",you can retrieve it by specifying http://my_storageAcount.blob.core.windows.net/mycontainer/MyBlob.


4 Answers

While blob content may be streamed through a web server, and along to the end user via browser, this solution puts load on the web server, both cpu and NIC.

An alternative approach is to provide the end user with a uri to the desired blob to be downloaded, which they may click in the html content. e.g. https://myaccount.blob.core.windows.net/mycontainer/myblob.ext.

The issue with this is if the content is private, since a uri such as the one above won't work unless using public blobs. For this, you can create a Shared Access Signature (or server-stored Policy), which then results in a hashed querystring appended to the uri. This new uri would be valid for a given length of time (10 minutes, for example).

Here's a small example of creating an SAS for a blob:

var sasConstraints = new SharedAccessBlobPolicy();
sasConstraints.SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5);
sasConstraints.SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(10);
sasConstraints.Permissions = SharedAccessBlobPermissions.Read;

var sasBlobToken = blob.GetSharedAccessSignature(sasConstraints);

return blob.Uri + sasBlobToken;

Note that the start time is set to be a few minutes in the past. This is to deal with clock-drift. Here is the full tutorial I grabbed/modified this code sample from.

By using direct blob access, you will completely bypass your VM/web role instance/web site instance (reducing server load), and have your end-user pull blob content directly from blob storage. You can still use your web app to deal with permissioning, deciding which content to deliver, etc. But... this lets you direct-link to blob resources, rather than streaming them through your web server.

like image 163
David Makogon Avatar answered Oct 13 '22 05:10

David Makogon


Once the user clicks a file the server responds with this

var blob = container.GetBlobReferenceFromServer(option);

var memStream = new MemoryStream();
blob.DownloadToStream(memStream);

Response.ContentType = blob.Properties.ContentType;
Response.AddHeader("Content-Disposition", "Attachment;filename=" + option);
Response.AddHeader("Content-Length", blob.Properties.Length.ToString());
Response.BinaryWrite(memStream.ToArray());

HUGE thanks to Dhananjay Kumar for this solution

like image 39
stackoverfloweth Avatar answered Oct 13 '22 04:10

stackoverfloweth


If you use ASP.NET (core), you can stream the content to the browser without saving the file on the server and using FileStreamResult which is IActionResult would be more elegant solution.

var stream = await blob.OpenReadAsync();
return File(stream, blob.Properties.ContentType, option);
like image 12
Andrew Chaa Avatar answered Oct 13 '22 04:10

Andrew Chaa


I have done a sample where you can upload and download blob file.

using System;
using System.Threading.Tasks;
using System.IO;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Linq;
using System.Collections.Generic;

namespace GetBackup
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string Config_string = "";

            using (StreamReader SourceReader = File.OpenText(@"appsettings.json"))
            {
                Config_string = await SourceReader.ReadToEndAsync();
            }

            var config = (JObject)JsonConvert.DeserializeObject(Config_string);

            if(config["Application_type"].ToString()== "Backup")
            {
                string Dir_path = config["Backup_Path"].ToString();
                string[] allfiles = Directory.GetFiles(Dir_path, "*.*", SearchOption.AllDirectories);


                string storageConnectionString = config["AZURE_STORAGE_CONNECTION_STRING"].ToString();
                CloudStorageAccount storageAccount;
                if (CloudStorageAccount.TryParse(storageConnectionString, out storageAccount))
                {
                    CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
                    CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference("rtddata");
                    //await cloudBlobContainer.CreateAsync();

                    string[] ExcludeFiles = config["Exception_File"].ToString().Split(',');

                    foreach (var file in allfiles)
                    {
                        FileInfo info = new FileInfo(file);
                        if (!ExcludeFiles.Contains(info.Name))
                        {
                            string folder = (Dir_path.Length < info.DirectoryName.Length) ? info.DirectoryName.Replace(Dir_path, "") : "";
                            folder = (folder.Length > 0) ? folder + "/" : "";
                            CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(folder + info.Name);
                            await cloudBlockBlob.UploadFromFileAsync(info.FullName);
                        }

                    }

                }
            }
            
            else if (config["Application_type"].ToString() == "Restore")
            {
                string storageConnectionString = config["AZURE_STORAGE_CONNECTION_STRING"].ToString();
                CloudStorageAccount storageAccount;
               
                if (CloudStorageAccount.TryParse(storageConnectionString, out storageAccount))
                {
                    CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
                    CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference("rtddata");
                    string Dir_path = config["Restore_Path"].ToString();

                    IEnumerable<IListBlobItem> results = cloudBlobContainer.ListBlobs(null,true);  
                    foreach (IListBlobItem item in results)
                    {
                        string name = ((CloudBlockBlob)item).Name;
                        if (name.Contains('/'))
                        {
                            string[] subfolder = name.Split('/');
                            if (!Directory.Exists(Dir_path + subfolder[0]))
                            {
                                Directory.CreateDirectory(Dir_path + subfolder[0]);
                            }
                            
                        }  
                            CloudBlockBlob blockBlob = cloudBlobContainer.GetBlockBlobReference(name);
                            string path = (Dir_path + name);
                            blockBlob.DownloadToFile(path, FileMode.Create);
                    }
                    

                }
                    
            }

            
            
        }
    }
}
like image 1
Sapnandu Avatar answered Oct 13 '22 03:10

Sapnandu