Friendly filename when public download Azure blob
Asked Answered
S

6

36

Is it possible to save a blob with a name of a GUID (or anything else) but when a user requests the files URI http://me.blob.core.windows.net/mycontainer/9BB34783-8F06-466D-AC20-37A03E504E3F the download comes down with a friendly name e.g. MyText.txt?

Submarginal answered 11/8, 2011 at 14:28 Comment(0)
C
47

Enabling users to download files (blobs) in Windows Azure can be done in 4 ways;

  • Direct Download – Set the Access of the Container to Public Read Access or Full Public Read Access and expose the URL to the end user. The drawback of this method is obviously security – you have no way of controlling access when the URL is exposed. There is also no way of detecting downloads, and to execute code before/after the download.

  • API Download – The user must run a Windows App, Silverlight etc. Also – the app must contain the storeaccount name and key – which might compromise security. Especially if you have several customers using the same account (they can still have their own containers of course).

  • Proxy Download – Have the user access a URL on YOUR server – which then retrieves the file from Azure and sends it to the user. The advantage of this is that you have full control of downloads, you can execute code before/after downloading and you don’t have to expose any Azure URL’s / account info. In fact – the end user will not even see that the file is stored in Azure. You can also override the filename this way. A downside is that all traffic passes through your server – so you' might get a bottleneck here.

  • Pass-through Download (Azure Shared Access Signature) - Creates a signature and inserts this signature in a URL where the user is redirected to Azure. The signature enables the user to access the file for a limited period of time. This is most likely your best option. It enables custom code to be executed before downloading, it will ensure max download speed for your users, and a good level of security is also ensured.

Here's a code snippet which streams files to user, and overrides the filename.

//Retrieve filenname from DB (based on fileid (Guid))
// *SNIP*
string filename = "some file name.txt"; 

//IE needs URL encoded filename. Important when there are spaces and other non-ansi chars in filename.
if (HttpContext.Current.Request.UserAgent != null &&     HttpContext.Current.Request.UserAgent.ToUpper().Contains("MSIE"))
filename = HttpUtility.UrlEncode(filename, System.Text.Encoding.UTF8).Replace("+", " ");

context.Response.Charset = "UTF-8";
//Important to set buffer to false. IIS will download entire blob before passing it on to user if this is not set to false
context.Response.Buffer = false;
context.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
context.Response.AddHeader("Content-Length", "100122334"); //Set the length the file
context.Response.ContentType = "application/octet-stream";
context.Response.Flush();

//Use the Azure API to stream the blob to the user instantly.
// *SNIP*
fileBlob.DownloadToStream(context.Response.OutputStream);

See this blogpost for more: http://blog.degree.no/2012/04/downloading-blobs-from-windows-azure/

Calvary answered 25/4, 2012 at 10:16 Comment(3)
Thank you for this very detailed answer. The pass-through Download sounds exactly what I need. One question, in your code example you set the size of the file, does this have to be done as I would not know this info without accessing the file.Submarginal
Setting filesize is not necessary. I do it to enable users (the browser) to see the download progress. If you don't set it, the browser will show "unknown filesize" etc. when downloading.Seventeen
In option 3- downloading the file first to the localresource(temp) folder of the webrole and then linking that url to the end user . is this possible? good idea? In option 4 is there a way to hide the SAS url from end user and make them just save the file. All files I am dealing here are xmls.Develop
C
101

Now it is possible by setting the content-disposition header on generating the Shared Access Signature:

  string sasBlobToken = blob.GetSharedAccessSignature(sharedPolicy, new SharedAccessBlobHeaders()
            {
                ContentDisposition = "attachment; filename=" + friendlyFileName
            });
  string downloadLink = blob.Uri + sasBlobToken;
Campman answered 10/6, 2015 at 13:28 Comment(2)
Genius, exactly what I was looking for!Inesinescapable
P L U S O N E. Just what I neededCullie
C
47

Enabling users to download files (blobs) in Windows Azure can be done in 4 ways;

  • Direct Download – Set the Access of the Container to Public Read Access or Full Public Read Access and expose the URL to the end user. The drawback of this method is obviously security – you have no way of controlling access when the URL is exposed. There is also no way of detecting downloads, and to execute code before/after the download.

  • API Download – The user must run a Windows App, Silverlight etc. Also – the app must contain the storeaccount name and key – which might compromise security. Especially if you have several customers using the same account (they can still have their own containers of course).

  • Proxy Download – Have the user access a URL on YOUR server – which then retrieves the file from Azure and sends it to the user. The advantage of this is that you have full control of downloads, you can execute code before/after downloading and you don’t have to expose any Azure URL’s / account info. In fact – the end user will not even see that the file is stored in Azure. You can also override the filename this way. A downside is that all traffic passes through your server – so you' might get a bottleneck here.

  • Pass-through Download (Azure Shared Access Signature) - Creates a signature and inserts this signature in a URL where the user is redirected to Azure. The signature enables the user to access the file for a limited period of time. This is most likely your best option. It enables custom code to be executed before downloading, it will ensure max download speed for your users, and a good level of security is also ensured.

Here's a code snippet which streams files to user, and overrides the filename.

//Retrieve filenname from DB (based on fileid (Guid))
// *SNIP*
string filename = "some file name.txt"; 

//IE needs URL encoded filename. Important when there are spaces and other non-ansi chars in filename.
if (HttpContext.Current.Request.UserAgent != null &&     HttpContext.Current.Request.UserAgent.ToUpper().Contains("MSIE"))
filename = HttpUtility.UrlEncode(filename, System.Text.Encoding.UTF8).Replace("+", " ");

context.Response.Charset = "UTF-8";
//Important to set buffer to false. IIS will download entire blob before passing it on to user if this is not set to false
context.Response.Buffer = false;
context.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
context.Response.AddHeader("Content-Length", "100122334"); //Set the length the file
context.Response.ContentType = "application/octet-stream";
context.Response.Flush();

//Use the Azure API to stream the blob to the user instantly.
// *SNIP*
fileBlob.DownloadToStream(context.Response.OutputStream);

See this blogpost for more: http://blog.degree.no/2012/04/downloading-blobs-from-windows-azure/

Calvary answered 25/4, 2012 at 10:16 Comment(3)
Thank you for this very detailed answer. The pass-through Download sounds exactly what I need. One question, in your code example you set the size of the file, does this have to be done as I would not know this info without accessing the file.Submarginal
Setting filesize is not necessary. I do it to enable users (the browser) to see the download progress. If you don't set it, the browser will show "unknown filesize" etc. when downloading.Seventeen
In option 3- downloading the file first to the localresource(temp) folder of the webrole and then linking that url to the end user . is this possible? good idea? In option 4 is there a way to hide the SAS url from end user and make them just save the file. All files I am dealing here are xmls.Develop
T
22

As of November 2013 there is now support for the content-disposition header in Windows Azure Blob Storage. You can assign the header either on blob creation or through a shared access signature.

I modified my save document method to take an optional parameter which is the friendly name to download.

public void SaveDocument(Stream documentToSave, string contentType, string containerName, string url, string friendlyName = null)
{
    var container = GetContainer(containerName);
    container.CreateIfNotExists();
    var blob = container.GetBlockBlobReference(url);
    blob.Properties.ContentType = contentType;
    if (friendlyName != null)
      blob.Properties.ContentDisposition = "attachment; filename=" + friendlyName;
    blob.UploadFromStream(documentToSave);
}

More about it: http://blog.simontimms.com/2013/12/02/content-disposition-comes-to-azure/

Treponema answered 2/12, 2013 at 21:26 Comment(0)
A
4

The answer is no. You would need to set the content-disposition header on the blob, and there's no way to set that header in blob storage.

Adamec answered 11/8, 2011 at 17:45 Comment(3)
Thank you. I thought so but just wanted to be sure.Submarginal
Aside from the other workarounds suggested, if you always want the same friendly name, you might just name things like http://me.blob.core.windows.net/mycontainer/9BB34783-8F06-466D-AC20-37A03E504E3F/MyText.txtAdamec
Thanks @smarx - this is a really simple and elegant solutionPastis
B
1

I have tried setting ContentDisposition property, both through code and manually. And it didn't work for me. I tweaked some stuff and it started to work. This might be weird, but content-disposition header in resposne was missing, until I added filename to both Properties and Metadata during blob upload.

NuGet Package: WindowsAzure.Storage (Version=9.3.3)
Storage account kind: StorageV2 (general purpose v2)

        var cloudStorageAccount = CloudStorageAccount.Parse(chatStorageConnectionString);
        var cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient();
        string extension = ".jpg";
        string containerName = "foobar";

        var container = cloudBlobClient.GetContainerReference(containerName);

        if(!(await container.ExistsAsync()))
        {
            logger.LogError($"Unable to connect to container {containerName: containerName}");
            return "";
        }

        var imageBlob = container.GetBlockBlobReference(Guid.NewGuid().ToString() + extension);

        // These two lines are what I'm talking about
        imageBlob.Metadata.Add("ContentDisposition", "attachment; filename=\"testfire.jpg\"");
        imageBlob.Properties.ContentDisposition = "attachment; filename=\"testfire.jpg\"";

        await imageBlob.UploadFromStreamAsync(stream);
        if(!await imageBlob.ExistsAsync())
        {
            logger.LogError("Image was not uploaded succesfully.");
            return "";
        }
Blanche answered 15/11, 2020 at 13:41 Comment(0)
C
0

Firstly you can use your own custom domain for blob URI: http://blogs.msdn.com/b/avkashchauhan/archive/2011/03/22/using-custom-domain-name-with-windows-azure-storage-instead-of-windows-stroage-name-blob-core-windows-net.aspx

Exemple of solution for you...

using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

StorageCredentialsAccountAndKey credentials = new StorageCredentialsAccountAndKey(ConfigurationManager.AppSettings["WindowsAzureStorageAccountName"], ConfigurationManager.AppSettings["WindowsAzureStorageAccountKey"]);
CloudStorageAccount cloudStorageAccount = new CloudStorageAccount(credentials, true);

CloudBlobContainer blobContainer = blobClient.GetContainerReference("mycontainer");

// e.g. GUID of currently logged in user
string fileName = System.Web.Security.Membership.GetUser().ProviderUserKey.ToString();

CloudBlob blob = blobContainer.GetBlobReference(fileName);

byte[] blobArray = blob.DownloadByteArray();

Response.ContentType = "text/plain";
Response.AddHeader("content-disposition", "attachment; filename=" + "MyText.txt");
Response.BinaryWrite(blobArray);
Response.End();
Campo answered 11/8, 2011 at 15:4 Comment(3)
I see what you are saying but that relies on me downloading the file to my server before passing it onto the client. I need for the client to be able to download direct from the azure server.Submarginal
You need some kind of URL rewriting based on user GUID. Maybe this one can help: forums.asp.net/t/1574817.aspx/1Campo
I am accessing it using ABP in dotnt core 3.1 & CASE 1 I have url to download in that case it's working with filename but when CASE 2 I have only blob of multiple file in one then it will download in browser with download.extension as filename Please helpUphemia

© 2022 - 2024 — McMap. All rights reserved.