0

I've been banging my head for a few weeks on a problem.

I provide a download url to the client in order to download content from the storage. Here's how I do that:

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

This way I generate a link to the Azure storage blob.

Now I send this link to the client and open it as :

  let a = document.createElement('a');
  a.download = data.fileName;
  a.href = data.url
  a.click()
  document.removeChild(a)

But it still doesn't download the file with the correct file name ( it downloads it as the GUID of the blob). This happens because the azure storage overrides with headers the name i specified in the download attribute. How do I get the correct file name to be delivered? Should I try to disable the headers of the Azure storage? Should I change the code on the client?

Christo S. Christov
  • 2,268
  • 3
  • 32
  • 57
  • which browser does this fail in – Jaromanda X Nov 02 '17 at 21:54
  • @JaromandaX currently running on latest version of Chrome – Christo S. Christov Nov 02 '17 at 21:55
  • your code works as you require in Firefox (except you forgot to add the tag to the DOM, so it won't work at all) – Jaromanda X Nov 02 '17 at 21:55
  • @JaromandaX It's important that your endpoint returns headers to override the file name. Is that indeed the case? – Christo S. Christov Nov 02 '17 at 21:56
  • @JaromandaX uhh, yeah. Anyway - that was just for example purposes - real code does contain adding :) – Christo S. Christov Nov 02 '17 at 21:57
  • your code (except for the omission I mentioned) works as written, in firefox and in chrome - the issue is probably something you haven't mentioned - so I have no idea what you are asking about endpojnts and headers - what does data.url look like – Jaromanda X Nov 02 '17 at 21:57
  • oh ... wait ... I thought you were dealing with a javascript Blob turned into a DATA URI – Jaromanda X Nov 02 '17 at 21:59
  • @JaromandaX http://127.0.0.1:10000/devstoreaccount1/uploads/cc876ecd-0ebf-49c0-ad70-27f382be61cc?sv=2017-04-17&sr=b&sig=nMWkrfsae6PUbYDYjlna%2Blc%2FZRJzoyFXdfZe%2Ffh9Rqw%3D&st=2017-11-02T21%3A54%3A48Z&se=2017-11-02T22%3A09%3A48Z&sp=r – Christo S. Christov Nov 02 '17 at 22:00
  • If you want to specify a filename on a server sent download, the server should send the following header `Content-Disposition: attachment; filename="whatever-name-you-want.xxx"` – Jaromanda X Nov 02 '17 at 22:09
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/158116/discussion-between-hristo-and-jaromanda-x). – Christo S. Christov Nov 02 '17 at 22:09

2 Answers2

3

Assuming you have a blob called abc.png and you want it to be downloaded as def.png, what you could do is overwrite Content-Disposition header in your SAS token. Then when the user clicks on the download link, the file will be saved as def.png by default.

Please see the sample code below:

    private static void OverrideContentDispositionHeaderInSharedAccessSignature()
    {
        var cred = new StorageCredentials(accountName, accountKey);
        var account = new CloudStorageAccount(cred, true);
        var blobClient = account.CreateCloudBlobClient();
        var container = blobClient.GetContainerReference("container-name");
        var blob = container.GetBlockBlobReference("abc.png");
        var sasConstraints = new SharedAccessBlobPolicy();
        sasConstraints.SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5);
        sasConstraints.SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(10);
        sasConstraints.Permissions = SharedAccessBlobPermissions.Read;
        var sasBlobHeaders = new SharedAccessBlobHeaders()
        {
            ContentDisposition = "attachment; filename=\"def.png\""
        };
        var sasBlobToken = blob.GetSharedAccessSignature(sasConstraints, sasBlobHeaders);
        var sasUrl = blob.Uri.AbsoluteUri + sasBlobToken;
    }

With this you don't need to set the download attribute on your link element. Your client side code would be much simple. Something like:

        let a = document.createElement('a');
        a.href = data.url
        a.click()
        document.removeChild(a)
Gaurav Mantri
  • 128,066
  • 12
  • 206
  • 241
0

You cannot force a client to download a file having a specific file name. The client can change the name of the file at either the Save File dialog or when the file is saved at local filesystem.

guest271314
  • 1
  • 15
  • 104
  • 177
  • while you can't FORCE the name, you can "suggest" the name in any usable browser - and chrome won't let you change the name, it blindly downloads it – Jaromanda X Nov 02 '17 at 21:55
  • I have no issue with providing the user with save file dialog either. I would like the user to download the file with the same file name - one way or another. – Christo S. Christov Nov 02 '17 at 21:57
  • @JaromandaX Yes. It is possible to perform a more elaborate check, see [this Answer](https://stackoverflow.com/a/41336449/) at [Detect when user accepts to download a file](https://stackoverflow.com/questions/41334881/detect-when-user-accepts-to-download-a-file/41336449#41336449) – guest271314 Nov 02 '17 at 21:57
  • 1
    @Hristo See the Answer linked at previous comment if the requirement is stricter than simply suggesting a file name for the file offered for download. Note, that is not possible to even check if the visitor downloads the file at all. Depending on the browser used for download you can suggest the visitor store the file at `LocalFileSystem` using `requestFileSystem`, then check the file name – guest271314 Nov 02 '17 at 21:58
  • @Hristo Why do you need for the file offered for download to be downloaded to user filesystem having a specific file name, if the user accepts the file for download? – guest271314 Nov 02 '17 at 22:04
  • @guest271314 because downloading a file with a strange GUID is not very nice for a non dev person to see :) – Christo S. Christov Nov 02 '17 at 22:06
  • @Hristo The user can already change the name of the file at `Save File` prompt, or after downloading file. What you can do is notify the user of that option if the filename is a concern. Note also, `.removeChild()` would throw an error if `a` is not appended to `document`, and appending the element to `document` is necessary at Firefox – guest271314 Nov 02 '17 at 22:06
  • @guest271314 This is not correct. With Azure Blob Storage, you have the capability to override certain response headers (content-disposition being one of them). Let me add an answer shortly on how you could do that. – Gaurav Mantri Nov 03 '17 at 03:16
  • @GauravMantri What is not correct? How is a header related to file name of file offered for download? – guest271314 Nov 03 '17 at 03:17
  • Let's say you saved the file in blob storage as `a.txt` and now when you want the user to download the file, by default it should be saved as `b.txt`. It is certainly possible to do so without doing anything special on the client side. From your answer, somehow I inferred that you can't do that (hence my comment, apologies if I misunderstood your answer). – Gaurav Mantri Nov 03 '17 at 03:20
  • @GauravMantri It is possible to suggest a file name to client when offering a file for download. That is only a suggestion. The client can change the file name at any time. – guest271314 Nov 03 '17 at 03:22
  • On that I completely agree with you. However as mentioned in other comments, some browser don't even offer you that option. It will simply download the file. – Gaurav Mantri Nov 03 '17 at 03:24
  • _"It will simply download the file."_ Which browser? – guest271314 Nov 03 '17 at 03:25
  • Right now I am using Safari on Mac trying to download VS Code. It downloaded the file without giving me an option to change the file name (like most users I simply clicked the download link). – Gaurav Mantri Nov 03 '17 at 03:27
  • Same's the deal with Chrome as well. – Gaurav Mantri Nov 03 '17 at 03:31
  • @GauravMantri Do you have "Ask where to save each file before downloading" set to off at Settings at Chrome? – guest271314 Nov 03 '17 at 03:32
  • Which OS please? Did you take any additional steps to invoke "Save As" functionality? – Gaurav Mantri Nov 03 '17 at 03:34
  • *nix. "Ask where to save each file before downloading" is set to on at Settings at Chromium – guest271314 Nov 03 '17 at 03:34
  • Aah I see. Let me give it a try. – Gaurav Mantri Nov 03 '17 at 03:37