csvファイルをPOSTリクエストに返すサービスがあります。非同期の手法を使用して、上記のファイルをダウンロードしたいと思います。ファイルを取得することはできますが、コードにはいくつかの未解決の問題と質問があります。
1)これは本当に非同期ですか?
2)チャンク形式で送信されている場合でも、コンテンツの長さを知る方法はありますか?プログレスバーを考えてください)。
3)すべての作業が完了するまでプログラムの終了を延期するために、どうすれば進行状況を最適に監視できますか。
using System;
using System.IO;
using System.Net.Http;
namespace TestHttpClient2
{
class Program
{
/*
* Use Yahoo portal to access quotes for stocks - perform asynchronous operations.
*/
static string baseUrl = "http://real-chart.finance.yahoo.com/";
static string requestUrlFormat = "/table.csv?s={0}&d=0&e=9&f=2015&g=d&a=4&b=5&c=2000&ignore=.csv";
static void Main(string[] args)
{
while (true)
{
Console.Write("Enter a symbol to research or [ENTER] to exit: ");
string symbol = Console.ReadLine();
if (string.IsNullOrEmpty(symbol))
break;
DownloadDataForStockAsync(symbol);
}
}
static async void DownloadDataForStockAsync(string symbol)
{
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(baseUrl);
client.Timeout = TimeSpan.FromMinutes(5);
string requestUrl = string.Format(requestUrlFormat, symbol);
//var content = new KeyValuePair<string, string>[] {
// };
//var formUrlEncodedContent = new FormUrlEncodedContent(content);
var request = new HttpRequestMessage(HttpMethod.Post, requestUrl);
var sendTask = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
var response = sendTask.Result.EnsureSuccessStatusCode();
var httpStream = await response.Content.ReadAsStreamAsync();
string OutputDirectory = "StockQuotes";
if (!Directory.Exists(OutputDirectory))
{
Directory.CreateDirectory(OutputDirectory);
}
DateTime currentDateTime = DateTime.Now;
var filePath = Path.Combine(OutputDirectory, string.Format("{1:D4}_{2:D2}_{3:D2}_{4:D2}_{5:D2}_{6:D2}_{7:D3}_{0}.csv",
symbol,
currentDateTime.Year, currentDateTime.Month, currentDateTime.Day,
currentDateTime.Hour, currentDateTime.Minute, currentDateTime.Second, currentDateTime.Millisecond
));
using (var fileStream = File.Create(filePath))
using (var reader = new StreamReader(httpStream))
{
httpStream.CopyTo(fileStream);
fileStream.Flush();
}
}
}
catch (Exception ex)
{
Console.WriteLine("Error, try again!");
}
}
}
}
- 「これは本当に非同期ですか?」
はい、ほとんどです。このDownloadDataForStockAsync()
メソッドは、操作が完了する前にawait response.Content.ReadAsStreamAsync()
ステートメントで返されます。
主な例外は、メソッドの終わり近くで、を呼び出しますStream.CopyTo()
。これは非同期ではなく、操作が長くなる可能性があるため、顕著な遅延が発生する可能性があります。ただし、コンソールプログラムでは、メソッドの継続は元の呼び出しスレッドではなくスレッドプールで実行されるため、気付かないでしょう。
このコードをWinformsやWPFなどのGUIフレームワークに移動する場合は、ステートメントを次のように変更する必要があります。 await httpStream.CopyToAsync(fileStream);
- チャンク形式で送信されている場合でも、コンテンツの長さを知る方法はありますか?プログレスバーを考えてください)。
サーバーがContent-Length
ヘッダーにを含むと仮定すると(そしてそうすべきです)、はい。これは可能であるはずです。
を使用している場合HttpWebRequest
、応答オブジェクトにはContentLength
この値を直接与えるプロパティがあることに注意してください。HttpRequestMessage
代わりにここを使用していますが、私はあまり詳しくありません。しかし、私が知る限り、次のContent-Length
ような値にアクセスできるはずです。
long? contentLength = response.Content.Headers.ContentLength;
if (contentLength != null)
{
// use value to initialize "determinate" progress indication
}
else
{
// no content-length provided; will need to display progress as "indeterminate"
}
- すべての作業が完了するまでプログラムの終了を延期するために、どうすれば進行状況を最もよく監視できますか。
方法はたくさんあります。私は、任意の合理的な方法は、あなたが変更することを必要とすることを指摘しますDownloadDataForStockAsync()
、それは返すようにメソッドをTask
していませんvoid
。そうしないと、作成されたタスクにアクセスできません。とにかくこれを行う必要があるので、それは大したことではありません。:)
最も簡単なのは、開始するすべてのタスクのリストを保持し、それらを待ってから終了することです。
static void Main(string[] args)
{
List<Task> tasks = new List<Task>();
while (true)
{
Console.Write("Enter a symbol to research or [ENTER] to exit: ");
string symbol = Console.ReadLine();
if (string.IsNullOrEmpty(symbol))
break;
tasks.Add(DownloadDataForStockAsync(symbol));
}
Task.WaitAll(tasks);
}
もちろん、これには、Task
すでに完了しているオブジェクトを含め、各オブジェクトのリストを明示的に維持する必要があります。これを長時間実行して非常に多くのシンボルを処理する場合は、法外なことになる可能性があります。その場合、CountDownEvent
オブジェクトを使用することをお勧めします。
static void Main(string[] args)
{
CountDownEvent countDown = new CountDownEvent();
while (true)
{
Console.Write("Enter a symbol to research or [ENTER] to exit: ");
string symbol = Console.ReadLine();
if (string.IsNullOrEmpty(symbol))
break;
countDown.AddCount();
DownloadDataForStockAsync(symbol).ContinueWith(task => countdown.Signal()) ;
}
countDown.Wait();
}
これは、CountDownEvent
作成するタスクごとにカウンターをインクリメントし、各タスクに継続をアタッチしてカウンターをデクリメントするだけです。カウンタがゼロに達すると、イベントが設定され、呼び出しWait()
を返すことができます。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加