如何在内存中创建一个文件供用户下载,而不是通过服务器?
Posted
技术标签:
【中文标题】如何在内存中创建一个文件供用户下载,而不是通过服务器?【英文标题】:How to create a file in memory for user to download, but not through server? 【发布时间】:2011-04-09 13:41:34 【问题描述】:有什么方法可以在客户端创建一个文本文件并提示用户下载它,而无需与服务器进行任何交互? 我知道我不能直接写入他们的机器(安全和所有),但我可以创建并提示他们保存吗?
【问题讨论】:
自 2014 年 4 月起,FileSytem API 可能未在 W3C 中标准化。我想,任何使用 blob 解决方案的人都应该谨慎行事。 html5 rocks heads upW3C Mailing List on FileSytem API 另见:javascript: Create and save file 【参考方案1】: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 所拥有的posted here about 3 years ago。 是的,但是使用download
属性可以指定文件名;-)
正如@earcam 已经指出的in the comments above。
如果您没有在文件名中提供扩展名,Chrome 只会附加 txt
扩展名。如果您执行download("data.json", data)
,它将按预期工作。
这在 Chrome (73.0.3683.86) 和 Firefox (66.0.2) 中对我有用。它在 IE11 (11.379.17763.0) 和 Edge (44.17763.1.0) 中确实不工作。【参考方案2】:
您可以使用数据 URI。浏览器支持各不相同;见Wikipedia。示例:
<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 demo。
【讨论】:
这不是跨浏览器解决方案,但绝对值得一看。例如 IE 限制对数据 uri 的支持。 IE 8 将大小限制为 32KB,而 IE 7 及更低版本根本不支持。 在 Chrome 版本 19.0.1084.46 中,此方法会生成以下警告:“资源解释为文档但使用 MIME 类型 text/csv 传输:”data:text/csv,field1%2Cfield2%0Afoo%2Cbar %0Agoo%2Cgai%0A"。"未触发下载 它现在可以在 Chrome 中工作(针对 v20 和 v21 进行测试)但不能在 IE9 中工作(可能只是 jsFiddle,但不知何故我对此表示怀疑)。 正确的字符集几乎肯定是 UTF-16,除非您有代码将其转换为 UTF-8。 JavaScript 在内部使用 UTF-16。如果您有文本或 CSV 文件,请以 '\ufeff' 开头的字符串,UTF-16BE 的字节顺序标记,文本编辑器将能够正确读取非 ASCII 字符。 只需添加 download="txt.csv" 属性以获得正确的文件名和扩展名,并告诉您的操作系统如何处理它。【参考方案3】: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);
请注意,根据您的情况,您可能还想在删除 elem
后调用 URL.revokeObjectURL。根据URL.createObjectURL 的文档:
每次调用 createObjectURL() 时,都会创建一个新的对象 URL,即使您已经为同一个对象创建了一个 URL。当您不再需要它们时,必须通过调用 URL.revokeObjectURL() 来释放它们中的每一个。当文档被卸载时,浏览器会自动释放它们;但是,为了获得最佳性能和内存使用率,如果您可以在安全时间显式卸载它们,则应该这样做。
【讨论】:
对于 AngularJS 1.x 应用程序,您可以在创建 URL 时构建一个数组,然后在组件的 $onDestroy 函数中清理它们。这对我很有用。 其他答案导致 Chrome 中的Failed: network error
。这个效果很好。
这适用于我的 Chrome (73.0.3683.86)、Firefox (66.0.2)、IE11 (11.379.17763.0) 和 Edge (44.17763.1.0)。
对于那些希望避免 URL 上的垃圾收集或奇怪行为的人,只需像这样声明您的 blob:const url = URL.createObjectURL(blob, oneTimeOnly: true )
。如果需要,您可以随时保存 blob 并在以后生成新的 Url。
如果您想避免任何潜在的视觉故障,请考虑在document.body.appendChild(elem);
之前添加elem.style.display = 'none';
【参考方案4】:
上述所有示例在 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);
【讨论】:
但是:an open bug in IE 10 (and I've still seen it in 11) 在a.click()
行上抛出“访问被拒绝”,因为它认为 blob URL 是跨域的。
@Matt data uri 在某些浏览器中是跨源的。据我所知,不仅在 msie 中,而且在 chrome 中也是如此。您可以通过尝试使用数据 uri 注入 javascript 来测试它。它将无法访问网站的其他部分...
"上述所有示例在 chrome 和 IE 中都可以正常工作,但在 Firefox 中失败。"。由于答案的顺序会随着时间的推移而改变,所以当你写这篇文章时,不清楚哪些答案高于你的答案。您能否准确指出哪些方法在 Firefox 中不起作用?
? 这种 blob 方法对于非常大的文件效果更好。【参考方案5】:
我很高兴使用FileSaver.js。它的兼容性很好(IE10+ 和其他),而且使用起来非常简单:
var blob = new Blob(["some text"],
type: "text/plain;charset=utf-8;",
);
saveAs(blob, "thing.txt");
【讨论】:
这在 Chrome 上效果很好。如何允许用户指定文件在磁盘上的位置? 哇,感谢易于使用的库。这无疑是最好的答案,谁在乎人们现在以任何方式使用 HTML @gregm 我不确定你是否可以使用这个插件。 @gregm:你的意思是下载位置?这与 FileSaver.js 无关,您需要设置浏览器配置,以便在每次下载之前要求一个文件夹,或者在<a>
上使用相当新的 download attribute。
这是 IE 10+ 系列浏览器的绝佳解决方案。 IE 还不支持下载 HTML 5 标记,并且此页面上的其他解决方案(以及讨论相同问题的其他 SO 页面)根本不适合我。 FileSaver ftw!【参考方案6】:
以下方法适用于 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,可用于创建和下载文件。
【讨论】:
我只是使用 jquery 来附加事件(onclick 和 onready)并设置属性,您也可以使用 vanilla JS 来执行此操作。核心部分(window.navigator.msSaveOrOpenBlob)不需要jquery。 data uri 方式还是有大小限制的吧? msSaveOrOpenBlob 在此处显示为已过时:developer.mozilla.org/en-US/docs/Web/API/Navigator/msSaveBlob【参考方案7】:来自github.com/kennethjiang/js-file-download 的包js-file-download 处理浏览器支持的边缘情况:
View source 看看它如何使用本页提到的技术。
安装
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')
【讨论】:
谢谢 - 刚刚测试过 - 适用于 Windows 上的 Firefox、Chrome 和 Edge【参考方案8】:此解决方案直接从 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 存储库: Download saver module
【讨论】:
它在 Chrome 上运行得非常好,但在 Firefox 上却不行。它确实会创建一个文件并下载它,但该文件是空的。无内容。任何想法为什么?没有在 IE 上测试过... 除了函数没有名字,这是我最喜欢的【参考方案9】:我们可以使用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>
奖励!下载任何cyclic objects,避免错误:
TypeError: 循环对象值 (Firefox) TypeError: Converting
JSON 的循环结构(Chrome 和 Opera)TypeError: Circular
不支持值参数中的引用(边缘)
使用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+"]"))elsenu=;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()
【讨论】:
【参考方案10】:如果您只想将字符串转换为可供下载,您可以尝试使用 jQuery。
$('a.download').attr('href', 'data:application/csv;charset=utf-8,' + encodeURI(data));
【讨论】:
可能需要使用 encodeURI 的 Scape 数据,正如我在这里建议的那样,然后才能发表评论:***.com/a/32441536/4928558【参考方案11】:适用于 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")
【讨论】:
Ludovic's answer 包括这个大,加上对其他浏览器的支持。【参考方案12】:如前所述,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()
【讨论】:
文本编码器现在是高度实验性的,我建议避免(或填充)它。【参考方案13】:var element = document.createElement('a');
element.setAttribute('href', 'data:text/text;charset=utf-8,' + encodeURI(data));
element.setAttribute('download', "fileName.txt");
element.click();
【讨论】:
这种方法和创建 Blob 有什么区别?【参考方案14】:基于@Rick 的回答,这真的很有帮助。
如果你想以这种方式分享,你必须对字符串 data
进行转义:
$('a.download').attr('href', 'data:application/csv;charset=utf-8,'+ encodeURI(data));
` 抱歉,由于我目前在 *** 中的声誉很低,我无法评论 @Rick 的回答。
edit suggestion 被分享并被拒绝。
【讨论】:
我无法接受这个建议。奇怪...我更新了代码。【参考方案15】: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
【讨论】:
【参考方案16】:使用Blob:
function download(content, mimeType, filename)
const a = document.createElement('a')
const blob = new Blob([content], type: mimeType)
const url = URL.createObjectURL(blob)
a.setAttribute('href', url)
a.setAttribute('download', filename)
a.click()
所有现代浏览器都支持 Blob。 Blob 的 Caniuse 支持表:
Here is a Fiddle【讨论】:
【参考方案17】:下面这个函数起作用了。
private createDownloadableCsvFile(fileName, content)
let link = document.createElement("a");
link.download = fileName;
link.href = `data:application/octet-stream,$content`;
return link;
【讨论】:
你能在新标签页中打开文件,保持分配的文件名,但不下载,只是在标签页中打开吗?【参考方案18】:以下方法适用于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');
【讨论】:
【参考方案19】:对我来说,这非常有效,下载了相同的文件名和扩展名
<a href="data:application/octet-stream;charset=utf-16le;base64," + file64 download=title >title</a>
'title'是带有扩展名的文件名,即sample.pdf
、waterfall.jpg
等。
'file64' 是类似这样的 base64 内容,即Ww6IDEwNDAsIFNsaWRpbmdTY2FsZUdyb3VwOiAiR3JvdXAgQiIsIE1lZGljYWxWaXNpdEZsYXRGZWU6IDM1LCBEZW50YWxQYXltZW50UGVyY2VudGFnZTogMjUsIFByb2NlZHVyZVBlcmNlbnQ6IDcwLKCFfSB7IkdyYW5kVG90YWwiOjEwNDAsIlNsaWRpbmdTY2FsZUdyb3VwIjoiR3JvdXAgQiIsIk1lZGljYWxWaXNpdEZsYXRGZWUiOjM1LCJEZW50YWxQYXltZW50UGVyY2VudGFnZSI6MjUsIlByb2NlZHVyZVBlcmNlbnQiOjcwLCJDcmVhdGVkX0J5IjoiVGVycnkgTGVlIiwiUGF0aWVudExpc3QiOlt7IlBhdGllbnRO
【讨论】:
【参考方案20】:我会使用<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" />
</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");
【讨论】:
【参考方案21】:在示例中下载带扩展名或不带扩展名的文件,我使用的是 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);
【讨论】:
【参考方案22】:如果文件包含文本数据,我使用的一种技术是将文本放入 textarea 元素并让用户选择它(单击 textarea 然后按 ctrl-A)然后复制,然后粘贴到文本编辑器。
【讨论】:
我曾考虑过,但从用户友好的角度来看,这是灾难性的。此外,该文件必须以 CSV 扩展名保存。试着告诉你的用户。以上是关于如何在内存中创建一个文件供用户下载,而不是通过服务器?的主要内容,如果未能解决你的问题,请参考以下文章
如何在Javascript中创建动态文件+链接以供下载? [复制]
在使用 Flask 的 python 中,如何写出一个对象以供下载?
Spring MVC - 在内存中创建一个ZIP文件,让用户下载
如何在Dynamic CRM 2013中创建WebService接口供其它系统调用