FileStream to save file then immediately unlock in .NET?
Asked Answered
D

6

10

I have this code that saves a pdf file.

FileStream fs = new FileStream(SaveLocation, FileMode.Create);
fs.Write(result.DocumentBytes, 0, result.DocumentBytes.Length);
fs.Flush();
fs.Close();

It works fine. However sometimes it does not release the lock right away and that causes file locking exceptions with functions run after this one run.

Is there a ideal way to release the file lock right after the fs.Close()

Doris answered 19/4, 2010 at 20:0 Comment(1)
Is the file on a local hard drive or a network share? Do you have any antivirus software installed?Sachet
E
17

Here's the ideal:

using (var fs = new FileStream(SaveLocation, FileMode.Create))
{
    fs.Write(result.DocumentBytes, 0, result.DocumentBytes.Length);
}

which is roughly equivalent to:

FileStream fs =  null;
try
{
    fs = new FileStream(SaveLocation, FileMode.Create);
    fs.Write(result.DocumentBytes, 0, result.DocumentBytes.Length);
}
finally
{
    if (fs != null)
    {
        ((IDisposable)fs).Dispose();
    }
}

the using being more readable.


UPDATE:

@aron, now that I'm thinking

File.WriteAllBytes(SaveLocation, result.DocumentBytes);

looks even prettier to the eye than the ideal :-)

Euterpe answered 19/4, 2010 at 20:1 Comment(8)
This is a great example of how to use the using statement, but since it's ultimately equivalent to the OP's code, it does nothing to address the question that was asked.Briquette
@Jeffrey, I know that I drink lots of beer but I really don't see how this is equivalent to the OPs code.Euterpe
Dispose calls Close. Close calls Flush. The end result is the same. Your change is benificial to avoid accidently leaving the file open in the case of exceptions or early returns, but it doesn't cause the file lock to be removed any quicker.Briquette
equivalent in basic functionality. However this code is radically different in it's handling of garbage collection.. Which is the OP's problem. +1Balakirev
@Chris Lively - Close() will release the unmanaged resources. The garbage collection will be exactly the same in both cases. This problem has nothing to do with garbage collection.Briquette
@Chris Lively - I just verified my suspicion that calling Close() will also call GC.SurpressFinalize(). The garbage collection is identical between Close() and Dispose().Briquette
+1 A using() statement is not the solution here, we have seen this issue even with every write being wrapped in using(). See my answer for more comments.Sachet
Jeffrey is right. using doesn't answer the real problem, unfortunately.Unilingual
S
5

We have seen this same issue in production with a using() statement wrapping it.

One of the top culprits here is anti-virus software which can sneak in after the file is closed, grab it to check it doesn't contain a virus before releasing it.

But even with all anti-virus software out of the mix, in very high load systems with files stored on network shares we still saw the problem occasionally. A, cough, short Thread.Sleep(), cough, after the close seemed to cure it. If someone has a better solution I'd love to hear it!

Sachet answered 19/4, 2010 at 20:35 Comment(1)
Hard to recommend a specific value since it will depend on a lot of factors; start small, say 10.Sachet
F
3

I can't imagine why the lock would be maintained after the file is closed. But you should consider wrapping this in a using statment to ensure that the file is closed even if an exception is raised

using (FileStream fs = new FileStream(SaveLocation, FileMode.Create))
{
  fs.Write(result.DocumentBytes, 0, result.DocumentBytes.Length);  
}
Fleda answered 19/4, 2010 at 20:3 Comment(0)
B
3

If the functions that run after this one are part of the same application, then a better approach might be to open the file for read/write at the beginning of the entire process, and then pass the file to each function without closing it until the end of the process. Then it will be unnecessary for the application to block waiting for the IO operation to complete.

Briquette answered 19/4, 2010 at 20:5 Comment(0)
A
1

This worked for me when using .Flush() I had to add a close inside the using statement.

 using (var imageFile = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite,FileShare.ReadWrite))
 {
     imageFile.Write(bytes, 0, bytes.Length);
     imageFile.Flush();
     imageFile.Close();
  }
Auramine answered 28/1, 2015 at 16:41 Comment(1)
I had to do the same when I use WhiteAsync inside using too.Diction
F
0

Just had the same issue when I closed a FileStream and opened the file immediately in another class. The using statement was not a solution since the FileStream had been created at another place and stored in a list. Clearing the list was not enough.

It looks like the stream needs to be freed by the garbage collector before the file can be reused. If the time between closing and opening is too short, you can use

GC.Collect();

right after you closed the stream. This worked for me.

I guess the solutions of Ian Mercer to put the thread to sleep might have the same effect, giving the GC time to free the resources.

Fibroma answered 29/5, 2019 at 10:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.