kazuakix の日記

Windows Phone とか好きです

StreamReader が最後にストリームを閉じていた件

とある処理でデータの取得と保存をまとめてみたところ例外を吐いて落ちてしました。

すごく端折ったコードはこんな感じ

public async Task GetData()
{
    try
    {
        using (var req = new HttpClient())
        using (var res = await req.GetAsync("http://~"))
        {
            if (res.IsSuccessStatusCode)
            {
                var stream = await res.Content.ReadAsStreamAsync();

                // 読み込み
                using (var reader = new StreamReader(stream))
                {
                    while (!reader.EndOfStream)
                    {
                        var row = reader.ReadLine();
                        // いろいろ
                    }
                }

                // 先頭に戻る
                stream.Seek(0, SeekOrigin.Begin);

                // ローカルに保存
                var file = await ApplicationData.Current.LocalFolder.CreateFileAsync(
                    "file.txt", CreationCollisionOption.ReplaceExisting);
                using (var local = await file.OpenStreamForWriteAsync())
                {
                    await stream.CopyToAsync(local);
                }
            }
        }
    }
    catch (Exception ex)
    {
        Debug.Assert(false, ex.Message);
    }
}

 
このコードだと ローカルに保存しようとしたところで ObjectDisposedException が出ます。内容には "Cannot access a closed Stream." とありました。

f:id:kazuakix:20140820012145j:plain
 
調べてみると StreamReader が最後までデータを読んだときにストリームを閉じているようです。

StreamReader のコンストラクタでストリームを開いたままにするオプションを指定したりもできるようなのですが、バイト順序マークの検索有無やバッファサイズまで指定する必要があっててイマイチ使い勝手が悪いですね。

仕方がないので先にローカルに保存してから StreamReader で読み込むことにしました。

public async Task GetData()
{
    try
    {
        using (var req = new HttpClient())
        using (var res = await req.GetAsync("http://~"))
        {
            if (res.IsSuccessStatusCode)
            {
                var stream = await res.Content.ReadAsStreamAsync();

                // ローカルに保存
                var file = await ApplicationData.Current.LocalFolder.CreateFileAsync(
                    "file.txt", CreationCollisionOption.ReplaceExisting);
                using (var local = await file.OpenStreamForWriteAsync())
                {
                    await stream.CopyToAsync(local);
                }

                // 先頭に戻る
                stream.Seek(0, SeekOrigin.Begin);

                // 読み込み
                using (var reader = new StreamReader(stream))
                {
                    while (!reader.EndOfStream)
                    {
                        var row = reader.ReadLine();
                        // いろいろ
                    }
                }

            }
        }
    }
    catch (Exception ex)
    {
        Debug.Assert(false, ex.Message);
    }
}

でも、StreamReader のコンストラクタをちゃんと指定するのが本来なんでしょうね。