使用HTML5 / JavaScript生成并保存文件


317

我最近一直在摆弄WebGL,并让Collada阅读器工作。问题是它相当慢(Collada是一种非常冗长的格式),因此我将开始将文件转换为更易于使用的格式(可能是JSON)。我已经有了使用JavaScript解析文件的代码,因此我也可以将其用作导出器!问题正在保存。

现在,我知道我可以解析文件,将结果发送到服务器,并让浏览器从服务器请求文件下载作为下载。但是实际上,服务器与该特定过程无关,那么为什么要参与其中呢?我已经在内存中保存了所需文件的内容。有什么办法可以使用户使用纯JavaScript进行下载?(我对此表示怀疑,但也可能会问...)

需要明确的是:我不会在用户不知情的情况下尝试访问文件系统!用户将提供一个文件(可能通过拖放操作),脚本将转换文件在内存中的位置,并提示用户下载结果。就浏览器而言,所有这些都应该是“安全”的活动。

[编辑]:我没有提前提到它,所以回答“ Flash”的海报是足够有效的,但是我正在做的部分工作是试图强调使用纯HTML5可以完成的工作...因此Flash是就我而言。(尽管对于任何使用“真实” Web应用程序的人来说,这都是一个非常有效的答案。)在这种情况下,除非我不想让服务器参与其中,否则看起来我很不走运。不管怎么说,还是要谢谢你!


8
您可以考虑更改接受的答案,现在看来似乎是纯HTML方式
Pekka

Answers:


256

好的,创建一个data:URI绝对对我有用,这要感谢Matthew和Dennkster指出了这一选择!基本上,这是我的操作方式:

1)将所有内容放入一个名为“内容”的字符串中(例如,通过在此开始创建内容或读取已构建页面的标签的innerHTML)。

2)建立资料URI:

uriContent = "data:application/octet-stream," + encodeURIComponent(content);

长度会受到浏览器类型等的限制,但是例如Firefox 3.6.12至少可以运行到256k。在Base64中进行编码而不是使用encodeURIComponent可能会使事情更高效,但是对我来说没关系。

3)打开一个新窗口,并将其“重定向”到此URI提示输入我的JavaScript生成页面的下载位置:

newWindow = window.open(uriContent, 'neuesDokument');

而已。


34
如果要避免使用会被阻止的弹出窗口,可以将设置location.href为内容。location.href = uriContent
Alex Turpin

12
嗨,我尝试过此方法,但它会将文件下载为.part文件。如何设置文件类型?
塞达(SedatBaşar)2011年

6
@SedatBaşar数据URI不支持设置文件名,您必须依靠浏览器使用mime类型设置适当的扩展名。但是,如果MIME类型可以由浏览器呈现,它将不会下载它,但会显示它。还有其他一些方法可以执行此操作,但是在IE <10中均不能使用。
panzi 2012年

7
IE并没有真正支持数据URI,Firefox似乎使用随机名称保存文件。
nylund

25
我认为我们正在使这一过程变得更加困难。在此页面上打开您的JS控制台并将其放入location.href = "data:application/octet-stream," + encodeURIComponent(jQuery('#answer-4551467 .post-text').first().text());,它将@Nøk答案的内容保存到文件中。我没有IE对其进行测试,但它适用于webkit。
布鲁诺·布罗诺斯基

278

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

function download(filename, text) {
    var pom = document.createElement('a');
    pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
    pom.setAttribute('download', filename);

    if (document.createEvent) {
        var event = document.createEvent('MouseEvents');
        event.initEvent('click', true, true);
        pom.dispatchEvent(event);
    }
    else {
        pom.click();
    }
}

用法

download('test.txt', 'Hello world!');

2
如果您知道要下载的源URL,则这是最佳解决方案!
sidonaldson

31
设置文件名的能力使它成为赢家。
Omn

9
在几天前我收到最新更新(35.0.1916.114 m)之前,此方法在Chrome中一直有效。现在它可以部分起作用,因为文件名和扩展名不再起作用(无论传递什么,它始终将文件命名为download.txt。)
Sevin7,2014年

6
我有Chrom 42.0.2311.90,这对我来说应该是预期的文件名。
Saurabh Kumar 2015年

4
是否可以包含的数据量有限制?
卡斯珀·李

80

HTML5定义了一种window.saveAs(blob, filename)方法。目前没有任何浏览器支持。但是,有一个名为FileSaver.js的兼容性库,可以将该功能添加到大多数现代浏览器(包括Internet Explorer 10+)中。Internet Explorer 10支持一种navigator.msSaveBlob(blob, filename)方法(MSDN),该方法在FileSaver.js中用于Internet Explorer支持。

我写了一篇博客文章,详细介绍了这个问题。


1
阻止弹出窗口呢?该库的行为类似于@Nøk的解决方案。Firefox中的纯文本将以新的方式打开。只有Chrome浏览器会尝试保存它,但是会更改扩展名(我需要一个没有扩展名的dotfile)。
cimbor 2012年

1
@ciembor(object url +)download属性变体(我在chrome中使用)可让您设置文件名。它适用于Chrome。
panzi 2012年

1
@ciembor aha和弹出窗口如果被单击直接导致,则不会被阻止。
panzi 2012年

6
FileSaver.js现在支持IE
Eli Gray

15
W3C表示:该文档的工作已经停止,不应引用或用作实施的基础。
威Kit Kung 2014年

48

保存大文件

长数据URI可能会在浏览器中带来性能问题。保存客户端生成的文件的另一种方法是将其内容放入Blob(或File)对象中,并使用创建下载链接URL.createObjectURL(blob)。这将返回一个URL,该URL可用于检索Blob的内容。该Blob存储在浏览器内部,直到URL.revokeObjectURL()在URL上调用它或创建它的文档关闭为止。大多数Web浏览器都支持对象URL,Opera Mini是唯一不支持对象URL的对象

强制下载

如果数据是文本或图像,则浏览器可以打开文件,而不是将其保存到磁盘。要在单击链接时下载文件,可以使用download属性。但是,并非所有的Web浏览器都支持download属性。另一种选择是application/octet-stream用作文件的mime类型,但这会导致文件显示为二进制blob,如果您不指定文件名或不指定文件名,则该文件特别不便于用户使用。另请参见“ 强制打开“另存为...”弹出窗口,该链接在文本链接处单击,单击HTML中pdf ”。

指定文件名

如果blob是使用File构造函数创建的,则还可以设置文件名,但是只有少数Web浏览器(包括Chrome和Firefox)支持File构造函数。文件名也可以指定为download属性的参数,但这需要考虑大量的安全性。Internet Explorer 10和11提供了自己的方法msSaveBlob来指定文件名。

范例程式码

var file;
var data = [];
data.push("This is a test\n");
data.push("Of creating a file\n");
data.push("In a browser\n");
var properties = {type: 'text/plain'}; // Specify the file's mime-type.
try {
  // Specify the filename using the File constructor, but ...
  file = new File(data, "file.txt", properties);
} catch (e) {
  // ... fall back to the Blob constructor if that isn't supported.
  file = new Blob(data, properties);
}
var url = URL.createObjectURL(file);
document.getElementById('link').href = url;
<a id="link" target="_blank" download="file.txt">Download</a>


1
我可以显示一个对话框(弹出窗口)以指定一个文件夹(目录)来保存文件吗?
加尔文

@Calvin:我已经更新了答案,以解释如何强制下载并提供文件名。对于IE,我相信您可以msSaveBlob用来打开“另存为”对话框。对于其他浏览器,唯一的选择是从下载链接的上下文菜单中手动选择“另存为”。
bcmpinc

1
@ Jek-fdrv:仅blob网址在Safari中有效。Safari不支持download属性和File构造函数,因此您无法强制进行下载,这意味着该blob可能会在浏览器本身中打开,并且您无法指定文件名。对于给定的示例,您仍然应该能够使用链接的上下文菜单通过Safari下载文件。
bcmpinc '16


这是一个非常有用和有益的答案。还有一件可能对像我这样的人有所帮助的事情:设置之后document.getElementById('link').href = url;,您的代码可以继续使用元素的.click()方法触发链接。
LarsH 18/09/17

36
function download(content, filename, contentType)
{
    if(!contentType) contentType = 'application/octet-stream';
        var a = document.createElement('a');
        var blob = new Blob([content], {'type':contentType});
        a.href = window.URL.createObjectURL(blob);
        a.download = filename;
        a.click();
}

3
contentType有什么作用?这有什么用途?
2014年

3
与@MatějPokorný的答案不同,即使在最新的Chrome浏览器中,此功能也能正常工作。谢谢。
亚历山大·阿梅尔金

2
在FF36或IE11上,这对我不起作用。如果我按照MatějPokorný的建议a.click使用代码替换document.createEvent(),则它适用于FF,但不适用于IE。我没有尝试过Chrome。
Peter Hull 2015年

25

看一下Doug Neiner的Downloadify,它是基于Flash的JavaScript接口。

Downloadify是一个很小的JavaScript + Flash库,可在浏览器中即时生成和保存文件,而无需服务器交互。


6
对于大多数人来说,这可能就是他们需要的答案。因此,即使它不符合我的特定要求(如上所述),我仍将其标记为可接受的答案。
东芝

2
@Toji啊,我明白了。也许在HTML 5横幅下重新询问和重新措辞并相应地标记?这很可能会吸引那些知道该特定领域的用户(我想现在还是一个相对较小的人群)。我很确定它可以在HTML 5中完成,但我不知道如何做。
Pekka 2010年

1
downloadify.info Downloadify域名被购买/转移,如果是的话是有一个新的位置?本站点似乎与给出的答案完全无关。
Christos Hayward

17
这无法回答“ 使用HTML5 ... ”标题问题。
Ixx 2012年

5
@Ixx很好,在发布答案后添加。不过,你是对的。下面的答案应该被接受
Pekka

17

简单的解决方案!

<a download="My-FileName.txt" href="data:application/octet-stream,HELLO-WORLDDDDDDDD">Click here</a>

在所有现代浏览器中均可使用。


嗨,您知道如何使用window.open或其他javascript函数指定“下载”属性的行为吗?
yucer '16

2
@yucer没有的下载属性(或与此有关的任何属性)window.open()。没关系 您可以使用此方法并对其进行强制.click(),但是请注意计时,因为.click()在将元素附加到正文之前调用Firefox并不喜欢它。
布拉德

但是遗憾的是所有空格都被删除了。就我而言,那真是太糟糕了,因为我想下载SVG文件的源代码。
frankenapps

如果你使用encodeURIComponent方法(内容)空格都被保留
麦克Redrobe

无法在firefox中选择名称,但chrome起作用了
Bhikkhu Subhuti

10

您可以生成数据URI。但是,存在特定于浏览器的限制。


1
这是有趣的。如果有机会,我会进一步研究。谢谢!
东芝

@quantumpotato,实际上生成URL有点棘手。所有细节都在RFC 2397中。您可以使用工具,如测试。然后,对于您的真实应用程序,您可以搜索数据URI或基于64位语言的库。如果找不到,请随时提出后续问题。Wikipedia 文章中提供了一些特定于浏览器的限制。例如,IE限制了长度和类型(例如,不是text / html)。
马修·弗拉申

生成数据URL并不是那么棘手:"data:text/plain;charset=UTF-8,"+encodeURIComponent(text)但是,是的,IE将数据URL的大小限制为无法使用,并且不支持诸如window.open(...)iframe之类的东西(我认为)。
panzi 2013年

@panzi,也不是那么容易。一方面,对于Collada或JSON,这不是正确的MIME类型。
马修·弗莱申

太无用的答案。如果提及它们,请添加浏览器的规范。
T.Todua '16

10

我已经使用了FileSaver(https://github.com/eligrey/FileSaver.js),并且工作正常。
例如,我执行此功能来导出页面上显示的日志。
您必须为Blob的实例传递一个数组,所以我也许没有编写正确的方法,但是它对我有用。
以防万一,请谨慎对待replace:这是将其全局化的语法,否则它将仅替换他遇到的第一个。

exportLogs : function(){
    var array = new Array();

    var str = $('#logs').html();
    array[0] = str.replace(/<br>/g, '\n\t');

    var blob = new Blob(array, {type: "text/plain;charset=utf-8"});
    saveAs(blob, "example.log");
}

2
FileSaver很棒,这是针对IE10之前的功能preIE10SaveAs的IE Shim :(文件名,文件内容,模仿类型){var w = window.open(); var doc = w.document; doc.open(mimetype,'replace'); doc.charset =“ utf-8”; doc.write(filecontent); doc.close(); doc.execCommand(“ SaveAs”,null,文件名); }
aqm

关于@aqm填充程序的警告:它执行脚本标签。
GameFreak

此外,可能希望放w.close();execCommand
GameFreak '17

8

我发现了两种对我有用的简单方法。首先,使用已经单击的a元素并注入下载数据。其次,a使用下载数据生成一个元素,a.click()然后再次执行并删除它。但是,第二种方法也仅在用户单击操作也调用时才有效。(某些)浏览器会阻止click()其他上下文,例如在加载时或在超时(setTimeout)之后触发。

<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="UTF-8">
    <script type="text/javascript">
      function linkDownload(a, filename, content) {
        contentType =  'data:application/octet-stream,';
        uriContent = contentType + encodeURIComponent(content);
        a.setAttribute('href', uriContent);
        a.setAttribute('download', filename);
      }
      function download(filename, content) {
        var a = document.createElement('a');
        linkDownload(a, filename, content);
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
      }
    </script>
   </head>
  <body>
    <a href="#" onclick="linkDownload(this, 'test.txt', 'Hello World!');">download</a>
    <button onclick="download('test.txt', 'Hello World!');">download</button>
  </body>
</html>

2
您也可以将插入a文档(可能带有"display:none"),单击它,然后将其删除。
Teepeemm 2014年

1
可以在不支持下载属性的浏览器(例如现代的IE和Safari浏览器)中工作吗。caniuse.com/#feat=download
Muhammad Umer,

1
刚刚在葡萄酒下测试了Safari 5.0。第一个版本有效,第二个无效。我也测试了IE 8(葡萄酒),但它不起作用。
maikel 2014年

6

这是Mathew建议的数据URI方法的链接,它可用于Safari浏览器,但效果不佳,因为我无法设置文件类型,将其保存为“未知”,然后我不得不稍后再去那里并按顺序对其进行更改查看文件...

http://www.nihilogic.dk/labs/canvas2image/


4

您可以使用localStorage。这是Html5的cookie。它似乎可以在Firefox上的Chrome和Firefox BUT上运行,我需要将其上传到服务器。也就是说,直接在我的家用计算机上进行测试无效。

我正在处理HTML5示例。转到http://faculty.purchase.edu/jeanine.meyer/html5/html5explain.html 并滚动到一个迷宫。使用localStorage存储重建迷宫的信息。

我来这篇文章时正在寻找用于加载和使用xml文件的HTML5 JavaScript。与旧的html和JavaScript相同吗?


4

尝试

let a = document.createElement('a');
a.href = "data:application/octet-stream,"+encodeURIComponent('"My DATA"');
a.download = 'myFile.json';
a.click(); // we not add 'a' to DOM so no need to remove

如果要下载二进制数据,请点击此处

更新资料

2020.06.14我将Chrome升级到83.0及更高版本,因此代码段停止运行(由于沙箱安全性限制)-但JSFiddle版本有效- 在这里


3

如前所述,File API以及FileWriterFileSystem API可以用于从浏览器选项卡/窗口的上下文将文件存储在客户端计算机上。

但是,您应注意与后两个API有关的几件事:

  • 目前仅在基于Chromium的浏览器(Chrome和Opera)中存在API的实现
  • 这两个API均已于2014年4月24日脱离W3C标准轨道,并且目前为专有
  • 将来有可能从实现浏览器中删除(现在是专有的)API
  • 沙箱(在磁盘上以外的位置,其中文件可以产生没有影响)用于存储与所述的API所创建的文件
  • 使用虚拟文件系统(目录结构不一定会以从浏览器内部进行访问时的形式存在于磁盘上)代表使用API​​创建的文件

以下是简单说明如何直接或间接使用API​​来实现此目的的示例:

烘焙食品 *

bakedGoods.get({
        data: ["testFile"],
        storageTypes: ["fileSystem"],
        options: {fileSystem:{storageType: Window.PERSISTENT}},
        complete: function(resultDataObj, byStorageTypeErrorObj){}
});

使用原始File,FileWriter和FileSystem API

function onQuotaRequestSuccess(grantedQuota)
{

    function saveFile(directoryEntry)
    {

        function createFileWriter(fileEntry)
        {

            function write(fileWriter)
            {
                var dataBlob = new Blob(["Hello world!"], {type: "text/plain"});
                fileWriter.write(dataBlob);              
            }

            fileEntry.createWriter(write);
        }

        directoryEntry.getFile(
            "testFile", 
            {create: true, exclusive: true},
            createFileWriter
        );
    }

    requestFileSystem(Window.PERSISTENT, grantedQuota, saveFile);
}

var desiredQuota = 1024 * 1024 * 1024;
var quotaManagementObj = navigator.webkitPersistentStorage;
quotaManagementObj.requestQuota(desiredQuota, onQuotaRequestSuccess);

尽管FileSystem和FileWriter API不再处于标准轨道上,但在我看来,在某些情况下可以合理使用它们,因为:

  • 停用浏览器供应商的新兴趣可能使他们重新回到浏览器上
  • 实施(基于铬)浏览器的市场渗透率很高
  • Google(Chromium的主要贡献者)尚未给出API的使用期限和终止日期

但是,“某些情况”是否包含您自己的情况由您决定。

* BakedGoods由这个人负责维护:)


2

该线程非常有用,可以弄清楚如何生成二进制文件并提示下载命名的文件,所有这些都是在没有服务器的客户端代码中进行的。

对我来说,第一步是根据我保存的数据生成二进制Blob。有很多针对单个二进制类型的示例,在我的情况下,我有一个具有多种类型的二进制格式,可以将其作为数组传递以创建blob。

saveAnimation: function() {

    var device = this.Device;
    var maxRow = ChromaAnimation.getMaxRow(device);
    var maxColumn = ChromaAnimation.getMaxColumn(device);
    var frames = this.Frames;
    var frameCount = frames.length;

    var writeArrays = [];


    var writeArray = new Uint32Array(1);
    var version = 1;
    writeArray[0] = version;
    writeArrays.push(writeArray.buffer);
    //console.log('version:', version);


    var writeArray = new Uint8Array(1);
    var deviceType = this.DeviceType;
    writeArray[0] = deviceType;
    writeArrays.push(writeArray.buffer);
    //console.log('deviceType:', deviceType);


    var writeArray = new Uint8Array(1);
    writeArray[0] = device;
    writeArrays.push(writeArray.buffer);
    //console.log('device:', device);


    var writeArray = new Uint32Array(1);
    writeArray[0] = frameCount;
    writeArrays.push(writeArray.buffer);
    //console.log('frameCount:', frameCount);

    for (var index = 0; index < frameCount; ++index) {

      var frame = frames[index];

      var writeArray = new Float32Array(1);
      var duration = frame.Duration;
      if (duration < 0.033) {
        duration = 0.033;
      }
      writeArray[0] = duration;
      writeArrays.push(writeArray.buffer);

      //console.log('Frame', index, 'duration', duration);

      var writeArray = new Uint32Array(maxRow * maxColumn);
      for (var i = 0; i < maxRow; ++i) {
        for (var j = 0; j < maxColumn; ++j) {
          var color = frame.Colors[i][j];
          writeArray[i * maxColumn + j] = color;
        }
      }
      writeArrays.push(writeArray.buffer);
    }

    var blob = new Blob(writeArrays, {type: 'application/octet-stream'});

    return blob;
}

下一步是使浏览器提示用户下载具有预定义名称的Blob。

我需要的只是在HTML5中添加的一个命名链接,我可以重复使用该链接重命名初始文件名。由于链接不需要显示,因此我将其隐藏。

<a id="lnkDownload" style="display: none" download="client.chroma" href="" target="_blank"></a>

最后一步是提示用户下载文件。

var data = animation.saveAnimation();
var uriContent = URL.createObjectURL(data);
var lnkDownload = document.getElementById('lnkDownload');
lnkDownload.download = 'theDefaultFileName.extension';
lnkDownload.href = uriContent;
lnkDownload.click();

1

这是一个将文件导出为ZIP的教程:

在开始之前,有一个用于保存文件的库,该库的名称为fileSaver.js,您可以在这里找到该库。让我们开始吧,现在,包括所需的库:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.4/jszip.min.js"  type="text/javascript"></script>
<script type="text/javascript" src="https://fastcdn.org/FileSaver.js/1.1.20151003/FileSaver.js" ></script>

现在复制此代码,此代码将下载一个包含内容Hello World的zip文件,其中包含hello.txt文件。如果一切正常,这将下载文件。

<script type="text/javascript">
    var zip = new JSZip();
    zip.file("Hello.txt", "Hello World\n");
    zip.generateAsync({type:"blob"})
    .then(function(content) {
        // see FileSaver.js
        saveAs(content, "file.zip");
    });
</script>

这将下载一个名为file.zip的文件。您可以在此处了解更多信息:http : //www.wapgee.com/story/248/guide-to-create-zip-files-using-javascript-by-using-jszip-library

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.