2011-03-30 63 views
0

假设我有一个对象叫做data,它包含各种信息。让我们假设data图中实际上有相当多的东西。C#对象序列化的元信息

如果我使用BinaryFormatter将其序列化,那么我得到一个5Mb的文件。 如果我将序列化流封装在GZipStream中,那么我会得到一个更小的文件,比如1Mb。

如果需要,我可以在压缩流时加密流,或者在不压缩流的情况下加密流。

问题是:我需要知道在序列化过程中做了什么,以便在反序列化时知道该怎么做。

一种技术是使用不同的文件扩展名。例如,未压缩的未加密文件可能具有.dat扩展名,.zdat用于压缩,.cdat用于加密,.czdat用于压缩和加密。

这会工作,但它引入了一个潜在的问题:如果用户更改扩展名等等。这也意味着如果我想在Windows中关联文件,则有4个扩展名而不是1个,这需要相关联 - 将与现有协会冲突的风险翻两番。

如果我换我的数据对象在一个简单的类:

[Serializable] 
public class SerialisationContainer 
{ 
    public string SerialisedData { get; private set; } 

    public bool Compressed { get; private set; } 
    public bool Encrypted { get; private set; } 

    public SerialisationContainer() 
    { 
    // etc... 
    } 

    public object GetObject() 
    { 
    // etc... 
    } 
} 

然后我基本上连载其中有一个序列化流可以压缩和/或加密的对象,但我们不”因为元信息存储在SerialisationContainer中,所以此时不​​知道或小心。

您认为如何?我基本上只是好奇你对这种方法的看法,以及你在类似情况下做了什么。我认为上述方法是一种非常浪费的做我想做的事情的方式。我基本上需要序列化我的数据图到内存流,将其转换为字符串,将字符串放入我的容器中,然后再次将其串行化。

另一个问题是string SerialisedData的长度。在我给出的例子中,我们只有大约5Gb的BinaryData,但是什么时候它开始变大?我知道在64位操作系统上,string的上限大约为2GB,而32位操作系统的上限要小得多。流是否有这样的限制?由于数据流是以字节为单位写入的,所以它们不会。

回答

1

首先,懒惰的解决方案:你不必直接序列化到一个文件。您可以序列化到内存,然后编写一个格式为1个字节的文件,然后是序列化数据。

其次,你可以变得更聪明:打开一个文件;写一个字节(格式);序列化成相同的字符串。要反序列化,请读取一个字节以确定格式,然后将该数据流传递给解串器;它只会在一个字节后读取数据。

如果你有方法

void SerializeToStream(Stream stream, bool compress, bool encrypt); 
void DeserializeFromStream(Stream stream, bool compressed, bool encrypted); 

你的代码可以是这样的:

// Could also use a flags enum for these 
const int EncryptBit = 1; 
const int CompressBit = 2; 

public void SaveToFile(string filename, bool compress, bool encrypt) { 
    byte format = (byte)((compress ? CompressBit : 0) | (encrypt ? EncryptBit : 0)); 
    using (Stream stream = File.OpenWrite(filename)) { 
     stream.WriteByte(format); 
     SerializeToStream(stream, compress, encrypt); 
    } 
} 

public void LoadFromFile(string filename) { 
    using (Stream stream = File.OpenRead(filename)) { 
     int format = stream.ReadByte(); 
     if (format < 0 || format >= 4) { 
      throw new InvalidOperationException("Unknown file format"); 
     } 

     bool compressed = format & CompressBit != 0; 
     bool encrypted = format & EncryptBit != 0; 
     DeserializeFromStream(stream, compressed, encrypted); 
    } 
} 
1

我就在那确切情况一次。我为我手动写出的文件创建了一个标题,其后是压缩和/或加密(或可能是纯文本)流。当我打开文件时,我首先在头文件中读取,然后根据该信息将输入流的位置设置为数据的开头,然后创建解压缩和/或解密流。它像魅力一样工作,是一块蛋糕和其他几个陈词滥调。

我的报头是在纯文本和包括:

  1. 该被选定在设计过程的早期随机识别该文件作为以正确的格式被短字符串。

  2. 一个文件版本号,所以我们可以在将来更改格式并仍然读取旧的文件。

  3. 以纯文本形式显示的各种业务特定的摘要信息将显示在列表中,以便用户知道即使文件名称已更改也要打开哪个文件。显然,这不是安全敏感数据。

  4. 一个指示符,表明文件是加密的还是压缩的,还是两者兼有。此外,它可以作为一个整体或逐行进行加密,以支持即时附加加密数据。纯文本选项用于开发目的和偶尔的数据手术操作,但由于这种设计,它可以像任何其他文件一样自动读取或写入。

  5. 如果文件是用AES加密的,那么加密密钥就存储在下一个,它本身是用RSA加密的,并用base-64进行序列化。

  6. ASCII 0x02 START OF TEXT字符,纯粹是为了好玩。 (虽然如果它不在那里,那么读取文件将失败。)

然后传来数据流。

+1

谢谢:)我没有想过写头字节 - 但现在我明白了。他们说事后总是20/20。 – Ozzah 2011-03-30 23:58:08