JavaScript blob filename without link
Asked Answered
E

10

308

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.

Endodontics answered 11/10, 2013 at 21:44 Comment(0)
S
489

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.
Sochi answered 11/10, 2013 at 23:47 Comment(22)
Oddly it seems to be failing when I copy and paste this into CodePen. Although your code seems legit and good codepen.io/ashblue/pen/jEhmHEndodontics
Your codepen code works for me in Chrome. The json file is saved into the download folder of Chrome.Sochi
@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.jsSochi
I'm getting "Failed - No file" on Chrome Windows ver 30. Is it maybe a Windows exclusive bug?Endodontics
The download works for me on a Windows 7 PC, but I got the "Failed - No file" message on Windows XP. Interestingly, The FileSaver.js demo page is able to download files to XP.Sochi
I've found a workaround: if I change the extension to txt, then the download works.Sochi
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.Sochi
i allready have an <a id="saveToJson">save as JSON</a> link. i do the Blob creatin logic in the $("#saveToJson") callback function but the problem is i can't do this.click() unless the function is gonna run itself over and over again. ps: i don't like to create those hidden workaround filds so i wanted to set the attributes of my element when it's clicked.Stator
@Stator Since you already have an <a> tag, and create the Blob in its click handler, you don't have to call .click() yourself, because it will cause infinite recursion.Sochi
@xheyhenry FileSaver.js should work in IE10+: github.com/eligrey/FileSaver.js/#supported-browsers There is a workaround for IE<10: github.com/eligrey/FileSaver.js/#ie--10Sochi
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.Glycerite
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.Shikoku
Can I do this kind of thing without using <a> tag and window. Window is not available in all browsers and I do not want to create an <a> tag in my angular app.Subassembly
https://mcmap.net/q/55248/-javascript-blob-filename-without-link works for me without needing the <a> trick used here and in the other answers. Is there a reason why it's not more upvoted and that this is still the top answer ?Bostow
@Bostow Isn't msSaveOrOpenBlob Internet Explorer specific?Sochi
@Sochi probably but I used the technique for other browsers: window.location = URL.createObjectURL(new File(...)) and it works fine for me on Firefox (I don't know about other browsers).Bostow
For some reason, this fails for me when using Babel with Webpack, because the body element is not available yet, even if the code is only called after the document is loaded or not called at all. When using the untranspiled ES6 module source code, it works fine. I changed it to a normal function and declaring a reused a element outside of the function.Imagination
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)Continuo
How can I make works for the video tag with blob data.How to set the download file extension for blob dataQuake
Why is saveData declared as an IIFE?Gehrke
weirdly enough, this adds auto file extensions when the filename is without one... I had some logfile called logfile and chrome made it logfile.xml without any indication from my side (it's not even an xml file)Shotgun
Wow. a.click() just sopped working for my users in Chrome / downloads the ugly random filename. Looking at the FileSaver.js source, they've had a replacement for click() since 2018: e.g. a.dispatchEvent(new MouseEvent('click'))Divinity
R
71

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.

Rachitis answered 27/4, 2016 at 20:8 Comment(2)
Worked perfectly.Outrider
I used the accepted solution but it didn't work at firefox! I still don't know why. Your solution worked in firefox. Thanks.Hydromancy
X
57

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

Xray answered 24/2, 2018 at 23:26 Comment(2)
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.Interne
I found that the "if (window.navigator.msSaveOrOpenBlob)" is what did the trick for meChronologist
S
39

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>
Snatch answered 11/9, 2018 at 10:28 Comment(12)
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"Feverous
Calling revokeObjectURL after location.assign works fine in Firefox, but breaks the download on Chrome.Caparison
Note that "Edge does not support the File constructor." Ref. caniuse.com/#feat=fileapiPhotoelectrotype
This should be the correct answer. No point in creating useless objects in the DOM treeSolingen
Now it does, since Jan '20Solingen
As @RamBabuS says, this is not keeping fileName, but besides that works perfectly for meBrittenybrittingham
There is no need to call revokeObjectURL, the URL will automatically be revoked when the creating document (the page you were on) is closed. Which is also why you must use a new tab to view the file (at least in chrome).Boesch
Can I download a blob video with this function?Shawndashawnee
The filename property works in firefox, but not in chrome... anyone a solution for chrome?Paint
The file name is not working in Chrome, so don't waste your timeClarisaclarise
Great solution, still filename not working in Chrome (or Safari)...Any news on this for these browsers?!Mythologize
That's the way! Good answer.Hawley
C
9

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>
Cabinetwork answered 24/2, 2020 at 14:55 Comment(1)
You are leaking a blob url here though.Rodin
G
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);
        }
    }
Glair answered 13/4, 2018 at 15:27 Comment(2)
is there any way to open in it a new window?Workday
I think you can call link.click() instead of dispatching a mouse event.Caparison
A
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();
}
Arillode answered 3/9, 2019 at 20:31 Comment(1)
well done, thanks for help!Wiretap
A
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);
Atal answered 7/10, 2019 at 0:38 Comment(0)
H
2

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

Hawley answered 18/7, 2023 at 20:27 Comment(0)
H
-3

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();
}
Hicks answered 3/6, 2022 at 10:55 Comment(2)
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.Almoner
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.Amey

© 2022 - 2024 — McMap. All rights reserved.