Sftp::BeginGetFile blocks local (destination) file from reading during downloading

+1 vote
asked Jul 21, 2010 by _FRED_ (640 points)
edited Jul 23, 2010

Can you share a local file for reading? I know, that I can use an overloading with Stream parameter, but default behavior is strange.

I'm using the following code:

var client = new Sftp();
client.Connect("Server");
client.Login("UserName", "Password");
var destinationFile = @"C:\Temp\Eee.txt";
var destination = destinationFile;
//var destination = new FileStream(destinationFile, FileMode.Create, FileAccess.Write, FileShare.Read);
client.BeginGetFile("FileName.txt", destination, null, null);
while (!File.Exists(destinationFile))
{
  Thread.Sleep(100);
}
var test = new FileStream(destinationFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
// "The process cannot access the file 'C:\\Temp\\Eee.txt' because it is being used by another process."

If you uncomment line "var destination = new FileStream(…" (and comment "var destination = destinationFile;"), the test stream opens successfully.

Applies to: Rebex SFTP

2 Answers

+1 vote
answered Jul 23, 2010 by Lukas Pokorny (121,330 points)
edited Jul 23, 2010
 
Best answer

It turned out we were using File.Create in the GetFile method, and apparently this implies FileShare.None, which was indeed an oversight that resulted in strange behavior. Thanks for letting us know about this! We will definitely fix it for the next release. Sorry for inconvenience!

+1 vote
answered Jul 22, 2010 by Lukas Pokorny (121,330 points)
edited Jul 23, 2010

Update: This turned out not to be the answer to Fred's question (see the other answer), but we are keeping it because someone else might still find it useful later.

Short answer:

You have to specify FileShare.ReadWrite when opening the currently-downloaded file for reading. It's a feature of the OS and there is no way around it.

Long answer:

GetFile/BeginGetFile methods actually do share the local file for reading. This is how we open it:

Stream localFile = File.Open(localPath, FileMode.Create, FileAccess.Write, FileShare.Read)

However, you probably tried accessing the file using a code like this:

Stream stream = File.OpenRead(path);

or this:

Stream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);

and it failed.

As you can see, this fails even though we did share the file for reading. But this is not Rebex issue - identical behavior can be reproduced using the following simple code:

string path = ...
Stream s1 = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.Read);
Stream s2 = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);

Why does this fail? Because you have to specify FileShare.ReadWrite when opening the file for reading. If only FileShare.Read is specified, you are actually trying to deny others to write the file at the same time, which is not possible since there is already a writer.

And indeed, the following code works fine:

string path = ...
Stream s1 = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.Read);
Stream s2 = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

It might seem a bit odd that File.OpenRead can't be used in this case and many classes that rely on it fail, but there is nothing we can do about that. Apparently, this is a feature of the filesystem itself.

commented Jul 23, 2010 by _FRED_ (640 points)
Please, see my code sample. I'm using "Rebex SFTP for .NET - Trial Version" 2.0.3854.0
commented Jul 23, 2010 by Lukas Pokorny (121,330 points)
Thanks, it looks you are right!
...