我有一个使用代码创建的文件,如下所示:
using (var fs=File.OpenWrite("tmp"))
{
using (GZipStream gs=new GZipStream(fs,CompressionMode.Compress,true))
{
using (StreamWriter sw=new StreamWriter(gs))
{
sw.WriteLine("hello ");
}
}
using (GZipStream gs = new GZipStream(fs, CompressionMode.Compress, true))
{
using (StreamWriter sw = new StreamWriter(gs))
{
sw.WriteLine("world");
}
}
}
现在我尝试使用以下代码从此文件中读取数据:
string txt;
using (var fs=File.OpenRead("tmp"))
{
using (GZipStream gs=new GZipStream(fs,CompressionMode.Decompress,true))
{
using (var rdr = new StreamReader(gs))
{
txt = rdr.ReadToEnd();
}
}
using (GZipStream gs = new GZipStream(fs, CompressionMode.Decompress, true))
{
using (StreamReader sr = new StreamReader(gs))
{
txt+=sr.ReadToEnd();
}
}
}
第一个流读取正常,但第二个流无法读取。
如何读取第二个流?
这是 GzipStream 处理具有多个 gzip 条目的 gzip 文件的方式的问题。它读取第一个条目,并将所有后续条目视为垃圾(有趣的是,gzip 和 winzip 等实用程序通过将它们全部提取到一个文件中来正确处理它)。有几种解决方法,或者您可以使用第三方实用程序,例如点网压缩 (http://dotnetzip.codeplex.com/).
也许最简单的方法是扫描文件中的所有 gzip 标头,然后手动将流移动到每个标头并解压缩内容。这可以通过在原始文件字节中查找ID1、ID2和0x8来完成(Deflate压缩方法,请参阅规范:http://www.gzip.org/zlib/rfc-gzip.html)。这并不总是足以保证您正在查看 gzip 标头,因此您需要读取标头的其余部分(或至少前十个字节)来验证:
const int Id1 = 0x1F;
const int Id2 = 0x8B;
const int DeflateCompression = 0x8;
const int GzipFooterLength = 8;
const int MaxGzipFlag = 32;
/// <summary>
/// Returns true if the stream could be a valid gzip header at the current position.
/// </summary>
/// <param name="stream">The stream to check.</param>
/// <returns>Returns true if the stream could be a valid gzip header at the current position.</returns>
public static bool IsHeaderCandidate(Stream stream)
{
// Read the first ten bytes of the stream
byte[] header = new byte[10];
int bytesRead = stream.Read(header, 0, header.Length);
stream.Seek(-bytesRead, SeekOrigin.Current);
if (bytesRead < header.Length)
{
return false;
}
// Check the id tokens and compression algorithm
if (header[0] != Id1 || header[1] != Id2 || header[2] != DeflateCompression)
{
return false;
}
// Extract the GZIP flags, of which only 5 are allowed (2 pow. 5 = 32)
if (header[3] > MaxGzipFlag)
{
return false;
}
// Check the extra compression flags, which is either 2 or 4 with the Deflate algorithm
if (header[8] != 0x0 && header[8] != 0x2 && header[8] != 0x4)
{
return false;
}
return true;
}
请注意,如果直接使用文件流,GzipStream 可能会将流移动到文件末尾。您可能希望将每个部分读入 MemoryStream,然后在内存中单独解压缩每个部分。
另一种方法是修改 gzip 标头以指定内容的长度,这样您就不必扫描文件中的标头(您可以以编程方式确定每个标头的偏移量),这需要更深入地研究gzip 规范
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)