System.Io.Filestream Fileaccess VS Fileshare

System.IO.FileStream FileAccess vs FileShare

FileAccess says what you are going to do with the file. Pretty easy to understand, you'll know you are going to read or write.

FileShare is the much trickier one since it requires you to step into the shoes of another programmer. It determines what another process can do if it also has the file opened. Two processes accessing a file can be very troublesome, you'll need to reason through the possible failure modes. The value you pick is strongly correlated to the type of the file and the access you want. Breaking it down by what you are going to do:

FileAccess.Read

There is never any trouble if another process also reads from the file. So FileShare.Read is the default choice.

You may need FileShare.ReadWrite if another process has already opened the file for writing. It already gained write access so you can never open the file yourself with just FileShare.Read, you can't deny writing since that other process was first, you'll be denied access instead. This generally only comes to a good on a text file, the kind where you can be sure that the other process is only ever appending text to the end of the file. A log file is the very common scenario. Still possibly tricky, it matters exactly when that process flushes changes to the file. You might observe partially written lines of text, beware of this.

FileAccess.Write

You cannot use FileShare.Write or FileShare.ReadWrite. Since that would allow two processes writing to the file at the same time, the file content will be a jumble of output from both programs. A possible workaround is these processes arbitrating access to the file, ensuring only one of them can ever access the file at the same time. Normally implemented by a named mutex.

You can use FileShare.Read if it is text file, same scenario I described above with the log file. The default choice otherwise should be FileShare.None

FileAcces.ReadWrite

Not that common, only used if you write binary data and use Seek(). There is no hope that any other process could ever read the file correctly while you are doing this, assuming they don't arbitrate access themselves, you must use FileShare.None.

File access differences between FileStream and System.IO.File method

The FileStream constructor passes off to another constructor overload.. namely this one:

internal FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, string msgPath, bool bFromProxy, bool useLongPath, bool checkHost)

The FileAccess it passes by default is FileAccess.ReadWrite. If your application does not have write permissions.. chances are this will fail.

On the other hand, File.ReadAllBytes does this:

using (var fileStream = new FileStream(..., ..., FileAccess.Read, ...
// ^^^^ this

Notice File.ReadAllBytes uses the FileAccess.Read value.

So that's the difference. FileStream.ctor uses FileAccess.ReadWrite, File.ReadAllBytes uses FileAccess.Read.

TLDR: Your app needs write permissions to use that basic constructor for FileStream. Remember there is a File.Exists method that is meant for this exact purpose. Opening and closing a file to see if it exists is not the right way to go about it.

FileShare.ReadWrite in System.IO.Packaging

It's not clear why you want to do this, but you can try specifying options on file stream directly like this:

using (var file = new FileStream(@"myfile", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
var myPackage = Package.Open(file);
}

UPDATE to elaborate a bit more about when this might be useful. Suppose you try to do something like this:

using (var fs1 = new FileStream("myfile", FileMode.Append, FileAccess.Write, FileShare.Read))
using (var fs2 = new FileStream("myfile", FileMode.Open, FileAccess.Read, FileShare.Read)) {
}

So first file is opened for writing, with FileShare = Read. Then there is another attempt to open file, for reading, again with FileShare = Read. This won't work, because if file has already been opened for write, any request with FileShare = read will fail. To make this work you have to do it like this:

using (var fs1 = new FileStream("myfile", FileMode.Append, FileAccess.Write, FileShare.Read))
using (var fs2 = new FileStream("myfile", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
}

Here you request FileShare.ReadWrite which allows you to read file opened by fs1 for writing.

Now, if writing and reading the same file concurrently is a good idea is completely dependent of what you want to achieve and if you know what you are doing. In most cases it's not a good idea, but again it depends.

UPDATE 2. It's perfectly possible to use code above to achieve your goal (open .docx for reading while MS Word has it open for writing:

using (var file = new FileStream(@"my.docx", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
var myPackage = Package.Open(file);
// here do what you want with your .docx
}

File sharing not working as expected

 var fileStream2 = new FileStream(..., FileShare.Read)

This trips up lots of programmers. Everybody assumes that this added read sharing. It didn't, the original file access request already allowed reading and specifying it again doesn't change anything. Instead it denies write sharing. And that cannot work because somebody already got write access. And is using it, you cannot remove that right. So your request to access the file will fail.

You must include FileShare.Write.

System.IO effects on OS's file system performance in C#

According to reflector, File.Create(path) is just:

new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.None);

and new FileStream(@"file.dat", FileMode.Create, FileAccess.Write) also gets the 4096 buffer - so no, it is just the FileAccess.ReadWrite which is different.

And I don't think FileAccess.Write is much better than FileAccess.ReadWrite because they both locks the file - but I may be wrong.

to comment: because FileShare is set to None and it is write access you want they properly (=I don't know for sure) ain't much of a difference. However, if you wanted Read access the new FileStream(path, mode, access) approach is different because FileShare is set to Read as a default. However, in this case you want to create a file, and therefore it does not make much sense to only read ;-)

FileStream in app cannot access file locked by service despite FileShare.Read option

On the non-service application, it also needs the FileShare.ReadWrite parameter, resulting:

using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))

The problem you're having isn't that the service isn't allowing the non-service application to open the file, but that your non-service application tries to read the file, declaring that it cannot be read/written/deleted by other processes at the same, when some other process already has it open.

FileStream keeps saying that the file is busy while it's not opened by any application at all

Your process is conflicting with itself.

You're opening it here (in a way that would be simpler with File.CreateText, by the way) - but never actually using the writer you've created:

FileStream stream = new FileStream("DATA.txt", FileMode.Create, FileAccess.Write);
StreamWriter writer = new StreamWriter(stream);

... then you're separately trying to append to it here, even though stream is already open:

File.AppendAllText("DATA.txt", studentID + Environment.NewLine);

Pick one way of writing to the file, and stick to it. Personally, I'd suggest creating a List<string> or an array for all the lines you want to write, and just call File.WriteAllLines once, but you could continue to use either the writer approach or the File.AppendAllText approach... just not together like this.

Here's a simpler implementation of your saveFile method (renamed to follow normal naming conventions):

public static void SaveFile(string studentId, string studentName, string subjectA, string subjectB)
{
var lines = new[] { studentId, studentName, subjectA, subjectB };
// (Write the lines to the console here if you want.)
File.WriteAllLines("DATA.txt", lines);
}

If you want to append data instead of recreating the file, just use File.AppendAllLines instead.

Is there a limit for System.IO.FileShare?

I don't know the exact limit imposed by .NET/windows, so I have created a real test for you. I ran the following test code for few minutes and I found that up to 635908 counts of system.io.fileshare usage, it still functional, i.e. you can still read the flat database file's content.

Here is the code (it is a winform application, .Net 4):

Public Class Form1

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim filepath As String = "c:\database.txt"

Dim filestream As System.IO.FileStream

Dim count As Int32

For count = 0 To System.Int32.MaxValue
filestream = New System.IO.FileStream(filepath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read)
AppendLog(count, filestream.ReadByte)
Next
End Sub

Private LogFilepath As String = "C:\LogInfo.txt"
Private Enter As String = Chr(13) & Chr(10)
Private Space As String = " "

Private Sub AppendLog(ByVal Sequence As Int32, ByVal info As Byte)
System.IO.File.AppendAllText(LogFilepath, Enter & Sequence & Space & CStr(info))
End Sub

End Class


Related Topics



Leave a reply



Submit