是否可以流式传输在javascript中生成的八位字节流?

时间:2014-12-22 07:11:12

标签: javascript

让我们假设这样一种情况:使用一些javascript逻辑从一个小字符串生成一个巨大的字符串,然后强制在浏览器上下载文本文件。

这可以通过将其作为href使用八位字节流下载来实现,如本答案中所述:

Create a file in memory for user to download, not through server

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

但是这个解决方案要求在推送下载之前完全生成“text”, 因此它必须完全保存在浏览器内存中。

是否可以在使用CLIENT SIDE LOGIC生成文本时进行流式传输?

例如:

var inputString = "A";
var outStr = "";
for(var i = 0; i < 10000000 ; i++)
 {
   /* concatenate inputString to output on the go */
 }

3 个答案:

答案 0 :(得分:8)

是&amp;没有。不,因为没有办法只使用客户端javascript写入文件。均田。您可以提示用户下载&amp;保存文件,但正如您所提到的,代码必须在下载发生之前生成整个文件。注意:通过“流”我假设你的意思是流到文件(不断写入文件)&amp;通过“仅限客户端逻辑”我假设您的意思是在浏览器中。

看起来Mozilla一直致力于让客户端代码与文件进行交互。这是肯定的。的种类。它们有自己的file system api,可以让您与本地机器文件系统进行交互(写入)。具体来说,有一个功能可以让你write an input stream to a file。但是,有一些星号:

1)看起来整个系统都被弃用了;他们鼓励开发人员使用OS.file而不是文件I / O

2)您必须使用XPConnect,这是一个允许您在javascript中访问Mozilla的XPCOM(组件库)的系统。如果你想在浏览器中这样做,看起来只有firefox扩展具有与这些组件交互的适当权限()。如果你不想在浏览器中这样做,你显然可以使用node。

确实,在实施过程中必然会出现更多并发症。但这看起来是最确定的前进路径,看看OS.File如何让您访问OS.File.writeAtomic()和&amp;等功能。基本write to file

话虽如此,它并不是一条很棒的道路,但希望这能给你一个坚实的起点。正如@dandavis所提到的,浏览器(即“客户端逻辑”)被设计为不允许这种事情。如果一个网站可以与任何用户的本地文件系统进行交互,那将是一个非常巨大的疏忽/安全漏洞。

其他资源:
Wikipedia on XPConnect
Guide on working with XPCOM in javascript - 可能没那么有用

答案 1 :(得分:4)

有一种方法可以做到这一点,但它依赖于仅限Chrome的文件系统API。我们将在沙盒文件系统中创建并写入临时文件,并在完成后将其复制到常规文件系统。这样您就不必将整个文件存储在内存中。目前,W3C尚未考虑将Chrome API的异步版本用于标准化,而是使用同步版本(使用Web工作者)。如果担心浏览器支持,那么这个答案不适合你。

API的工作原理如下: 首先,我们从浏览器中获取requestFileSystem()函数。目前它以“webkit”为前缀:

window.requestFileSystem  = window.requestFileSystem || window.webkitRequestFileSystem;

接下来,我们请求一个临时文件系统(这样我们就不需要请求用户权限):

var fileSystem; //This will store the fileSystem for later access
var fileSize = 1024*1024 //Our maximum file system size.
function errorHandler(e) {
  console.log('Error: ' + e.name);
}
window.requestFileSystem(window.TEMPORARY, fileSize, function (fs) { fileSystem = fs; }, errorHandler);

现在我们可以访问文件系统,现在是时候创建一个文件了:

var fileOptions = {
    create: true, //If the file is not found, create it
    exclusive: false //Don't throw an error if the file doesn't exist
};

这里我们调用getFile()函数,如果它不存在,可以创建一个文件。在回调内部,我们可以创建一个新的fileWriter来写入文件。然后将fileWriter移动到文件的末尾,我们创建一个新的文本blob以附加到它。

fileSystem.root.getFile(fileName, fileOptions, function(fileEntry) {
    fileEntry.createWriter(function(fileWriter) {
      fileWriter.seek(fileWriter.length); 
      var blob = new Blob([STRING_TO_WRITE], {type: 'text/plain'});
      fileWriter.write(blob);
    }, errorHandler);
});

请注意,此API不会保存到正常的用户文件系统。而是将其保存到特殊的沙盒文件夹中。如果要将其保存到用户的文件系统,可以创建filesystem:链接。当用户点击它时,它会提示他们保存它。保存后,您可以删除临时文件。

此函数使用filesystem的{​​{1}}函数生成fileEntry链接:

toURL()

使用带有download属性的链接将强制下载文件。

var save = function () {
  var download = document.querySelector("a[download]");
  if (!fileSystem) { return; }
   fileSystem.root.getFile(fileName, {create: false, exclusive: true}, function(fileEntry) {
    download.href = fileEntry.toURL();
  }, errorHandler);
}

这是一个演示此内容的插件:http://plnkr.co/edit/q6ihXWEXSOtutbEy1b5G?p=preview

希望这能实现你想要的。您可以连续附加到文件,它不会保留在内存中,但它将在沙盒文件系统中,直到用户将其保存到常规文件系统。

如果您想使用较新的同步Web Worker API,请查看此HTML5rocks articlethis one

答案 2 :(得分:2)

我会以@quantumwannabe describes的方式建议它,使用临时沙盒文件来附加块。

但今天可以使用一种新的方法(在标志后面),但将在下一版本的chrome(52)中启用

这就是我要让@ KeenanLidral-Porter回答不正确的地方。并且@quantumwannabe回答了不必要的步骤 因为现在有一种方法可以直接将文件流写入文件系统:StreamSaver.js

就好像有服务器发送八位字节流标题并告诉浏览器在服务工作者的帮助下下载数据块

const writeStream = streamSaver.createWriteStream('filename.txt')
const encoder = new TextEncoder
let data = 'a'.repeat(1024) // Writing some stuff triggers the save dialog to show
let uint8array = encoder.encode(data + "\n\n")

writeStream.write(uint8array) // Write some data when you got some
writeStream.close() // End the saving