我有一个本可以创建的文件:
stream.Write(headerBytes, 0, headerBytes.Count);
using (var gz = new GZipStream(stream, Compress, leaveOpen: true);
{
gz.Write(otherBytes, 0, otherBytes.Count);
}
stream.Write(moreBytes, 0, moreBytes.Count);
现在读取文件
stream.Read(headerBytes, 0, headerBytes.Count);
// in reality I make sure that indeed headerBytes.Count get read,
// something the above line omits
using (var gz = new GZipStream(stream, Decompress, leaveOpen: true)
{
do { /* use buffer... */}
while ((bytesRead = gz.Read(buffer, 0, buffer.Length)) != 0);
}
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
// use buffer...
事实证明GZipStream
(同样适用于DeflateStream
)从stream
读取16384个字节,而不是我检查的情况下的实际13293个压缩字节。
假设我既不知道文件压缩部分的大小,也不知道压缩数据后面的字节数,有没有办法使用GzipStream / DeflateStream
stream
stream.Position -= actuallyRead - compressedSize
吗?答案 0 :(得分:1)
该界面似乎没有提供你想要的方法,one of many reasons不使用.NET的GZipStream或DeflateStream。
您应该使用DotNetZip代替。
答案 1 :(得分:0)
此答案相当于丑陋的解决方法。我不是特别喜欢它,但它确实有效(除非它没有),即使只是GZipStream
。
- 或者至少弄清楚压缩数据部分的大小,我可以
醇>stream.Position -= actuallyRead - compressedSize
手动?
作为每个gzip文件(实际上每个 gzip成员)ends with
+---+---+---+---+---+---+---+---+
| CRC32 | ISIZE |
+---+---+---+---+---+---+---+---+
CRC32
This contains a Cyclic Redundancy Check value of the
uncompressed data
ISIZE
This contains the size of the original (uncompressed) input
data modulo 2^32.
我可以使用未压缩的大小(模块2 ^ 32),我在关闭GzipStream
后知道,然后在流中向后搜索,直到找到与之匹配的4个字节。
为了使其更加健壮,我还应该在解压缩时计算CRC32,并在8个字节形成正确的CRC32和ISIZE之后在流中向后搜索。
丑陋,但我确实警告过你。
< sarcasm>我如何爱封装。将所有有用的东西封装起来,留给我们一个解压缩的Stream,它完全适用于所有知道的API设计师预见的一个用例。< / sarcasm>
这是一个迄今为止有效的快速SeekBack
实现:
/// <returns>the number of bytes sought back (including bytes.Length)
/// or 0 in case of failure</returns>
static int SeekBack(Stream s, byte[] bytes, int maxSeekBack)
{
if (maxSeekBack != -1 && maxSeekBack < bytes.Length)
throw new ArgumentException("maxSeekBack must be >= bytes.Length");
int soughtBack = 0;
for (int i = bytes.Length - 1; i >= 0; i--)
{
while ((maxSeekBack == -1 || soughtBack < maxSeekBack)
&& s.Position > i)
{
s.Position -= 1;
// as we are seeking back, the following will never become
// -1 (EOS), so coercing to byte is OK
byte b = (byte)s.ReadByte();
s.Position -= 1;
soughtBack++;
if (b == bytes[i])
{
if (i == 0)
return soughtBack;
break;
}
else
{
var bytesIn = (bytes.Length - 1) - i;
if (bytesIn > 0) // back to square one
{
soughtBack -= bytesIn;
s.Position += bytesIn;
i = bytes.Length - 1;
}
}
}
}
// no luck? return to original position
s.Position += soughtBack;
return 0;
}
答案 2 :(得分:0)
根据Mark Adler的建议,我尝试DotNetZip,并且看,它的GZipStream.Position
属性不仅不会抛出,它甚至会返回读入的实际gzip字节数(加8,for某些原因,我仍然需要弄清楚。)
所以它读取的内容超出了严格要求,但它可以让我计算回溯量。
以下适用于我:
var posBefore = fileStream.Position;
long compressedBytesRead;
using (var gz = new GZipStream(fileStream, CompressionMode.Decompress, true))
{
while (gz.Read(buffer, 0, buffer.Length) != 0)
; // use it!
compressedBytesRead = gz.Position;
}
var gzipStreamAdvance = fileStream.Position - posBefore;
var seekBack = gzipStreamAdvance - compressedBytesRead - 8; // but why "- 8"?
fileStream.Position -= seekBack;