如何在内存中创建一个文件供用户下载,而不是通过服务器?

IT技术 javascript file web-applications client-side
2021-01-06 16:33:31

有什么办法可以在客户端创建一个文本文件并提示用户下载它,而无需与服务器进行任何交互?我知道我不能直接写入他们的机器(安全和所有),但是我可以创建并提示他们保存它吗?

6个回答

适用于 HTML5 浏览器的简单解决方案...

function download(filename, text) {
  var element = document.createElement('a');
  element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
  element.setAttribute('download', filename);

  element.style.display = 'none';
  document.body.appendChild(element);

  element.click();

  document.body.removeChild(element);
}
form * {
  display: block;
  margin: 10px;
}
<form onsubmit="download(this['name'].value, this['text'].value)">
  <input type="text" name="name" value="test.txt">
  <textarea name="text"></textarea>
  <input type="submit" value="Download">
</form>

用法

download('test.txt', 'Hello world!');
这在 Chrome (73.0.3683.86) 和 Firefox (66.0.2) 中对我有用。它确实不是在IE11(11.379.17763.0)和边缘(44.17763.1.0)工作。
2021-02-07 16:33:31
正如@earcam在上面的评论中已经指出的那样
2021-02-11 16:33:31
是的。这正是@MatthewFlaschen大约 3 年前在这里发布的内容
2021-03-02 16:33:31
是的,但download您可以使用属性指定文件名;-)
2021-03-05 16:33:31
txt如果您未在文件名中提供扩展名,Chrome 只会附加扩展名。如果你这样做,download("data.json", data)它会按预期工作。
2021-03-05 16:33:31

您可以使用数据 URI。浏览器支持各不相同;参见维基百科例子:

<a href="data:application/octet-stream;charset=utf-16le;base64,//5mAG8AbwAgAGIAYQByAAoA">text file</a>

octet-stream 是强制下载提示。否则,它可能会在浏览器中打开。

对于 CSV,您可以使用:

<a href="data:application/octet-stream,field1%2Cfield2%0Afoo%2Cbar%0Agoo%2Cgai%0A">CSV Octet</a>

试试jsFiddle 演示

这不是跨浏览器的解决方案,但绝对值得一看。例如 IE 限制对数据 uri 的支持。IE 8 将大小限制为 32KB,而 IE 7 及更低版本根本不支持。
2021-02-11 16:33:31
只需添加 download="txt.csv" 属性以获得正确的文件名和扩展名,并告诉您的操作系统如何处理它。
2021-02-19 16:33:31
在 Chrome 版本 19.0.1084.46 中,此方法生成以下警告:“资源被解释为文档但使用 MIME 类型 text/csv 传输:“data:text/csv,field1%2Cfield2%0Afoo%2Cbar%0Agoo%2Cgai%0A”。 ” 不触发下载
2021-02-23 16:33:31
正确的字符集几乎肯定是 UTF-16,除非您有将其转换为 UTF-8 的代码。JavaScript 在内部使用 UTF-16。如果您有文本或 CSV 文件,请以 '\ufeff' 开头,这是 UTF-16BE 的字节顺序标记,文本编辑器将能够正确读取非 ASCII 字符。
2021-02-25 16:33:31
它现在可以在 Chrome 中运行(针对 v20 和 v21 进行了测试),但不能在 IE9 中运行(这可能只是 jsFiddle,但不知何故我对此表示怀疑)。
2021-03-07 16:33:31

IE 10+、Firefox 和 Chrome(没有jQuery 或任何其他库)的示例

function save(filename, data) {
    const blob = new Blob([data], {type: 'text/csv'});
    if(window.navigator.msSaveOrOpenBlob) {
        window.navigator.msSaveBlob(blob, filename);
    }
    else{
        const elem = window.document.createElement('a');
        elem.href = window.URL.createObjectURL(blob);
        elem.download = filename;        
        document.body.appendChild(elem);
        elem.click();        
        document.body.removeChild(elem);
    }
}

请注意,根据您的情况,您可能还想在删除.revokeObjectURL调用URL.revokeObjectURLelem根据URL.createObjectURL 的文档

每次调用 createObjectURL() 时,都会创建一个新的对象 URL,即使您已经为同一个对象创建了一个。当您不再需要它们时,必须通过调用 URL.revokeObjectURL() 来释放它们中的每一个。当文档被卸载时,浏览器会自动释放这些;但是,为了获得最佳性能和内存使用,如果有可以明确卸载它们的安全时间,则应该这样做。

对于 AngularJS 1.x 应用程序,您可以在创建 Url 时构建一个数组,然后在组件的 $onDestroy 函数中清理它们。这对我很有用。
2021-02-13 16:33:31
如果您想避免任何潜在的视觉故障,请考虑elem.style.display = 'none';在之前添加document.body.appendChild(elem);
2021-02-23 16:33:31
这在 Chrome (73.0.3683.86)、Firefox (66.0.2)、IE11 (11.379.17763.0) 和 Edge (44.17763.1.0) 中对我有用。
2021-03-01 16:33:31
对于那些希望避免垃圾收集的网址或奇怪的行为,只是声明你的斑点是这样的:const url = URL.createObjectURL(blob, { oneTimeOnly: true })如果需要,您可以随时保存 blob 并在以后生成新的 Url。
2021-03-02 16:33:31
Failed: network errorChrome 中的其他答案这个效果很好。
2021-03-05 16:33:31

以上所有示例在 chrome 和 IE 中都可以正常工作,但在 Firefox 中却失败了。请考虑将锚点附加到正文并在单击后将其删除。

var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(new Blob(['Test,Text'], {type: 'text/csv'}));
a.download = 'test.csv';

// Append anchor to body.
document.body.appendChild(a);
a.click();

// Remove anchor from body
document.body.removeChild(a);
“以上所有示例在 chrome 和 IE 中都可以正常工作,但在 Firefox 中却失败了。”。由于答案的顺序会随着时间的推移而改变,因此不清楚您在撰写本文时哪些答案高于您的答案。你能准确指出哪些方法在 Firefox 中不起作用吗?
2021-02-17 16:33:31
但是:IE 10 中一个开放的错误(我仍然在 11 中看到它),它在线上抛出“访问被拒绝”,a.click()因为它认为 blob URL 是跨域的。
2021-02-28 16:33:31
@Matt 数据 uri 在某些浏览器中是跨源的。据我所知,不仅在 msie 中,而且在 chrome 中也是如此。您可以通过尝试使用数据 uri 注入 javascript 来测试它。它将无法访问网站的其他部分...
2021-03-04 16:33:31
👍 这种 blob 方法对于非常大的文件效果更好。
2021-03-05 16:33:31

我很高兴使用FileSaver.js它的兼容性非常好(IE10+和其他所有东西),并且使用起来非常简单:

var blob = new Blob(["some text"], {
    type: "text/plain;charset=utf-8;",
});
saveAs(blob, "thing.txt");
@gregm:你是说下载位置?这与 FileSaver.js 无关,您需要设置浏览器配置,以便它在每次下载之前请求一个文件夹,或者在 .js使用相当新的下载属性<a>
2021-02-07 16:33:31
这在 Chrome 上效果很好。如何允许用户指定文件在磁盘上的位置?
2021-02-10 16:33:31
@gregm 我不确定你能不能使用这个插件。
2021-02-10 16:33:31
这是 IE 10+ 系列浏览器的绝佳解决方案。IE 尚不支持下载 HTML 5 标记,并且此页面上的其他解决方案(以及其他讨论相同问题的 SO 页面)根本不适合我。FileSaver ftw!
2021-02-24 16:33:31
哇,感谢易于使用的库。这很容易成为最好的答案,而且现在谁在乎人们以任何方式使用 HTML < 5?
2021-03-02 16:33:31