使用JavaScript创建和保存文件

346

我有一些数据想要写入一个文件,并打开一个文件对话框,让用户选择保存文件的位置。最好是能在所有浏览器上都能正常运行,但至少需要在Chrome上正常工作。 我希望这一切都是在客户端完成的。

基本上,我想知道在这个函数中应该放什么:

saveFile: function(data)
{
}

该函数接受数据,要求用户选择保存文件的位置,并在该位置创建一个包含该数据的文件。

如果有帮助的话,也可以使用HTML。


7
多年来,Create a file in memory for user to download, not through server 一直是一个完全的副本。 - Dan Dascalescu
11个回答

340

根据trueimage的建议(支持IE),Awesomeness01的代码进行了非常小的改进(不需要锚标签):

// Function to download data to a file
function download(data, filename, type) {
    var file = new Blob([data], {type: type});
    if (window.navigator.msSaveOrOpenBlob) // IE10+
        window.navigator.msSaveOrOpenBlob(file, filename);
    else { // Others
        var a = document.createElement("a"),
                url = URL.createObjectURL(file);
        a.href = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        setTimeout(function() {
            document.body.removeChild(a);
            window.URL.revokeObjectURL(url);  
        }, 0); 
    }
}

已在Chrome、FireFox和IE10中测试并正常工作。

在Safari中,数据会在新标签页中打开,您需要手动保存该文件。


1
具体是哪个版本的IE? - Aaron Yarborough
1
这在IE 11.0.9600.18426中无法工作,但在Chrome 52.0.2743.116 m中可以工作。有什么想法让它在IE 11中工作吗? - trueimage
2
如何在下载时设置此脚本的位置? - Bravo Yeung
1
在移动版Chrome中它不起作用。 - Jp_
2
我们可以使用什么作为“type”? - posfan12
显示剩余11条评论

156

function download(text, name, type) {
  var a = document.getElementById("a");
  var file = new Blob([text], {type: type});
  a.href = URL.createObjectURL(file);
  a.download = name;
}
<a href="" id="a">click here to download your file</a>
<button onclick="download('file text', 'myfilename.txt', 'text/plain')">Create file</button>

然后,您可以在锚标签上加上下载属性来下载文件。

我喜欢这种方法胜过创建数据URL的原因是您不必生成一个很长的URL,而是可以生成一个临时URL。


@Banjocat 你应该检查浏览器是否支持某些对象。例如检测:if("URL"in window&&"createObjectURL"in URL&&"download"in Element.prototype),否则你需要更改下载方法或者注意到浏览器不支持所需的对象来下载文件。 - user5066707
1
在Firefox中,如果您单击链接,它可以正常工作,但是如果您右键单击并选择“另存为...”,则什么也不会发生。 - aamarks
1
在Google Chrome中无法工作:下载被禁止。启动或实例化下载的框架被沙盒化,但未设置“allow-downloads”标志。有关更多详细信息,请参见https://www.chromestatus.com/feature/5706745674465280。 - posfan12
@posfan12 试着在你自己的页面实现它,SO将代码片段呈现在一个iframe中,如果没有allow-downloads标志是不允许下载文件的。 - Awesomeness01

139

44
http://www.w3.org/TR/file-writer-api/#the-filesaver-interface 中写道:“该文档的工作已经停止,不应将其作为实现的依据或参考。” - Godsmith
实现了w3C的saveAs是什么?演示无用,没有代码。 - CashCow
2
所有代码都可以在Git上找到,这是人们所预期的。不确定“那是什么?”评论的目的是什么。如果它真的想知道是什么,那么https://www.w3.org/TR/file-writer-api/会有帮助。如果它是在指出该项目已取消,那么该信息在Git的README中。 - WesR
1
@Godsmith,我认为这并不重要,因为它似乎会回退到createObjectURL或任何可用的方法来保存文件,并尽可能地处理浏览器差异。 - aamarks

42

在创建文件之前选择文件保存位置是不可能的。但至少在Chrome浏览器中,可以仅使用JavaScript生成文件。以下是我早期创建CSV文件的一个示例。用户将被提示下载它。不幸的是,在其他浏览器中,特别是IE中,这种方法并不起作用。

<!DOCTYPE html>
<html>
<head>
    <title>JS CSV</title>
</head>
<body>
    <button id="b">export to CSV</button>
    <script type="text/javascript">
        function exportToCsv() {
            var myCsv = "Col1,Col2,Col3\nval1,val2,val3";

            window.open('data:text/csv;charset=utf-8,' + escape(myCsv));
        }

        var button = document.getElementById('b');
        button.addEventListener('click', exportToCsv);
    </script>
</body>
</html>

1
当我使用它时,它会在新标签页中打开文本,而不是打开文件对话框窗口。 - user1756980
@user1756980 - 是的。你需要从那个新标签页中选择“保存到文件”。 - Jesse Chisholm
这取决于浏览器、操作系统等。在我回答这个问题的时候,Chrome中的CSV数据URL会弹出一个保存对话框。 - Matt Greer
1
@JesseChisholm,你可以用JavaScript实现这个功能。只需将一个锚点标签创建为一个变量,并在其中添加下载属性(例如:a.download =“downloadname.txt”),然后使用a.click()方法进行点击即可。 - Awesomeness01

16

对于像Chrome这样的最新浏览器,您可以使用此教程中所示的文件API:

window.requestFileSystem  = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(window.PERSISTENT, 5*1024*1024 /*5MB*/, saveFile, errorHandler);

2
我认为这段代码片段更接近提问者的意图。 - Fabrício Matté
2
这里有它已经死亡的证据:http://lists.w3.org/Archives/Public/public-webapps/2014AprJun/0010.html - voidstate

11
function SaveBlobAs(blob, file_name) {
    if (typeof navigator.msSaveBlob == "function")
        return navigator.msSaveBlob(blob, file_name);

    var saver = document.createElementNS("http://www.w3.org/1999/xhtml", "a");
    var blobURL = saver.href = URL.createObjectURL(blob), 
        body = document.body;

    saver.download = file_name;

    body.appendChild(saver);
    saver.dispatchEvent(new MouseEvent("click"));
    body.removeChild(saver);
    URL.revokeObjectURL(blobURL);
}

10

我在控制台尝试了一下,它有效。

var aFileParts = ['<a id="a"><b id="b">hey!</b></a>'];
var oMyBlob = new Blob(aFileParts, {type : 'text/html'}); // the blob
window.open(URL.createObjectURL(oMyBlob));

5

您无法仅使用Javascript完成此操作。由于安全原因,运行在浏览器上的Javascript权限还不足(曾经有提议)。

相反,我建议使用Downloadify

一个微小的javascript + Flash库,使得能够创建和下载文本文件而无需与服务器交互。

您可以在这里看到一个简单的演示,其中您提供内容并可以测试保存/取消/错误处理功能。


过时的答案 - undefined

4

StreamSaver是一个用于保存非常大的文件而不必将所有数据保存在内存中的替代方案。
实际上,它模拟了服务器在保存文件时所做的一切工作,但全部在客户端使用服务工作者完成。

您可以获取写入器并手动向其中写入Uint8Array,也可以将二进制readableStream传输到可写流。

这里有一些示例展示:

  • 如何将多个文件保存为zip文件
  • 将可读流从eg Responseblob.stream()传输到StreamSaver
  • 在键入内容时手动写入可写流
  • 或记录视频/音频

这是最简单形式的示例:

const fileStream = streamSaver.createWriteStream('filename.txt')

new Response('StreamSaver is awesome').body
  .pipeTo(fileStream)
  .then(success, error)

如果您想保存一个 blob,您只需要将其转换为可读流。
new Response(blob).body.pipeTo(...) // response hack
blob.stream().pipeTo(...) // feature reference

这看起来是一个非常有用的库。谢谢! - lovasoa

3
对于Chrome和Firefox,我一直在使用纯JavaScript方法。(我的应用程序不能使用像Blob.js这样的包,因为它是从特殊引擎提供的:一个带有WWWeb服务器的DSP,并且几乎没有任何空间。)
function FileSave(sourceText, fileIdentity) {
    var workElement = document.createElement("a");
    if ('download' in workElement) {
        workElement.href = "data:" + 'text/plain' + "charset=utf-8," + escape(sourceText);
        workElement.setAttribute("download", fileIdentity);
        document.body.appendChild(workElement);
        var eventMouse = document.createEvent("MouseEvents");
        eventMouse.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
        workElement.dispatchEvent(eventMouse);
        document.body.removeChild(workElement);
    } else throw 'File saving not supported for this browser';
}

注意事项、警告及需要说明的:

  • 我已经在Linux(Maipo)和Windows(7和10)环境下的Chrome和Firefox客户端中使用此代码并获得成功。
  • 然而,如果sourceText大于1 MB,Chrome有时(仅有时)会陷入自己的下载中而没有任何失败指示; 到目前为止,Firefox还没有表现出这种行为。原因可能是Chrome中某些blob限制。老实说,我只是不知道;如果有人有任何想法如何纠正(或至少检测),请发帖。如果出现下载异常,则关闭Chrome浏览器时,它会生成诊断文件,例如Chrome browser diagnostic
  • 此代码不兼容Edge或Internet Explorer; 我没有尝试过Opera或Safari。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接