如何将大文件(12gb)拆分为多个 1GB 压缩(.gz)档案? C#

Posted

技术标签:

【中文标题】如何将大文件(12gb)拆分为多个 1GB 压缩(.gz)档案? C#【英文标题】:How to split big file(12gb) into multiple 1GB compressed(.gz) archives? C# 【发布时间】:2019-07-18 19:15:30 【问题描述】:

我有一个很大的 .bak 文件 - 接近 12GB。 我需要在代码中将其拆分为多个 2gb .gz 档案。

最大的问题是我需要稍后验证这个档案。

你知道当你在 3 或 4 个档案上用 winrar 拆分一个文件,然后你只需按下“解包”,它会将它们全部解包到一个文件中,或者如果没有足够的档案(你删除一个)崩溃.

我需要这样的东西。

public void Compress(DirectoryInfo directorySelected)

    int writeStat = 0;

    foreach (FileInfo fileToCompress in directorySelected.GetFiles())
    
        using (FileStream originalFileStream = fileToCompress.OpenRead())
        
            if ((File.GetAttributes(fileToCompress.FullName) &
               FileAttributes.Hidden) != FileAttributes.Hidden & fileToCompress.Extension != ".gz")
            
                bytesToRead = new byte[originalFileStream.Length];
                int numBytesRead = bytesToRead.Length;

                while (_nowOffset < originalFileStream.Length)
                                                
                    writeStat = originalFileStream.Read(bytesToRead, 0, homMuchRead);

                    using (FileStream compressedFileStream = File.Create(fileToCompress.FullName + counter + ".gz"))
                    
                        using (GZipStream compressionStream = new GZipStream(compressedFileStream,
                           CompressionMode.Compress))
                        
                            compressionStream.Write(bytesToRead, 0, writeStat);
                        
                    
                    _nowOffset = _nowOffset + writeStat;                        
                    counter++;
                
                FileInfo info = new FileInfo(directoryPath + Path.DirectorySeparatorChar + fileToCompress.Name + ".gz");
                //Console.WriteLine($"Compressed fileToCompress.Name from fileToCompress.Length.ToString() to info.Length.ToString() bytes.");
            
        
    

效果很好,但我不知道如何验证它们的计数。

我有 7 个关于测试对象的存档。但是如何在一个文件中读取它们,并验证该文件是否已满。

【问题讨论】:

当我到达存档的末尾时它崩溃了 - 什么崩溃了?您是否收到任何类型的异常错误?错误的细节是什么?请在您的问题上单击edit 并添加这些详细信息以供我们帮助。 修复代码删除 - homMuchRead += 10000;偏移量加上计数大于目标数组的长度时崩溃。 我无法理解如何在压缩后验证这个档案,我有 7 个测试档案,但是如何验证它们的数量并阅读它们? 如何读回大块请check this link 为什么不使用 7-zip?它有一个可以从 C# 调用的命令行界面。 【参考方案1】:

GZip 格式本身并不支持你想要的。

Zip 可以,该功能称为“跨区存档”,但 .NET 中的 ZipArchive 类没有。为此,您需要一个第三方库,例如 DotNetZip。

但有解决方法。

创建一个继承自Stream抽象类的类,对外假装它是一个可以写入但不能读取或查找的单个流,在实现中写入多个片段,2GB/每个。在实现中使用 .NET 提供的 FileStream。在您班级的long 字段中跟踪写入的总长度。只要下一次Write() 调用超过 2GB,就写入足够的字节以达到 2GB,关闭并处理底层 FileStream,打开另一个具有下一个文件名的文件,将文件长度计数器重置为 0,然后从您在 Write() 调用中获得的缓冲区。重复直到关闭。

创建自定义流的实例,传递给 GZipStream 的构造函数,并将完整的 12GB 源数据复制到 GZipStream 中。

如果你做对了,在输出时你会得到大小正好 2GB 的文件(除了最后一个)。

要读取和解压缩它们,您需要使用自定义流实现类似的技巧。编写一个动态连接多个文件的流类,假装它是一个流,但这次你只需要实现Read() 方法。将该连接流从框架中提供给GZipStream。如果您要重新排序或销毁某些部件,GZipStream 很有可能(但不是 100%)解压失败,抱怨 CRC 校验和。

附:要实现和调试上述 2 个流,我建议使用更小的数据集,例如12 MB 数据,分成 1MB 压缩块。一旦你让它工作,增加常数并使用完整的 12GB 数据进行测试。

【讨论】:

非常感谢您的回答,它真的很有帮助。我处理这个任务,在循环和流中创建读写,dotnetzip - 可能不,因为程序必须是跨平台的,但我考虑了一下。关于解压 - 没有 gzipstream 像往常一样读取它们。 CRC - 稍后再考虑它们,我认为如果我们在文件开头写 CRC 会有所帮助。 @Rumata GZip 格式已经包含 CRC 校验和,会写在最后一段:en.wikipedia.org/wiki/Gzip#File_format 如果解压时不匹配,我认为 GZipStream 应该抛出一个异常来说明这一点。 @Rumata 另外,对于阅读流,您可以尝试以下实现:c-sharpcorner.com/article/… 但是速度很慢,每次阅读都使用线性搜索..

以上是关于如何将大文件(12gb)拆分为多个 1GB 压缩(.gz)档案? C#的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 GoogleDrive REST API 将大文件 (1 GB +) 上传到 Google Drive

如何传递大文件(GB级别)

在客户端将大文件(> 2GB)压缩成 ZIP

MySQL:将大表拆分为分区或单独的表?

将大文件 (2GB) 上传到 Autodesk Forge Data Management API

将大fasta拆分成多个文件,不能用GI号命名