C#: запись CookieContainer на диск и загрузка обратно для использования

У меня есть CookieContainer извлечено из сеанса HttpWebRequest/HttpWebResponse с именем CookieJar. Я хочу, чтобы мое приложение сохраняло файлы cookie между запусками, поэтому файлы cookie собираются в CookieContainer при одном запуске программы будет использоваться и следующий запуск.

Я думаю, что способ сделать это - записать содержимое CookieContainer на диск. Мой вопрос:

  • Как вы можете записать CookieContainer на диск? Есть ли встроенные функции для этого, или, если нет, какие подходы люди использовали? Есть ли какие-либо классы для упрощения этого?
  • Как только вы записали CookieContainer на диск, как вы снова загрузите его для использования?

ОБНОВЛЕНИЕ: первый ответ предложил сериализацию CookieContainer, Однако я не очень хорошо знаю, как сериализовать и десериализовать такие сложные объекты. Не могли бы вы предоставить пример кода? Предложение было использовать SOAPFormatter,

5 ответов

Решение

Я не пробовал, но он имеет атрибут Serializable и поэтому может быть [de] сериализован с помощью двоичной сериализации.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 для сериализации [de] с помощью XmlSerializer и ручного преобразования между ним и CookieContainer.

Эта проблема мучила меня целую вечность, я ничего не мог найти, работал. Я разработал это, так что выпустить эту информацию в мир.

Ответ с помощью 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(); 
        }
    }

Все предыдущие ответы устарели с момента сериализации с использованием 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);

Интересно иметь файлы cookie в текстовом формате. Помимо того, что его можно использовать для записи на диск, его можно использовать для других целей.

РАБОТАЕТ НА МЕНЯ!

Использовать LoadCookiesFromFile а также SaveCookiesToFile функции для загрузки и записи файлов cookie на диск соответственно.

Или используйте GetCookies а также SetCookies функции, чтобы делать то же самое, но манипулировать им как строкой.

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.
    }
}

Другой альтернативой является использование сериализации Json ( Json.NET):

// other includes
using Newtonsoft.Json;

Класс с печеньем:

public class WithCookie
{
    public Cookie MyCookie { get; set; }

    public WithCookie()
    {
        MyCookie = new Cookie("CF788DF", "A cookie value!", "/", ".test.com");
    }
}

Вот как его сериализовать в Json:

class Program
{
    static void Main(string[] args)
    {
        try
        {
            WithCookie wc1 = new WithCookie();
            // Expires a month from now
            wc1.MyCookie.Expires = DateTime.Now.AddMonths(1);

            string wc1json = JsonConvert.SerializeObject(new WithCookie());

            WithCookie wc2 = JsonConvert.DeserializeObject < WithCookie>(wc1json);

            string wc2json = JsonConvert.SerializeObject(wc2);

            if (wc2json == wc1json)
            {
                Console.WriteLine("HORRAY!");
            }
            else
            {
                // The strings will not be equal, because the Cookie.TimeStamp 
                // changes but the cookies are in fact the same!
                Console.WriteLine("FAIL!");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("Exception: " + e.ToString());
        }
        Console.ReadKey();
    }
}
Другие вопросы по тегам