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

评论

另请参阅:JavaScript:创建和保存文件

#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演示。

评论


这不是跨浏览器解决方案,但绝对值得一看。例如,IE限制了对数据uri的支持。 IE 8将大小限制为32KB,而IE 7和更低版本完全不支持。

–达琳·迪米特洛夫(Darin Dimitrov)
2010-09-08 6:32



在Chrome版本19.0.1084.46中,此方法生成以下警告:“资源被解释为文档,但以MIME类型text / csv传输:“ data:text / csv,field1%2Cfield2%0Afoo%2Cbar%0Agoo%2Cgai%0A”。 ”未触发下载

–克里斯
2012年5月16日11:44



它现在确实可以在Chrome中运行(已针对v20和v21进行了测试),但不适用于IE9(可能只是jsFiddle,但我对此表示怀疑)。

–earcam
2012年8月30日在16:20

正确的字符集几乎可以肯定是UTF-16,除非您有将其转换为UTF-8的代码。 JavaScript在内部使用UTF-16。如果有文本或CSV文件,则以'\ ufeff'(UTF-16BE的字节顺序标记)开头的字符串,文本编辑器将能够正确读取非ASCII字符。

– Larspars
2014年11月19日在9:06

只需添加download =“ txt.csv”属性即可获得正确的文件名和扩展名,并告诉您的操作系统该怎么做。

–elshnkhll
16年1月15日在16:33

#2 楼

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


评论


是的这正是@MatthewFlaschen大约3年前在这里发布的内容。

–约瑟夫·西尔伯(Joseph Silber)
13年8月12日在22:01



是的,但是具有下载属性,您可以指定文件名;-)

–MatějPokorný
13年8月12日在22:08

正如@earcam在上面的评论中已经指出的那样。

–约瑟夫·西尔伯(Joseph Silber)
13年8月13日在0:30

如果您没有在文件名中提供扩展名,Chrome只会附加txt扩展名。如果您下载(“ data.json”,data),它将按预期工作。

–卡尔·史密斯
14年7月19日在14:34

这在Chrome(73.0.3683.86)和Firefox(66.0.2)中对我有用。在IE11(11.379.17763.0)和Edge(44.17763.1.0)中不起作用。

–山姆
19年3月29日在8:40

#3 楼

上述所有解决方案均无法在所有浏览器中正常运行。这是最终在IE 10 +,Firefox和Chrome(并且没有jQuery或任何其他库)上可以使用的功能:

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


请注意,根据您的情况,您可能会还希望在删除elem之后调用URL.revokeObjectURL。根据URL.createObjectURL的文档,每次调用createObjectURL()都会创建一个新的对象URL,即使您已经为同一对象创建了一个URL。当不再需要它们时,必须通过调用URL.revokeObjectURL()释放它们。卸载文档时,浏览器将自动释放这些文件;但是,为了获得最佳性能和内存使用,如果有安全的时间可以明确卸载它们,则应该这样做。


评论


太感谢了。我已经尝试了此处列出的所有示例,并且只有此示例适用于任何浏览器。这应该是公认的答案。

– LEM
16年5月27日在16:51

对于AngularJS 1.x应用程序,您可以在创建它们时构建一组Urls,然后在组件的$ onDestroy函数中对其进行清理。这对我来说很棒。

– Splaktar
16年9月6日在18:12

其他答案导致失败:Chrome中的网络错误。这个很好用。

–瞻博网络-
17/09/27在19:25

这对我来说适用于Chrome(73.0.3683.86),Firefox(66.0.2),IE11(11.379.17763.0)和Edge(44.17763.1.0)。

–山姆
19年3月29日在8:41

对于那些希望避免在URL上进行垃圾回收或其他奇怪行为的人,只需这样声明您的blob:const url = URL.createObjectURL(blob,{oneTimeOnly:true})。您随时可以保存该Blob,并在以后需要时生成新的网址。

–丹尼尔(Daniel)
5月1日21:10

#4 楼

上面的所有示例在chrome和IE上都可以正常运行,但在Firefox中无法运行。
请务必考虑将锚点附加到主体上,并在单击后将其删除。

评论


但是:IE 10中有一个打开的错误(我仍然在11中看到过),在a.click()行上抛出“访问被拒绝”,因为它认为Blob URL是跨域的。

–马特
2014-12-16 19:44



@Matt数据uri在某些浏览器中是跨源的。据我所知,不仅是msie,还有chrome。您可以通过尝试向JavaScript注入数据uri来对其进行测试。它将无法访问网站的其他部分...

– inf3rno
2015年9月13日在0:13



“以上所有示例在chrome和IE中都可以正常运行,但在Firefox中无法运行。”由于答案的顺序会随时间变化,因此您撰写本文时,尚不清楚哪个答案在您的答案之上。您能确切指出哪些方法在Firefox中不起作用吗?

–凯文
18年4月2日在18:57

b这种Blob方法对于非常大的文件效果更好。

– joe
7月29日13:05

#5 楼

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

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


评论


这在Chrome上效果很好。如何允许用户指定磁盘上文件的位置?

– gregm
13年5月22日在18:40

哇,感谢您易于使用的库。这很容易是最好的答案,如今,谁在乎使用HTML <5的人呢?

–notbad.jpeg
13年6月19日在21:16

@gregm我不确定您可以使用此插件。

–丹尼尔·巴克马斯特(Daniel Buckmaster)
2013年6月20日下午6:23

@gregm:您的意思是下载位置?这与FileSaver.js无关,您需要设置浏览器配置,以便它在每次下载前都要求一个文件夹,或者使用上的新下载属性。

– CodeManX
2014年12月3日15:54

这是IE 10+系列浏览器的绝佳解决方案。 IE尚不支持下载HTML 5标记,并且此页面上的其他解决方案(以及其他讨论相同问题的SO页面)对我来说根本不起作用。 FileSaver ftw!

–TMc
2015年1月12日在20:32



#11 楼

我们可以使用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的下载。



 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:Circular
引用不支持value参数(Edge)

使用https://github.com/douglascrockford/JSON-js/blob/master/cycle.js
在此示例中,将document对象下载为json。



 /* 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()