是否可以直接从网络工作者保存文件?

Posted

技术标签:

【中文标题】是否可以直接从网络工作者保存文件?【英文标题】:Is it possible to save a file directly from a web worker? 【发布时间】:2016-04-05 20:09:38 【问题描述】:

我有一个完全基于浏览器(即没有后端)的应用程序,它分析平均每个大约 250MB 的文件中的 XML 数据。实际的解析和分析发生在 Web Worker 中,FileReader 实例以 64KB 块的形式提供数据,这一切都非常高效。

我有一个来自客户端的请求来扩展这个应用程序,以便它可以生成一个包含原始输入文件和分析结果的 .zip 文件,并允许用户将该文件保存到她的本地计算机上。在内存中生成包含这些内容的 .zip 文件不是问题。问题在于将大量数据从生成它的网络工作者传输回主浏览器线程,以便可以保存;尝试这样做总是会引发崩溃或内存不足的异常。 (我尝试过一次传输所有字符串,一次传输一个块,我尝试使用 ArrayBuffer 作为可传输对象以避免复制。所有操作都以相同的方式失败。)

不幸的是,我不知道有什么方法可以直接从工作线程调用文件保存操作。我从主浏览器线程中知道了几种这样做的方法,但它们都需要能够创建 DOM 节点(工作线程当然不能这样做),或者使用接口(即 msSaveBlob、saveAs)浏览器似乎暴露给工作线程。我花了一段时间在网上寻找可能性,但没有发现任何可用的; FileWriterSync 看起来不错,但只有 Chrome 支持,我还需要针对 IE 和 Firefox。

是否有我忽略的直接从网络工作者保存文件的方法?如果是这样,它是什么?还是我在这里运气不好?

【问题讨论】:

令我惊讶的是,将现有的 ArrayBuffer 从工作线程传输(而不是复制)到主线程会导致问题。你能发布一个最小的例子吗? @MichalCharemza 这也让我感到惊讶。我测试它的实验代码太深地融入(内部,专有)应用程序,使发布示例变得容易,但编写一个不应该太难;毕竟,你可以在工作线程中生成一个 250M 的 ArrayBuffer 并尝试将其转移到主线程。在我的整个测试过程中,这确实在 IE 11、Firefox 38 和 Chrome 49 中引发了 OOM 异常或崩溃。 你找到办法了吗? @AmriteshAnand 不是真的。我已经能够将数据传输回主线程,但是还没有找到一个可靠的选项来实际将它写到磁盘上——我尝试过的所有事情都涉及将缓冲区变成一个字符串,这由于内存耗尽,浏览器总是崩溃。 【参考方案1】:

tl;dr demo

您根本不需要将整个文件复制到客户端。事实上,你甚至不需要转移它。首先回顾一下。

这是从某个类型化数组创建Blob 的方法:

// Some arbitrary binary data
const mydata = new Uint16Array([1,2,3,4,5]);
// mydata vs. mydata.buffer does not seem to make any difference
const blob = new Blob([mydata], type: "octet/stream");

您可以创建一个对象 URL,它是由浏览器管理的原始 Blob 的副本,可作为 URL 访问。我已经对大型文件进行了此操作,但没有看到性能影响:

const url = URL.createObjectURL(blob);

这是我通常下载 URL 的方式:

const link = document.createElement("a");
link.download = "data.bin";
link.href = e.data.link;
link.appendChild(new Text("Download data"));
link.addEventListener("click", function() 
    this.parentNode.removeChild(this);
    // remember to free the object url, but wait until the download is handled
    setTimeout(()=>URL.revokeObjectURL(e.data.link);, 500)
);
document.body.appendChild(link);

您可以通过在该链接上调用click 事件来自动触发下载。我更喜欢让用户决定何时下载。

所以,一起来:

worker.js

// Some arbitrary binary data
const mydata = new Uint16Array([1,2,3,4,5]);

self.onmessage = function(e) 
  console.log("Message: ",e.data)
  switch(e.data.name) 
    case "make-download" : 
        const blob = new Blob([mydata.buffer], type: "octet/stream");
        const url = URL.createObjectURL(blob);
        self.postMessage(name:"download-link", link:url);
    break;
    default:
      console.error("Unknown message:", e.data.name);
  

main.js

var worker = new Worker("worker.js");
worker.addEventListener("message", function(e) 
  switch(e.data.name) 
    case "download-link" : 
       if(e.data.error) 
          console.error("Download error: ", e.data.error);
       
       else 
          const link = document.createElement("a");
          link.download = "data.bin";
          link.href = e.data.link;
          link.appendChild(new Text("Download data"));
          link.addEventListener("click", function() 
              this.parentNode.removeChild(this);
              // remember to free the object url, but wait until the download is handled
              setTimeout(()=>URL.revokeObjectURL(e.data.link);, 500)
          );
          document.body.appendChild(link);
       
       break;
    
  default:
    console.error("Unknown message:", e.data.name);
  
);

function requestDownload() 
  worker.postMessage(name:"make-download");

当我在演示中单击“下载”时,我可以在我的 HEX 编辑器中看到:

看起来不错:)

【讨论】:

对于 main.js 上涉及较少的实现,您可以使用 FileSaver.js 并在 blob URL 上将其称为 saveAs :)。它使实现成为单线并提供一定程度的向后兼容性(或者至少我希望如此)。

以上是关于是否可以直接从网络工作者保存文件?的主要内容,如果未能解决你的问题,请参考以下文章

从网站直接保存到 SharePoint Online

如何从网络浏览器保存所有原始文件?

OpenCV从文本文件创建视频

如何直接在网页中打开文件 不出现下载保存提示框!

是否可以在不先将对象作为文件保存到谷歌驱动器的情况下从谷歌 colab 下载对象作为文件?

保存名称从文本框中读取的文件。 C#