C#: 将 CookieContainer 写入磁盘并加载以备使用

35

我有一个从名为CookieJar的HttpWebRequest/HttpWebResponse会话中提取的CookieContainer。 我希望我的应用程序能够在运行之间存储cookie,这样程序在下次运行时也可以使用上次收集到的在CookieContainer中的cookie。

我认为做到这一点的方法是以某种方式将CookieContainer的内容写入磁盘。我的问题是:

  • 如何将CookieContainer写入磁盘?是否有内置的函数可以实现此功能,如果没有,人们采取了哪些方法?是否有可用于简化此过程的类?
  • 一旦您将CookieContainer写入磁盘,如何加载它以供使用

更新: 第一个答案建议对CookieContainer进行序列化。 但是,我对如何序列化和反序列化这样复杂的对象并不熟悉。 您能提供一些示例代码吗? 建议利用SOAPFormatter

4个回答

39

这个问题困扰了我很久,找到的所有方法都无效。最终我解决了它,现在分享给大家。

使用BinaryFormatter进行回答:

    public static void WriteCookiesToDisk(string file, CookieContainer cookieJar)
    {
        using(Stream stream = File.Create(file))
        {
            try {
                Console.Out.Write("Writing cookies to disk... ");
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, cookieJar);
                Console.Out.WriteLine("Done.");
            } catch(Exception e) { 
                Console.Out.WriteLine("Problem writing cookies to disk: " + e.GetType()); 
            }
        }
    }   

    public static CookieContainer ReadCookiesFromDisk(string file)
    {

        try {
            using(Stream stream = File.Open(file, FileMode.Open))
            {
                Console.Out.Write("Reading cookies from disk... ");
                BinaryFormatter formatter = new BinaryFormatter();
                Console.Out.WriteLine("Done.");
                return (CookieContainer)formatter.Deserialize(stream);
            }
        } catch(Exception e) { 
            Console.Out.WriteLine("Problem reading cookies from disk: " + e.GetType()); 
            return new CookieContainer(); 
        }
    }

有没有办法可以将数据存储在 SQL 中而不是文件中? - juanora
3
BinaryFormatter现已过时。请查看https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/5.0/binaryformatter-serialization-obsolete了解更多信息。此解决方案不再有效。 - yefajouri

22

我没有尝试过,但它具有可序列化属性,因此可以使用.NET二进制序列化(例如SoapFormatter)进行[反]序列化。

这里是您要求的代码片段。

var formatter = new SoapFormatter();
string file = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "cookies.dat"); 

using (Stream s = File.Create (file))
    formatter.Serialize(s, cookies);                            
...
CookieContainer retrievedCookies = null;
using (Stream s = File.OpenRead (file))
    retrievedCookies = (CookieContainer) formatter.Deserialize(s);

在查看 msdn 后,发现 SoapFormatter 已经在 .net 3.5 中被弃用,并建议使用 Binaryformatter。过去我发现 SoapFormatter 很有用,因为文件是可读的,这在反序列化失败时有助于诊断!即使在程序集版本上,这些格式化程序也会对版本更改敏感(所以如果您使用一个版本的框架进行反序列化,然后升级框架,则可能无法反序列化,不确定),但如果遇到问题,通过 Binder 属性可以解决。我认为它们主要是为短期持久性/远程而设计的,但它们在这里可能已经足够好了。

新的 DataContractSerializer 似乎无法使用,所以这个选项被排除掉了。

另一种选择是编写一个 CookieContainerData 类来使用 XmlSerializer 进行 [反]序列化,并在 CookieContainer 和该类之间手动转换。


我对序列化不是很了解,你能提供一些示例代码吗? - Maxim Zaslavsky

7
所有之前的答案都已过时,因为使用IFormatter类进行序列化已被弃用https://aka.ms/binaryformatter 所以现在正确的方法是使用支持IEnumerable<T>的其他东西进行序列化。
这里有一个使用System.Text.Json的示例

序列化

await using var fs = File.OpenWrite("cookies.json");
// Beware: GetAllCookies is available starting with .NET 6
JsonSerializer.Serialize(fs, cookieContainer.GetAllCookies());

反序列化

var cookieContainer = new CookieContainer();
await using var fs = File.OpenRead("cookies.json");
var cookieCollection = JsonSerializer.Deserialize<CookieCollection>(fs);
cookieContainer.Add(cookieCollection);

6

cookie以文本格式存储非常有趣。除了可以用于写入磁盘,还可以用于其他用途。

对我来说很好用!

使用LoadCookiesFromFileSaveCookiesToFile函数分别从磁盘加载和写入cookie。

或者使用GetCookiesSetCookies函数完成相同的操作,但是将其作为字符串进行操作。

CookieContainer cookieContainer = new CookieContainer();

void LoadCookiesFromFile(string path)
{
    SetCookies(cookieContainer, File.ReadAllText(path));
}

void SaveCookiesToFile(string path)
{
    File.WriteAllText(path, GetCookies(cookieContainer));
}

string GetCookies(CookieContainer cookieContainer)
{
    using (MemoryStream stream = new MemoryStream())
    {
        new BinaryFormatter().Serialize(stream, cookieContainer);
        var bytes = new byte[stream.Length];
        stream.Position = 0;
        stream.Read(bytes, 0, bytes.Length);
        return Convert.ToBase64String(bytes);
    }
}

void SetCookies(CookieContainer cookieContainer, string cookieText)
{
    try
    {
        var bytes = Convert.FromBase64String(cookieText);
        using (MemoryStream stream = new MemoryStream(bytes))
        {
            cookieContainer = (CookieContainer)new BinaryFormatter().Deserialize(stream);
        }
    }
    catch
    {
        //Ignore if the string is not valid.
    }
}

1
我不会称之为“文本格式的cookies”。它是“以base64编码的二进制数据”。我认为cookies本身就是文本,因此如果您编写自己的基于文本的序列化程序,将cookies存储为可读文本是可能的 - 它可以是JSON,也可以是简单的缩进文本部分。 - Kind Contributor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接