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

How to create a file in memory for user to download, but not through server?

提问人:Joseph Silber 提问时间:9/8/2010 最后编辑:EflyaxJoseph Silber 更新时间:6/15/2023 访问量:648075

问:

有没有办法在客户端创建一个文本文件并提示用户下载它,而无需与服务器进行任何交互?

我知道我不能直接写入他们的计算机(安全和所有),但我可以创建文件并提示他们保存它吗?

JavaScript 文件 Web 应用程序 客户端

评论

7赞 Henry Woody 3/23/2019
另请参阅:JavaScript:创建和保存文件
4赞 pravin 1/20/2015
截至 2014 年 4 月,FileSytem API 可能未在 W3C 中标准化。我想,任何使用 blob 查看解决方案的人都应该谨慎使用。HTML5 在 FileSytem API 上引发了 W3C 邮件列表的注意

答:

485赞 Matthew Flaschen 9/8/2010 #1

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

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

八位字节流用于强制下载提示。否则,它可能会在浏览器中打开。

对于 CSV,您可以使用:

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

尝试 jsFiddle 演示

评论

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

如果文件包含文本数据,我使用的一种技术是将文本放入 textarea 元素中并让用户选择它(单击 textarea,然后单击 ctrl-A),然后复制然后粘贴到文本编辑器中。

评论

40赞 Joseph Silber 9/8/2010
我曾考虑过这一点,但从用户友好的角度来看,这是灾难性的。此外,该文件必须使用 CSV 扩展名保存。试着告诉你的用户。
135赞 Daniel Buckmaster 5/9/2013 #3

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

var blob = new Blob(["some text"], {
    type: "text/plain;charset=utf-8;",
});
saveAs(blob, "thing.txt");

评论

0赞 gregm 5/23/2013
这在 Chrome 上效果很好。如何允许用户指定文件在磁盘上的位置?
6赞 notbad.jpeg 6/20/2013
哇,感谢您易于使用的库。这很容易成为最好的答案,现在谁在乎人们使用 HTML < 5 呢?
0赞 Daniel Buckmaster 6/20/2013
@gregm 我不确定您可以使用此插件。
0赞 CodeManX 12/3/2014
@gregm:你是说下载位置?这与FileSaver.js无关,您需要设置浏览器配置,以便在每次下载之前要求一个文件夹,或者使用相当新的下载属性<a>
1赞 TMc 1/13/2015
这是IE 10+系列浏览器的绝佳解决方案。IE 还不支持下载 HTML 5 标签,此页面上的其他解决方案(以及其他讨论相同问题的 SO 页面)根本不适合我。FileSaver ftw!
1004赞 Matěj Pokorný 8/13/2013 #4

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

评论

13赞 Joseph Silber 8/13/2013
是的。这正是@MatthewFlaschen大约 3 年前在这里发布的内容。
66赞 Matěj Pokorný 8/13/2013
是的,但使用属性可以指定文件名;-)download
6赞 Carl Smith 7/19/2014
Chrome 只有在文件名中未提供扩展名时才会附加扩展名。如果你这样做,它将按预期工作。txtdownload("data.json", data)
9赞 Sam 3/29/2019
这在 Chrome (73.0.3683.86) 和 Firefox (66.0.2) 中对我有用。它在 IE11 (11.379.17763.0) 和 Edge (44.17763.1.0) 中不起作用。
5赞 sean 12/6/2019
无需将元素附加到 DOM。
11赞 Dzarek 11/8/2013 #5

适用于 IE10 的解决方案: (我需要一个csv文件,但足以将类型和文件名更改为txt)

var csvContent=data; //here we load our csv data 
var blob = new Blob([csvContent],{
    type: "text/csv;charset=utf-8;"
});

navigator.msSaveBlob(blob, "filename.csv")

评论

2赞 Dan Dascalescu 11/6/2016
Ludovic 的回答包括这个大,以及对其他浏览器的支持。
203赞 naren 11/25/2013 #6

上面的所有例子在 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);

评论

5赞 Matt 12/17/2014
但是:IE 10 中有一个未解决的错误(我仍然在 11 中看到过它),它在网上抛出“访问被拒绝”,因为它认为 blob URL 是跨域的。a.click()
1赞 inf3rno 9/13/2015
@Matt数据 URI 在某些浏览器中是跨域的。据我所知,不仅在 msie 中,而且在 chrome 中也是如此。您可以通过尝试使用数据 URI 注入 JavaScript 来测试它。它将无法访问网站的其他部分......
11赞 Kevin 4/3/2018
“上面的所有示例在 chrome 和 IE 中都很好用,但在 Firefox 中就失败了。由于答案的顺序会随着时间的推移而改变,因此不清楚在您撰写本文时哪些答案高于您的答案。您能确切地指出哪些方法在Firefox中不起作用吗?
2赞 joe 7/29/2020
👍 此 blob 方法适用于非常大的文件。
22赞 dinesh ygv 12/27/2013 #7

以下方法适用于 IE11+、Firefox 25+ 和 Chrome 30+:

<a id="export" class="myButton" download="" href="#">export</a>
<script>
    function createDownloadLink(anchorSelector, str, fileName){
        if(window.navigator.msSaveOrOpenBlob) {
            var fileData = [str];
            blobObject = new Blob(fileData);
            $(anchorSelector).click(function(){
                window.navigator.msSaveOrOpenBlob(blobObject, fileName);
            });
        } else {
            var url = "data:text/plain;charset=utf-8," + encodeURIComponent(str);
            $(anchorSelector).attr("download", fileName);               
            $(anchorSelector).attr("href", url);
        }
    }

    $(function () {
        var str = "hi,file";
        createDownloadLink("#export",str,"file.txt");
    });

</script>

在行动中查看此内容:http://jsfiddle.net/Kg7eA/

Firefox 和 Chrome 支持用于导航的数据 URI,这允许我们通过导航到数据 URI 来创建文件,而出于安全目的,IE 不支持它。

另一方面,IE 具有用于保存 blob 的 API,可用于创建和下载文件。

评论

1赞 dinesh ygv 11/6/2016
我只是使用 jquery 来附加事件(onclick 和 onready)并设置属性,您也可以使用 vanilla JS 来执行。核心部件 (window.navigator.msSaveOrOpenBlob) 不需要 jquery。
1赞 phil 6/26/2017
数据 uri 方法仍然存在大小限制,不是吗?
0赞 eflat 5/14/2020
msSaveOrOpenBlob 在此处显示为已过时:developer.mozilla.org/en-US/docs/Web/API/Navigator/msSaveBlob
12赞 Rick 2/2/2015 #8

如果您只想转换字符串以供下载,则可以使用jQuery进行尝试。

$('a.download').attr('href', 'data:application/csv;charset=utf-8,' + encodeURI(data));

评论

2赞 atfornes 6/15/2016
可能需要带有 encodeURI 的 scape 数据,正如我在这里建议的那样,然后才能发表评论:stackoverflow.com/a/32441536/4928558
8赞 MANVENDRA LODHI 5/9/2015 #9
var element = document.createElement('a');
element.setAttribute('href', 'data:text/text;charset=utf-8,' +      encodeURI(data));
element.setAttribute('download', "fileName.txt");
element.click();

评论

2赞 Dan Dascalescu 11/6/2016
此方法与创建 Blob 之间有什么区别?
16赞 Danielo515 7/16/2015 #10

该解决方案直接从 tiddlywiki 的 (tiddlywiki.com) github 存储库中提取。我几乎在所有浏览器中都使用过 tiddlywiki,它就像一个魅力:

function(filename,text){
    // Set up the link
    var link = document.createElement("a");
    link.setAttribute("target","_blank");
    if(Blob !== undefined) {
        var blob = new Blob([text], {type: "text/plain"});
        link.setAttribute("href", URL.createObjectURL(blob));
    } else {
        link.setAttribute("href","data:text/plain," + encodeURIComponent(text));
    }
    link.setAttribute("download",filename);
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}

Github repo:下载保护程序模块

评论

1赞 Narxx 2/17/2017
它在 Chrome 上运行得很好,但在 Firefox 上则不然。它确实制作了一个文件并下载了它,但该文件是空的。暂无内容。有什么想法吗?尚未在 IE 上测试...
4赞 Yoraco Gonzales 8/21/2018
除了功能没有名字,这是我的最爱
7赞 atfornes 9/7/2015 #11

基于@Rick答案,这真的很有帮助。

如果你想以这种方式分享它,你必须对字符串进行转义:data

$('a.download').attr('href', 'data:application/csv;charset=utf-8,'+ encodeURI(data));

` 对不起,由于我目前在 StackOverflow 中的声誉较低,我无法对 @Rick 的回答发表评论。

编辑建议被共享并被拒绝。

评论

2赞 Rick 6/16/2016
我无法接受这个建议。奇怪。。。我更新了代码。
321赞 Ludovic Feltz 11/5/2015 #12

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

请注意,根据您的具体情况,您可能还需要在删除 .根据 URL.createObjectURL 的文档:elem

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

评论

2赞 Splaktar 9/7/2016
对于 AngularJS 1.x 应用,您可以在创建 Url 时构建一个数组,然后在组件的 $onDestroy 函数中清理它们。这对我来说效果很好。
3赞 juniper- 9/28/2017
其他答案在Chrome中出现。这个效果很好。Failed: network error
6赞 Sam 3/29/2019
这在 Chrome (73.0.3683.86)、Firefox (66.0.2)、IE11 (11.379.17763.0) 和 Edge (44.17763.1.0) 中对我有用。
5赞 Daniel 5/2/2020
对于那些希望避免在 URL 上进行垃圾回收或奇怪行为的人,只需像这样声明你的 Blob:.如果需要,始终可以保存 Blob,并在以后生成新的 Url。const url = URL.createObjectURL(blob, { oneTimeOnly: true })
5赞 JamesTheAwesomeDude 7/30/2020
如果您想避免任何潜在的视觉故障,请考虑添加 beforeelem.style.display = 'none';document.body.appendChild(elem);
9赞 Mostafa Barmshory 5/17/2017 #13

如前所述,filesaver 是一个很棒的软件包,可以在客户端处理文件。但是,它不能很好地处理大文件。StreamSaver.js 是一种可以处理大文件的替代解决方案(在 FileServer.js 中指向):

const fileStream = streamSaver.createWriteStream('filename.txt', size);
const writer = fileStream.getWriter();
for(var i = 0; i < 100; i++){
    var uint8array = new TextEncoder("utf-8").encode("Plain Text");
    writer.write(uint8array);
}
writer.close()

评论

1赞 Brandon.Blanchard 9/23/2017
文本编码器现在是高度实验性的,我建议避免(或填充)它。
1赞 abdul 10/17/2018 #14

对我来说,这非常有效,下载了相同的文件名和扩展名

<a href={"data:application/octet-stream;charset=utf-16le;base64," + file64 } download={title} >{title}</a>

“title”是带有扩展名的文件名,即、、等。sample.pdfwaterfall.jpg

'file64' 是 base64 内容,如下所示,即Ww6IDEwNDAsIFNsaWRpbmdTY2FsZUdyb3VwOiAiR3JvdXAgQiIsIE1lZGljYWxWaXNpdEZsYXRGZWU6IDM1LCBEZW50YWxQYXltZW50UGVyY2VudGFnZTogMjUsIFByb2NlZHVyZVBlcmNlbnQ6IDcwLKCFfSB7IkdyYW5kVG90YWwiOjEwNDAsIlNsaWRpbmdTY2FsZUdyb3VwIjoiR3JvdXAgQiIsIk1lZGljYWxWaXNpdEZsYXRGZWUiOjM1LCJEZW50YWxQYXltZW50UGVyY2VudGFnZSI6MjUsIlByb2NlZHVyZVBlcmNlbnQiOjcwLCJDcmVhdGVkX0J5IjoiVGVycnkgTGVlIiwiUGF0aWVudExpc3QiOlt7IlBhdGllbnRO

17赞 Beau Smith 2/5/2019 #15

来自 github.com/kennethjiang/js-file-downloadjs-file-download 软件包处理浏览器支持的边缘情况:

查看源代码以了解它如何使用本页提到的技术。

安装

yarn add js-file-download
npm install --save js-file-download

用法

import fileDownload from 'js-file-download'

// fileDownload(data, filename, mime)
// mime is optional

fileDownload(data, 'filename.csv', 'text/csv')

评论

2赞 Brian Burns 4/14/2019
谢谢 - 刚刚测试过 - 适用于 Windows 上的 Firefox、Chrome 和 Edge
3赞 giapnh 2/25/2019 #16

下面的这个功能起作用了。

 private createDownloadableCsvFile(fileName, content) {
   let link = document.createElement("a");
   link.download = fileName;
   link.href = `data:application/octet-stream,${content}`;
   return link;
 }

评论

0赞 Carl Kroeger Ihl 12/6/2019
您可以在新选项卡中打开文件,保持分配的 fileName,但不下载,只是在选项卡中打开吗?
22赞 NVRM 8/1/2019 #17

我们可以使用 URL api,特别是 URL.createObjectURL()Blob api 来编码和下载几乎任何内容。

如果您的下载量很小,则可以正常工作:

document.body.innerHTML += 
`<a id="download" download="PATTERN.json" href="${URL.createObjectURL(new Blob([JSON.stringify("HELLO WORLD", null, 2)]))}"> Click me</a>`
download.click()
download.outerHTML = ""


如果你的下载量很大,而不是使用 DOM,更好的方法是创建一个带有下载参数的链接元素,并触发点击。

请注意,链接元素不会附加到文档中,但无论如何单击都可以!这可以通过这种方式创建数百个 Mo 的下载,因为 DOM 没有被修改(否则 DOM 中的巨大 URL 可能会成为选项卡冻结的来源)。

const stack = {
 some: "stuffs",
 alot: "of them!"
}

BUTTONDOWNLOAD.onclick = (function(){
  let j = document.createElement("a")
  j.download = "stack_"+Date.now()+".json"
  j.href = URL.createObjectURL(new Blob([JSON.stringify(stack, null, 2)]))
  j.click()
})
<button id="BUTTONDOWNLOAD">DOWNLOAD!</button>


奖金!下载任何循环对象,避免错误:

TypeError:循环对象值 (Firefox) TypeError:正在转换

JSON 的循环结构(Chrome 和 Opera) TypeError:循环

不支持值参数中的引用 (Edge)

使用 https://github.com/douglascrockford/JSON-js/blob/master/cycle.js

在此示例中,将对象下载为 json。document

/* JSON.decycle */
if(typeof JSON.decycle!=="function"){JSON.decycle=function decycle(object,replacer){"use strict";var objects=new WeakMap();return(function derez(value,path){var old_path;var nu;if(replacer!==undefined){value=replacer(value)}
if(typeof value==="object"&&value!==null&&!(value instanceof Boolean)&&!(value instanceof Date)&&!(value instanceof Number)&&!(value instanceof RegExp)&&!(value instanceof String)){old_path=objects.get(value);if(old_path!==undefined){return{$ref:old_path}}
objects.set(value,path);if(Array.isArray(value)){nu=[];value.forEach(function(element,i){nu[i]=derez(element,path+"["+i+"]")})}else{nu={};Object.keys(value).forEach(function(name){nu[name]=derez(value[name],path+"["+JSON.stringify(name)+"]")})}
return nu}
return value}(object,"$"))}}


document.body.innerHTML += 
`<a id="download" download="PATTERN.json" href="${URL.createObjectURL(new Blob([JSON.stringify(JSON.decycle(document), null, 2)]))}"></a>`
download.click()

评论

3赞 enes islam 5/1/2022
最好和最简单的解决方案!谢谢
1赞 darrell 11/15/2019 #18

我会使用一个标签,然后设置.然后,在元素之间放置一个图像,以便我可以有一个视觉效果来看到它。如果你愿意,你可以创建一个函数来改变 这样它就不仅仅是同一个链接,而是动态的。<a></a>href='path'<a>href

如果您想使用 javascript 访问标签,也给它一个。<a>id

从 HTML 版本开始:

<a href="mp3/tupac_shakur-how-do-you-want-it.mp3" download id="mp3Anchor">
     <img src="some image that you want" alt="some description" width="100px" height="100px" />
</a>

现在使用 JavaScript:

*Create a small json file*;

const array = [
     "mp3/tupac_shakur-how-do-you-want-it.mp3",
     "mp3/spice_one-born-to-die.mp3",
     "mp3/captain_planet_theme_song.mp3",
     "mp3/tenchu-intro.mp3",
     "mp3/resident_evil_nemesis-intro-theme.mp3"
];

//load this function on window
window.addEventListener("load", downloadList);

//now create a function that will change the content of the href with every click
function downloadList() {
     var changeHref=document.getElementById("mp3Anchor");

     var j = -1;

     changeHref.addEventListener("click", ()=> {

           if(j < array.length-1) {
               j +=1;
               changeHref.href=""+array[j];
          }
           else {
               alert("No more content to download");
          }
}
1赞 Denys Rusov 3/14/2020 #19

以下方法适用于 IE10+、Edge、Opera、FF 和 Chrome:

const saveDownloadedData = (fileName, data) => {
    if(~navigator.userAgent.indexOf('MSIE') || ~navigator.appVersion.indexOf('Trident/')) { /* IE9-11 */
        const blob = new Blob([data], { type: 'text/csv;charset=utf-8;' });
        navigator.msSaveBlob(blob, fileName);
    } else {
        const link = document.createElement('a')
        link.setAttribute('target', '_blank');
        if(Blob !== undefined) {
            const blob = new Blob([data], { type: 'text/plain' });
            link.setAttribute('href', URL.createObjectURL(blob));
        } else {
            link.setAttribute('href', 'data:text/plain,' + encodeURIComponent(data));
        }

        ~window.navigator.userAgent.indexOf('Edge')
            && (fileName = fileName.replace(/[&\/\\#,+$~%.'':*?<>{}]/g, '_')); /* Edge */

        link.setAttribute('download', fileName);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    }
}

因此,只需调用函数:

saveDownloadedData('test.txt', 'Lorem ipsum');
43赞 Stelios333 11/19/2020 #20

使用 Blob

function download(content, mimeType, filename){
  const a = document.createElement('a') // Create "a" element
  const blob = new Blob([content], {type: mimeType}) // Create a blob (file-like object)
  const url = URL.createObjectURL(blob) // Create an object URL from blob
  a.setAttribute('href', url) // Set "a" element link
  a.setAttribute('download', filename) // Set download filename
  a.click() // Start downloading
}

所有现代浏览器都支持 Blob。
Blob 的 Caniuse 支持表:

这是一把小提琴

这里是 MDN 文档

Blob 对象表示一个 Blob,它是不可变的原始数据的类似文件的对象;它们可以作为文本或二进制数据读取......

14赞 pixparker 3/23/2021 #21
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);
}



// Start file download.
download("hello.txt","This is the content of my file :)");

原文 : https://ourcodeworld.com/articles/read/189/how-to-create-a-file-and-generate-a-download-with-javascript-in-the-browser-without-a-server

2赞 Subhan Ali 10/15/2021 #22

下载示例中带有扩展名或不带扩展名的文件,我使用的是 JSON。您可以添加您的数据和扩展程序。您可以根据自己的意愿在此处使用“MAC-Addresses.json”。如果要添加扩展名,请在此处添加,否则,只需编写不带扩展名的文件名即可。

let myJson = JSON.stringify(yourdata);
    let element = document.createElement('a');
    element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(myJson));
    element.setAttribute('download', 'MAC-Addresses.json');
    element.style.display = 'none';
    document.body.appendChild(element);
    element.click();
    document.body.removeChild(element);

评论

0赞 Franco Gil 11/13/2022
不错的代码片段,对我有用。