JavaScript中的Blob文件名(不包含链接)

305

当通过window.location强制下载Blob文件时,如何在JavaScript中设置该文件的名称?

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

运行上述代码,不需要页面刷新即可立即下载一个文件,其外观如下:

bfefe410-8d9c-4883-86c5-d76c50a24a1d

我想将文件名设置为my-download.json

10个回答

484
我所知道的唯一方法是FileSaver.js使用的技巧:
  1. 创建一个隐藏的<a>标签。
  2. 将其href属性设置为Blob的URL。
  3. 将其download属性设置为文件名。
  4. 点击<a>标签。
这里是一个简化的示例(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);

我写了这个例子只是为了说明这个想法,在生产代码中请使用FileSaver.js代替。

注意事项

  • 旧版浏览器不支持“download”属性,因为它是HTML5的一部分。
  • 某些文件格式被浏览器视为不安全,下载会失败。将JSON文件保存为txt扩展名对我来说有效。

2
@AshBlue "download"属性需要HTML5支持。我的代码只是一个示例,你也可以尝试使用FileSaver.js演示页面:http://eligrey.com/demos/FileSaver.js/ - kol
1
有趣的是,如果你一遍又一遍地尝试以这种方式下载txt文件(通过不断地在jsfiddle.net上按“运行”按钮),有时会下载失败。 - kol
4
想要提一下,这个解决方案对于文件大小超出特定阈值的文件无效。例如:对于 Chrome 浏览器,阈值为 2MB。这个阈值因浏览器而异。 - manojadams
33
这对我不起作用,因为我需要在新标签页中打开文件。我必须在Chrome中展示一个PDF,但我需要在URL工具栏中展示一个用户友好的名称,如果用户想通过下载图标下载,我必须将相同的用户友好名称放在文件中。 - Adrián Paredes
4
只是补充一下,为了使这个方法有效(我刚在Chrome上试过),你不需要实际将a标签挂载到body上。 - beyond-code
显示剩余17条评论

70

我只是想在接受的答案上扩展一下,增加对Internet Explorer(最新版本)的支持,并使用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();
}

这是一个示例 Fiddle祝你好运


完美运行。 - N8allan
1
我使用了被接受的解决方案,但在Firefox上它不起作用!我仍然不知道原因。您的解决方案在Firefox中起作用。谢谢。 - elahehab

56

和上面的解决方案原理相同。但是我在Firefox 52.0(32位)上遇到了问题,其中大文件(> 40 MBytes)在随机位置被截断。重新安排revokeObjectUrl()的调用可以解决此问题。

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示例


2
我发现这个setTimeout()的技巧可以解决MS Edge的问题,否则文件根本无法下载。然而,只有对revokeObjectURL()的调用需要延迟。 - Russell Phillips
我发现 "if (window.navigator.msSaveOrOpenBlob)" 就是为我解决问题的方法。 - Jacques Olivier

38
晚了点,但既然我也遇到了同样的问题,我来分享一下我的解决方案:

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>


8
谢谢@ben。这个很好用,没有dom元素或者需要触发点击事件之类的东西。只要使用适当的扩展功能就能完美运行。但是给定的文件名没有被考虑在内,下载的是"<object_url_id>.csv"而不是"<myfileName>.csv"。 - Ram Babu
3
在Firefox中,调用location.assign后再调用revokeObjectURL没有问题;但在Chrome中会导致下载出现问题。 - Fred
2
正如@RamBabuS所说,这并没有保留fileName,但除此之外对我来说完美无缺。 - Manu Artero
7
文件名属性在Firefox中可用,但在Chrome中不行... 有没有Chrome的解决方案? - Gerros
1
文件名在Chrome中无法工作,所以不要浪费时间。 - Experimenter
显示剩余7条评论


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

有没有办法在新窗口中打开它? - Enrique Altuna
1
我认为你可以调用link.click()而不是分派鼠标事件。 - Fred

5

下载按钮的工作示例,从某个 URL 下载猫咪照片并保存为“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();
}

干得好,感谢你的帮助! - Andrej Gaspar

3

我使用 window.location.assign 无法正常工作。在 Windows 平台上,它可以成功下载一个 CSV 文件,但没有文件扩展名。以下方式对我有效。

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

0

使用文件替代

文件 支持名称,此外它是建立在 Blob 之上的,并且可以在您想要获取用户文件系统中文件的 Blob 对象时使用。

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


-1

这是一个很好的简单解决方案。

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

您的回答可以通过添加更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认您的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Ethan
这对我没有起作用。你必须添加 "link.href = window.URL.createObjectURL(blob);" 而不是 "link.href = blob"。否则,它将给你一个类似于 "http://localhost:3001/[object%20 Blob]" 的 URL,文件将为空,而且无法运行。 - Eduard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接