我正在尝试对应用程序实施文件压缩。该应用程序已存在一段时间,因此它需要能够读取以前版本编写的未压缩文档。我希望DeflateStream能够处理一个未压缩的文件,但对于GZipStream,我得到“GZip头中的幻数不正确”错误。对于DeflateStream,我得到“解码时发现无效数据”。我猜它没有找到将文件标记为类型的标题。
如果不能简单地处理未压缩的文件,那么第二好的方法是确定文件是否被压缩,并选择读取文件的方法。我找到了这个链接:http://blog.somecreativity.com/2008/04/08/how-to-check-if-a-file-is-compressed-in-c/,但这是特定于实现的,并且感觉不是正确的方法。它也可以提供误报(我确信这很少见,但它确实表明这不是正确的方法)。
我考虑的第三个选项是尝试使用DeflateStream,如果发生异常则回退到普通流IO。这也感觉很乱,并导致VS在异常处中断(除非我解开该异常,我真的不想这样做。)
当然,我可能只是以错误的方式去做。这是我在.Net 3.5中试过的代码:
Stream reader = new FileStream(fileName, FileMode.Open, readOnly ? FileAccess.Read : FileAccess.ReadWrite, readOnly ? FileShare.ReadWrite : FileShare.Read);
using (DeflateStream decompressedStream = new DeflateStream(reader, CompressionMode.Decompress))
{
workspace = (Workspace)new XmlSerializer(typeof(Workspace)).Deserialize(decompressedStream);
if (readOnly)
{
reader.Close();
workspace.FilePath = fileName;
}
else
workspace.SetOpen(reader, fileName);
}
有什么想法吗?
谢谢! 路加。
答案 0 :(得分:1)
您的文件格式是否有标题?如果没有,现在是添加一个的时间(无论如何,您通过支持压缩来更改文件格式)。选择一个好的magic value,确保标题是可扩展的(添加版本字段,或对特定版本使用特定的魔术值),然后就可以开始了。
加载后,检查神奇值。如果不存在,请使用当前的旧版加载例程。如果存在,标题将告诉您内容是否被压缩。
压缩流意味着文件不再是XML文档,因此没有太多理由期望文件不能包含超过您的数据流。你真的想要一个标识你文件的标题:)
以下是示例(伪)代码;我不知道.net是否有“子流”,SubRangeStream很可能是你自己需要编写代码的东西(DeflateStream可能会添加它自己的头文件,因此可能不需要子流;可能会在未来的路上变得有用,虽然)。
Int64 oldPosition = reader.Position;
reader.Read(magic, 0, magic.length);
if(IsRightMagicValue(magic))
{
Header header = ReadHeader(reader);
Stream furtherReader = new SubRangeStream(reader, reader.Position, header.ContentLength);
if(header.IsCompressed)
{
furtherReader = new DeflateStream(furtherReader, CompressionMode.Decompress);
}
XmlSerializer xml = new XmlSerializer(typeof(Workspace));
workspace = (Workspace) xml.Deserialize(furtherReader);
} else
{
reader.Position = oldPosition;
LegacyLoad(reader);
}
在现实生活中,我会做一些不同的事情 - 例如,一些正确的错误处理和清理。另外,我不会在IsRightMagicValue
块中直接使用新的加载器代码,而是根据魔术值(每个文件版本的一个魔法值)分离工作,或者我会保留一个“common header”部分,包含所有版本共有的字段。对于两者,我会使用Factory Method返回IWorkspaceReader
,具体取决于文件版本。
答案 1 :(得分:1)
你不能只是创建一个包装类/函数来读取文件并捕获异常吗?像
这样的东西try
{
// Try return decompressed stream
}
catch(InvalidDataException e)
{
// Assume it is already decompressed and return it as it is
}