Reusing a filestream

只谈情不闲聊 提交于 2019-12-18 12:20:23

问题


In the past I've always used a FileStream object to write or rewrite an entire file after which I would immediately close the stream. However, now I'm working on a program in which I want to keep a FileStream open in order to allow the user to retain access to the file while they are working in between saves. ( See my previous question).

I'm using XmlSerializer to serialize my classes to a from and XML file. But now I'm keeping the FileStream open to be used to save (reserialized) my class instance later. Are there any special considerations I need to make if I'm reusing the same File Stream over and over again, versus using a new file stream? Do I need to reset the stream to the beginning between saves? If a later save is smaller in size than the previous save will the FileStream leave the remainder bytes from the old file, and thus create a corrupted file? Do I need to do something to clear the file so it will behave as if I'm writing an entirely new file each time?


回答1:


Your suspicion is correct - if you reset the position of an open file stream and write content that's smaller than what's already in the file, it will leave trailing data and result in a corrupt file (depending on your definition of "corrupt", of course).

If you want to overwrite the file, you really should close the stream when you're finished with it and create a new stream when you're ready to re-save.

I notice from your linked question that you are holding the file open in order to prevent other users from writing to it at the same time. This probably wouldn't be my choice, but if you are going to do that, then I think you can "clear" the file by invoking stream.SetLength(0) between successive saves.




回答2:


There are various ways to do this; if you are re-opening the file, perhaps set it to truncate:

using(var file = new FileStream(path, FileMode.Truncate)) {
    // write
}

If you are overwriting the file while already open, then just trim it after writing:

file.SetLength(file.Position); // assumes we're at the new end

I would try to avoid delete/recreate, since this loses any ACLs etc.




回答3:


Another option might be to use SetLength(0) to truncate the file before you start rewriting it.




回答4:


Recently ran into the same requirement. In fact, previously, I used to create a new FileStream within a using statement and overwrite the previous file. Seems like the simple and effective thing to do.

using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write)
{
   ProtoBuf.Serializer.Serialize(stream , value);
}

However, I ran into locking issues where some other process is locking the target file. In my attempt to thwart this I retried the write several times before pushing the error up the stack.

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

That seemed to work for almost everyone. Then that problem machine came along and forced me down the path of maintaining a lock on the file the entire time. So in lieu of retrying to write the file in the case it's already locked, I'm now making sure I get and hold the stream open so there are no locking issues with later writes.

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

Now when I write the file the FileStream position must be reset to zero, as Aaronaught said. I opted to "clear" the file by calling _stream.SetLength(0). Seemed like the simplest choice. Then using our serializer of choice, Marc Gravell's protobuf-net, serialize the value to the stream.

_stream.SetLength(0);
ProtoBuf.Serializer.Serialize(_stream, value);

This works just fine most of the time and the file is completely written to the disk. However, on a few occasions I've observed the file not being immediately written to the disk. To ensure the stream is flushed and the file is completely written to disk I also needed to call _stream.Flush(true).

_stream.SetLength(0);
ProtoBuf.Serializer.Serialize(_stream, value);
_stream.Flush(true);



回答5:


Based on your question I think you'd be better served closing/re-opening the underlying file. You don't seem to be doing anything other than writing the whole file. The value you can add by re-writing Open/Close/Flush/Seek will be next to 0. Concentrate on your business problem.



来源:https://stackoverflow.com/questions/2313728/reusing-a-filestream

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!