MemoryStream CopyTo仅部分写入

本文关键字:仅部 CopyTo MemoryStream | 更新日期: 2023-09-27 18:09:28

我要疯了。在过去的一个小时里,我一直试图让GzipStream压缩字符串,但无论出于何种原因,它拒绝将整个字节数组写入内存流。起初,我认为这与using语句有关,但即使在删除它们之后,似乎也没有什么不同。

初始配置:

var str = "Here is a relatively simple string to compress";
byte[] compressedBytes;
string returnedData;
var bytes = Encoding.UTF8.GetBytes(str);

工作正常(写入64字节数组):

using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream()) {
   using (var gs = new GZipStream(mso, CompressionMode.Compress)) {
       msi.CopyTo(gs);
   }
   compressedBytes = mso.ToArray();
}

失败(写入10字节数组):

using(var mso = new MemoryStream())
using(var msi = new MemoryStream(bytes))
using(var zip = new GZipStream(mso, CompressionMode.Compress))
{
    msi.CopyTo(zip);
    compressedBytes = mso.ToArray();
}

也失败(写入10长度的字节数组):

var mso = new MemoryStream();
var msi = new MemoryStream(bytes);
var zip = new GZipStream(mso, CompressionMode.Compress);
msi.CopyTo(zip);
compressedBytes = mso.ToArray();

谁能解释一下为什么第一个可以,而在其他两个中,我得到的是这些不完整的数组?是不是有什么东西从我眼皮底下被处理掉了?就此而言,是否有一种方法可以避免使用两个内存流?

谢谢,Zoombini

MemoryStream CopyTo仅部分写入

System.IO.Compression.GZipStream必须在您可以使用底层流之前被关闭(处置),因为

  1. 它面向块工作
  2. 它必须写页脚,包括校验和(参见维基百科上的文件格式描述)

您试图在GZipStream关闭之前获得压缩数据。正如您所看到的,这不会返回完整的数据。第一个工作的原因是因为您在 GZipStream被处置后调用compressedBytes = mso.ToArray(); 。因此,虽然没有经过测试,但理论上,您应该能够像这样稍微修改第二段代码以使其工作。

using(var mso = new MemoryStream())
{
   using(var msi = new MemoryStream(bytes))
   using(var zip = new GZipStream(mso, CompressionMode.Compress))
   {
       msi.CopyTo(zip);
   }
   compressedBytes = mso.ToArray();
}

正如其他人所说,您需要关闭GZipStream才能获得完整的数据。using语句将导致在块末尾的流上调用Dispose方法,如果流尚未关闭,则将关闭流。如果您将zip.Close();放在msi.CopyTo(zip);之后,上述所有示例都将按预期工作。

如果你这样写,你可以消除一个MemoryStreams:

using (MemoryStream mso = new MemoryStream())
{
    using (GZipStream zip = new GZipStream(mso, CompressionMode.Compress))
    {
        zip.Write(bytes, 0, bytes.Length);
    }
    compressedBytes = mso.ToArray();
}