C# 如何序列化对象+;压缩它,然后解压缩+;在没有第三方库的情况下反序列化?

C# 如何序列化对象+;压缩它,然后解压缩+;在没有第三方库的情况下反序列化?,c#,serialization,.net-3.5,gzipstream,C#,Serialization,.net 3.5,Gzipstream,我在内存中有一个大对象,我想把它作为blob保存到数据库中。 我想在保存之前压缩它,因为数据库服务器通常不是本地的 这就是我目前的情况: using (var memoryStream = new MemoryStream()) { using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress)) { BinaryFormatter binaryFormatter = new Binar

我在内存中有一个大对象,我想把它作为blob保存到数据库中。 我想在保存之前压缩它,因为数据库服务器通常不是本地的

这就是我目前的情况:

using (var memoryStream = new MemoryStream())
{
  using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress))
  {
    BinaryFormatter binaryFormatter = new BinaryFormatter();
    binaryFormatter.Serialize(gZipStream, obj);

    return memoryStream.ToArray();
  }
}
然而,当我使用Total Commander压缩相同的字节时,它总是将大小至少减少50%。使用上面的代码,它将58MB压缩到48MB,任何小于15MB的文件都会变得更大

我应该使用第三方zip库,还是在.NET3.5中有更好的方法。 我的问题还有别的解决办法吗

编辑:

刚刚在上面的代码中发现了一个bug。Angelo谢谢你的修复

GZipStream压缩仍然不是很好。 gZipStream的平均压缩率为35%,而TC的压缩率为48%

我不知道上一个版本的字节是什么:)

EDIT2:

我发现了如何将压缩率从20%提高到47%。 我不得不使用两个内存流而不是一个!有人能解释为什么会这样吗

这是一个有2个内存流的代码,它可以做更好的压缩

using (MemoryStream msCompressed = new MemoryStream())
using (GZipStream gZipStream = new GZipStream(msCompressed, CompressionMode.Compress))
using (MemoryStream msDecompressed = new MemoryStream())
{
  new BinaryFormatter().Serialize(msDecompressed, obj);
  byte[] byteArray = msDecompressed.ToArray();

  gZipStream.Write(byteArray, 0, byteArray.Length);
  gZipStream.Close();
  return msCompressed.ToArray();
}

使用的默认压缩级别是
最优的
,至少根据,因此没有办法告诉GZipStream“更努力”。。在我看来,第三方lib会更好

我个人从未认为GZipStream在压缩方面是“好的”——可能他们把精力放在了最小化内存占用或最大化速度上。但是,看看WindowsXP/WindowsVista/Windows7如何在资源管理器中以本机方式处理ZIP文件-很好。。我不能说它既不快,也没有很好的压缩性。。如果Win7中的资源管理器实际使用GZipStream,我不会感到惊讶——总的来说,他们已经实现了GZipStream并将其应用到了框架中,所以他们可能在很多地方使用它(例如,似乎在中使用),所以我会远离它,因为我需要高效的处理。。我从来没有在这个主题上做过任何认真的研究,因为我的公司在很多年前购买了一个不错的拉链处理程序,当时.Net还处于早期阶段

编辑:

更多参考资料:
-但在2009年标记为“已关闭/已解决”。。也许你会在代码中发现一些有趣的东西

嘿,在谷歌搜索了几分钟后,7Zip似乎暴露了一些C#绑定:

编辑#2:


仅供参考.net4.5:

来自.NET3.5的GZipStream不允许您设置压缩级别。这个参数是在.NET4.5中引入的,但我不知道它是否会给您带来更好的结果,或者升级是否适合您。 由于AFAIK的专利,内置算法不是很理想。
因此,在3.5中,获得更好压缩的唯一方法是使用第三方库,如or提供的SDK。也许你应该尝试一下不同的lib,以更好地压缩你的数据。

你的代码中有一个bug,解释太长,无法发表评论,所以我将它作为一个答案,即使它没有回答你真正的问题

您需要在关闭
gzip流之后调用
memoryStream.ToArray()
,否则您将创建无法反序列化的压缩数据

固定代码如下:

using (var memoryStream = new System.IO.MemoryStream())
{
  using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress))
  {
    BinaryFormatter binaryFormatter = new BinaryFormatter();
    binaryFormatter.Serialize(gZipStream, obj);
  }
  return memoryStream.ToArray();
}
GZipStream
以块的形式写入底层缓冲区,并在流的末尾追加一个页脚,这仅在关闭流时执行

您可以通过运行以下代码示例轻松证明这一点:

byte[] compressed;
int[] integers = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

var mem1 = new MemoryStream();
using (var compressor = new GZipStream(mem1, CompressionMode.Compress))
{
    new BinaryFormatter().Serialize(compressor, integers);
    compressed = mem1.ToArray();
}

var mem2 = new MemoryStream(compressed);
using (var decompressor = new GZipStream(mem2, CompressionMode.Decompress))
{
    // The next line will throw SerializationException
    integers = (int[])new BinaryFormatter().Deserialize(decompressor);
}

最初的问题与.NET3.5有关。 三年后,.NET4.5更有可能被使用,我的答案只对4.5有效。正如前面提到的,压缩算法在.NET4.5中得到了很好的改进

今天,我想压缩我的数据集以节省一些空间。除了.NET4.5之外,与原始问题非常相似。 因为我记得很多年前在double MemoryStream中使用过相同的技巧,所以我只是尝试一下。 我的数据集是一个容器对象,包含许多哈希集和具有string/int/DateTime属性的自定义对象列表。该数据集包含大约45000个对象,在不进行压缩的情况下进行序列化时,会创建一个3500 kB的二进制文件

现在,使用GZipStream,使用问题中描述的单或双内存流,或者使用deflatesttream(在4.5中使用zlib),我总是得到一个818kb的文件。 因此,我只想在这里强调,双内存流的技巧在.NET4.5中毫无用处

最终,我的通用代码如下所示:

     public static byte[] SerializeAndCompress<T, TStream>(T objectToWrite, Func<TStream> createStream, Func<TStream, byte[]> returnMethod, Action catchAction)
        where T : class
        where TStream : Stream
     {
        if (objectToWrite == null || createStream == null)
        {
            return null;
        }
        byte[] result = null;
        try
        {
            using (var outputStream = createStream())
            {
                using (var compressionStream = new GZipStream(outputStream, CompressionMode.Compress))
                {
                    var formatter = new BinaryFormatter();
                    formatter.Serialize(compressionStream, objectToWrite);
                }
                if (returnMethod != null)
                    result = returnMethod(outputStream);
            }
        }
        catch (Exception ex)
        {
            Trace.TraceError(Exceptions.ExceptionFormat.Serialize(ex));
            catchAction?.Invoke();
        }
        return result;
    }
public static byte[]SerializeAndCompress(T objectToWrite、Func createStream、Func returnMethod、Action catchAction)
T:在哪里上课
其中TStream:Stream
{
if(objectToWrite==null | | createStream==null)
{
返回null;
}
字节[]结果=空;
尝试
{
使用(var outputStream=createStream())
{
使用(var compressionStream=new GZipStream(outputStream,CompressionMode.Compress))
{
var formatter=新的二进制格式化程序();
序列化(压缩流,objectToWrite);
}
if(returnMethod!=null)
结果=返回方法(outputStream);
}
}
捕获(例外情况除外)
{
Trace.TraceError(Exceptions.ExceptionFormat.Serialize(ex));
catchAction?.Invoke();
}
返回结果;
}
这样我就可以使用不同的TStream,例如

    public static void SerializeAndCompress<T>(T objectToWrite, string filePath) where T : class
    {
        //var buffer = SerializeAndCompress(collection);
        //File.WriteAllBytes(filePath, buffer);
        SerializeAndCompress(objectToWrite, () => new FileStream(filePath, FileMode.Create), null, () =>
        {
            if (File.Exists(filePath))
                File.Delete(filePath);
        });
    }

    public static byte[] SerializeAndCompress<T>(T collection) where T : class
    {
        return SerializeAndCompress(collection, () => new MemoryStream(), st => st.ToArray(), null);
    }
publicstaticvoidserializeandcompress(T objectToWrite,stringfilepath),其中T:class
{
//var buffer=SerializeAndCompress(集合);
//writealBytes(文件路径、缓冲区);
序列化和压缩(objectToWrite,()=>ne
public class GZipFormatter : IFormatter
{
    IFormatter formatter;
    public GZipFormatter()
    {
        this.formatter = new BinaryFormatter();
    }
    public GZipFormatter(IFormatter formatter)
    {
        this.formatter = formatter; 
    }
    ISurrogateSelector IFormatter.SurrogateSelector { get => formatter.SurrogateSelector; set => formatter.SurrogateSelector = value; }
    SerializationBinder IFormatter.Binder { get => formatter.Binder; set => formatter.Binder = value; }
    StreamingContext IFormatter.Context { get => formatter.Context; set => formatter.Context = value; }

    object IFormatter.Deserialize(Stream serializationStream)
    {
        using (GZipStream gZipStream = new GZipStream(serializationStream, CompressionMode.Decompress))
        {
            return formatter.Deserialize(gZipStream);                
        }
    }
    void IFormatter.Serialize(Stream serializationStream, object graph)
    {
        using (GZipStream gZipStream = new GZipStream(serializationStream, CompressionMode.Compress))
        using (MemoryStream msDecompressed = new MemoryStream())
        {
            formatter.Serialize(msDecompressed, graph);
            byte[] byteArray = msDecompressed.ToArray();

            gZipStream.Write(byteArray, 0, byteArray.Length);
            gZipStream.Close();                
        }
    }
IFormatter formatter = new GZipFormatter();
using (Stream stream = new FileStream(path...)){
   formatter.Serialize(stream, obj); 
}