【问题标题】:Is it possible to stream an octet stream being generated in javascript?是否可以流式传输在 javascript 中生成的八位字节流?
【发布时间】:2015-02-20 06:43:05
【问题描述】:

假设这样一种情况,使用一些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();
}

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

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

例如:

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

【问题讨论】:

  • 如果您没有找到该问题的解决方案,可以将数据放入Blob,然后使用createObjectURL() 生成下载链接。浏览器可能会将数据缓存在磁盘上,尤其是您不会有大的数据 URL。
  • @Phillip 我不想将所有内容存储到 blob 或 zip 文件中,或者使用 indexDb ,流式传输是必需的。
  • 没有。所有当前的浏览器技术都需要在发送第一个字节之前知道整体大小;只有像 node.js 这样较新的服务器端东西才能使用 http1.1 分块。您将不得不使用许多连接或缓冲区。您也许可以使用 websocket,但这需要在两端进行一些自定义。

标签: javascript


【解决方案1】:

我会以@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

【讨论】:

  • 请发布该库如何工作的说明,以及它使用的 API 的具体代码,而不仅仅是 github 链接。
  • @Endless ReadableStream 适用于铬,铬 50
【解决方案2】:

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

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 然后被移动到文件的末尾,我们创建一个新的文本块来附加到它。

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

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

如果您想使用更新的同步 Web Worker API,请查看此HTML5rocks articlethis one 了解更多信息。

【讨论】:

    【解决方案3】:

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

    看起来 Mozilla 一直在研究一种让客户端代码与文件交互的方法。是的。的种类。他们有自己的file system api,可让您与本地机器文件系统进行交互(写入)。具体来说,有一个函数可以让你write an input stream to a file。但是,有几个星号:

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

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

    毫无疑问,在实施过程中肯定会出现更多复杂情况。但这看起来是最可靠的前进道路,看看OS.File 如何让您访问OS.File.writeAtomic() 和基本write to file 等功能

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多