GZipStream -写不写所有压缩数据,即使与刷新
本文关键字:刷新 数据 压缩 GZipStream | 更新日期: 2023-09-27 18:10:55
我有一个讨厌的问题,gzipstream针对。net 3.5。这是我第一次使用gzipstream,但是我已经模仿了一些教程,包括这里,我仍然卡住了。
我的应用程序将一个数据表序列化为xml并插入到数据库中,将压缩后的数据存储到varbinary(max)字段以及未压缩缓冲区的原始长度中。然后,当我需要它时,我检索该数据并解压缩它并重新创建数据表。解压失败了。
EDIT:遗憾的是,根据建议将GetBuffer更改为ToArray后,我的问题仍然存在。以下更新代码
压缩代码:
DataTable dt = new DataTable("MyUnit");
//do stuff with dt
//okay... now compress the table
using (MemoryStream xmlstream = new MemoryStream())
{
//instead of stream, use xmlwriter?
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings();
settings.Encoding = Encoding.GetEncoding(1252);
settings.Indent = false;
System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(xmlstream, settings);
try
{
dt.WriteXml(writer);
writer.Flush();
}
catch (ArgumentException)
{
//likely an encoding issue... okay, base64 encode it
var base64 = Convert.ToBase64String(xmlstream.ToArray());
xmlstream.Write(Encoding.GetEncoding(1252).GetBytes(base64), 0, Encoding.GetEncoding(1252).GetBytes(base64).Length);
}
using (MemoryStream zipstream = new MemoryStream())
{
GZipStream zip = new GZipStream(zipstream, CompressionMode.Compress);
log.DebugFormat("Compressing commands...");
zip.Write(xmlstream.GetBuffer(), 0, xmlstream.ToArray().Length);
zip.Flush();
float ratio = (float)zipstream.ToArray().Length / (float)xmlstream.ToArray().Length;
log.InfoFormat("Resulting compressed size is {0:P2} of original", ratio);
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = "INSERT INTO tinydup (lastid, command, compressedlength) VALUES (@lastid,@compressed,@length)";
cmd.Connection = db;
cmd.Parameters.Add("@lastid", SqlDbType.Int).Value = lastid;
cmd.Parameters.Add("@compressed", SqlDbType.VarBinary).Value = zipstream.ToArray();
cmd.Parameters.Add("@length", SqlDbType.Int).Value = xmlstream.ToArray().Length;
cmd.ExecuteNonQuery();
}
}
压缩代码:
/* This is an encapsulation of what I get from the database
public class DupUnit{
public uint lastid;
public uint complength;
public byte[] compressed;
}*/
//I have already retrieved my list of work to do from the database in a List<Dupunit> dupunits
foreach (DupUnit unit in dupunits)
{
DataSet ds = new DataSet();
//DataTable dt = new DataTable();
//uncompress and extract to original datatable
try
{
using (MemoryStream zipstream = new MemoryStream(unit.compressed))
{
GZipStream zip = new GZipStream(zipstream, CompressionMode.Decompress);
byte[] xmlbits = new byte[unit.complength];
//WHY ARE YOU ALWAYS 0!!!!!!!!
int bytesdecompressed = zip.Read(xmlbits, 0, unit.compressed.Length);
MemoryStream xmlstream = new MemoryStream(xmlbits);
log.DebugFormat("Uncompressed XML against {0} is: {1}", m_source.DSN, Encoding.GetEncoding(1252).GetString(xmlstream.ToArray()));
try{
ds.ReadXml(xmlstream);
}catch(Exception)
{
//it may have been base64 encoded... decode first.
ds.ReadXml(Encoding.GetEncoding(1254).GetString(
Convert.FromBase64String(
Encoding.GetEncoding(1254).GetString(xmlstream.ToArray())))
);
}
xmlstream.Dispose();
}
}
catch (Exception e)
{
log.Error(e);
Thread.Sleep(1000);//sleep a sec!
continue;
}
注意上面的注释…解压后的字节数总是0。什么好主意吗?我做错了吗?
编辑2:
这很奇怪。我在解压缩例程中添加了以下调试代码:
GZipStream zip = new GZipStream(zipstream, CompressionMode.Decompress);
byte[] xmlbits = new byte[unit.complength];
int offset = 0;
while (zip.CanRead && offset < xmlbits.Length)
{
while (zip.Read(xmlbits, offset, 1) == 0) ;
offset++;
}
在调试时,该循环有时会完成,但有时会挂起。当我停止调试时,它将是1616字节中的1600字节。我想继续,但它根本不动。
EDIT 3:这个bug似乎在压缩代码中。由于某种原因,它没有保存所有的数据。当我尝试使用第三方gzip机制解压缩数据时,我只得到原始数据的一部分。
我想开始赏金,但我现在真的没有太多的声誉可以给予:-(
终于找到了答案。压缩的数据没有完成,因为GZipStream.Flush()绝对没有做任何事情来确保所有的数据都在缓冲区之外——你需要使用这里指出的GZipStream.Close()。当然,如果压缩结果不理想,就会每况愈下——如果您尝试解压缩它,Read()总是会返回0。
我得说,至少这一行是最错误的:
cmd.Parameters.Add("@compressed", SqlDbType.VarBinary).Value = zipstream.GetBuffer();
MemoryStream。GetBuffer:
请注意,缓冲区中包含可能未使用的已分配字节。例如,如果将字符串"test"写入
MemoryStream
对象,则从GetBuffer
返回的缓冲区长度为256,而不是4,其中有252个字节未使用。如果只获取缓冲区中的数据,使用ToArray
方法。
应该注意的是,在zip格式中,它首先通过定位存储在文件末尾的数据来工作-因此,如果您存储的数据多于所需的数据,则在文件"末尾"的所需条目不存在。
作为题外话,我还建议为您的
compressedlength
专栏使用不同的名称-我最初将它(尽管您的叙述)用于存储压缩数据的长度(并编写了我的回答的一部分来解决这个问题)。也许originalLength
会是一个更好的名字?