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

时间:2017-03-15 17:45:13

标签: c# compression gzip gz gzipstream

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

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

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

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

逻辑

    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 如果有人来复制此代码,请注意这不是一个好代码,因为你可能会有一些严重此代码的内存管理问题!最佳做法是将其流式传输到磁盘,在此代码中完成!我的需求非常具体,因为我必须将这些文件同时存储在内存中,以便我与他们一起构建。

1 个答案:

答案 0 :(得分: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);
                    }
                }
            }