1

複数のファイルを Silverlight クライアントから直接 Amazon S3 にアップロードしようとしています。ユーザーは標準のファイルを開くダイアログからファイルを選択し、アップロードを連鎖させて、一度に 1 つずつ順番に実行したいと考えています。これはアプリの複数の場所から発生する可能性があるため、選択したファイルの IEnumerable を受け入れる素敵なユーティリティ クラスにまとめようとして、ファイルがアップロードされるときにファイルの IObservable を公開して、UI が各ファイルに応じて応答できるようにしました。終了しました。

Silverlight と AmazonS3 の両方のすべてのセキュリティ要件のために、これはかなり複雑です。コンテキストのために環境全体を簡単に説明しようとしますが、コードを以下に投稿する小さなコンソール アプリケーションで問題を再現しました。

標準のイベント ベースの非同期メソッドを公開する Silverlight から S3 へのアップロードを処理するサード パーティのユーティリティがあります。アップロードされたファイルごとに、そのユーティリティのインスタンスを 1 つ作成します。署名されていないリクエスト文字列を作成し、それをサーバーに投稿して秘密鍵で署名します。その署名要求は、イベント ベースの非同期メソッドも使用するサービス プロキシ クラスを介して行われます。署名済みのリクエストを取得したら、それをアップローダ インスタンスに追加してアップロードを開始します。

Concat を使用してみましたが、プロセスを通過するのは最初のファイルだけです。Merge を使用すると、すべてのファイルが正常に完了しますが、順次ではなく並列で完了します。Merge(2) を使用すると、すべてのファイルが最初のステップを開始しますが、その後は 2 つだけが通過して完了します。

Rx が期待どおりに動作していないため、明らかに Rx に関連する何かが欠けています。

namespace RxConcat
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reactive.Linq;
    using System.Timers;

    public class SignCompletedEventArgs : EventArgs
    {
        public string SignedRequest { get; set; }
    }

    public class ChainUploader
    {
        public IObservable<string> StartUploading(IEnumerable<string> files)
        {
            return files.Select(
                     file => from signArgs in this.Sign(file + "_request")
                             from uploadArgs in this.Upload(file, signArgs.EventArgs.SignedRequest)
                             select file).Concat();
        }

        private IObservable<System.Reactive.EventPattern<SignCompletedEventArgs>> Sign(string request)
        {
            Console.WriteLine("Signing request '" + request + "'");
            var signer = new Signer();
            var source = Observable.FromEventPattern<SignCompletedEventArgs>(ev => signer.SignCompleted += ev, ev => signer.SignCompleted -= ev);
            signer.SignAsync(request);
            return source;
        }

        private IObservable<System.Reactive.EventPattern<EventArgs>> Upload(string file, string signedRequest)
        {
            Console.WriteLine("Uploading file '" + file + "'");
            var uploader = new Uploader();
            var source = Observable.FromEventPattern<EventArgs>(ev => uploader.UploadCompleted += ev, ev => uploader.UploadCompleted -= ev);
            uploader.UploadAsync(file, signedRequest);
            return source;
        }
    }

    public class Signer
    {
        public event EventHandler<SignCompletedEventArgs> SignCompleted;

        public void SignAsync(string request)
        {
            var timer = new Timer(1000);
            timer.Elapsed += (sender, args) =>
            {
                timer.Stop();
                if (this.SignCompleted == null)
                {
                    return;
                }

                this.SignCompleted(this, new SignCompletedEventArgs { SignedRequest = request + "signed" });
            };
            timer.Start();
        }
    }

    public class Uploader
    {
        public event EventHandler<EventArgs> UploadCompleted;

        public void UploadAsync(string file, string signedRequest)
        {
            var timer = new Timer(1000);
            timer.Elapsed += (sender, args) =>
            {
                timer.Stop();
                if (this.UploadCompleted == null)
                {
                    return;
                }

                this.UploadCompleted(this, new EventArgs());
            };
            timer.Start();
        }
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            var files = new[] { "foo", "bar", "baz" };
            var uploader = new ChainUploader();
            var token = uploader.StartUploading(files).Subscribe(file =>   Console.WriteLine("Upload completed for '" + file + "'"));
            Console.ReadLine();
        }
    }
}
4

1 に答える 1

1

各ファイルの 2 ステップ アップロードを処理しているベース オブザーバブルは決して「完了」しないため、チェーン内の次のアップロードが開始されません。Concat() を呼び出す前に、そのオブザーバブルに Limit(1) を追加すると、正しく機能します。

return files.Select(file => (from signArgs in this.Sign(file + "_request")
                             from uploadArgs in this.Upload(file, signArgs.EventArgs.SignedRequest)
                             select file).Take(1)).Concat();
于 2012-06-08T18:20:39.450 に答える