Подтвердить что ты не робот

Повторное использование фильтра

В прошлом я всегда использовал объект FileStream для записи или перезаписи целого файла, после чего я сразу же закрою поток. Однако теперь я работаю над программой, в которой я хочу, чтобы FileStream был открыт, чтобы позволить пользователю сохранить доступ к файлу во время работы между ними. (См. Мой предыдущий вопрос).

Я использую XmlSerializer для сериализации моих классов в файле from и XML. Но теперь я держу FileStream открытым, чтобы использовать для сохранения (повторного сеанса) экземпляра класса позже. Есть ли какие-то особые соображения, которые мне нужно сделать, если я повторно использую один и тот же поток файлов снова и снова, в сравнении с использованием нового потока файлов? Нужно ли мне reset поток в начало между сохранениями? Если последующее сохранение меньше по размеру, чем предыдущее сохранение, FileStream оставляет оставшиеся байты из старого файла и, таким образом, создает поврежденный файл? Нужно ли мне что-то делать, чтобы очистить файл, поэтому он будет вести себя так, как будто я пишу совершенно новый файл каждый раз?

4b9b3361

Ответ 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. Сосредоточьтесь на своей бизнес-проблеме.