321

How do you set the name of a blob file in JavaScript when force downloading it through window.location?

function newFile(data) {
    var json = JSON.stringify(data);
    var blob = new Blob([json], {type: "octet/stream"});
    var url  = window.URL.createObjectURL(blob);
    window.location.assign(url);
}

Running the above code downloads a file instantly without a page refresh that looks like:

bfefe410-8d9c-4883-86c5-d76c50a24a1d

I want to set the filename as my-download.json instead.

10 Answers 10

503

The only way I'm aware of is the trick used by FileSaver.js:

  1. Create a hidden <a> tag.
  2. Set its href attribute to the blob's URL.
  3. Set its download attribute to the filename.
  4. Click on the <a> tag.

Here is a simplified example (jsfiddle):

var saveData = (function () {
    var a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";
    return function (data, fileName) {
        var json = JSON.stringify(data),
            blob = new Blob([json], {type: "octet/stream"}),
            url = window.URL.createObjectURL(blob);
        a.href = url;
        a.download = fileName;
        a.click();
        window.URL.revokeObjectURL(url);
    };
}());

var data = { x: 42, s: "hello, world", d: new Date() },
    fileName = "my-download.json";

saveData(data, fileName);

I wrote this example just to illustrate the idea, in production code use FileSaver.js instead.

Notes

  • Older browsers don't support the "download" attribute, since it's part of HTML5.
  • Some file formats are considered insecure by the browser and the download fails. Saving JSON files with txt extension works for me.
Sign up to request clarification or add additional context in comments.

22 Comments

@AshBlue The "download" attribute needs HTML5. My code is just an example, you could also try the FileSaver.js demo page: eligrey.com/demos/FileSaver.js
Interestingly, if you repeatedly try to download a txt this way (by pressing the Run button on jsfiddle.net again and again), the download sometimes fails.
Just wanted to mention that this solution will not work for files with sizes greater than a particular threshold. e.g-> 2 MB for chrome. This size varies from browser to browser.
This does not work for me because I need to open the file in a new tab. I have to show a PDF in Chrome, but I need to show a user friendly name in the URL toolbar, and if the user wants to download through the download icon, I have to put the same user friendly name in the file.
Just to add, you don't need to actually mount the a tag to the body in order for this to work (tried just now in Chrome)
|
72

I just wanted to expand on the accepted answer with support for Internet Explorer (most modern versions, anyways), and to tidy up the code using jQuery:

$(document).ready(function() {
    saveFile("Example.txt", "data:attachment/text", "Hello, world.");
});

function saveFile (name, type, data) {
    if (data !== null && navigator.msSaveBlob)
        return navigator.msSaveBlob(new Blob([data], { type: type }), name);
    var a = $("<a style='display: none;'/>");
    var url = window.URL.createObjectURL(new Blob([data], {type: type}));
    a.attr("href", url);
    a.attr("download", name);
    $("body").append(a);
    a[0].click();
    window.URL.revokeObjectURL(url);
    a.remove();
}

Here is an example Fiddle. Godspeed.

2 Comments

Worked perfectly.
I used the accepted solution but it didn't work at firefox! I still don't know why. Your solution worked in firefox. Thanks.
58

Same principle as the solutions above. But I had issues with Firefox 52.0 (32 bit) where large files (>40 MBytes) are truncated at random positions. Re-scheduling the call of revokeObjectUrl() fixes this issue.

function saveFile(blob, filename) {
  if (window.navigator.msSaveOrOpenBlob) {
    window.navigator.msSaveOrOpenBlob(blob, filename);
  } else {
    const a = document.createElement('a');
    document.body.appendChild(a);
    const url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = filename;
    a.click();
    setTimeout(() => {
      window.URL.revokeObjectURL(url);
      document.body.removeChild(a);
    }, 0)
  }
}

jsfiddle example

2 Comments

I found that this setTimeout() hack fixes MS Edge, where the file would not download at all. However, only the call to revokeObjectURL() needs to be delayed.
I found that the "if (window.navigator.msSaveOrOpenBlob)" is what did the trick for me
41

Late, but since I had the same problem I add my solution:

function newFile(data, fileName)
{
    var json = JSON.stringify(data);
    //IE11 support
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
        let blob = new Blob([json], {type: "application/json"});
        window.navigator.msSaveOrOpenBlob(blob, fileName);
    } else {// other browsers
        let file = new File([json], fileName, {type: "application/json"});
        let exportUrl = URL.createObjectURL(file);
        window.location.assign(exportUrl);
        URL.revokeObjectURL(exportUrl);
    }
}
<button onclick="newFile({a:1}, 'file.json')">Export data to JSON</button>

12 Comments

Thanks @ben. This is working fine. No dom elements, nothing like to trigger like click event. It just works awesome with proper extension. But the given file name is not considered, downloading "<object_url_id>.csv" instead of "<myfileName>.csv"
Calling revokeObjectURL after location.assign works fine in Firefox, but breaks the download on Chrome.
As @RamBabuS says, this is not keeping fileName, but besides that works perfectly for me
The filename property works in firefox, but not in chrome... anyone a solution for chrome?
The file name is not working in Chrome, so don't waste your time
|
10

This is my solution. From my point of view, you can not bypass the <a>.

function export2json() {
  const data = {
    a: '111',
    b: '222',
    c: '333'
  };
  const a = document.createElement("a");
  a.href = URL.createObjectURL(
    new Blob([JSON.stringify(data, null, 2)], {
      type: "application/json"
    })
  );
  a.setAttribute("download", "data.json");
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}
<button onclick="export2json()">Export data to json file</button>

1 Comment

You are leaking a blob url here though.
7
saveFileOnUserDevice = function(file){ // content: blob, name: string
        if(navigator.msSaveBlob){ // For ie and Edge
            return navigator.msSaveBlob(file.content, file.name);
        }
        else{
            let link = document.createElement('a');
            link.href = window.URL.createObjectURL(file.content);
            link.download = file.name;
            document.body.appendChild(link);
            link.dispatchEvent(new MouseEvent('click', {bubbles: true, cancelable: true, view: window}));
            link.remove();
            window.URL.revokeObjectURL(link.href);
        }
    }

2 Comments

is there any way to open in it a new window?
I think you can call link.click() instead of dispatching a mouse event.
6

Use File instead

File support name, moreover it's created on top of Blob and can be used when you want to obtain a Blob object for a file on the user's file system.

var file = new File([json], name, {type: "octet/stream"});

1 Comment

Although the file name gets disregarded, the file extension gets appended to the object url-like file name, which is a big improvement
5

Working example of a download button, to save a cat photo from an url as "cat.jpg":

HTML:

<button onclick="downloadUrl('https://i.imgur.com/AD3MbBi.jpg', 'cat.jpg')">Download</button>

JavaScript:

function downloadUrl(url, filename) {
  let xhr = new XMLHttpRequest();
  xhr.open("GET", url, true);
  xhr.responseType = "blob";
  xhr.onload = function(e) {
    if (this.status == 200) {
      const blob = this.response;
      const a = document.createElement("a");
      document.body.appendChild(a);
      const blobUrl = window.URL.createObjectURL(blob);
      a.href = blobUrl;
      a.download = filename;
      a.click();
      setTimeout(() => {
        window.URL.revokeObjectURL(blobUrl);
        document.body.removeChild(a);
      }, 0);
    }
  };
  xhr.send();
}

Comments

3

window.location.assign did not work for me. it downloads fine but downloads without an extension for a CSV file on Windows platform. The following worked for me.

    var blob = new Blob([csvString], { type: 'text/csv' });
    //window.location.assign(window.URL.createObjectURL(blob));
    var link = window.document.createElement('a');
    link.href = window.URL.createObjectURL(blob);
    // Construct filename dynamically and set to link.download
    link.download = link.href.split('/').pop() + '.' + extension; 
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);

Comments

-4

this is a good easy solution for it.

function downloadBloob(blob,FileName) {
    var link = document.createElement("a"); // Or maybe get it from the current document
    link.href = blob;
    link.download = FileName;
    link.click();
}

2 Comments

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
This didn't work for me. You have to add "link.href = window.URL.createObjectURL(blob);" instead of "link.href = blob". Otherwise, it will give you an URL like this "localhost:3001/[object%20 Blob]" and the file will be empty and it will not work.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.