Stream转byte[]时别直接用ToArray()
很多开发者看到
MemoryStream有
ToArray()方法就直接调用,但这是个陷阱:它返回的是内部缓冲区的**完整底层数组**,而非当前已写入的有效数据。如果只写了100字节,但缓冲区已扩容到4096字节,
ToArray()会返回4096字节,末尾全是0。
正确做法是用
GetBuffer()配合
Length截取有效部分,或更稳妥地用
ToArray()——但仅限于确定来源是
MemoryStream且不关心内存浪费时;生产环境推荐: 对
MemoryStream:用
stream.ToArray()(安全,复制有效数据) 对任意
Stream(如
FileStream):先
stream.Position = 0,再用
new BinaryReader(stream).ReadBytes((int)stream.Length),或手动
Read循环 若流不支持
CanSeek(如网络响应流),必须边读边写入
List<byte></byte>再转数组
byte[]转Stream优先用MemoryStream构造函数
new MemoryStream(byteArray)是最轻量、零拷贝的方式——它直接包装原数组,读写会修改原数组内容。适合只读场景或你明确需要共享底层内存。
但要注意副作用:
MemoryStream默认可写,如果后续调用
Write或
SetLength,可能意外覆盖原数组或抛
NotSupportedException(取决于构造参数)。安全起见: 只读用途:用
new MemoryStream(byteArray, false)(第二个
false禁写) 需读写且允许修改原数组:用
new MemoryStream(byteArray, 0, byteArray.Length, true, true)完全隔离、避免副作用:用
new MemoryStream(byteArray, true)(深拷贝数组)
大文件别硬转byte[],改用Stream接力
把几百MB的
FileStream一次性读进
byte[],容易触发
OutOfMemoryException,尤其在32位或内存紧张环境。这不是转换效率问题,而是设计误用。
真正高效的做法是跳过数组中间层:
上传/下载:用sourceStream.CopyToAsync(destinationStream)(.NET 4.5+) 加密/压缩:用
CryptoStream或
GZipStream包装源流,直连目标流 需要分块处理:用固定大小
byte[8192]缓冲区循环
Read/
Write,避免全量加载
只有当业务逻辑**强制要求随机访问全部字节**(比如图像像素计算、协议头解析)时,才考虑转数组,且务必加长度校验和OOM防护。
异步转换注意Stream的生命周期
用
ReadAsync或
WriteAsync时,常见错误是提前
Dispose了流,导致
ObjectDisposedException。尤其在ASP.NET Core中,
HttpRequest.Body是短命流,不能跨
await保存引用。
可靠模式是:
读取前确认stream.CanRead且未关闭 异步读取后立即处理或复制到新
MemoryStream,别持有原始请求流 写入目标流时,确保目标流未被其他线程/作用域关闭(比如用
using包裹整个操作,而非只包声明)
最容易被忽略的是:某些
Stream子类(如
HttpContent.ReadAsStreamAsync()返回的流)在读完后自动关闭,再次读取会失败——必须一次读尽或缓存结果。
