适用于辅助角色的同步文件系统 API

发布于 2023-05-13 11:13:50 字数 12150 浏览 71 评论 0

HTML5 文件系统 API Web Workers 本身就非常强大。文件系统 API 最终为 Web 应用程序带来了分层存储和文件 I/O,而 Workers 为 JavaScript 带来了真正的异步“多线程”。但是,当您一起使用这些 API 时,您可以构建一些真正有趣的应用程序。

本教程提供了利用 Web Worker 内部 HTML5 文件系统的指南和代码示例。它假定具有两个 API 的工作知识。如果您还没有准备好深入了解或有兴趣了解有关这些 API 的更多信息,请阅读两个讨论基础知识的精彩教程: 探索文件系统 API Web Worker 的基础知识

同步与异步 API

异步 JavaScript API 可能很难使用。它们很大。它们很复杂。但最令人沮丧的是,它们为事情出错提供了很多机会。您要处理的最后一件事是在已经异步的世界(Workers)中对复杂的异步API(文件系统)进行分层!好消息是, 文件系统 API 定义了一个同步版本来减轻 Web Worker 的痛苦。

在大多数情况下,同步 API 与其异步表亲完全相同。方法、属性、特性和功能将很熟悉。主要偏差是:

  • 同步 API 只能在 Web Worker 上下文中使用,而异步 API 可以在 Worker 上下文中使用,也可以在 Worker 外部使用。
  • 回调已出。API 方法现在返回值。
  • 窗口对象 ( requestFileSystem()resolveLocalFileSystemURL()) 成为 requestFileSystemSync()resolveLocalFileSystemSyncURL(). 注意: 这些方法是工作人员全局范围的成员,而不是 window 对象。

除了这些例外,API 是相同的。好的,我们很好!

请求文件系统

Web 应用程序通过请求 LocalFileSystemSync Web 辅助角色中的对象。这 requestFileSystemSync() 暴露在辅助角色的全局范围内:

var fs = requestFileSystemSync(TEMPORARY, 1024*1024 /*1MB*/);

请注意新的返回值,现在我们使用的是同步 API,并且没有成功和错误回调。

与普通的文件系统 API 一样,方法目前带有前缀:

self.requestFileSystemSync = self.webkitRequestFileSystemSync ||
  self.requestFileSystemSync;

处理配额

目前,无法 请求 PERSISTENT 辅助角色上下文中的配额。我建议在worker之外处理配额问题。该过程可能如下所示:

  1. worker.js:将任何文件系统 API 代码包装在 try/catch 所以任何 QUOTA_EXCEED_ERR 捕获错误。
  2. worker.js:如果你抓住一个 QUOTA_EXCEED_ERR,发送 postMessage('get me more quota') 返回主应用程序。
  3. 主应用:通过 window.webkitStorageInfo.requestQuota() 收到 #2 时跳舞。
  4. 主应用:用户授予更多配额后,发送 postMessage('resume writes') 返回给工作人员以告知其额外的存储空间。

这是一个相当复杂的解决方法,但它应该有效。有关 requesting quota 使用 PERSISTENT 使用文件系统 API 进行存储。

使用文件和目录

的同步版本 getFile()getDirectory() 返回一个 FileEntrySyncDirectoryEntrySync分别。

例如,下面的代码在根目录中创建一个名为“log.txt”的空文件。

var fileEntry = fs.root.getFile('log.txt', {create: true});

下面在根文件夹中创建一个新目录。

var dirEntry = fs.root.getDirectory('mydir', {create: true});

处理错误

如果你从来没有调试过 Web Worker 代码,我很羡慕你!找出出了什么问题可能真的很痛苦。

同步世界中缺少错误回调使得处理问题变得比应有的更棘手。如果我们增加调试 Web Worker 代码的一般复杂性,您很快就会感到沮丧。可以简化生活的一件事是将所有相关的 Worker 代码包装在 try/catch 中。然后,如果发生任何错误,请使用以下命令将错误转发到主应用程序 postMessage()

function onError(e) {
  postMessage('ERROR: ' + e.toString());
}

try {
  // Error thrown if "log.txt" already exists.
  var fileEntry = fs.root.getFile('log.txt', {create: true, exclusive: true});
} catch (e) {
  onError(e);
}

传递文件、Blob 和 ArrayBuffers

当 Web Workers 第一次出现时,他们只允许发送字符串数据。 postMessage().后来,浏览器开始接受可序列化的数据,这意味着可以传递 JSON 对象。然而,最近,像Chrome这样的一些浏览器接受更复杂的数据类型来传递。 postMessage() 使用 结构化克隆算法

这到底意味着什么?这意味着在主应用程序和 Worker 线程之间传递二进制数据要容易得多。支持工作线程结构化克隆的浏览器允许您传递类型化数组, ArrayBuffers, Files,或 Blobs进入worker。尽管数据仍然是副本,但能够通过 File 意味着与前一种方法相比具有性能优势,前一种方法涉及在将文件传递到 postMessage().

下面的示例将用户选择的文件列表传递给专用辅助角色。Worker 只需遍历文件列表(简单显示返回的数据实际上是一个 FileList),主应用程序将每个文件读取为 ArrayBuffer.

此示例还使用 的改进 中所述 内联 Web 辅助角色技术在 Web 辅助角色基础知识 版本。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="chrome=1">
  <title>Passing a FileList to a Worker</title>
  <script type="javascript/worker">
  self.onmessage = function(e) {
    // TODO: do something interesting with the files.
    postMessage(e.data); // Pass through.
  };
  </script>
</head>
<body>
</body>

<input type="file" multiple>

<script>
document.querySelector('input[type="file"]').addEventListener('change', function(e) {
  var files = this.files;
  loadInlineWorker('#fileListWorker', function(worker) {

    // Setup handler to process messages from the worker.
    worker.onmessage = function(e) {

      // Read each file aysnc. as an array buffer.
      for (var i = 0, file; file = files[i]; ++i) {
        var reader = new FileReader();
        reader.onload = function(e) {
          console.log(this.result); // this.result is the read file as an ArrayBuffer.
        };
        reader.onerror = function(e) {
          console.log(e);
        };
        reader.readAsArrayBuffer(file);
      }

    };

    worker.postMessage(files);
  });
}, false);

function loadInlineWorker(selector, callback) {
  window.URL = window.URL || window.webkitURL || null;

  var script = document.querySelector(selector);
  if (script.type === 'javascript/worker') {
    var blob = new Blob([script.textContent]);
    callback(new Worker(window.URL.createObjectURL(blob));
  }
}
</script>
</html>

读取工作线程中的文件

使用异步是完全可以接受的 FileReader 用于读取 辅助角色中的文件的 API。但是,有更好的方法。在工作线程中,有一个同步 API ( FileReaderSync) 简化了读取文件的过程:

主应用:

<!DOCTYPE html>
<html>
<head>
  <title>Using FileReaderSync Example</title>
  <style>
    #error { color: red; }
  </style>
</head>
<body>
<input type="file" multiple />
<output></output>
<script>
  var worker = new Worker('worker.js');

  worker.onmessage = function(e) {
    console.log(e.data); // e.data should be an array of ArrayBuffers.
  };

  worker.onerror = function(e) {
    document.querySelector('#error').textContent = [
        'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message].join('');
  };

  document.querySelector('input[type="file"]').addEventListener('change', function(e) {
    worker.postMessage(this.files);
  }, false);
</script>
</body>
</html>

worker.js

self.addEventListener('message', function(e) {
  var files = e.data;
  var buffers = [];

  // Read each file synchronously as an ArrayBuffer and
  // stash it in a global array to return to the main app.
  [].forEach.call(files, function(file) {
    var reader = new FileReaderSync();
    buffers.push(reader.readAsArrayBuffer(file));
  });

  postMessage(buffers);
}, false);

正如预期的那样,回调随同步一起消失 FileReader.这简化了读取文件时的回调嵌套量。相反,readAs* 方法返回读取文件。

示例:获取所有条目

在某些情况下,同步 API 对于某些任务要简洁得多。更少的回调很好,当然会让事情更具可读性。同步 API 的真正缺点源于 Worker 的限制。

出于安全原因,调用应用与 Web 辅助角色线程之间的数据永远不会共享。在以下情况下,数据始终复制到工作线程和从工作线程复制数据 postMessage() 被称为。因此,并非每种数据类型都可以传递。

不幸 FileEntrySyncDirectoryEntrySync 当前不属于可接受的类型。那么,如何将条目恢复到调用应用程序呢?规避此限制的一种方法是返回 文件系统列表:URL ,而不是条目列表。 filesystem: URL 只是字符串,因此它们非常容易传递。此外,可以使用以下命令将它们解析为主应用程序中的条目 resolveLocalFileSystemURL().这让你回到 FileEntrySync/ DirectoryEntrySync 对象。

主应用:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<title>Listing filesystem entries using the synchronous API</title>
</head>
<body>
<script>
  window.resolveLocalFileSystemURL = window.resolveLocalFileSystemURL ||
                                     window.webkitResolveLocalFileSystemURL;

  var worker = new Worker('worker.js');
  worker.onmessage = function(e) {
    var urls = e.data.entries;
    urls.forEach(function(url, i) {
      window.resolveLocalFileSystemURL(url, function(fileEntry) {
        console.log(fileEntry.name); // Print out file's name.
      });
    });
  };

  worker.postMessage({'cmd': 'list'});
</script>
</body>
</html>

worker.js

self.requestFileSystemSync = self.webkitRequestFileSystemSync ||
                             self.requestFileSystemSync;

var paths = []; // Global to hold the list of entry filesystem URLs.

function getAllEntries(dirReader) {
  var entries = dirReader.readEntries();

  for (var i = 0, entry; entry = entries[i]; ++i) {
    paths.push(entry.toURL()); // Stash this entry's filesystem: URL.

    // If this is a directory, we have more traversing to do.
    if (entry.isDirectory) {
      getAllEntries(entry.createReader());
    }
  }
}

function onError(e) {
  postMessage('ERROR: ' + e.toString()); // Forward the error to main app.
}

self.onmessage = function(e) {
  var data = e.data;

  // Ignore everything else except our 'list' command.
  if (!data.cmd || data.cmd != 'list') {
    return;
  }

  try {
    var fs = requestFileSystemSync(TEMPORARY, 1024*1024 /*1MB*/);

    getAllEntries(fs.root.createReader());

    self.postMessage({entries: paths});
  } catch (e) {
    onError(e);
  }
};

示例:使用 XHR2 下载文件

Workers的一个常见用例是使用 XHR2 下载一堆文件,并将这些文件写入HTML5文件系统。对于工作线程来说,这是一项完美的任务!

以下示例仅提取和写入一个文件,但您可以映像扩展该文件以下载一组文件。

主应用:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<title>Download files using a XHR2, a Worker, and saving to filesystem</title>
</head>
<body>
<script>
  var worker = new Worker('downloader.js');
  worker.onmessage = function(e) {
    console.log(e.data);
  };
  worker.postMessage({fileName: 'GoogleLogo',
                      url: 'googlelogo.png', type: 'image/png'});
</script>
</body>
</html>

下载者.js:

self.requestFileSystemSync = self.webkitRequestFileSystemSync ||
                             self.requestFileSystemSync;

function makeRequest(url) {
  try {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, false); // Note: synchronous
    xhr.responseType = 'arraybuffer';
    xhr.send();
    return xhr.response;
  } catch(e) {
    return "XHR Error " + e.toString();
  }
}

function onError(e) {
  postMessage('ERROR: ' + e.toString());
}

onmessage = function(e) {
  var data = e.data;

  // Make sure we have the right parameters.
  if (!data.fileName || !data.url || !data.type) {
    return;
  }

  try {
    var fs = requestFileSystemSync(TEMPORARY, 1024 * 1024 /*1MB*/);

    postMessage('Got file system.');

    var fileEntry = fs.root.getFile(data.fileName, {create: true});

    postMessage('Got file entry.');

    var arrayBuffer = makeRequest(data.url);
    var blob = new Blob([new Uint8Array(arrayBuffer)], {type: data.type});

    try {
      postMessage('Begin writing');
      fileEntry.createWriter().write(blob);
      postMessage('Writing complete');
      postMessage(fileEntry.toURL());
    } catch (e) {
      onError(e);
    }

  } catch (e) {
    onError(e);
  }
};

结论

Web Workers 是 HTML5 中未被充分利用和未被充分重视的功能。与我交谈过的大多数开发人员不需要额外的计算优势,但它们不仅可以用于纯计算。如果你持怀疑态度(就像我一样),我希望这篇文章能帮助你改变主意。将磁盘操作(文件系统 API 调用)或 HTTP 请求卸载到 Worker 等内容是很自然的,也有助于划分代码。Workers内部的HTML5文件API为Web应用程序打开了一个全新的令人敬畏的功能,这是很多人没有探索过的。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

明媚如初

暂无简介

0 文章
0 评论
539 人气
更多

推荐作者

daid

文章 0 评论 0

我心依旧

文章 0 评论 0

晒暮凉

文章 0 评论 0

微信用户

文章 0 评论 0

DS

文章 0 评论 0

〆凄凉。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文