GZipStreamを使用した進行状況(バー)の計算
-
03-07-2019 - |
質問
低速のソース(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ファイルサイズを取得できますが、コンテンツが圧縮解除される大きさの手がかりが得られませんでした。 ファイルを1行ずつ読み込むと、非圧縮バイトがいくつ読み取られたかはよくわかりますが、これが圧縮ファイルサイズにどのように関係するかはわかりません。
では、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;
FileStream で動作します。 GetResponseStream( ) Position プロパティとFTPサーバーは、ダウンロードしたファイルの長さに関する情報を返します: http://msdn.microsoft.com/en-us/library/system.net.ftpwebresponse.contentlength(v = vs.110).aspx