遭遇した現象

Azure Blob Storageにあるcsvの中身を画面に表示させるためにcsvダウンロード→MemoryStream→StreamReaderと処理して使おうとしたがMemoryStreamまでデータがあるのにいくらがんばってもStreamReaderが即刻EndOfStreamになってしまう。

結論

Stream.CopyToメソッドでストリームをコピーした際はストリームの位置がリセットされていないので開始位置に戻してあげないとStreamReaderが即読み込み終了してしまう。

環境

C#
.Net Core 3.1

だめだったコード

public IEnumerable<CloudCsvEntity> GetItems(string containerName, string blobName)
{
    var result = new BlockingCollection<CloudCsvEntity>();
    var tsk = Task.Run(async () =>
    {
        var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
        var blobClient = containerClient.GetBlobClient(blobName);
        BlobDownloadInfo download = await blobClient.DownloadAsync(); // ここでBlobを落としてくる
        using (var ms = new MemoryStream())
        {
            await download.Content.CopyToAsync(ms); // ここでMemoryStreamにデータは入る
            using (var sr = new StreamReader(ms, Encoding.GetEncoding("SHIFT_JIS"))) // ★この時点でEndOfStreamになってしまう
            using (var csv = new CsvReader(sr))
            {
                csv.Configuration.HasHeaderRecord = true;
                var records = csv.GetRecords<CloudCsvEntity>().ToList();
                foreach (var r in records) { result.Add(r); }
            }
        }
    });
    tsk.Wait();
    return result;
}

動作したコード

public IEnumerable<CloudCsvEntity> GetItems(string containerName, string blobName)
{
    var result = new BlockingCollection<CloudCsvEntity>();
    var tsk = Task.Run(async () =>
    {
        var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
        var blobClient = containerClient.GetBlobClient(blobName);
        BlobDownloadInfo download = await blobClient.DownloadAsync();
        using (var ms = new MemoryStream())
        {
            await download.Content.CopyToAsync(ms);
            ms.Seek(0, SeekOrigin.Begin); // ★CopyToAsync後にストリーム位置を開始位置に戻すコードを追加
            using (var sr = new StreamReader(ms, Encoding.GetEncoding("SHIFT_JIS"))) // ★無事読み込める
            using (var csv = new CsvReader(sr))
            {
                csv.Configuration.HasHeaderRecord = true;
                var records = csv.GetRecords<CloudCsvEntity>().ToList();
                foreach (var r in records) { result.Add(r); }
            }
        }
    });
    tsk.Wait();
    return result;
}

Stream.CopyToを使う際は気を付ける

いつものことながら公式ドキュメントには「注釈」として書いてあった。

> 注釈
> コピーは現在のストリームの現在位置から開始し、コピー操作の完了後にコピー先ストリームの位置はリセットされません。

無知って怖い。

Category
Tags
No Tag

No responses yet

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です