仅使用JavaScript是否可以将数据写入文件?

293
我想使用JavaScript向现有文件写入数据。 我不想将它打印到控制台。 我想实际写入数据到abc.txt。 我阅读了很多回答的问题,但是每个地方都在控制台上打印。 某些地方他们提供了代码,但是它不起作用。 所以,请问是否有任何人可以帮助我如何实际将数据写入文件。
我参考了代码,但它不起作用: 它会报错:

Uncaught TypeError: Illegal constructor

在谷歌浏览器上和

SecurityError: The operation is insecure.

在Mozilla浏览器上。
var f = "sometextfile.txt";

writeTextFile(f, "Spoon")
writeTextFile(f, "Cheese monkey")
writeTextFile(f, "Onion")

function writeTextFile(afilename, output)
{
  var txtFile =new File(afilename);
  txtFile.writeln(output);
  txtFile.close();
}

那么我们能否仅使用Javascript将数据写入文件呢?


2
检查这个兄弟:https://dev59.com/ZnRB5IYBdhLWcg3wkH9N - welbornio
1
https://dev59.com/I27Xa4cB1Zd3GeqPoEl1 - Emilio Gort
11个回答

282

你可以使用BlobURL.createObjectURL在浏览器中创建文件。所有最新版本的浏览器都支持这个功能

由于这样做会导致严重的安全问题,因此您不能直接保存您创建的文件。但是,您可以将其作为下载链接提供给用户。如果浏览器支持下载属性,您可以通过链接的download属性建议一个文件名。与任何其他下载一样,下载文件的用户最终会对文件名进行决定。

var textFile = null,
  makeTextFile = function (text) {
    var data = new Blob([text], {type: 'text/plain'});

    // If we are replacing a previously generated file we need to
    // manually revoke the object URL to avoid memory leaks.
    if (textFile !== null) {
      window.URL.revokeObjectURL(textFile);
    }

    textFile = window.URL.createObjectURL(data);

    // returns a URL you can use as a href
    return textFile;
  };

这里有一个示例,使用这种技术可以保存来自textarea的任意文本。

如果想要立即启动下载而不需要用户点击链接,可以使用鼠标事件模拟单击该链接,就像Lifecube回答所示。我创建了一个更新的示例,使用了这种技术。

  var create = document.getElementById('create'),
    textbox = document.getElementById('textbox');

  create.addEventListener('click', function () {
    var link = document.createElement('a');
    link.setAttribute('download', 'info.txt');
    link.href = makeTextFile(textbox.value);
    document.body.appendChild(link);

    // wait for the link to be added to the document
    window.requestAnimationFrame(function () {
      var event = new MouseEvent('click');
      link.dispatchEvent(event);
      document.body.removeChild(link);
    });

  }, false);

1
我在尝试在Safari 5.1上运行它 :) - First Blood
2
保存的文档中缺少换行符。 - Benny
1
@Benny,换行符已经存在。JS使用换行符\n来表示新行,就像UNIX程序一样。你可能在Windows程序(例如记事本)中查看它,该程序不会将\n字符呈现为新行。如果您希望在记事本和其他一些Windows程序中正确呈现换行符,请在将文本放入“Blob”之前,将每个\n替换为\r\ntext = text.replace(/\n/g, '\r\n') - Useless Code
1
虽然这种方式可以运行,但是你的小提琴在IE上无法工作,因为IE不支持download属性。 - Banjocat
2
@user3241111,不是很确定,但应该可以。这种事情并不罕见。我曾经看到过更加hackyer的方法;-) 过去我也曾尝试在链接上的“mouseover”事件中生成文件,但这取决于它需要处理多少内容,可能效果不太好。 - Useless Code
显示剩余5条评论

112

以下是一些建议 -

  1. 如果您要在客户端机器上编写文件,则无法以任何跨浏览器的方式完成此操作。 IE确实有方法可以使“受信任”的应用程序使用ActiveX对象来读/写文件。
  2. 如果要保存文件到服务器,则只需将文本数据传递给服务器并使用某些服务器端语言执行文件编写代码即可。
  3. 对于存储规模相对较小的一些客户端信息,可以选择使用cookie。
  4. 使用HTML5 API进行本地存储。

36
HTML5 API 在最大限制上只能达到 5 mb。 - Pacerier
是的,如果没有选择它,您无法写入系统文件。请阅读官方文档:https://www.w3.org/TR/file-upload/#security-discussion - Manoj Ghediya
你能解释一下第二点吗?“如果你想要将它保存在你的服务器上,那么只需将文本数据传递给你的服务器,并使用一些服务器端语言执行文件写入代码。”你能提供任何教程链接吗? - s1w_
1
@s1w_ 这很容易理解,根据文本数据(来自客户端)在服务器端创建/追加文件。即在服务器端编写文件代码。 - Akash Jain
本地存储在大多数浏览器中仅限于5MB,因此我建议使用IndexedDB来存储更大的文件。 - Anderson Green

52

如果你正在谈论浏览器javascript,出于安全原因你不能直接将数据写入本地文件。HTML 5新增的API只允许你读取文件。

但是如果你想要写入数据,并允许用户下载到本地作为文件,以下代码可行:

    function download(strData, strFileName, strMimeType) {
    var D = document,
        A = arguments,
        a = D.createElement("a"),
        d = A[0],
        n = A[1],
        t = A[2] || "text/plain";

    //build download link:
    a.href = "data:" + strMimeType + "charset=utf-8," + escape(strData);


    if (window.MSBlobBuilder) { // IE10
        var bb = new MSBlobBuilder();
        bb.append(strData);
        return navigator.msSaveBlob(bb, strFileName);
    } /* end if(window.MSBlobBuilder) */



    if ('download' in a) { //FF20, CH19
        a.setAttribute("download", n);
        a.innerHTML = "downloading...";
        D.body.appendChild(a);
        setTimeout(function() {
            var e = D.createEvent("MouseEvents");
            e.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
            a.dispatchEvent(e);
            D.body.removeChild(a);
        }, 66);
        return true;
    }; /* end if('download' in a) */



    //do iframe dataURL download: (older W3)
    var f = D.createElement("iframe");
    D.body.appendChild(f);
    f.src = "data:" + (A[2] ? A[2] : "application/octet-stream") + (window.btoa ? ";base64" : "") + "," + (window.btoa ? window.btoa : escape)(strData);
    setTimeout(function() {
        D.body.removeChild(f);
    }, 333);
    return true;
}

如何使用:

download('文件内容', '文件名.txt', 'text/plain');


3
以上解决方案有点过时了。您可能需要考虑使用HTML 5 JavaScript库。https://github.com/eligrey/FileSaver.js/ - Lifecube
2
@Lifecube使用FileSaver.js,有没有一种方法可以在没有用户交互的情况下自动将文本保存到文件中? 谢谢!我是JS新手;感谢您的所有帮助。 - Nathan majicvr.com
3
对于如何在用户不知情的情况下保存文件的若干问题:这种行为正是设计所要避免的。这将打开一个易于使用的安全威胁的潘多拉之盒。Cookie 用于收集营销数据。 - Ari Okkonen
注意:我无法在Windows 10上的Firefox v76中将HTML文件下载为.html格式。下载的文件名末尾会附加.pdf。 - Schwarz Software
这只是另一个开发者在讽刺吗? - Stephen Duffy
不确定这个与时俱进,七年后在Brave中对我仍然完美地工作。剪切、粘贴、享受。 - John Smith

37
尝试

let a = document.createElement('a');
a.href = "data:application/octet-stream,"+encodeURIComponent("My DATA");
a.download = 'abc.txt';
a.click();

如果你想下载二进制数据,请在这里查看

更新

2020.06.14 我将Chrome升级到83.0及以上版本,SO片段停止工作(原因:沙箱安全性限制)- 但JSFiddle版本可以使用 - 在这里


27

上面的答案很有用,但是我找到了一段代码,它可以帮助你在按钮点击时直接下载文本文件。 在这段代码中,您也可以根据需要更改filename。这是一个纯JavaScript函数与HTML5配合使用。 对我很有效!

function saveTextAsFile()
{
    var textToWrite = document.getElementById("inputTextToSave").value;
    var textFileAsBlob = new Blob([textToWrite], {type:'text/plain'});
    var fileNameToSaveAs = document.getElementById("inputFileNameToSaveAs").value;
      var downloadLink = document.createElement("a");
    downloadLink.download = fileNameToSaveAs;
    downloadLink.innerHTML = "Download File";
    if (window.webkitURL != null)
    {
        // Chrome allows the link to be clicked
        // without actually adding it to the DOM.
        downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
    }
    else
    {
        // Firefox requires the link to be added to the DOM
        // before it can be clicked.
        downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
        downloadLink.onclick = destroyClickedElement;
        downloadLink.style.display = "none";
        document.body.appendChild(downloadLink);
    }

    downloadLink.click();
}

3
很好。 在Opera上对我起作用。 除了需要替换未知函数:“destroyClickedElement”为语句“document.body.removeChild(event.target)”。 - steveOw
6
使用createObjectURL时需要小心。与JS中的大多数东西不同,使用它创建的对象在没有引用时不会自动进行垃圾回收;它们只有在页面关闭时才会被垃圾回收。由于在此代码中没有使用URL.revokeObjectURL()来释放上次调用所使用的内存,因此存在内存泄漏的风险。如果用户多次调用saveTextFile,它们将继续消耗越来越多的内存,因为您从未释放它。 - Useless Code
你好,我不是网站开发员,首先感谢您的代码,看起来很棒。但对我来说它并不能正常工作。我尝试添加了 <form onsubmit="saveTextAsFile()"> 到我的表单中,并把 downloadLink.download = "/home/myuser/myfile.txt" 进行了更改,但它却没有创建任何东西。能否提供帮助?谢谢。 - Alejandro Galera

12
const data = {name: 'Ronn', age: 27};              //sample json
const a = document.createElement('a');
const blob = new Blob([JSON.stringify(data)]);
a.href = URL.createObjectURL(blob);
a.download = 'sample-profile';                     //filename to download
a.click();

查看Blob文档,请点击此处Blob MDN,以获取文件类型的额外参数。默认情况下,它会生成.txt文件。


9
在无法使用新的Blob解决方案时(这是现代浏览器中最好的解决方案),仍然可以使用这种更简单的方法,但文件大小有一定限制:
function download() {
                var fileContents=JSON.stringify(jsonObject, null, 2);
                var fileName= "data.json";

                var pp = document.createElement('a');
                pp.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(fileContents));
                pp.setAttribute('download', fileName);
                pp.click();
            }
            setTimeout(function() {download()}, 500);

$('#download').on("click", function() {
  function download() {
    var jsonObject = {
      "name": "John",
      "age": 31,
      "city": "New York"
    };
    var fileContents = JSON.stringify(jsonObject, null, 2);
    var fileName = "data.json";

    var pp = document.createElement('a');
    pp.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(fileContents));
    pp.setAttribute('download', fileName);
    pp.click();
  }
  setTimeout(function() {
    download()
  }, 500);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="download">Download me</button>


4
使用用户@useless-code在上面提供的代码(https://dev59.com/CWEi5IYBdhLWcg3wueR0#21016088) 生成文件。 如果你想自动下载文件,请将刚生成的textFile传递给此函数:
var downloadFile = function downloadURL(url) {
    var hiddenIFrameID = 'hiddenDownloader',
    iframe = document.getElementById(hiddenIFrameID);
    if (iframe === null) {
        iframe = document.createElement('iframe');
        iframe.id = hiddenIFrameID;
        iframe.style.display = 'none';
        document.body.appendChild(iframe);
    }
    iframe.src = url;
}

6
不知道为什么这个被踩了。它对我有效。给出负评的人应该至少留下一条评论解释为什么会被踩! - RPM
5
我没有投下反对票,但实际上评论投票是不被鼓励的。用户应该评论帖子的内容,并根据帖子的内容进行投票,但不应该评论他们的投票。如果有人投票而没有发表评论,则可以理解为“这个答案是有用的”或“这个答案是无用的”,具体取决于所投票的选项。 - user4639281
这个不起作用。它没有下载文件,只是创建了一个隐藏的 iframe。我在 Chrome 和 Firefox 上测试过。 - NaiveCoder

3
我在这里找到了好的答案,但也发现了一种更简单的方法。
将创建 blob 和下载链接的按钮合并成一个链接,因为链接元素可以有 onclick 属性。(反过来似乎不行,给按钮添加 href 不起作用。)
你可以使用 Bootstrap 将链接样式设置为按钮,Bootstrap 仍然是纯 JavaScript,除了样式方面。
将按钮和下载链接结合起来还可以减少代码量,因为需要的那些丑陋的 getElementById 调用变少了。
这个例子只需要点击一个按钮就可以创建文本 blob 并下载它:
<a id="a_btn_writetofile" download="info.txt" href="#" class="btn btn-primary" 
   onclick="exportFile('This is some dummy data.\nAnd some more dummy data.\n', 'a_btn_writetofile')"
>
   Write To File
</a>

<script>
    // URL pointing to the Blob with the file contents
    var objUrl = null;
    // create the blob with file content, and attach the URL to the downloadlink; 
    // NB: link must have the download attribute
    // this method can go to your library
    function exportFile(fileContent, downloadLinkId) {
        // revoke the old object URL to avoid memory leaks.
        if (objUrl !== null) {
            window.URL.revokeObjectURL(objUrl);
        }
        // create the object that contains the file data and that can be referred to with a URL
        var data = new Blob([fileContent], { type: 'text/plain' });
        objUrl = window.URL.createObjectURL(data);
        // attach the object to the download link (styled as button)
        var downloadLinkButton = document.getElementById(downloadLinkId);
        downloadLinkButton.href = objUrl;
    };
</script>

被卡巴斯基阻止了 :( - Lokinou
@Lokinou 安全万岁,但是你知道它为什么被阻止了吗? - Roland
我的猜测是防止未经用户操作下载不需要的文件。 - Lokinou

2
这是谷歌搜索中关于“js如何写入本地文件”的最佳结果,因此需要根据2023年新增的API(在Chrome等浏览器中可用)提供一个更新的答案。
'安全环境'中,网页现在可以请求访问目录或本地文件。当您请求访问目录时,浏览器会提示用户接受或拒绝请求。
一旦用户接受了您的请求,您就可以访问该目录及其所有子目录。此时,您可以请求写入权限。(浏览器会解释该请求的含义。)
// open a directory picker and wait for the user to pick a file
const directory = await showDirectoryPicker();

// list the files in the directory
for await (const fileEntity of directory.values()) {
    if (fileEntity.kind === "file") {
        console.log(fileEntity.name);
    }
}

对于给定的FileSystemFileHandle,您可以通过调用createWritable将其转换为FileSystemWritableFileStream。在Chrome中,对createWritable的第一次调用会提示用户授予代码写入本地文件的访问权限。
// write a file (and implicitly ask for write permission)
const writable = await fileEntity.createWritable();
await writable.write("Hello!");
await writable.close();

所有现代浏览器似乎都支持这个API,除了Safari不支持createWritable

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