GZipStream在写入FileStream时可以正常工作,但无法在MemoryStream中工作。

8
如果压缩一些JSON文本,并使用FileStream将其写入文件,我会得到预期的结果。然而,我不想写入磁盘。我只想要压缩数据的内存流。 FileStream 压缩:
string json = Resource1.json;

using (MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)))
using (FileStream output = File.Create(@"C:\Users\roarker\Desktop\output.json.gz"))
{
    using (GZipStream compression = new GZipStream(output, CompressionMode.Compress))
    {
        input.CopyTo(compression);
    }
}

上面的代码可以正常运行。下面的输出内存流长度为10,并导致一个空的 .gz 文件。
string json = Resource1.json;

using (MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)))
using (MemoryStream output = new MemoryStream())
{
    using (GZipStream compression = new GZipStream(output, CompressionMode.Compress))
    {
        input.CopyTo(compression);

        byte[] bytes = output.ToArray();
    }
}

编辑:output.ToArray()移至内部using子句之外似乎可行。但是,这会关闭大多数用途的输出流。即:

        using (MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)))
        using (MemoryStream output = new MemoryStream())
        {
            using (GZipStream compression = new GZipStream(output, CompressionMode.Compress))
            {
                input.CopyTo(compression);
            }
            WriteToFile(output);
        }

位置:

    public static void WriteToFile(Stream stream)
    {
        using (FileStream output = File.Create(@"C:\Users\roarker\Desktop\output.json.gz"))
        {
            stream.CopyTo(output);
        }
    }

在执行stream.CopyTo时会失败,因为流已关闭。我知道我可以从output.ToArray()的字节中创建一个新的Stream,但为什么需要这样做?为什么在流关闭时ToArray()仍然有效?

最终编辑:

只需使用带有leaveOpen参数的GZipStream构造函数即可。


请明确,最终 bytes.Length == 10 - Dave
1
如果将 byte[] bytes = output.ToArray(); 移到内部的 using 块之外,您会得到相同的行为吗? - GalacticCowboy
3
流(Streams)就像厕所一样,除非它们被冲洗掉,否则你还没处理完它们。除非你是某种恶心的动物。我的意思是,真的。 - user1228
1
相关链接:https://dev59.com/upLea4cB1Zd3GeqP4ZvU#34566825 - H H
1个回答

17

在关闭GZipStream之前调用ToArray(),这意味着它还没有机会刷新其缓冲区的最后一些位。这是压缩和加密流常见的问题,关闭流需要写入一些最终的数据片段。(例如,即使显式调用Flush()也无济于事。)

只需将ToArray调用移到合适的位置:

using (MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)))
using (MemoryStream output = new MemoryStream())
{
    using (GZipStream compression = new GZipStream(output, CompressionMode.Compress))
    {
        input.CopyTo(compression);
    }
    byte[] bytes = output.ToArray();
    // Use bytes
}

(请注意,当您调用ToArray时,流将被处理,但这没关系。)


我感到困惑,因为在内部 using 后,输出流被视为已关闭。我可以使用 ToArray() 方法获取所有字节,但无法将 output 传递给其他函数以像内存流一样处理。这有意义吗? - Dave
@Dave:是的 - 基本上你正在使用 MemoryStream 的额外功能(它将数据存储在内存中),但你不能像常规流一样使用它。能够这样做确实非常方便 - 特别是在这种情况下。 - Jon Skeet
我编辑了这个问题,但我猜你的意思是如果我想要访问那些内存中的数据,我需要将那些字节复制到一个新的流中? - Dave
@Dave:不,我完全没有这样说。你调用 ToArray(),就会得到一个包含内容的字节数组。随意访问它...除非你想要创建一个新的流来处理数据,否则没有必要这样做。如果你需要将其转换为流的形式,那么是的,你需要创建一个新的MemoryStream来包装它。在某些情况下,有一些方法可以使“包装”流不关闭底层流,但这并不普遍适用。 - Jon Skeet
2
啊,我可以使用leaveOpen构造函数参数来实现我想要的功能。 - Dave
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接