【问题标题】:Computing progress (bar) using GZipStream使用 GZipStream 计算进度(条形图)
【发布时间】:2009-01-05 10:53:34
【问题描述】:

我正在从一些慢速源(如 FTP 服务器)读取 .gz 文件,并立即处理接收到的数据。看起来像这样:

FtpWebResponse response = ftpclientRequest.GetResponse() as FtpWebResponse;
using (Stream ftpStream = response.GetResponseStream())
using (GZipStream unzipped = new GZipStream(ftpStream, CompressionMode.Decompress))
using (StreamReader linereader = new StreamReader(unzipped))
{
  String l;
  while ((l = linereader.ReadLine()) != null)
  {
    ...
  }
}

我的问题是显示准确的进度条。事先我可以得到压缩的 .gz 文件大小,但我不知道解压缩的内容有多大。 逐行读取文件我很清楚我读取了多少未压缩字节,但我不知道这与压缩文件大小有何关系。

那么,有什么方法可以从 GZipStream 中获取文件指针在压缩文件中的前进距离?我只需要当前位置,读取文件前可以获取的gz文件大小。

【问题讨论】:

    标签: ftp filesize gzipstream


    【解决方案1】:

    您可以在其中插入一个流,计算 GZipStream 读取了多少字节。

      public class ProgressStream : Stream
      {
        public long BytesRead { get; set; }
        Stream _baseStream;
        public ProgressStream(Stream s)
        {
          _baseStream = s;
        }
        public override bool CanRead
        {
          get { return _baseStream.CanRead; }
        }
        public override bool CanSeek
        {
          get { return false; }
        }
        public override bool CanWrite
        {
          get { return false; }
        }
        public override void Flush()
        {
          _baseStream.Flush();
        }
        public override long Length
        {
          get { throw new NotImplementedException(); }
        }
        public override long Position
        {
          get
          {
            throw new NotImplementedException();
          }
          set
          {
            throw new NotImplementedException();
          }
        }
        public override int Read(byte[] buffer, int offset, int count)
        {
          int rc = _baseStream.Read(buffer, offset, count);
          BytesRead += rc;
          return rc;
        }
        public override long Seek(long offset, SeekOrigin origin)
        {
          throw new NotImplementedException();
        }
        public override void SetLength(long value)
        {
          throw new NotImplementedException();
        }
        public override void Write(byte[] buffer, int offset, int count)
        {
          throw new NotImplementedException();
        }
      }
    
    // usage
    FtpWebResponse response = ftpclientRequest.GetResponse() as FtpWebResponse;
    using (Stream ftpStream = response.GetResponseStream())
    using (ProgressStream progressStream = new ProgressStream(ftpstream))
    using (GZipStream unzipped = new GZipStream(progressStream, CompressionMode.Decompress))
    using (StreamReader linereader = new StreamReader(unzipped))
    {
      String l;
      while ((l = linereader.ReadLine()) != null)
      {
        progressStream.BytesRead(); // does contain the # of bytes read from FTP so far.
      }
    }
    

    【讨论】:

    • 太好了,这就是我要找的!可惜 Ftp-Stream 不支持返回已经读取的字节!
    【解决方案2】:

    我建议你看看下面的代码:

    public static readonly byte[] symbols = new byte[8 * 1024];
    
    public static void Decompress(FileInfo inFile, FileInfo outFile)
    {
        using (var inStream = inFile.OpenRead())
        {
            using (var zipStream = new GZipStream(inStream, CompressionMode.Decompress))
            {
                using (var outStream = outFile.OpenWrite())
                {
                    var total = 0;
                    do
                    {
                        var async = zipStream.BeginRead(symbols, 0, symbols.Length, null, null);
                        total = zipStream.EndRead(async);
                        if (total != 0)
                        {
                            // Report progress. Read total bytes (8K) from the zipped file.
                            outStream.Write(symbols, 0, total);
                        }
                    } while (total != 0);
                }
            }
        }
    }
    

    【讨论】:

    • 过度和不必要地使用 var 关键字。让代码真的不可读。
    • 'var' 确实可以轻松输入示例并让编译器解决。
    • 对不起,我不明白这将如何帮助我计算我在 gz 文件中的距离。 'total' 包含未压缩的进度,这对我没有帮助,因为我不知道未压缩时文件有多大。我需要知道我在压缩字节中的位置。
    【解决方案3】:

    我重新审视了我的代码并进行了一些测试。恕我直言,达林是对的。但是我认为可以只读取压缩流的标题(大小?)并找出生成的文件大小。 (WinRar“知道”解压缩文件的大小,而无需解压缩整个 zip 存档。它从存档的标题中读取此信息。)如果您找到生成的文件大小,此代码将帮助您报告精确的进度。

    public static readonly byte[] symbols = new byte[8 * 1024];
    
    public static void Decompress(FileInfo inFile, FileInfo outFile, double size, Action<double> progress)
    {
        var percents = new List<double>(100);
    
        using (var inStream = inFile.OpenRead())
        {
            using (var zipStream = new GZipStream(inStream, CompressionMode.Decompress))
            {
                using (var outStream = outFile.OpenWrite())
                {
                    var current = 0;
    
                    var total = 0;
                    while ((total = zipStream.Read(symbols, 0, symbols.Length)) != 0)
                    {
                        outStream.Write(symbols, 0, total);
                        current += total;
    
                        var p = Math.Round(((double)current / size), 2) * 100;
                        if (!percents.Contains(p))
                        {
                            if (progress != null)
                            {
                                progress(p);
                            }
                            percents.Add(p);
                        }
                    }
                }
            }
        }
    }
    

    我希望这会有所帮助。

    【讨论】:

    • Petar,就像在您的第一个示例中一样,未压缩文件中的当前位置是正确的,但由于我不知道未压缩文件的大小,所以它对我没有用。我不认为 GZip 不会像 Rar 那样存储文件大小,所以我无法获得 uncomp。大小。
    【解决方案4】:

    作为解压缩进度的代理,您可以尝试使用以下方法从底层流中获取文件下载进度的信息:

    var percentageProgress = ftpStream.Position / (double)ftpWebResponse.ContentLength;
    

    var percentageProgress = ftpStream.Position / (double)ftpStream.Length;
    

    它适用于FileStream,它应该适用于GetResponseStream(),前提是它实现了Position 属性并且FTP 服务器返回有关下载文件长度的信息:http://msdn.microsoft.com/en-us/library/system.net.ftpwebresponse.contentlength(v=vs.110).aspx

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-04
      • 2010-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多