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

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

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

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


当前回答

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版本工作-在这里

其他回答

好的,创建一个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');

就是这样。

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

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

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

对于像“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'});

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

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

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