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

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

593148 次浏览

您可以使用数据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演示

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

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

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

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

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

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

以上所有示例在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 bodydocument.body.removeChild(a);

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

<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尝试此操作。

$('a.download').attr('href', 'data:application/csv;charset=utf-8,' + encodeURI(data));
var element = document.createElement('a');element.setAttribute('href', 'data:text/text;charset=utf-8,' +      encodeURI(data));element.setAttribute('download', "fileName.txt");element.click();

这个解决方案直接从tiddlywiki的(tiddlywiki.com)github存储库中提取。我几乎在所有浏览器中都使用过tiddlywiki,它的工作原理非常棒:

function(filename,text){// Set up the linkvar 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:下载保护程序模块

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

如果您想以这种方式共享它,您必须对字符串data进行景观处理:

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

'对不起,我不能评论@Rick的回答,因为我目前在StackOverflow中的声誉很低。

编辑建议被共享和拒绝。

例如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.revokeObjectURL()来释放它们。浏览器将在文档卸载时自动释放这些;但是,为了获得最佳性能和内存使用,如果您可以在安全时间内显式卸载它们,您应该这样做。

如前所述,文件保存是处理客户端文件的绝佳软件包。但是,它不适合大文件。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()

对我来说,这工作得很好,下载了相同的文件名和扩展名

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

'title'是扩展名为sample.pdfwaterfall.jpg等的文件名。

'file64'是Base64内容,类似于这样,即Ww6IDEwNDAsIFNsaWRpbmdTY2FsZUdyb3VwOiAiR3JvdXAgQiIsIE1lZGljYWxWaXNpdEZsYXRGZWU6IDM1LCBEZW50YWxQYXltZW50UGVyY2VudGFnZTogMjUsIFByb2NlZHVyZVBlcmNlbnQ6IDcwLKCFfSB7IkdyYW5kVG90YWwiOjEwNDAsIlNsaWRpbmdTY2FsZUdyb3VwIjoiR3JvdXAgQiIsIk1lZGljYWxWaXNpdEZsYXRGZWUiOjM1LCJEZW50YWxQYXltZW50UGVyY2VudGFnZSI6MjUsIlByb2NlZHVyZVBlcmNlbnQiOjcwLCJDcmVhdGVkX0J5IjoiVGVycnkgTGVlIiwiUGF0aWVudExpc3QiOlt7IlBhdGllbnRO

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

查看源代码查看它如何使用此页面上提到的技术。

安装

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

用法

import fileDownload from 'js-file-download'
// fileDownload(data, filename, mime)// mime is optional
fileDownload(data, 'filename.csv', 'text/csv')

下面的功能起作用了。

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

我们可以使用url api,特别是URL.createObjectURL()二进制大对象 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,更好的方法是使用下载参数创建一个链接元素,并触发单击。

请注意,link元素没有附加到文档中,但单击无论如何都可以工作!这可以通过这种方式创建数百个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>


Bonus! Download any cyclic objects, avoid the errors:

TypeError: cyclic object value (Firefox) TypeError: Converting

circular structure to JSON (Chrome and Opera) TypeError: Circular

reference in value argument not supported (Edge)

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

On this example, downloading the document object as 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()

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

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

从超文本标记语言版本开始:

<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 windowwindow.addEventListener("load", downloadList);
//now create a function that will change the content of the href with every clickfunction 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");}}

以下方法适用于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');

使用二进制大对象

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

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

这是一个小提琴

在这里MDN文档

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

下载带扩展名或不带扩展名的文件在示例中,我使用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);