Прогресс вычисления (bar) с использованием GZipStream

StackOverflow https://stackoverflow.com/questions/412785

  •  03-07-2019
  •  | 
  •  

Вопрос

Я читаю файл .gz из какого-то медленного источника (например, FTP-сервера) и сразу же обрабатываю полученные данные.Выглядит примерно так:

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, который я могу получить перед чтением файла.

Это было полезно?

Решение

Вы можете подключить поток между подсчетами, сколько байтов прочитал 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.
  }
}

Другие советы

Я предлагаю вам взглянуть на следующий код:

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);
            }
        }
    }
}

Я пересмотрел свой код и провел несколько тестов. ИМХО Дарин прав. Однако я думаю, что можно прочитать только заголовок (размер?) Сжатого потока и узнать размер результирующего файла. (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);
                    }
                }
            }
        }
    }
}

Надеюсь, это поможет.

В качестве прокси-сервера для выполнения распаковки вы можете попытаться получить информацию о ходе загрузки файла из базового потока, используя:

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

или

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

Это работает на Файловый поток и это должно сработать на GetResponseStream() при условии, что он реализует Положение свойство и FTP-сервер возвращает информацию о длине загруженного файла: http://msdn.microsoft.com/en-us/library/system.net.ftpwebresponse.contentlength (v=против 110).aspx

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top