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

Is it possible to stream an octet stream being generated in javascript?

本文关键字:字节流 八位 javascript 传输 是否      更新时间:2023-09-26

假设一个案例,使用一些javascript逻辑从一个小字符串生成一个巨大的字符串,然后强制在浏览器上下载文本文件。

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

在内存中创建一个文件供用户下载,而不是通过服务器。

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

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

是否可以仅使用客户端逻辑在生成文本时流式传输文本?

例如:

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

Yes & No。不,因为没有办法只用客户端javascript写入文件。有点。您可以提示用户下载并保存文件,但如您所提到的,代码必须在下载发生之前生成整个文件。注意:通过"流",我假设你的意思是流到文件(不断写入文件(和"客户端逻辑",我假设你的意思是在浏览器中。

看起来Mozilla一直在研究一种让客户端代码与文件交互的方法。是的来了。有点。它们有自己的文件系统 api,可让您与本地计算机文件系统进行交互(写入(。具体来说,有一个函数可以让您将输入流写入文件。但是,有几个星号:

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

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

当然,在实施过程中必然会出现更多的复杂情况。但这看起来是最确定的前进道路,看看OS.File如何让你访问OS.File.writeAtomic()和基本写入文件等功能。

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

其他资源:
Wikipedia on XPConnect
在javascript中使用XPCOM的指南 - 可能不是那么有用

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

该 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:链接。当用户单击它时,它会提示他们保存它。保存后,您可以删除临时文件。

此函数使用 fileEntrytoURL() 函数生成filesystem链接:

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

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

<a download></a>

这里有一个 plunker 来证明这一点:http://plnkr.co/edit/q6ihXWEXSOtutbEy1b5G?p=preview

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

有关更多信息,请查看这篇HTML5rocks文章,如果您想使用更新的同步Web Worker API,请查看这篇文章。

我会按照@quantumwannabe描述它的方式建议它,使用临时沙箱文件来附加块。

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

这就是我要让@KeenanLidral-波特回答不正确的地方。@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