Domanda

Sto leggendo un file .gz da una fonte lenta (come il server FTP) e sto elaborando immediatamente i dati ricevuti. Sembra qualcosa del genere:

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

Il mio problema sta mostrando una barra di avanzamento accurata. In anticipo posso ottenere la dimensione del file .gz compresso, ma non ho idea di quanto sia grande il contenuto non compresso. Leggendo il file riga per riga, conosco abbastanza bene quanti byte non compressi ho letto, ma non so come ciò si riferisca alla dimensione del file compresso.

Quindi, c'è un modo per ottenere da GZipStream quanto è avanzato il puntatore del file nel file compresso? Ho solo bisogno della posizione corrente, la dimensione del file gz che posso recuperare prima di leggere il file.

È stato utile?

Soluzione

Puoi collegare un flusso tra i conteggi, quanti byte ha letto 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.
  }
}

Altri suggerimenti

Ti suggerisco di dare un'occhiata al seguente codice:

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

Ho rivisitato il mio codice e ho eseguito alcuni test. IMHO, darin ha ragione. Tuttavia penso che sia possibile leggere solo l'intestazione (dimensione?) Del flusso zippato e scoprire la dimensione del file risultante. (WinRar "conosce" qual è la dimensione del file decompresso senza decomprimere l'intero archivio zip. Legge queste informazioni dall'intestazione dell'archivio.) Se trovi la dimensione del file risultante questo codice ti aiuterà a segnalare un progresso 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);
                    }
                }
            }
        }
    }
}

Spero che questo aiuti.

Come proxy per i progressi di decompressione puoi provare a ottenere le informazioni sull'avanzamento del download del file dallo stream sottostante usando:

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

o

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

Funziona su un FileStream e dovrebbe lavorare su un GetResponseStream ( ) a condizione che implementi Posizione e il server FTP restituisce informazioni sulla lunghezza del file scaricato: http://msdn.microsoft.com/en-us/library/system.net.ftpwebresponse.contentlength (v = vs.110) .aspx

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top