You are right, this is actually a limitation of .NET's System.IO classes. Trying to open the file using System.IO.File.OpenRead(path) fails with the same exception.
We will consider utilizing the \? naming convention for too-long paths in one of the next releases, but you can easily work around this with the current version of Rebex SFTP by using the stream based-API and the following custom stream object:
C#:
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
public class FileStream2 : FileStream
{
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const uint FILE_SHARE_READ = 0x00000001;
private const uint FILE_SHARE_WRITE = 0x00000002;
private const uint FILE_SHARE_DELETE = 0x00000004;
private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
private const int CREATE_NEW = 1;
private const int CREATE_ALWAYS = 2;
private const int OPEN_EXISTING = 3;
private const int OPEN_ALWAYS = 4;
private const int TRUNCATE_EXISTING = 5;
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
public FileStream2(string path, FileMode mode, FileAccess access, FileShare share)
: base(Open(path, mode, access, share), access)
{
}
private static SafeFileHandle Open(string path, FileMode mode, FileAccess access, FileShare share)
{
uint dwDesiredAccess = 0;
if ((access & FileAccess.Read) == FileAccess.Read)
dwDesiredAccess |= GENERIC_READ;
if ((access & FileAccess.Write) == FileAccess.Write)
dwDesiredAccess |= GENERIC_WRITE;
uint dwShareMode = ((uint)share) & 7;
uint dwCreationDisposition;
switch (mode)
{
case FileMode.CreateNew:
case FileMode.Create:
case FileMode.Open:
case FileMode.OpenOrCreate:
case FileMode.Truncate:
dwCreationDisposition = (uint)mode;
break;
default:
throw new ArgumentException("Unsupported mode.", "mode");
}
SafeFileHandle handle = CreateFile(path, dwDesiredAccess, dwShareMode, IntPtr.Zero, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
if (handle.IsInvalid)
{
int errorCode = Marshal.GetLastWin32Error();
string errorMessage = new Win32Exception(errorCode).Message;
throw new IOException(errorMessage, errorCode);
}
return handle;
}
}
With this class, you can open file stream using the \? naming convention and use them with Rebex SFTP API:
using (FileStream2 source = new FileStream2(longLocalPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
sftp.PutFile(source, remotePath);
}
using (FileStream2 target = new FileStream2(longLocalPath, FileMode.Create, FileAccess.Write, FileShare.Read))
{
sftp.GetFile(remotePath, target);
}
A similar approach will work with BeginPutFile
/BeginGetFile
as well, but instead of using
, you would have to pass the stream to the callback method (using the state object, for example) and close it there.