我可以从 file.OpenStreamForWriteAsync() 得到 file.OpenStreamForReadAsync() 吗?

Can I get file.OpenStreamForReadAsync() from file.OpenStreamForWriteAsync()?

我仍在学习 read/write 文件流的技巧,如果我正在寻找的东西可行,希望有人能提供帮助。

下面的代码调用 WebApi(注意第 2 行的 GetAsync())以获取图像文件 ID 并使用计算的 Md5Hash 将下载的文件保存到数据库。代码工作正常,但为了提高效率,我想知道是否有可能从 file.OpenStreamForWriteAsync() 中获取 file.OpenStreamForReadAsync() (不确定是否可行,但我可以看到一些扩展方法在流,但到目前为止我所做的尝试没有运气)。如果可能的话,我可以避免保存文件并再次打开它,方法是在 using (var fileStream = await file.OpenStreamForWriteAsync()){ ... } 块中调用 GetMD5Hash() 方法。

我能否拥有 Utils.GetMD5Hash(stream); 的等价物,如下所示 using 块外,块内,目的是避免在 using 块外打开文件?

var client = new HttpClient();
var response = await client.GetAsync(new Uri($"{url}{imageId}")); // call to WebApi; url and imageId defined earlier

if (response.IsSuccessStatusCode)
{
    using (var contentStream = await response.Content.ReadAsInputStreamAsync())
    {
        var stream = contentStream.AsStreamForRead();
        var file = await imagesFolder.CreateFileAsync(imageFileName, CreationCollisionOption.ReplaceExisting);
        using (var fileStream = await file.OpenStreamForWriteAsync())
        {
            await stream.CopyToAsync(fileStream, 4096);

            // >>>> At this point, from the Write fileStream, can I get the equivalent of file.OpenStreamForReadAsync() ??

        }

        var stream = await file.OpenStreamForReadAsync();
        string md5Hash = await Utils.GetMD5Hash(stream);
        await AddImageToDataBase(file, md5Hash);
    }
}

A MemoryStream 是 read/write,所以如果您只想计算哈希值,那么像这样的事情应该可以解决问题:

var stream = contentStream.AsStreamForRead();
using (var ms = new MemoryStream())
{
    stream.CopyTo(ms);
    ms.Seek(0, SeekOrigin.Begin);
    string md5Hash = await Utils.GetMD5Hash(ms);
}

但是既然你想保存文件(毕竟它被传递给 AddImageToDataBase),考虑

  • 保存到MemoryStream
  • 重置内存流
  • 将内存流复制到文件流
  • 重置内存流
  • 计算哈希值

不过,我建议您进行性能测量。 OS 确实缓存文件 I/O,因此您实际上不太可能必须执行物理磁盘读取来计算哈希值。性能提升可能不是你想象的那样。

这是使用 MemoryStream 的完整答案,正如 Petter Hesselberg 在他的答案中所建议的那样。记下我不得不遇到的几个棘手的情况,做我想做的事情:(1)确保 fileStream 是一次性的 using 块,以及(2)确保 MemoryStream在使用前设置为ms.Seek(0, SeekOrigin.Begin);开头,无论是对于要保存的文件还是为了计算MD5Hash而交出的MemoryStream对象。

var client = new HttpClient();
var response = await client.GetAsync(new Uri($"{url}{imageId}")); // call to WebApi; url and imageId defined earlier

if (response.IsSuccessStatusCode)
{
    using (var contentStream = await response.Content.ReadAsInputStreamAsync())
    {
        var stream = contentStream.AsStreamForRead();
        var file = await imagesFolder.CreateFileAsync(imageFileName, CreationCollisionOption.ReplaceExisting);

        using (MemoryStream ms = new MemoryStream())
        {
            await stream.CopyToAsync(ms);
            using (var fileStream = await file.OpenStreamForWriteAsync())
            {
                ms.Seek(0, SeekOrigin.Begin);
                await ms.CopyToAsync(fileStream);
                ms.Seek(0, SeekOrigin.Begin); // rewind for next use below
            }

            string md5Hash = await Utils.GetMD5Hash(ms);
            await AddImageToDataBase(file, md5Hash);
        }
    }
}