我最近一直在摆弄WebGL,并得到了一个Collada阅读器工作。问题是它非常慢(Collada是一种非常冗长的格式),所以我将开始将文件转换为更容易使用的格式(可能是JSON)。我已经有代码来解析JavaScript文件,所以我不妨使用它作为我的出口商太!问题在于储蓄。

现在,我知道我可以解析文件,将结果发送到服务器,并让浏览器从服务器请求返回文件作为下载。但实际上,服务器与这个特定的进程没有任何关系,那么为什么要把它牵扯进来呢?我在内存中已经有了所需文件的内容。是否有任何方法可以使用纯JavaScript向用户提供下载?(我对此表示怀疑,但不妨问问……)

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

[编辑]:我没有在前面提到它,所以那些回答“Flash”的帖子是有道理的,但我所做的部分工作是试图强调纯HTML5可以做什么……所以闪电侠正好适合我。(尽管这对于任何制作“真正的”web应用程序的人来说都是一个非常有效的答案)在这种情况下,除非我想要涉及服务器,否则我看起来很不走运。谢谢!


看看Doug Neiner的downadify,这是一个基于Flash的JavaScript界面。

downloadadify是一个很小的JavaScript + Flash库,可以在浏览器中生成和保存文件,而不需要服务器交互。


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


您可以使用localStorage。这相当于Html5中的cookie。它似乎可以在Chrome和Firefox上运行,但在Firefox上,我需要将它上传到服务器上。也就是说,直接在我的家用电脑上测试不起作用。

我正在做HTML5的例子。访问http://faculty.purchase.edu/jeanine.meyer/html5/html5explain.html 转到迷宫一。重建迷宫的信息使用localStorage存储。

我读这篇文章是为了寻找用于加载和处理xml文件的HTML5 JavaScript。它是否与旧的html和JavaScript相同????


这里有一个链接到Mathew建议的数据URI方法,它在safari上工作,但不是很好,因为我不能设置文件类型,它被保存为“未知”,然后我不得不再次去那里,改变它,以便查看文件…

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


好的,创建一个data:URI对我来说确实很有用,感谢Matthew和Dennkster指出了这个选项!以下是我的基本做法:

1)获取所有的内容到一个名为“content”的字符串(例如,通过最初创建它或通过读取已构建页面的标签的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');

就是这样。


HTML5定义了一个窗口。saveAs(blob, filename)方法。目前任何浏览器都不支持它。但是,有一个名为filesver .js的兼容性库将此功能添加到大多数现代浏览器(包括Internet Explorer 10+)。Internet Explorer 10支持导航器。msSaveBlob(blob, filename)方法(MSDN),在filesver .js中用于Internet Explorer支持。

我写了一篇关于这个问题的详细博文。


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

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();
}

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

我发现了两种简单的方法。首先,使用已单击的元素并注入下载数据。其次,使用下载数据生成一个a元素,执行a.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>

保存大文件

Long data URIs can give performance problems in browsers. Another option to save client-side generated files, is to put their contents in a Blob (or File) object and create a download link using URL.createObjectURL(blob). This returns an URL that can be used to retrieve the contents of the blob. The blob is stored inside the browser until either URL.revokeObjectURL() is called on the URL or the document that created it is closed. Most web browsers have support for object URLs, Opera Mini is the only one that does not support them.

强制下载

If the data is text or an image, the browser can open the file, instead of saving it to disk. To cause the file to be downloaded upon clicking the link, you can use the the download attribute. However, not all web browsers have support for the download attribute. Another option is to use application/octet-stream as the file's mime-type, but this causes the file to be presented as a binary blob which is especially user-unfriendly if you don't or can't specify a filename. See also 'Force to open "Save As..." popup open at text link click for pdf in HTML'.

指定文件名

如果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>


如前所述,文件API以及FileWriter和FileSystem API可用于从浏览器选项卡/窗口的上下文中在客户端机器上存储文件。

然而,关于后两个api,有几件事你应该知道:

Implementations of the APIs currently exist only in Chromium-based browsers (Chrome & Opera) Both of the APIs were taken off of the W3C standards track on April 24, 2014, and as of now are proprietary Removal of the (now proprietary) APIs from implementing browsers in the future is a possibility A sandbox (a location on disk outside of which files can produce no effect) is used to store the files created with the APIs A virtual file system (a directory structure which does not necessarily exist on disk in the same form that it does when accessed from within the browser) is used represent the files created with the APIs

下面是一些简单的例子,说明了如何直接或间接地使用api来实现这一点:

烘焙食品*

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

使用原始的文件、FileWriter和文件系统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已经不在标准轨道上,但在某些情况下,它们的使用是合理的,在我看来,因为:

未实现的浏览器供应商重新燃起的兴趣可能会让他们重新回到这个平台上 实现(基于chromium的)浏览器的市场渗透率很高 谷歌(Chromium的主要贡献者)没有给出api的生命终止日期

然而,“某些情况”是否包括你自己的情况,由你自己决定。

*BakedGoods是由这里的这个家伙维护的:)


简单的解决方案!

<a download=“My-FileName.txt” href=“data:application/octet-stream,HELLO-WORLDDDDDDDD”>点击这里</a>

适用于所有现代浏览器。


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

在开始之前,有一个库来保存文件,库的名字是filesver .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 .txt文件的zip文件,其中包含Hello World的内容。如果一切正常,这将下载一个文件。

<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


这个线程对于如何生成二进制文件并提示下载已命名的文件非常有价值,所有这些都在客户机代码中,没有服务器。

我的第一步是从我保存的数据中生成二进制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();

try

let a = document.createElement('a'); a.href = "data:application/octet-stream,"+encodeURIComponent('我的数据'); a.download = 'myFile.json'; a.click ();//我们不添加'a'到DOM,所以不需要删除

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

更新

2020.06.14我将Chrome升级到83.0及以上SO代码片段停止工作(由于沙箱安全限制)-但JSFiddle版本工作-在这里


在测试“ahref”方法时,我发现Firefox和Chrome的web开发工具很容易混淆。我需要在a.click()发出后重新启动调试。FileSaver也发生了同样的情况(它使用相同的ahref方法进行实际保存)。为了解决这个问题,我创建了一个新的临时窗口,将元素a添加到其中,并单击它。

    function download_json(dt) {
        var csv = ' var data = ';
        csv += JSON.stringify(dt, null, 3);

        var uricontent = 'data:application/octet-stream,' + encodeURI(csv);

        var newwin = window.open( "", "_blank" );
        var elem = newwin.document.createElement('a');
        elem.download = "database.js";
        elem.href = uricontent;
        elem.click();
        setTimeout(function(){ newwin.close(); }, 3000);
        
    }

对于像“txt”或“js”这样的简单文件,您可以使用fs-浏览器包。 它有很好的和简单的下载和导出方法的客户端,不涉及任何服务器。

import {exportFile} from 'fs-browsers'; const onExportClick = (textToExport) => { //导出到txt文件 exportFile (textToExport); }

如果你想改变文件的名称,甚至它的类型,你可以很容易地用这个:

import {exportFile} from 'fs-browsers'; const onExportClick = (textToExport) => { //导出到js文件file.js exportFile(textToExport, {fileName: 'file.js'}); }

对于更复杂的文件,您将需要使用服务器。 如果这是你需要的,这个包也可以对excel文件('xls')这样做。

import {exportFile, EXCEL_FILE} from 'fs-browsers'; 常量数据= [{" id ": 5,“名字”:“约翰”,“等级”:90年,“年龄”:15},{" id ": 7,“名字”:“尼克”、“年级”:70年,“年龄”:17}); const heading =["学号","学生姓名","考试等级","学生年龄"]; exportFile(数据,{类型:EXCEL_FILE,标题:标题,文件名:'grades.xls'});

也许将来还会有其他类型的文件。


你可以用它来保存文本和其他数据:

function downloadFile(name, data) {
    let a = document.createElement("a");
    if (typeof a.download !== "undefined") a.download = name;
    a.href = URL.createObjectURL(new Blob([data], {
        type: "application/octet-stream"
    }));
    a.dispatchEvent(new MouseEvent("click"));
}

这个函数将创建一个Anchor元素,通过.download(如果支持的话)设置名称,分配一个从对象(url . createobjecturl)创建的url (.href),在本例中是一个Blob对象,并分派一个单击事件。简而言之:就好像你在点击一个下载链接。

示例代码

downloadFile("textfile.txt", "A simple text file");
downloadFile(
    "circle.svg",
    `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
        <circle cx="50" cy="50" r="42" />
    </svg>`
);
downloadFile(
    "utf8string.txt",
    new Uint8Array([85, 84, 70, 45, 56, 32, 115, 116, 114, 105, 110, 103]) // "UTF-8 string"
);

这个函数也接受File, Blob和MediaSource:

function downloadFile(name, data) {
    if (!(data instanceof File || data instanceof Blob || data instanceof MediaSource)) {
        return downloadFile(name, new Blob([data], {
            type: "application/octet-stream"
        }));
    }

    let a = document.createElement("a");
    if (typeof a.download !== "undefined") a.download = name;
    a.href = URL.createObjectURL(data);
    a.dispatchEvent(new MouseEvent("click"));
}

或者你可以使用两个函数:

function downloadFile(name, data) {
    return downloadObject(new Blob([data], {
        type: "application/octet-stream"
    }));
}

function downloadObject(name, object) {
    let a = document.createElement("a");
    if (typeof a.download !== "undefined") a.download = name;
    a.href = URL.createObjectURL(object);
    a.dispatchEvent(new MouseEvent("click"));
}