ChatGPT解决这个技术问题 Extra ChatGPT

JavaScript blob filename without link

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.


k
kol

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

Create a hidden tag. Set its href attribute to the blob's URL. Set its download attribute to the filename. Click on the 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.


@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)
N
N8allan

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.


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.
K
Kim Nyholm

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


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
M
Martin Schmelzer

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

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 ".csv" instead of ".csv"
Calling revokeObjectURL after location.assign works fine in Firefox, but breaks the download on Chrome.
Note that "Edge does not support the File constructor." Ref. caniuse.com/#feat=fileapi
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?
J
Jean-Philippe
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);
        }
    }

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

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


W
WSBT

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

well done, thanks for help!
S
Sacky San

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

s
segu

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

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.
R
Rey0bs

If you want to download a pdf and using window.location is not mandatory, you can use jsPdf like this following :

// Create document
const doc = new jsPDF('l', 'px', 'a4');

// [...] Add here the jsPdf doc filling

// Launch the document downloading
doc.output('save', 'filename.pdf');