Pregunta

Estoy leyendo un archivo .gz de una fuente lenta (como el servidor FTP) y estoy procesando los datos recibidos de inmediato. Se parece a esto:

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)
  {
    ...
  }
}

Mi problema es mostrar una barra de progreso precisa. De antemano puedo obtener el tamaño de archivo comprimido .gz, pero no tengo idea de qué tan grande sería el contenido sin comprimir. Leyendo el archivo línea por línea, sé muy bien cuántos bytes sin comprimir leo, pero no sé cómo esto se relaciona con el tamaño del archivo comprimido.

Entonces, ¿hay alguna forma de llegar desde GZipStream hasta qué punto el puntero del archivo avanza en el archivo comprimido? Solo necesito la posición actual, el tamaño de archivo gz que puedo buscar antes de leer el archivo.

¿Fue útil?

Solución

Puede conectar una secuencia entre la que cuenta, cuántos bytes ha leído 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.
  }
}

Otros consejos

Te sugiero que eches un vistazo al siguiente código:

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

He revisado mi código y he realizado algunas pruebas. En mi humilde opinión, Darin tiene razón. Sin embargo, creo que es posible leer solo el encabezado (¿tamaño?) De la secuencia comprimida y averiguar el tamaño del archivo resultante. (WinRar " sabe " cuál es el tamaño del archivo descomprimido sin descomprimir todo el archivo zip. Lee esta información del encabezado del archivo). Si encuentra el tamaño del archivo resultante, este código le ayudará a informar un progreso preciso.

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

Espero que esto ayude.

Como proxy para descomprimir el progreso, puede intentar obtener la información para el progreso de la descarga del archivo de la secuencia subyacente mediante:

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

o

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

Funciona en un FileStream y debería trabaje en un GetResponseStream ( ) siempre que implemente Posición y el servidor FTP devuelve información sobre la longitud del archivo descargado: http://msdn.microsoft.com/en-us/library/system.net.ftpwebresponse.contentlength (v = vs.110) .aspx

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top