javascript - Web worker 在处理大型数组时内存不足

标签 javascript web-worker

我正在构建一个应用程序,该应用程序能够将文件上传到现有 API。此 API 获取文件元数据和 JSON 对象中的内容,因此我需要将文件的二进制内容转换为 base64 编码字符串。

由于这是一项可能很繁重的操作,我将此功能移到了 Web Worker 中。 worker 接收一个包含二进制文件内容的 ArrayBuffer 对象(从 FileReader.readAsArrayBuffer() 返回),并返回一个 base64 编码的字符串。

这适用于较小的文件,但对于我需要支持的最大文件(~40 MB),这会导致我的工作人员出现内存不足异常(Internet Explorer 中为 8007000E)。在极少数情况下它会通过,但大多数时候 worker 只是死了。在将其移至 worker 之前也发生了同样的情况,只是整个浏览器页面崩溃了(在 IE 和 Chrome 中)。 Chrome 似乎比 IE 对工作人员的内存压力更有弹性,但我仍然必须让它在 IE (10+) 中正常工作。

我的 worker :

onmessage = e => {
  const bytes = new Uint8Array(e.data);
  const l = bytes.length;
  const chars = new Array(l);
  for (let i = 0, j = l - 1; i <= j; ++i, --j) {
    chars[i] = String.fromCharCode(bytes[i]);
    chars[j] = String.fromCharCode(bytes[j]);
  }
  const byteString = chars.join('');
  const base64bytes = btoa(byteString);

  try {
    postMessage(base64bytes, [base64bytes]);
  } catch (e) {
    postMessage(base64bytes);
  }
};

我在这里做一些大禁忌吗?有什么方法可以减少内存消耗吗?我考虑过的一种解决方案是以 block 而不是整个文件的形式处理内容,然后连接生成的字符串并在外部对其进行编码。这是否可行,或者这会导致其自身出现问题吗?还有什么我不知道的神奇功能吗?我曾对 FileReader.readAsBinaryString() 抱有一线希望,但它现在已从标准中删除(无论如何在 IE10 中不受支持)所以我无法使用它。

(我意识到这个问题也可能与 Code Review 相关,但由于我的代码实际上崩溃了,所以我认为 SO 是正确的地方)

最佳答案

One solution I've thought about would be to process the contents in chunks rather than the whole file, then concatenate the resulting strings and encode it on the outside. Would that be viable, or will that cause problems of its own?

这就是https://github.com/beatgammit/base64-js似乎可以,一次做 ~16k。使用这个,在我的电脑上不使用 transferables(因为 IE 10 不支持它们),Chrome 设法编码一个 190mb ArrayBuffer(大于这个它提示无效的字符串长度)和 IE 11 40mb(大于这个我得到一个内存不足异常)。

你可以在 https://plnkr.co/edit/SShi1PE4DuMATcyqTRPx?p=preview 看到这个, worker 有代码

var exports = {};
importScripts('b64.js')

onmessage = function(e) {
  var base64Bytes = fromByteArray(new Uint8Array(e.data));
  postMessage(base64Bytes);
};

和主线程

var worker = new Worker('worker.js');
var length = 1024 * 1024 * 40;
worker.postMessage(new ArrayBuffer(length));

worker.onmessage = function(e) {
  console.log('Received Base64 in UI thread', e.data.length, 'bytes');
}

要超越 40mb 的限制,一种看起来很有希望的方法是一次只将一个较小的切片传递给 worker(例如 1mb),对其进行编码,返回结果,然后才将下一个切片传递给worker,最后连接所有结果。我已经设法使用它来编码更大的缓冲区(在 IE 11 中高达 250mb)。我怀疑异步性允许垃圾收集器在调用之间运行。

例如,https://plnkr.co/edit/un7TXeHwYu8eBltfYAII?p=preview ,在 worker 中使用与上面相同的代码,但在 UI 线程中:

var worker = new Worker('worker.js');
var length = 1024 * 1024 * 60;
var buffer = new ArrayBuffer(length);

var maxMessageLength = 1024 * 1024;
var i = 0;
function next() {
  var end = Math.min(i + maxMessageLength, length);
  var copy = buffer.slice(i, end);
  worker.postMessage(copy);
  i = end;
}

var results = [];
worker.onmessage = function(e) {
  results.push(e.data);
  if (i < length) {
    next();
  } else {
    results = results.join('');
    alert('done ' + results.length);
  }
};

next();

关于javascript - Web worker 在处理大型数组时内存不足,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36175480/

相关文章:

vue.js - 带有 Vue 和 Bundling 的 Service Workers

javascript - 使用 Emscripten C++ Web Worker 高效传输大型数组 : which JavaScript design is better?

javascript - 如何将 Web Worker 与 Webextensions 一起使用?

javascript - LokiJS 自动加载回调不起作用

javascript - 调整大小时 Div 移出窗口

javascript - npm 安装 : install Couldn't read dependencies

javascript - (语法错误): missing ] after element list when autocomplete

javascript - 在worker中使用_config.worker = false调用Papa.parse调用postMessage

javascript - 如何查询没有被指向的对象? (例如,尚未标记的帖子)

Javascript Worker 丢失属性