2012-06-08 4 views
0

Silverlight 클라이언트에서 Amazon S3로 여러 파일을 직접 업로드하려고합니다. 사용자는 표준 파일 열기 대화 상자에서 파일을 선택하고 한 번에 하나씩 연속적으로 업로드되도록 연결하고 싶습니다. 이것은 응용 프로그램의 여러 장소에서 일어날 수 있으므로 선택한 파일의 IEnumerable이 파일의 IObservable을 노출하므로 UI가 각 파일에 따라 적절하게 응답 할 수 있도록 업로드하는 멋진 유틸리티 클래스로 마무리하려고했습니다. 은 끝났어.Rx를 사용하여 여러 단계의 2 단계 파일 업로드

Silverlight 및 AmazonS3의 모든 보안 요구 사항으로 인해 상당히 복잡합니다. 컨텍스트에 대한 전체 환경을 간단히 설명하려고 시도 하겠지만 코드를 아래에 게시 할 작은 콘솔 응용 프로그램으로이 문제를 재현했습니다.

표준 이벤트 기반 비동기 메서드를 제공하는 Silverlight에서 S3 로의 업로드를 처리하는 타사 유틸리티가 있습니다. 업로드 된 파일 하나당 하나의 유틸리티 인스턴스를 만듭니다. 서명되지 않은 요청 문자열을 만들어 내 서버에 게시하여 개인 키로 서명합니다. 이 서명 요청은 이벤트 기반 비동기 메소드를 사용하는 서비스 프록시 클래스를 통해 발생합니다. 서명 된 요청이 있으면이를 업 로더 인스턴스에 추가하고 업로드를 시작합니다.

나는 Concat을 사용해 보았지만 첫 번째 파일 만 프로세스가 끝난다. Merge를 사용하면 모든 파일이 정상적으로 완료되지만 순차적으로가 아니라 병렬 방식으로 완료됩니다. Merge (2)를 사용하면 모든 파일이 첫 번째 단계를 시작하지만 2 번만 완료되어 완료됩니다.

내가 기대 한 것처럼 행동하지 않기 때문에 분명히 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(); 
     } 
    } 
} 

답변

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(); 
관련 문제