代码之家  ›  专栏  ›  技术社区  ›  Adrian Zanescu

附加到压缩流

  •  4
  • Adrian Zanescu  · 技术社区  · 14 年前

    我需要一个允许我创建压缩数据文件(gzip,zip)的解决方案。tar等——任何格式都可以工作),然后在不需要将整个文件加载到内存中并重新压缩的情况下,自由地将数据附加到它们上(在解压缩的同时进行查找也是非常棒的)。有人对.NET有建议吗?

    4 回复  |  直到 6 年前
        1
  •  2
  •   500 - Internal Server Error    14 年前

    你基本上不能这样做的原因是,所有现代的压缩算法都是基于字典的,当压缩机在输入上移动时,字典会被维护(添加、删除),当它生成输出时,字典也会被维护(添加、删除)。

    为了附加到压缩流(恢复压缩),需要字典处于压缩暂停时的状态。压缩算法不会持久保存字典,因为这会浪费空间——解压不需要它;解压阶段,它会从压缩输入重新构建。

    我可能会将输出分割成单独压缩的块。

        2
  •  1
  •   Nick Martyshchenko    14 年前

    也许我有一些建议给你。

    首先,为什么要寻求自己实现的编程解决方案?

    您可以简单地将大型日志文件分割成块,即以每小时甚至每分钟为单位,并以每天为单位将其收集到单独的字典中(为了不使一个目录中有大量文件的fs杂乱无章),因此,您必须处理和查找大量大型文件,而不是有许多小文件,可以通过结合简单规则的文件名快速访问。拥有大文件是一个坏主意(直到你有了某种索引),因为你必须通过它来查找合适的信息(例如,按操作日期时间),而查找操作将要长得多。

    当压缩开始起作用时,情况会变得更糟,因为您需要解压缩数据来搜索它或建立某种索引。不需要自己做,您可以在操作系统中启用文件夹压缩,并且在不进行任何编码的情况下透明地获得所有压缩好处。

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

    • 定期拆分日志数据,例如每小时,以减少压缩性能的影响
    • 启用OS文件夹压缩

    就这些,你会减少你的存储空间。


    自己滚(如果你真的想要的话)。您也可以这样做,将数据分割成块,压缩每个块,并保存在您的存储类型中。要实现类似的功能,我会考虑以下几点:

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

    所以,您将把信息写到未压缩的部分,直到出现某种情况,然后压缩它,最后添加到压缩的部分,更新索引文件。将索引文件保持为单独的可以在不重写巨大的压缩部分的情况下快速更新。


    我也建议你想想 为什么 你有这么大的日志文件。也许你可以优化你的存储格式。例如,如果日志是文本文件,则可以切换为二进制格式,例如,从原始字符串生成字典,只存储消息标识符而不是完整数据,即:

    更新区域1;

    更新区域2;

    压缩数据;

    存储为:

    X1 1

    X1 2

    X2

    上面的字符串只是示例,您可以根据需要在运行时“解压缩”它们,然后重新映射数据。您可以通过切换到二进制文件来节省大量空间,并且可能足以忘记压缩。

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

        3
  •  0
  •   Andrew Bezzub    14 年前

    你看到了吗 GZipStream 上课?您可以将其用作任何其他流。

        4
  •  0
  •   PJJ    6 年前

    没有广泛的测试,没有错误检查,但这个小测试编译并工作。技巧是: a)deflator.flushmode=flushtype.full; (在平减指数前写) b)在“using”结束前写出缓冲区作为释放调用,似乎会向数据添加结束标记。当然,这会阻止它被添加到! 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);
        }
    }