如何使用 GZipStream 解压缩内存中的 gz 文件?

Posted

技术标签:

【中文标题】如何使用 GZipStream 解压缩内存中的 gz 文件?【英文标题】:How do you unzip a gz file in memory using GZipStream? 【发布时间】:2017-08-06 14:56:49 【问题描述】:

我可能在这里做一些明显愚蠢的事情。请指出!

我有一些 C# 代码从 SFTP 拉下一堆 .gz 文件(使用 SSH.NET Nuget package - 效果很好!)。每个 gz 在其中仅包含一个 .CSV 文件。我想将这些文件保存在内存中而不碰到磁盘(是的,我知道,存在服务器内存管理问题 - 这很好,因为这些文件很小),在内存中解压缩它们以提取其中的 CSV 文件,然后返回一个集合自定义 DTO (FtpFile) 中的 CSV 文件。

我的问题是,虽然来自 SFTP 连接的 MemoryStream 中有数据,但它似乎从未填充到我的 GZipStream 中,或者从 GZipStream 复制到我的输出 MemoryStream 失败。我尝试过使用自己的缓冲区对 Read 进行更传统的循环,但结果与此代码相同。

除了连接细节(它连接成功,所以不用担心),这是我所有的代码:

逻辑

    public static List<FtpFile> Foo()
    
        var connectionInfo = new ConnectionInfo("example.com",
            "username",
            new PasswordAuthenticationMethod("username", "password"));
        using (var client = new SftpClient(connectionInfo))
        
            client.Connect();

            var searchResults = client.ListDirectory("/testdir")
                .Where(obj => obj.IsRegularFile
                              && obj.Name.ToLowerInvariant().StartsWith("test_")
                              && obj.Name.ToLowerInvariant().EndsWith(".gz"))
                .Take(2)
                .ToList();

            var fileResults = new List<FtpFile>();

            foreach (var file in searchResults)
            
                var ftpFile = new FtpFile  FileName = file.Name, FileSize = file.Length ;

                using (var fileStream = new MemoryStream())
                
                    client.DownloadFile(file.FullName, fileStream); // Success! All is good here, so far. :)

                    using (var gzStream = new GZipStream(fileStream, CompressionMode.Decompress))
                    
                        using (var outputStream = new MemoryStream())
                        
                            gzStream.CopyTo(outputStream);
                            byte[] outputBytes = outputStream.ToArray(); // No data. Sad panda. :'(
                            ftpFile.FileContents = Encoding.ASCII.GetString(outputBytes);
                            fileResults.Add(ftpFile);
                        
                    
                
            

            return fileResults;
        
    

FtpFile(只是我正在填充的一个简单 DTO):

public class FtpFile

    public string FileName  get; set; 
    public long FileSize  get; set; 
    public string FileContents  get; set; 


PSA 如果有人来复制这段代码,请注意这不是好的代码,因为您可能需要进行一些严重的内存管理这段代码有问题!最好将其流式传输到磁盘,这没有在此代码中完成!我的需求非常具体,因为我必须将这些文件同时保存在内存中,以便使用它们进行构建。

【问题讨论】:

Gzipped 值是否有效?如果您检查压缩后的字节 [],它的长度是否为 10 并且具有以下序列: 31,139,8,0,0,0,0,0,4,0 。这意味着它没有正确压缩。 @StfBln GZipped 值的长度为 1884,并以此序列开头:31,139,8,0,120,192,198,88。此外,这来自第三方生产系统,到目前为止,我们在初始测试中手动处理这些 gz 文件没有问题。 “client.DownloadFile(file.FullName, fileStream)”是否倒带流?否则文件流需要使用“Seek(0, SeekOrigin.Begin)”进行倒带 【参考方案1】:

如果您要将数据插入到流中,请确保在解压缩之前回溯到其来源。

以下应该可以解决您的问题:

            using (var fileStream = new MemoryStream())
            
                client.DownloadFile(file.FullName, fileStream); // Success! All is good here, so far. :)
                fileStream.Seek(0, SeekOrigin.Begin);

                using (var gzStream = new GZipStream(fileStream, CompressionMode.Decompress))
                
                    using (var outputStream = new MemoryStream())
                    
                        gzStream.CopyTo(outputStream);
                        byte[] outputBytes = outputStream.ToArray(); // No data. Sad panda. :'(
                        ftpFile.FileContents = Encoding.ASCII.GetString(outputBytes);
                        fileResults.Add(ftpFile);
                    
                
            

【讨论】:

就是这样!谢谢!!是和fileStream.Position = 0;一样还是两者不同? @Jaxidian fileStream.Position = 0;应该也可以。请参阅***.com/questions/7238929/… 了解更多信息。 再次感谢!! :-)

以上是关于如何使用 GZipStream 解压缩内存中的 gz 文件?的主要内容,如果未能解决你的问题,请参考以下文章

gzipstream 解压缩 UNIX 文件中止且没有错误

如何将 GZipStream 与 System.IO.MemoryStream 一起使用?

Java压缩流GZIPStream导致的内存泄露

Java压缩流GZIPStream导致的内存泄露

GZipStream - 块长度与其补码不匹配

在 .net 中以编程方式解压缩文件