16

パブリック URL から大きなファイルをダウンロードしようとしています。最初は正常に動作しているように見えましたが、1/10 のコンピューターがタイムアウトしているようです。私の最初の試みは使用するWebClient.DownloadFileAsyncことでしたが、それが完了することはなかったためWebRequest.Create、応答ストリームを使用して直接読み取ることに戻りました。

私の最初のバージョンの使用WebRequest.Createで、 と同じ問題が見つかりましたWebClient.DownloadFileAsync。操作がタイムアウトし、ファイルが完了しません。

次のバージョンでは、ダウンロードがタイムアウトした場合の再試行が追加されました。これが奇妙になる場合です。ダウンロードは最終的に、最後の 7092 バイトを完了するために 1 回の再試行で終了します。したがって、ファイルはまったく同じサイズでダウンロードされますが、ファイルは破損しており、ソース ファイルとは異なります。破損は最後の 7092 バイトにあると予想されますが、そうではありません。

BeyondCompare を使用すると、破損したファイルから欠落しているバイトのチャンクが 2 つあり、合計で 7092 バイトが欠落していることがわかりました! この不足しているバイトは、ダウンロードがタイムアウトして再開されるずっと前の と にあります1CA49FF01E31F380

ここで何が起こっている可能性がありますか?この問題をさらに追跡する方法に関するヒントはありますか?

これが問題のコードです。

public void DownloadFile(string sourceUri, string destinationPath)
{
    //roughly based on: http://stackoverflow.com/questions/2269607/how-to-programmatically-download-a-large-file-in-c-sharp
    //not using WebClient.DownloadFileAsync as it seems to stall out on large files rarely for unknown reasons.

    using (var fileStream = File.Open(destinationPath, FileMode.Create, FileAccess.Write, FileShare.Read))
    {
        long totalBytesToReceive = 0;
        long totalBytesReceived = 0;
        int attemptCount = 0;
        bool isFinished = false;

        while (!isFinished)
        {
            attemptCount += 1;

            if (attemptCount > 10)
            {
                throw new InvalidOperationException("Too many attempts to download. Aborting.");
            }

            try
            {
                var request = (HttpWebRequest)WebRequest.Create(sourceUri);

                request.Proxy = null;//http://stackoverflow.com/questions/754333/why-is-this-webrequest-code-slow/935728#935728
                _log.AddInformation("Request #{0}.", attemptCount);

                //continue downloading from last attempt.
                if (totalBytesReceived != 0)
                {
                    _log.AddInformation("Request resuming with range: {0} , {1}", totalBytesReceived, totalBytesToReceive);
                    request.AddRange(totalBytesReceived, totalBytesToReceive);
                }

                using (var response = request.GetResponse())
                {
                    _log.AddInformation("Received response. ContentLength={0} , ContentType={1}", response.ContentLength, response.ContentType);

                    if (totalBytesToReceive == 0)
                    {
                        totalBytesToReceive = response.ContentLength;
                    }

                    using (var responseStream = response.GetResponseStream())
                    {
                        _log.AddInformation("Beginning read of response stream.");
                        var buffer = new byte[4096];
                        int bytesRead = responseStream.Read(buffer, 0, buffer.Length);
                        while (bytesRead > 0)
                        {
                            fileStream.Write(buffer, 0, bytesRead);
                            totalBytesReceived += bytesRead;
                            bytesRead = responseStream.Read(buffer, 0, buffer.Length);
                        }

                        _log.AddInformation("Finished read of response stream.");
                    }
                }

                _log.AddInformation("Finished downloading file.");
                isFinished = true;
            }
            catch (Exception ex)
            {
                _log.AddInformation("Response raised exception ({0}). {1}", ex.GetType(), ex.Message);
            }
        }
    }
}

破損したダウンロードからのログ出力は次のとおりです。

Request #1.
Received response. ContentLength=939302925 , ContentType=application/zip
Beginning read of response stream.
Response raised exception (System.Net.WebException). The operation has timed out.
Request #2.
Request resuming with range: 939295833 , 939302925
Received response. ContentLength=7092 , ContentType=application/zip
Beginning read of response stream.
Finished read of response stream.
Finished downloading file.
4

4 に答える 4

1

これは私が通常使用する方法です。これまでのところ、必要な同じ種類の読み込みで失敗したことはありません。私のコードを使用してあなたのコードを少し変更してみて、それが役立つかどうかを確認してください。

if (!Directory.Exists(localFolder))
{
    Directory.CreateDirectory(localFolder);   
}


try
{
    HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(Path.Combine(uri, filename));
    httpRequest.Method = "GET";

    // if the URI doesn't exist, exception gets thrown here...
    using (HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse())
    {
        using (Stream responseStream = httpResponse.GetResponseStream())
        {
            using (FileStream localFileStream = 
                new FileStream(Path.Combine(localFolder, filename), FileMode.Create))
            {
                var buffer = new byte[4096];
                long totalBytesRead = 0;
                int bytesRead;

                while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    totalBytesRead += bytesRead;
                    localFileStream.Write(buffer, 0, bytesRead);
                }
            }
        }
    }
}
catch (Exception ex)
{        
    throw;
}
于 2016-05-24T12:00:31.030 に答える