2009-01-05 13 views
3

Je lis un fichier .gz à partir d'une source lente (comme le serveur FTP) et je suis en train de traiter les données reçues immédiatement. Ressemble à quelque chose comme ceci:Progression de l'ordinateur (barre) en utilisant GZipStream

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

Mon problème montre une barre de progression précise. À l'avance, je peux obtenir la taille du fichier .gz compressé, mais je n'ai aucune idée de la taille du contenu non compressé. Lire le fichier ligne par ligne Je sais très bien combien d'octets non compressés j'ai lus, mais je ne sais pas comment cela se rapporte à la taille du fichier compressé.

Alors, y a-t-il un moyen d'obtenir de GZipStream jusqu'à quel point le pointeur de fichier est avancé dans le fichier compressé? J'ai seulement besoin de la position actuelle, la taille du fichier gz je peux aller chercher avant de lire le fichier.

Répondre

3

Vous pouvez connecter un flux entre les comptages, le nombre d'octets lus par 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. 
    } 
} 
+0

Super, c'était ce que je cherchais! Dommage que le Ftp-Stream ne supporte pas le retour des octets déjà lus! – Sam

0

Je vous suggère de jeter un oeil sur le code suivant:

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

Utilisation excessive et inutile du mot-clé var. Rend le code vraiment illisible. –

+0

'var' permet de taper facilement un exemple et de laisser le compilateur s'en sortir. – kenny

+0

Je suis désolé, je ne comprends pas comment cela pourrait m'aider à calculer jusqu'où dans le fichier gz je suis. 'total' contient la progression non compressée, ce qui ne m'aide pas, car je n'ai aucune idée de la taille du fichier lorsqu'il n'est pas compressé. J'ai besoin de connaître ma position en ** compressé ** octets. – Sam

0

J'ai revisité mon code et je l'ai effectué quelques tests. IMHO darin a raison. Cependant, je pense qu'il est possible de lire uniquement l'en-tête (taille?) Du flux compressé et de trouver la taille du fichier résultant. (WinRar "sait" quelle est la taille du fichier décompressé sans décompresser l'archive zip entière.Il lit cette information depuis l'en-tête de l'archive.) Si vous trouvez la taille du fichier résultant, ce code vous aidera à signaler une progression précise.

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

J'espère que cela aide.

+0

Petar, comme dans votre premier exemple la position actuelle dans le fichier non compressé est correcte, encore, il ne sert à rien pour moi puisque je ne connais pas la taille du fichier non compressé. Je ne pense pas que GZip stocke la taille du fichier comme le fait Rar, donc il n'y a aucun moyen pour moi d'obtenir le uncomp. Taille. – Sam

0

En tant que proxy pour décomprimer progrès vous pouvez essayer d'obtenir les informations pour la progression du téléchargement de fichier à partir du flux sous-jacent à l'aide:

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

ou

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

Il fonctionne sur un FileStream et il devrait fonctionner sur un GetResponseStream() à condition qu'il implémente la propriété Position et que le serveur FTP renvoie des informations sur le fichier téléchargé ngth: http://msdn.microsoft.com/en-us/library/system.net.ftpwebresponse.contentlength(v=vs.110).aspx