My app require copying file using SFTP from a location directly to azure storage. Our app using C# with Dotnet 4.6 and our WinSCP version are 5.21.1.
My old code work using session.GetFileToDirectory() method, but the problem is it need to store the file on temp folder inside our hosting.
using (Session session = new Session()){
session.Open(sessionOptions);
TransferOptions transferOptions = new TransferOptions();
transferOptions.TransferMode = TransferMode.Binary;
var transfer = session.GetFileToDirectory(FilePath, fullPath);
using (Stream stream = File.OpenRead(transfer.Destination))
{
UploadToAzure(stream, Filename, Foldername);
}
}
As we planned to entirely using azure storage, I change my code like this
using (Session session = new Session())
{
session.Open(sessionOptions);
TransferOptions transferOptions = new TransferOptions();
transferOptions.TransferMode = TransferMode.Binary;
using (Stream stream = session.GetFile(FilePath, transferOptions))
{
UploadToAzure(stream, Filename, Foldername);
}
}
Here my library that upload the file using stream to azure. This code is working fine using my old code that still save to temp folder before send to azure.
public static string UploadToAzure(Stream attachment, string Filename, string Foldername)
{
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
var connectionString = $"{ConfigurationManager.AppSettings["AzureFileShareConnectionString"]}";
string shareName = $"{ConfigurationManager.AppSettings["AzureFileShareFolderName"]}";
string dirName = $"files\\{Foldername}";
string fileName = Filename;
try
{
ShareClient share = new ShareClient(connectionString, shareName);
share.CreateIfNotExists();
ShareDirectoryClient directory = share.GetDirectoryClient(dirName);
directory.CreateIfNotExists();
// Get a reference to a file and upload it
ShareFileClient file = directory.GetFileClient(fileName);
file.Create(attachment.Length);
file.UploadRange(
new HttpRange(0, attachment.Length),
attachment);
}
catch (Exception e)
{
return $"Uploaded {Filename} failed : {e.ToString()}";
}
return $"{Filename} Uploaded";
}
But currently my new code not working with error message : '((WinSCP.PipeStream)stream).Length' threw an exception of type 'System.NotSupportedException'.
This is the object description on creating stream using session.GetFile method
This is 'exception stacktrace' on sending the empty-stream to azure
CodePudding user response:
Try this way as we have many examples here,
// To download the file
TransferOptions transferOptions = new TransferOptions();
transferOptions.TransferMode = TransferMode.Binary;
TransferOperationResult transferResult = null;
try
{
transferResult = session.GetFiles(remoteFilePath, localPath, false, transferOptions);
transferResult.Check();
// Other code here ....
}
For best practice to download to a physical local file GetFileToDirectory
is preferred.
System.IO.Stream implementation that can be used to read the remote file contents. It implements the Read and Dispose methods only, and other methods derived from these two, like Close and CopyTo. Seeking and cancelling the transfer is not supported. Calling the Dispose (or the Close) will finish downloading the file.
References: Session.GetFileToDirectory()
CodePudding user response:
The Stream
returned by WinSCP Session.GetFile
does not implement the Stream.Length
property, because WinSCP cannot guarantee that the size of the file is fixed. The remote file might be changing while you are downloading the file. Not to mention ASCII transfer mode, when the file is converted while being transferred, with unpredictable impact on the final size.
You use the size (Stream.Length
) in two places:
When creating the file:
file.Create(attachment.Length);
The parameter of
ShareFileClient.Create
ismaxSize
. So it does not look like it's a real size. You can possibly just put an arbitrary large number here.Or if you prefer (and know that the file is not changing), read the current size of the remote file using
Session.GetFileInfo
andRemoteFileInfo.Length
:file.Create(session.GetFileInfo(FilePath).Length);
When uploading the contents:
file.UploadRange(new HttpRange(0, attachment.Length), attachment);
The above can be replaced with simple
ShareFileClient.Upload
:file.Upload(attachment);