Повторное использование фильтра
В прошлом я всегда использовал объект FileStream для записи или перезаписи целого файла, после чего я сразу же закрою поток. Однако теперь я работаю над программой, в которой я хочу, чтобы FileStream был открыт, чтобы позволить пользователю сохранить доступ к файлу во время работы между ними. (См. Мой предыдущий вопрос).
Я использую XmlSerializer для сериализации моих классов в файле from и XML. Но теперь я держу FileStream открытым, чтобы использовать для сохранения (повторного сеанса) экземпляра класса позже. Есть ли какие-то особые соображения, которые мне нужно сделать, если я повторно использую один и тот же поток файлов снова и снова, в сравнении с использованием нового потока файлов? Нужно ли мне reset поток в начало между сохранениями? Если последующее сохранение меньше по размеру, чем предыдущее сохранение, FileStream оставляет оставшиеся байты из старого файла и, таким образом, создает поврежденный файл? Нужно ли мне что-то делать, чтобы очистить файл, поэтому он будет вести себя так, как будто я пишу совершенно новый файл каждый раз?
Ответы
Ответ 1
Ваше подозрение правильное - если вы reset размещаете открытый поток файлов и записываете контент, который меньше, чем тот, что уже находится в файле, он оставит конечные данные и приведет к повреждению файла (в зависимости от вашего определения "коррумпированный", конечно).
Если вы хотите перезаписать файл, вам действительно нужно закрыть поток, когда вы его закончите, и создать новый поток, когда вы будете готовы повторно сохранить.
Из вашего связанного вопроса я замечаю, что вы держите файл открытым, чтобы другие пользователи не писали его одновременно. Вероятно, это не мой выбор, но если вы это сделаете, я думаю, вы можете "очистить" файл, вызвав stream.SetLength(0)
между последовательными сэйвами.
Ответ 2
Существуют различные способы сделать это; если вы повторно открываете файл, возможно, установите его для усечения:
using(var file = new FileStream(path, FileMode.Truncate)) {
// write
}
Если вы перезаписываете файл, хотя он уже открыт, просто обрезайте его после записи:
file.SetLength(file.Position); // assumes we're at the new end
Я бы постарался избежать delete/recreate, так как это теряет ACL и т.д.
Ответ 3
Другим вариантом может быть использование SetLength (0) для обрезания файла до начала его перезаписи.
Ответ 4
Недавно столкнулся с тем же требованием. На самом деле, ранее, я использовал для создания нового FileStream
внутри оператора using
и перезаписывал предыдущий файл. Похоже на простое и эффективное дело.
using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write)
{
ProtoBuf.Serializer.Serialize(stream , value);
}
Однако я столкнулся с проблемами блокировки, когда какой-то другой процесс блокирует целевой файл. В попытке сорвать это я несколько раз повторил запись, прежде чем нажимать ошибку на стек.
int attempt = 0;
while (true)
{
try
{
using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write)
{
ProtoBuf.Serializer.Serialize(stream , value);
}
break;
}
catch (IOException)
{
// could be locked by another process
// make up to X attempts to write the file
attempt++;
if (attempt >= X)
{
throw;
}
Thread.Sleep(100);
}
}
Это казалось почти для всех. Затем эта проблема возникла и заставила меня все время поддерживать блокировку файла. Поэтому вместо повторной попытки записать файл в том случае, когда он уже заблокирован, я теперь убедился, что получаю и удерживаю поток открытым, поэтому при записи позже нет проблем с блокировкой.
int attempt = 0;
while (true)
{
try
{
_stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
break;
}
catch (IOException)
{
// could be locked by another process
// make up to X attempts to open the file
attempt++;
if (attempt >= X)
{
throw;
}
Thread.Sleep(100);
}
}
Теперь, когда я пишу файл, позиция FileStream
должна быть reset равной нулю, как сказал Aaronaught. Я решил "очистить" файл, вызвав _stream.SetLength(0)
. Казалось, что это самый простой выбор. Затем, используя наш сериализатор выбора, Марк Гравелл protobuf-net
, сериализуйте значение в поток.
_stream.SetLength(0);
ProtoBuf.Serializer.Serialize(_stream, value);
Это прекрасно работает в большинстве случаев, и файл полностью записывается на диск. Однако несколько раз я наблюдал, как файл не был сразу записан на диск. Чтобы поток был сброшен, и файл полностью записан на диск, мне также нужно было вызвать _stream.Flush(true)
.
_stream.SetLength(0);
ProtoBuf.Serializer.Serialize(_stream, value);
_stream.Flush(true);
Ответ 5
Исходя из вашего вопроса, я думаю, вам лучше будет закрывать/повторно открывать основной файл. Кажется, вы ничего не делаете, кроме как писать весь файл. Значение, которое вы можете добавить, перезаписав Open/Close/Flush/Seek, будет рядом с 0. Сосредоточьтесь на своей бизнес-проблеме.