附加到压缩流

时间:2010-10-28 18:25:24

标签: .net compression

我需要一个允许我创建压缩数据文件的解决方案(gzip,zip.tar等 - 任何格式都可以工作),然后自由地将数据附加到它们,而无需将整个文件加载到内存中并重新压缩它(在解压缩时寻找也会很棒)。有人对.NET有什么建议吗?

4 个答案:

答案 0 :(得分:2)

你基本上不能按照描述的方式做到这一点的原因是所有现代压缩算法都是基于压缩器在输入上移动时保持(添加,删除)的字典,并且当它生成时输出。

为了附加到压缩流(恢复压缩),您需要处于压缩暂停状态时的字典。压缩算法不会持久化字典,因为它会浪费空间 - 解压缩不需要它;它在解压缩阶段从压缩输入中再次构建。

我可能会将输出拆分为单独压缩的块。

答案 1 :(得分:1)

也许我有一些建议。

首先,为什么要寻求自己实施的程序化解决方案?

您可以简单地将大型日志文件拆分为块,即每小时甚至每分钟,并将它们每天收集在单独的字典中(以便在一个目录中将大量文件混杂在一起) ),所以你需要处理和搜索相当大的文件,你将拥有许多小文件,可以通过文件名快速访问简单规则。拥有大文件是一个坏主意(直到你有某种索引),因为你必须通过它来寻找合适的信息(例如通过操作日期时间),并且查找操作将会相当长。

当压缩发挥作用时情况变得更糟,因为你必须解压缩数据以寻求通过它或构建某种索引。没有必要自己动手,您可以在操作系统中启用文件夹压缩,无需任何编码即可透明地获得所有压缩优势。

所以,我建议不要重新发明轮子(除非你真的需要,见下文):

  
      
  • 定期拆分日志数据,例如每小时降低压缩性能
  •   
  • 启用OS文件夹压缩
  •   

多数情况下,您将减少存储空间。


自己滚动(如果你真的想要的话)。您可以这样做,将数据拆分为块,压缩每个并保存在您的存储类型中。为了实现类似的东西,我会考虑以下内容:

  • 使用原始(未压缩)数据保留一个文件,您将在其中记录新信息;
  • 保留并更新索引文件,例如使用每个块存储的日期范围,以按日期快速查找压缩数据中的文件位置;
  • 保存文件以进行压缩数据存储,其中的每个块都包含其大小并压缩(例如使用GZipStream)数据;

所以你要将信息写入未压缩的部分直到某些条件,然后压缩它并尾部添加到压缩部分,更新索引文件。将索引文件保持为独立允许快速更新而无需重写大量压缩部分。


另外,我建议你认为为什么你有如此大的日志文件。您可以优化存储格式。例如。如果您的日志是文本文件,您可以切换到二进制格式,例如从原始字符串构建字典并仅存储消息标识符而不是完整数据,即:

  

更新区域1;

     

更新区域2;

     

压缩数据;

存储为:

  

x1 1

     

x1 2

     

X2

上面的字符串只是示例,您可以根据需要通过重新映射数据在运行时“解压缩”它们。你可以通过切换到二进制来节省相当多的空间,也可以忘记压缩。

我没有现成的实现或算法。也许其他人可以提出更好的建议,但希望我的想法会有所帮助。

答案 2 :(得分:0)

你见过GZipStream课吗?您可以将其用作任何其他流。

答案 3 :(得分:0)

没有广泛的测试,没有错误检查,但是这个小的测试可以编译并运行。技巧是: a)deflator.FlushMode = FlushType.Full;  (在deflator.Write之前) b)在“使用”结束之前写出缓冲区,因为调用dispose似乎为数据添加了结束标记。当然会停止添加它! c)使用非常通用的Ionic.Zlib.CF所有托管代码。

string m_FileCompressed;

void PartialFileTest()    {
    //m_FileCompressed = Application.persistentDataPath + "/" + "PartialWrite.dat";
    PartialFileWritePart("the quick brown fox ");
    PartialFileWritePart("jumps over the lazy dog!");
    string str = PartialFileReadAll();
}

System.Text.Encoding u8 = Encoding.UTF8;
void PartialFileWritePart(string str) {
    using (var ms = new MemoryStream()) {
        using (var deflator = new DeflateStream(ms, CompressionMode.Compress, true)){
            byte[]  s1 = u8.GetBytes(str);
            deflator.FlushMode = FlushType.Full;
            deflator.Write(s1, 0, s1.Length);
            deflator.Flush();
            CreateIfNeededAndAppendAllBytes(m_FileCompressed, ms.ToArray());
        }
    }
}

string PartialFileReadAll() {
    byte[] buf = new byte[100];
    using (var ms3 = new MemoryStream()){
        byte [] Bothparts = File.ReadAllBytes(m_FileCompressed);
        ms3.Write( Bothparts, 0, Bothparts.Length);
        using (var inflator = new DeflateStream(ms3, CompressionMode.Decompress)){
            ms3.Position = 0;
            inflator.Read(buf, 0, Bothparts.Length);
        }
    }
    return u8.GetString(buf);
}

byte [] ReadAllBytesIfExists(string path ) {
    if(!File.Exists(path)) return new byte[0];
    return File.ReadAllBytes(path);
}

public static void CreateIfNeededAndAppendAllBytes(string path, byte[] bytes) {
    if(!File.Exists(path)) File.Create(path).Dispose();
    using (var stream = new FileStream(path, FileMode.Append)) {
        stream.Write(bytes, 0, bytes.Length);
    }
}