6

私は、EvaporateJSを使用して大規模なファイルをマルチパートアップロードを使用してAmazon S3にアップロードするWebアプリケーションを構築しています。新しいチャンクが開始されるたびにブラウザが約2秒間フリーズするという問題に気づいた。アップロードが進行中にユーザーが自分のアプリを使い続けることができるようにしたいと思っています。このフリーズはそのことを悪い経験にしています。ChromeのワーカーブロッキングUIスレッド

私はChromeのタイムラインを使用して、これを引き起こしていたことを調べ、SparkMD5のハッシュであることを確認しました。そこで、アップロードプロセス全体をWorkerに移しました。問題を解決すると思っていました。

問題は現在、EdgeとFirefoxでは修正されていますが、Chromeではまったく同じ問題があります。あなたが見ることができるように、フリーズ中に私のメインスレッドがその時に実行されるJavaScriptの<速度8msで、基本的に何もしていない Timeline

は、ここに私のタイムラインのスクリーンショットです。すべての作業は私のワーカースレッドで発生していて、それは〜600ms程度しか実行されておらず、私のフレームがとる1386msではありません。

私は実際に何が問題を引き起こしているのかよく分かりません。私が気づくべきである労働者との間に問題がありますか?それはAWS公開鍵、AWSバケット名とAWSリージョン、AWSのオブジェクトキーと入力Fileオブジェクトでそれを提供するために、呼び出し元のスレッドに依存していることを

var window = self; // For Worker-unaware scripts 

// Shim to make Evaporate work in a Worker 
var document = { 
    createElement: function() { 
     var href = undefined; 

     var elm = { 
      set href(url) { 
       var 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() { 
       return href; 
      }, 
      protocol: undefined, 
      hostname: undefined, 
      pathname: undefined, 
      port: undefined, 
      search: undefined, 
      hash: undefined, 
      host: undefined 
     }; 

     return elm; 
    } 
}; 

importScripts("/lib/sha256/sha256.min.js"); 
importScripts("/lib/spark-md5/spark-md5.min.js"); 
importScripts("/lib/url-parse/url-parse.js"); 
importScripts("/lib/xmldom/xmldom.js"); 
importScripts("/lib/evaporate/evaporate.js"); 

DOMParser = self.xmldom.DOMParser; 

var defaultConfig = { 
    computeContentMd5: true, 
    cryptoMd5Method: function (data) { return btoa(SparkMD5.ArrayBuffer.hash(data, true)); }, 
    cryptoHexEncodedHash256: sha256, 
    awsSignatureVersion: "4", 
    awsRegion: undefined, 
    aws_url: "https://s3-ap-southeast-2.amazonaws.com", 
    aws_key: undefined, 
    customAuthMethod: function(signParams, signHeaders, stringToSign, timestamp, awsRequest) { 
     return new Promise(function(resolve, reject) { 
      var signingRequestId = currentSigningRequestId++; 

      postMessage(["signingRequest", signingRequestId, signParams.videoId, timestamp, awsRequest.signer.canonicalRequest()]); 
      queuedSigningRequests[signingRequestId] = function(signature) { 
       queuedSigningRequests[signingRequestId] = undefined; 
       if(signature) { 
        resolve(signature); 
       } else { 
        reject(); 
       } 
      } 
     }); 
    }, 
    //logging: false, 
    bucket: undefined, 
    allowS3ExistenceOptimization: false, 
    maxConcurrentParts: 5 
} 

var currentSigningRequestId = 0; 
var queuedSigningRequests = []; 

var e = undefined; 
var filekey = undefined; 
onmessage = function(e) { 
    var messageType = e.data[0]; 
    switch(messageType) { 
     case "init": 
      var globalConfig = {}; 
      for(var k in defaultConfig) { 
       globalConfig[k] = defaultConfig[k]; 
      } 
      for(var k in e.data[1]) { 
       globalConfig[k] = e.data[1][k]; 
      } 

      var uploadConfig = e.data[2]; 

      Evaporate.create(globalConfig).then(function(evaporate) { 
       var e = evaporate; 

       filekey = globalConfig.bucket + "/" + uploadConfig.name; 

       uploadConfig.progress = function(p, stats) { 
        postMessage(["progress", p, stats]); 
       }; 

       uploadConfig.complete = function(xhr, awsObjectKey, stats) { 
        postMessage(["complete", xhr, awsObjectKey, stats]); 
       } 

       uploadConfig.info = function(msg) { 
        postMessage(["info", msg]); 
       } 

       uploadConfig.warn = function(msg) { 
        postMessage(["warn", msg]); 
       } 

       uploadConfig.error = function(msg) { 
        postMessage(["error", msg]); 
       } 

       e.add(uploadConfig); 
      }); 
      break; 

     case "pause": 
      e.pause(filekey); 
      break; 

     case "resume": 
      e.resume(filekey); 
      break; 

     case "cancel": 
      e.cancel(filekey); 
      break; 

     case "signature": 
      var signingRequestId = e.data[1]; 
      var signature = e.data[2]; 
      queuedSigningRequests[signingRequestId](signature); 
      break; 
    } 
} 

注:ここでは

は私の労働者のためのコードですこれらはすべて「init」メッセージで提供されます。署名されたものが必要な場合、親スレッドに「signingRequest」メッセージを送信します。このメッセージは、APIの署名エンドポイントからフェッチされた「署名」メッセージに署名を提供することが期待されます。

+0

これはまったく役に立ちますか? https://github.com/TTLabs/EvaporateJS/issues/257 – user650881

+0

私はEvaporateJSのオーバーヘッドを認識しており、パフォーマンス上の問題が発生していました。これがワーカースレッドを使用した理由です。私の質問は、たとえすべての作業がワーカーで起きていてもUIスレッドがまだフリーズしている理由です。 –

+0

好奇心で、これを解決しましたか? – tony19

答えて

3

私は非常に良い例を挙げることはできませんし、ワーカーコードだけで何をしているのか分析することはできませんが、問題はメインスレッドのチャンクの読み込みか、メインスレッドのチャンクで処理しています。たぶん、postMessageを呼び出すメインのスレッドコードをWorkerにポストしてください。

私が今デバッグしていた場合は、FileReader操作をワーカーに移してみます。チャンクが読み込まれている間にWorkerのブロックが気にならない場合は、FileReaderSyncを使用することもできます。

ポストコメント更新

ファイルの内容+メタデータ+キーをハッシュ必要presigned URLを生成していますか?ハッシュファイルの内容はチャンクのサイズでO(n)をとり、ハッシュがBlobから読み取る最初の操作であれば、ハッシュが開始されるまでファイルコンテンツの読み込みが延期される可能性があります。メインスレッドに署名を残すように強制されていない限り(あなたは主要な資材で労働者を信用していませんか?)それは労働者にもたらすもう一つの良いことです。

労働者への署名を移動すると、多すぎる場合は、労働者を持つことができ、バックメインにファイルの内容を読み取ることがBlobを強制および/またはArrayBuffer(またはUint8Arrayか何を持っている)を渡すために何かをします署名のためのスレッド。これにより、メインスレッドでチャンクの読み取りが行われないことが保証されます。

+0

私は今すぐコードにアクセスすることはできません(また、賞金の支払いが終わるまで再度アクセスすることはできません)ので、悲しいことに私はメインのスレッドコードを投稿できません。私はそれがどのように動作するかの概要を提供するために最善を尽くします。 まず、FileReader操作が実際にWorker内で行われていることを明確にしたいと思います。 EvaporateJSはファイルの読み込みを処理するため、私が投稿したコードでは呼び出しを見ることはできませんが、メインスレッドではFileReader関数を使用せず、EvaporateJSをロードしています。 –

+0

メインスレッドの操作は次のとおりです。(1)ユーザーはinput [type = file]要素を使用してファイルを選択します。メインスレッドはファイル名を取得し、ファイルをアップロードする場所をAPIに要求します。次にメインスレッドはpostMessageを使用して 'init'メッセージをワーカーに送信します。 (2)作業者がアップロードプロセスを開始します。 AWSに登録済みのURLが必要な場合はいつでも、 'signingRequest'というpostMessageをメインスレッドに送信します。 (3)メインスレッドは非同期に、署名要求で自分のAPIを呼び出します。署名を取得すると、「署名」というpostMessageがワーカーに送信されます。 –

+0

(4)進行状況が更新されると、ワーカーはメインスレッドに「進行状況」メッセージを送信します。 (5)メインスレッドはAngularダイジェストを呼び出し、UIを新しい進捗状況で更新します。 (6)アップロードが完了すると、ワーカーは「完了」メッセージを送信します。 –