本稿はWano Group Advent Calendar 2021 用の記事となります。
経緯/書くこと
大きいサイズのファイルをWebブラウザからS3に上げるには、マルチパートアップロードする
必要があります。
この時、ブラウザによってはUIスレッドをブロックするケースがあり、対策としてWeb Worker上でのファイル部分読み出しとマルチパートアップロードを試すケースがありました。
事象自体はけっこう前なので状況が変わっている可能性もありますが、備忘録がてらそのときやったことをメモします。
web workerとは
ウェブ ワーカーの基本
シングルスレッドなjavascriptの世界において、独立したスレッドを実現するものです。
aws sdk console 上で直接ファイルをuploadするときも、WebWorkerが使われているのが確認できます.
uploadするためのモジュールの構成
ブラウザから s3 multi part upload する方法自体の手順は、STSやaws-sdk-jsを使った場合だと
-
- サーバーサイドで
aws sts
系のモジュールで Upload用一時ロールを発行
- サーバーサイドで
-
- フロントエンドで
aws-sdk/clients/s3
などで
- createMultipartUpload
- localのfile object を
file.slice(rangeStart, end);
などで部分読み出ししつつuploadPart
- 全て終わったら
completeMultipartUpload
- フロントエンドで
のようになります。
このへんは最近はsdkでも S3.ManagedUpload
とかでうまくやってくれるのかも?
本題は、これをどうweb worker版に落とし込むか。
こうなりました。
ポイントとしては、
- postMessageをうまく使って Main Thread側のモジュールとWeb Worker側でイベント登録<->コールバックの関係性を作る
- file object自体はMain Thread側で読んだものが直接web worker側に渡せるのでそれを使う
です。
綺麗にうまくいった!と思ったものの...
Web Worker上にないグローバルオブジェクトがある
ブラウザ版 aws-sdk-js はwindowオブジェクトへのアクセスやdocumentオブジェクトへのアクセスを行うようで、
実際に使うといろいろエラーが出てしまいました。
エラーメッセージに従って当時愚直に潰したものが下記です...
import { DOMParser } from '@xmldom/xmldom';
// @ts-ignore
global["window"] = {};
global["window"].DOMParser = DOMParser;
global["DOMParser"] = DOMParser;
// @ts-ignore
global["document"] = {
cookie: "",
createElement: function() {
// @ts-ignore
let href = string || undefined;
const elm: any = {
set href(url: string) {
const obj = new URL(url);
elm.protocol = obj.protocol;
elm.hostname = obj.hostname;
elm.pathname = obj.pathname;
elm.port = obj.port;
elm.search = obj.search;
elm.hash = obj.hash;
elm.host = obj.host;
href = url;
},
get href() {
// @ts-ignore
return href;
},
protocol: undefined,
hostname: undefined,
pathname: undefined,
port: undefined,
search: undefined,
hash: undefined,
host: undefined
};
return elm;
}
};
(ignoreやらanyばかりでtsの意味がない点は本題じゃないので今回ご容赦)
Web Worker には windowオブジェクトやらdocumentオブジェクトやらがないそうなので、aws-sdk-jsが使う最低限の仕様をグローバルに定義しています。
唯一、DOMParser
というモジュールは下記のものが使えました。
npm install --save @xmldom/xmldom @types/xmldom