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();
}
}