2011-11-18 11 views
15

私はWPFアプリケーションを持っています。私はそのファイルをソーシャルネットワークの1つに投稿しています。 自分自身をアップロードしても問題ありませんが、アップロードにどれくらいの時間がかかるかを示したいと思います。HTTPの進行状況のアップロード

私はこれを行う方法の束を試してみました:

1)HttpWebRequest.GetStream方法:(はるかに短い)

using (
var FS = File.Open(
    localFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) 
{ 
    long len = FS.Length; 
    HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url); 
    request.Method = "POST"; 
    request.ProtocolVersion = HttpVersion.Version11; 
    request.ContentType = "multipart/form-data; boundary=--AaB03x"; 
    //predata and postdata is two byte[] arrays, that contains 
    //strings for MIME file upload (defined above and is not important) 
    request.ContentLength = predata.Length + FS.Length + postdata.Length; 
    request.AllowWriteStreamBuffering = false; 
    using (var reqStream = request.GetRequestStream()) 
    { 
     reqStream.Write(predata, 0, predata.Length); 
     int bytesRead = 0; 
     int totalRead = 0; 
     do 
     { 
      bytesRead = FS.Read(fileData, 0, MaxContentSize); 
      totalRead += bytesRead; 
      reqStream.Write(fileData, 0, bytesRead); 
      reqStream.Flush(); //trying with and without this 
      //this part will show progress in percents 
      sop.prct = (int) ((100*totalRead)/len); 
     } while (bytesRead > 0); 
     reqStream.Write(postdata, 0, postdata.Length); 
    } 
    HttpWebResponse responce = (HttpWebResponse) request.GetResponse(); 
    using (var respStream = responce.GetResponseStream()) 
    { 
     //do things 
    } 
} 

2)Webクライアントの方法:

void UploadFile (url, localFilePath) 
{ 
    ... 
    WebClient client = new WebClient(); 
    client.UploadProgressChanged += new UploadProgressChangedEventHandler(UploadPartDone); 
    client.UploadFileCompleted += new UploadFileCompletedEventHandler(UploadComplete); 
    client.UploadFileAsync(new Uri(url), localFilePath); 
    done.WaitOne(); 

    //do things with responce, received from UploadComplete 
    JavaScriptSerializer jssSer = new JavaScriptSerializer(); 
    return jssSer.Deserialize<UniversalJSONAnswer>(utf8.GetString(UploadFileResponce)); 
    //so on... 
    ... 
} 

void UploadComplete(object sender, UploadFileCompletedEventArgs e) 
{ 
    UploadFileResponce=e.Result; 
    done.Set(); 
} 

void UploadPartDone(object sender, UploadProgressChangedEventArgs e) 
{ 
    //this part expected to show progress 
    sop.prct=(int)(100*e.BytesSent/e.TotalBytesToSend); 
} 

3)偶数TcpClient:

using (
var FS = File.Open(
    localFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) 
{ 
    long len = FS.Length; 
    long totalRead = 0; 
    using (var client = new TcpClient(urli.Host, urli.Port)) 
    { 
     using (var clearstream = client.GetStream()) 
     { 
      using (var writer = new StreamWriter(clearstream)) 
      using (var reader = new StreamReader(clearstream)) 
      { 
       //set progress to 0 
       sop.prct = 0; 
       // Send request headers 
       writer.WriteLine("POST " + urli.AbsoluteUri + " HTTP/1.1"); 
       writer.WriteLine("Content-Type: multipart/form-data; boundary=--AaB03x"); 
       writer.WriteLine("Host: " + urli.Host); 
       writer.WriteLine("Content-Length: " + (predata.Length + len + postdata.Length).ToString()); 
       writer.WriteLine(); 
       //some data for MIME 
       writer.Write(utf8.GetString(predata)); 
       writer.Flush(); 
       int bytesRead; 
       do 
       { 
        bytesRead = FS.Read(fileData, 0, MaxContentSize); 
        totalRead += bytesRead; 
        writer.BaseStream.Write(fileData, 0, bytesRead); 
        writer.BaseStream.Flush(); 
        sop.prct = (int) ((100*totalRead)/len); 
       } while (bytesRead > 0) 
       writer.Write(utf8.GetString(postdata)); 
       writer.Flush(); 
       //read line of response and do other thigs... 
       respStr = reader.ReadLine(); 
       ... 
      } 
     } 
    } 
} 

すべての場合、ファイルは正常にサーバーに送信されました。 しかし、常に進捗状況は次のようになります。数秒間、0から100まで実行され、ファイルが実際にアップロードされるまで待機します(約5分 - ファイルは400MBです)。

私はファイルからのデータがどこかでバッファリングされていると思うので、私はアップロードではなく、データをバッファリングしています。それがアップロードされるまで待つ必要があります。

私の質問は以下のとおりです。

1)実際アップロードデータを追跡する方法はありますか?メソッドStream.Write()またはFlush()(NetworkStreamのために私がどこかで読んだように動作しない)は、TCPパケットが受信したサーバーからの確認を受け取るまで返されませんでした。

2)または、バッファリングを拒否できますか(AllowWriteStreamBuffering for HttpWebRequestは機能しません)?

3)さらに「下に」進み、ソケットで試してみるのは理にかなっていますか?更新

をUIに表示進行の仕方であらゆる疑問を避けるために、私は、ファイルを記録するためのコードを書き直しました。 ので、ここでのコードは次のとおりです。あなたがプログラムは、それが約2秒間〜400メガバイトをアップロードすることを考えて見ることができるように

2011-11-19T22:00:54.5964408+04:00 Start write into request stream. 
2011-11-19T22:00:54.6404433+04:00 totalRead= 1048576/410746880 
2011-11-19T22:00:54.6424434+04:00 totalRead= 2097152/410746880 
2011-11-19T22:00:54.6434435+04:00 totalRead= 3145728/410746880 
2011-11-19T22:00:54.6454436+04:00 totalRead= 4194304/410746880 
2011-11-19T22:00:54.6464437+04:00 totalRead= 5242880/410746880 
2011-11-19T22:00:54.6494438+04:00 totalRead= 6291456/410746880 
.......  
2011-11-19T22:00:55.3434835+04:00 totalRead= 408944640/410746880 
2011-11-19T22:00:55.3434835+04:00 totalRead= 409993216/410746880 
2011-11-19T22:00:55.3464837+04:00 totalRead= 410746880/410746880 
2011-11-19T22:00:55.3464837+04:00 totalRead= 410746880/410746880 
2011-11-19T22:00:55.3464837+04:00 All sent!!! Waiting for responce... 
2011-11-19T22:07:23.0616597+04:00 Responce received! 

using (var LogStream=File.Open("C:\\123.txt",FileMode.Create,FileAccess.Write,FileShare.Read)) 
using (var LogWriter=new StreamWriter(LogStream)) 
using (var FS = File.Open(localFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) 
{ 
    long len = FS.Length; 
    HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url); 
    request.Timeout = 7200000; //2 hour timeout 
    request.Method = "POST"; 
    request.ProtocolVersion = HttpVersion.Version11; 
    request.ContentType = "multipart/form-data; boundary=--AaB03x"; 
    //predata and postdata is two byte[] arrays, that contains 
    //strings for MIME file upload (defined above and is not important) 
    request.ContentLength = predata.Length + FS.Length + postdata.Length; 
    request.AllowWriteStreamBuffering = false; 
    LogWriter.WriteLine(DateTime.Now.ToString("o") + " Start write into request stream. "); 
    using (var reqStream = request.GetRequestStream()) 
    { 
     reqStream.Write(predata, 0, predata.Length); 
     int bytesRead = 0; 
     int totalRead = 0; 
     do 
     { 
      bytesRead = FS.Read(fileData, 0, MaxContentSize); 
      totalRead += bytesRead; 
      reqStream.Write(fileData, 0, bytesRead); 
      reqStream.Flush(); //trying with and without this 
      //sop.prct = (int) ((100*totalRead)/len); //this part will show progress in percents 
      LogWriter.WriteLine(DateTime.Now.ToString("o") + " totalRead= " + totalRead.ToString() + "/" + len.ToString()); 
     } while (bytesRead > 0); 
     reqStream.Write(postdata, 0, postdata.Length); 
    } 
    LogWriter.WriteLine(DateTime.Now.ToString("o") + " All sent!!! Waiting for responce... "); 
    LogWriter.Flush(); 
    HttpWebResponse responce = (HttpWebResponse) request.GetResponse(); 
    LogWriter.WriteLine(DateTime.Now.ToString("o") + " Responce received! "); 
    using (var respStream = responce.GetResponseStream()) 
    { 
     if (respStream == null) return null; 
     using (var streamReader = new StreamReader(respStream)) 
     { 
      string resp = streamReader.ReadToEnd(); 
      JavaScriptSerializer jssSer = new JavaScriptSerializer(); 
      return jssSer.Deserialize<UniversalJSONAnswer>(resp); 
     } 
    } 
} 

、ここでは、結果(私は真ん中をカット)です。そして7分後にファイルが実際にアップロードされ、私は応答を受けます。

再び更新:

はこれに思えるは、Windows 7(x64のまたはx86についてSHUREない)の下で起こっています。 コードを実行すると、すべてが正常に動作し、進捗状況が正確に表示されます。

答えて

4

この質問が投稿されましたので、それが一年以上だが、私は私のポストをすることができると思う便利誰かのため。

私は進行状況を表示するのと同じ問題があり、それはあなたが説明したのとまったく同じように動作しました。だから私は正しくアップロードの進行状況を示すHttpClientを使用することにしました。それで、面白いバグに遭遇しました.FiddlerがHttpClientを立ち上げて、WebClient/HttpWebRequestのような予期しない方法でアップロードの進捗状況を表示し始めたとき、WebClientが正しくプログラムをアップロードしなかった理由が考えられました。それが始まった)。だから私はWebClientをもう一度試してみました。私はwin7とXPでいくつかのPCでテストして、すべてのケースで進歩が正しく表示されていた。

だから、私はフィドラー(おそらくシオマネキだけではなく)のような、そのようなプログラムは、いくつかのWebクライアントや他の.NETのクラスが進行をアップロードする方法を示し上で影響を与えたと思います。

この議論は、それを承認:

HttpWebRequest doesn't work except when fiddler is running

+0

ok。それは妥当と思われる。私は実際にオープンされたフィドラーでテストを行うことができました。 私が家に帰るともう一度チェックします。 – Lumen

+0

私は、フィドラーが、すべての.netネットワーククラスをそうしてはいけないように動作させるいくつかのシステムフックを設定すると思います。 – Vlad

+0

さて、Fiddlerは私が知る限りHTTPプロキシとして募集しています。したがって、宛先ホストに送信される前であっても、自分自身のバッファであり、アップロードホストへのパケットを確認することになります。逆に、Fiddlerを有効にしている間にファイルをダウンロードすると、進行状況は0%にとどまり、その後、ダウンロードされたファイルがプロキシからリクエストを開始したホストにストリーミングされるとすぐに100%にジャンプします。 –

0

このコードはUIスレッドで実行されています。新しいスレッドでアップロードするものを実行する必要があります。 その時点で2つのオプションがあります。 1)UIスレッドでタイマーを実行し、UIを更新します。 2)UIを更新するためにInvoke(別の​​スレッドからUIにアクセスできないため)を使用してUIを更新します。

+0

これらのすべては、独自のスレッドで実行されています。 sop.prctプロパティを変更するとWPFバインディングとUIの更新が行われます – Lumen

+0

デバッグモードで段階的に進捗を監視しました。たとえば、TcpClientでは99%の時間で次の行が得られます。 'respStr = reader.ReadLine();'他のすべては1分以内に実行されます。 – Lumen

+0

私は見えません(私は目が見えない、または他の場所で定義されています)fileDataバイト[]配列の大きさはどれくらいですか。どんな方法であれ、16 - 256kbよりも厄介であってはなりません。 – Ivar

0

最初の例では、ディスク上のファイルからストリームに書き込む速さをプログレスバーが示していると思います。実際のアップロードの進行状況ではありません。 *)。

私は間違っているかもしれません^^ WPFの経験はありませんが、私はSilverlightからWCFに大量のファイルをアップロードしており、モデルを使用してファイルをブロックに分割しています。各ブロックを送信します。サーバーからの応答を受け取ったら( "ブロック26を受信しました")、進行状況バーを実際に更新します。あなたが/ no/thatブロックxが作成していない限り、進行状況バーを更新できません。サーバーがそれを持っていると言っているかどうかを知る良い方法です。

* 5分で400Mbをアップロードできたらいいですか?いつでも私を連れて行くでしょう...

+0

WPFには何の注意も払っていませんが、インターフェイスにのみ責任があります。 私はそれがバッファへの書き込みの進捗状況を示していると考えています。それは、ネットワークレベルでサーバーに送信されたデータです。だから私は、データの一部が実際にサーバーまたはデータに送信されるまで返されないメソッドに送信されるときにのみ発生するいくつかのイベントが必要です。 残念ながら、サーバーは大量のHTTPリクエストを1つしか受け入れず、httpレベルのファイルを破棄することはできません。明日はコードを試してみます。 – Lumen

3

WebClientUploadFileを使って、書き込みファイルをファイルストリームとして使うのではなく、ファイルをアップロードすることができます。受信してアップロードしたデータのパーセンテージを追跡するには、UploadFileAsynを使用してそのイベントに登録することができます。

以下のコードでは、UploadFileAsynをアップロードファイルに同期して使用しましたが、アップローダのインスタンスを破棄しない限り、同期する必要はありません。

class FileUploader : IDisposable 
{ 
    private readonly WebClient _client; 
    private readonly Uri _address; 
    private readonly string _filePath; 
    private bool _uploadCompleted; 
    private bool _uploadStarted; 
    private bool _status; 

    public FileUploader(string address, string filePath) 
    { 
     _client = new WebClient(); 
     _address = new Uri(address); 
     _filePath = filePath; 
     _client.UploadProgressChanged += FileUploadProgressChanged; 
     _client.UploadFileCompleted += FileUploadFileCompleted; 
    } 

    private void FileUploadFileCompleted(object sender, UploadFileCompletedEventArgs e) 
    { 
     _status = (e.Cancelled || e.Error == null) ? false : true; 
     _uploadCompleted = true; 
    } 

    private void FileUploadProgressChanged(object sender, UploadProgressChangedEventArgs e) 
    { 
     if(e.ProgressPercentage % 10 == 0) 
     { 
      //This writes the pecentage data uploaded and downloaded 
      Console.WriteLine("Send: {0}, Received: {1}", e.BytesSent, e.BytesReceived); 
      //You can have a delegate or a call back to update your UI about the percentage uploaded 
      //If you don't have the condition (i.e e.ProgressPercentage % 10 == 0)for the pecentage of the process 
      //the callback will slow you upload process down 
     } 
    } 

    public bool Upload() 
    { 

     if (!_uploadStarted) 
     { 
      _uploadStarted = true; 
      _client.UploadFileAsync(_address, _filePath); 
     } 
     while (!_uploadCompleted) 
     { 
      Thread.Sleep(1000); 
     } 
     return _status; 
    } 

    public void Dispose() 
    { 
     _client.Dispose(); 
    } 
} 

クライアントコード:

  using (FileUploader uploader = new FileUploader("http://www.google.com", @"C:\test.txt")) 
     { 
      uploader.Upload(); 
     } 

あなたのWPF UIを更新するためにFileUploadProgressChangedイベントハンドラに(代理人でもよい)のカスタムコールバックを登録することができます。

イベントのコールバックでIOが発生した場合、アップロード進捗状況の変更イベントが頻繁に呼び出されます。その場合、ダウンロードの進行が遅くなります。たとえば更新頻度の低い更新が最もよい次のコードはeveyを10%アップさせるだけです。

private int _percentageDownloaded; 

    private void FileUploadProgressChanged(object sender, UploadProgressChangedEventArgs e) 
    { 
     if (e.ProgressPercentage % 10 == 0 && e.ProgressPercentage > _percentageDownloaded) 
     { 

      _percentageDownloaded = e.ProgressPercentage; 
      //Any callback instead of printline 
      Console.WriteLine("Send: {0} Received: {1}", e.BytesSent, e.BytesReceived); 
     } 
    } 
+0

ありがとうございます。しかし、それは上記の私の方法2)と比較して何の違いもないと私は思える。 – Lumen

+0

はい、それは私が最近した変更を見てください。コードの一番下。進捗状況の変更でコールバックを制限すると、パフォーマンスが向上します(コールバックにIO関連のものがある場合)。 –

+0

問題はまだ存在します:400MBのファイルをアップロードしようとすると、プログレスバーが10秒で0から50%に変わります(それほど速くアップロードできません)。その後、5分間フリーズしてからアップロードされた)進捗は、50秒から100秒に1秒未満になります。 – Lumen

1

私の提案は、新しいHTTPClientクラス(.NET 4.5で利用可能)を使用することです。進行状況をサポートします。 http://www.strathweb.com/2012/06/drag-and-drop-files-to-wpf-application-and-asynchronously-upload-to-asp-net-web-api/

アップロードファイルのための私のコード:

この記事は、これで私をたくさん助けた

private void HttpSendProgress(object sender, HttpProgressEventArgs e) 
    { 
     HttpRequestMessage request = sender as HttpRequestMessage; 
     Console.WriteLine(e.BytesTransferred); 
    } 

    private void Window_Loaded_1(object sender, RoutedEventArgs e) 
    { 
     ProgressMessageHandler progress = new ProgressMessageHandler(); 
     progress.HttpSendProgress += new EventHandler<HttpProgressEventArgs>(HttpSendProgress); 

     HttpRequestMessage message = new HttpRequestMessage(); 
     StreamContent streamContent = new StreamContent(new FileStream("e:\\somefile.zip", FileMode.Open)); 

     message.Method = HttpMethod.Put; 
     message.Content = streamContent; 
     message.RequestUri = new Uri("{Here your link}"); 

     var client = HttpClientFactory.Create(progress); 

     client.SendAsync(message).ContinueWith(task => 
     { 
      if (task.Result.IsSuccessStatusCode) 
      { 

      } 
     }); 
    } 
0

私は同じ問題を抱えていました。私は多くの時間を費やして、次のように問題を解決しました。 Antivirus AVAST私がそれをオフにすると、私のプログラムは完璧に動作します...

1

これは少なくとも1日私を悩ませています。私はWebClient.UploadFileAsyncを使って始めました、次にHttpClientのを試してから、HttpClientのために私自身のHttpContentを巻いてみました。これらのアプローチのどれも働かなかった(私のために)。

それはほとんどの一番下に座っている、HttpWebRequestを表示されます(すべて?).NETのHttp抽象WebClientHttpClientのように、私はILSpyでそれを見て、確認され、デフォルトでは、要求と応答ストリームを、バッファリング。

他の人が指摘しているように、リクエストストリームのバッファリングを効果的に無効にするチャンクエンコードを一方向に使用することもできますが、進捗報告を修正することはありません。

おそらくどこかNetworkStreamまたはOSで(私は私が正確に進捗を送る反映させるために送って、各ブロックの後に要求ストリームをフラッシュする必要があったことがわかった、または他のデータは、単純にパイプラインの下にさらに一歩バッファリングされます、チェックしなかった)。以下のサンプルコードは私のために働きます。また、HttpWebResponseからHttpResponseMessage(YMMVは必要ないかもしれません)への翻訳の際に最小限の仕事をします。

public async Task<HttpResponseMessage> UploadFileAsync(string uploadUrl, string absoluteFilePath, Action<int> progressPercentCallback) 
    { 
     var length = new FileInfo(absoluteFilePath).Length; 

     var request = new HttpWebRequest(new Uri(uploadUrl)) { 
      Method = "PUT", 
      AllowWriteStreamBuffering = false, 
      AllowReadStreamBuffering = false, 
      ContentLength = length 
     }; 

     const int chunkSize = 4096; 
     var buffer = new byte[chunkSize]; 

     using (var req = await request.GetRequestStreamAsync()) 
     using (var readStream = File.OpenRead(absoluteFilePath)) 
     { 
      progressPercentCallback(0); 
      int read = 0; 
      for (int i = 0; i < length; i += read) 
      { 
       read = await readStream.ReadAsync(buffer, 0, chunkSize); 
       await req.WriteAsync(buffer, 0, read); 
       await req.FlushAsync(); // flushing is required or else we jump to 100% very fast 
       progressPercentCallback((int)(100.0 * i/length)); 
      } 
      progressPercentCallback(100); 
     } 

     var response = (HttpWebResponse)await request.GetResponseAsync(); 
     var result = new HttpResponseMessage(response.StatusCode); 
     result.Content = new StreamContent(response.GetResponseStream()); 

     return result; 
    } 
関連する問題