ChatGPT解决这个技术问题 Extra ChatGPT

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

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

自 2014 年 4 月起,FileSytem API 可能未在 W3C 中标准化。我想,任何使用 blob 解决方案的人都应该谨慎行事。 HTML5 rocks heads up W3C Mailing List on FileSytem API

m
mikemaccana

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

函数下载(文件名,文本){ var element = document.createElement('a'); element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); element.setAttribute('下载', 文件名); element.style.display = '无'; document.body.appendChild(元素);元素.click(); document.body.removeChild(元素); } 形式 * { 显示:块;边距:10px; }

用法

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) 中不起作用。
T
Tom Burris

您可以使用数据 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" 属性以获得正确的文件名和扩展名,并告诉您的操作系统如何处理它。
K
Koray Tugay

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';
H
HoldOffHunger

上述所有示例在 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 方法对于非常大的文件效果更好。
D
Daniel Buckmaster

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

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

这在 Chrome 上效果很好。如何允许用户指定文件在磁盘上的位置?
哇,感谢易于使用的库。这很容易是最好的答案,谁在乎人们现在使用 HTML < 5 的任何方式?
@gregm 我不确定您是否可以使用此插件。
@gregm:您的意思是下载位置?这与 FileSaver.js 无关,您需要设置浏览器配置,以便它在每次下载之前要求一个文件夹,或者使用 <a> 上相当新的 download attribute
这是 IE 10+ 系列浏览器的绝佳解决方案。 IE 还不支持下载 HTML 5 标记,并且此页面上的其他解决方案(以及讨论相同问题的其他 SO 页面)根本不适合我。文件保护程序ftw!
d
dinesh ygv

以下方法适用于 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。
数据 uri 方法仍然存在大小限制,不是吗?
msSaveOrOpenBlob 在此处显示为已过时:developer.mozilla.org/en-US/docs/Web/API/Navigator/msSaveBlob
B
Beau Smith

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
N
NVRM

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

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

document.body.innerHTML += `点击我` 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() })

奖励!下载任何 cyclic objects,避免错误:

TypeError:循环对象值(Firefox) TypeError:将循环结构转换为 JSON(Chrome 和 Opera) TypeError:不支持值参数中的循环引用(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 += `` download.click()


最好和最容易使用的解决方案!谢谢
D
Danielo515

此解决方案直接从 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上测试过...
除了函数没有名字,这是我最喜欢的
S
Stelios333

使用 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 支持表:

Here is a Fiddle

这里MDN Docs


R
Rick

如果您只想将字符串转换为可供下载,您可以使用 jQuery 尝试此操作。

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

正如我在这里建议的那样,在能够发表评论之前,可能需要使用 encodeURI 的 Scape 数据:stackoverflow.com/a/32441536/4928558
D
Dzarek

适用于 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 包括这个大,加上对其他浏览器的支持。
p
pixparker
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


E
Erik

如前所述,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()

文本编码器现在是高度实验性的,我建议避免(或填充)它。
S
Soner Gönül
var element = document.createElement('a');
element.setAttribute('href', 'data:text/text;charset=utf-8,' +      encodeURI(data));
element.setAttribute('download', "fileName.txt");
element.click();

这种方法和创建 Blob 有什么区别?
a
atfornes

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

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

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

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

edit suggestion 已共享并被拒绝。


我无法接受这个建议。奇怪...我更新了代码。
g
giapnh

以下功能有效。

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

您可以在新选项卡中打开文件,保持分配的文件名,但不下载,只是在选项卡中打开?
D
Denys Rusov

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

a
abdul

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

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

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

'file64' 是类似这样的 base64 内容,即 Ww6IDEwNDAsIFNsaWRpbmdTY2FsZUdyb3VwOiAiR3JvdXAgQiIsIE1lZGljYWxWaXNpdEZsYXRGZWU6IDM1LCBEZW50YWxQYXltZW50UGVyY2VudGFnZTogMjUsIFByb2NlZHVyZVBlcmNlbnQ6IDcwLKCFfSB7IkdyYW5kVG90YWwiOjEwNDAsIlNsaWRpbmdTY2FsZUdyb3VwIjoiR3JvdXAgQiIsIk1lZGljYWxWaXNpdEZsYXRGZWUiOjM1LCJEZW50YWxQYXltZW50UGVyY2VudGFnZSI6MjUsIlByb2NlZHVyZVBlcmNlbnQiOjcwLCJDcmVhdGVkX0J5IjoiVGVycnkgTGVlIiwiUGF0aWVudExpc3QiOlt7IlBhdGllbnRO


D
Das_Geek

我会使用 <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");
          }
}

S
Sweta Jain

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

H
HBP

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


我曾考虑过这一点,但从用户友好的角度来看,这是灾难性的。此外,该文件必须以 CSV 扩展名保存。试着告诉你的用户。