这比我最初想象的要棘手一些。我正在尝试从流中读取n个字节。
MSDN声明Read不必返回n个字节,它必须返回至少1到最多n个字节,其中0个字节是到达流结束的特殊情况。
一般来说,我用的是
var buf = new byte[size];
var count = stream.Read (buf, 0, size);
if (count != size) {
buf = buf.Take (count).ToArray ();
}
yield return buf;
我希望确切的size
字节,但通过规范FileStream将被允许返回大量的1字节块为好。这必须避免。
解决这个问题的一种方法是有两个缓冲区,一个用于读取,一个用于收集块,直到我们得到请求的字节数。不过这有点麻烦。
我也看了一下BinaryReader,但它的规范也没有明确说明n字节将肯定返回。
澄清一下:当然,在流结束时返回的字节数可能小于size
-这不是问题。我只是在说没有接收到n个字节,即使它们在流中是可用的。
更易于阅读的版本:
int offset = 0;
while (offset < count)
{
int read = stream.Read(buffer, offset, count - offset);
if (read == 0)
throw new System.IO.EndOfStreamException();
offset += read;
}
或者写成Stream
类的扩展方法:
public static class StreamUtils
{
public static byte[] ReadExactly(this System.IO.Stream stream, int count)
{
byte[] buffer = new byte[count];
int offset = 0;
while (offset < count)
{
int read = stream.Read(buffer, offset, count - offset);
if (read == 0)
throw new System.IO.EndOfStreamException();
offset += read;
}
System.Diagnostics.Debug.Assert(offset == count);
return buffer;
}
}
简简单单;你循环;
int read, offset = 0;
while(leftToRead > 0 && (read = stream.Read(buf, offset, leftToRead)) > 0) {
leftToRead -= read;
offset += read;
}
if(leftToRead > 0) throw new EndOfStreamException(); // not enough!
在此之后,buf
应该已经从流中填充了正确数量的数据,或者将抛出EOF。
从这里的答案中收集了所有内容,我提出了以下解决方案。它依赖于源流的长度。工作在。net core 3.1
/// <summary>
/// Copy stream based on source stream length
/// </summary>
/// <param name="source"></param>
/// <param name="destination"></param>
/// <param name="bufferSize">
/// A value that is the largest multiple of 4096 and is still smaller than the LOH threshold (85K).
/// So the buffer is likely to be collected at Gen0, and it offers a significant improvement in Copy performance.
/// </param>
/// <returns></returns>
private async Task CopyStream(Stream source, Stream destination, int bufferSize = 81920)
{
var buffer = new byte[bufferSize];
var offset = 0;
while (offset < source.Length)
{
var leftToRead = source.Length - offset;
var lengthToRead = leftToRead - buffer.Length < 0 ? (int)(leftToRead) : buffer.Length;
var read = await source.ReadAsync(buffer, 0, lengthToRead).ConfigureAwait(false);
if (read == 0)
break;
await destination.WriteAsync(buffer, 0, lengthToRead).ConfigureAwait(false);
offset += read;
}
destination.Seek(0, SeekOrigin.Begin);
}