可以使用DeflateStream或GZipStream来收缩未压缩的文件吗?

时间:2010-09-09 09:53:48

标签: c# .net compression stream

我正在尝试对应用程序实施文件压缩。该应用程序已存在一段时间,因此它需要能够读取以前版本编写的未压缩文档。我希望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);
}

有什么想法吗?

谢谢! 路加。

2 个答案:

答案 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
}