Why Ftp Downloading is so slow with c# / FtpWebRequest - c#

I'm using the FTP function in c# in order to download somes files.
My problem is that the download is slow.
I don't have any problem with Filezilla, and my connection is OK.
I'm thinking about the FTP functions implementations, here is my code :
public static string DownloadFromList(string strParam,string filePath)
{
string[] FilesToDownload = strParam.Split('\n');
try
{
FtpWebRequest reqFTP;
foreach(string file in FilesToDownload)
{
string tmpfile = file;
if (tmpfile.Contains("\n"))
{
tmpfile = tmpfile.Replace('\n', ' ');
}
FileInfo fileToDownload = new FileInfo(tmpfile);
tmpfile = fileToDownload.Name;
FileStream outputStream = new FileStream(filePath.Trim() + "\\" + tmpfile.Trim(), FileMode.Create);
if (strDirectory.Trim() != "")
{
reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + strHost.Trim() + "/" + strDirectory.Trim() + "/" + tmpfile.Trim()));
}
else
{
reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + strHost.Trim() + "/" + tmpfile.Trim()));
}
reqFTP.ConnectionGroupName = "MyGroupName";
reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
reqFTP.UseBinary = true;
reqFTP.KeepAlive = true;
reqFTP.Timeout = -1;
reqFTP.Credentials = new NetworkCredential(strUser.Trim(), strPass.Trim());
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
Stream ftpStream = response.GetResponseStream();
StreamReader reader = new StreamReader(ftpStream, System.Text.Encoding.UTF8);
long cl = response.ContentLength;
int bufferSize = 8192;
char[] bytesInStream = new char[8192];
int readCount;
StreamWriter sw = new StreamWriter(outputStream);
int len;
while ((len = reader.Read(bytesInStream, 0, bytesInStream.Length)) > 0)
{
sw.Write(bytesInStream, 0, len);
}
sw.Close();
outputStream.Close();
ftpStream.Close();
response.Close();
}
return "ok";
}
catch (Exception ex)
{
return (ex.Message);
}
}
Is it necessary to set useBinary = true ?
KeepAlive is setted to true in order to use the same connection, but is it true ?
Is it possible to set the credential only one time ?
How can i improve this please?
Thanks a lot :)
Best regards,

Related

Get File Size from FTP (in SSIS, using Dts.Connections)

I have an issue sending big file to an FTP site and I would like to check the file size after the transfer (sometimes it works sometimes it fails). The transfer is within an SSIS and I'm using Dts.Connections in C#.
My code:
public long TransferFile(string file)
{
long filesize = 0L;
try
{
string[] newfile = new[] { file };
ConnectionManager ftpCM = Dts.Connections["ftp_server"];
string remoteDir = Dts.Variables["FtpWorkingDirectory"].Value.ToString();
FtpClientConnection ftpClient = new FtpClientConnection(ftpCM.AcquireConnection(null));
ftpClient.UsePassiveMode = true;
ftpClient.Connect();
ftpClient.Retries = 10;
ftpClient.SetWorkingDirectory(remoteDir);
ftpClient.SendFiles(newfile, remoteDir, true, false);
ftpClient.Close();
}
catch (Exception ex)
{
throw ex;
}
return filesize;
}
I found examples using FtpWebRequest but I don't have the ftp uri available so I don't see how to use this method. How can I get this file size?
UPDATE:
Adding this:
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + ftpCM.ConnectionString + remoteDir + "/" + file));
request.Proxy = null;
DtsProperty ServerUsername = ftpCM.Properties["ServerUserName"];
DtsProperty ServerPassword = ftpCM.Properties["ServerPassword"];
request.Credentials = new NetworkCredential(ServerUsername.GetValue(ftpCM).ToString(), ServerPassword.GetValue(ftpCM).ToString());
request.Method = WebRequestMethods.Ftp.GetFileSize;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
filesize = response.ContentLength;
response.Close();
I get error: The property ServerPassword is write-only.
The final solution was to store the username and password in Variables instead of connection parameters.
And with the following code:
public long TransferFile(string file)
{
long filesize = 0L;
try
{
string[] newfile = new[] { file };
ConnectionManager ftpCM = Dts.Connections["ftp_server"];
string remoteDir = Dts.Variables["FtpWorkingDirectory"].Value.ToString();
string ServerUsername = Dts.Variables["ServerUsername"].Value.ToString();
string ServerPassword = Dts.Variables["ServerPassword"].Value.ToString();
FtpClientConnection ftpClient = new FtpClientConnection(ftpCM.AcquireConnection(null));
ftpClient.UsePassiveMode = true;
ftpClient.ServerUserName = ServerUsername;
ftpClient.ServerPassword = ServerPassword;
ftpClient.Connect();
ftpClient.Retries = 10;
ftpClient.SetWorkingDirectory(remoteDir);
ftpClient.SendFiles(newfile, remoteDir, true, false);
ftpClient.Close();
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + ftpCM.ConnectionString + remoteDir + "/" + Path.GetFileName(file)));
request.Proxy = null;
request.Credentials = new NetworkCredential(ServerUsername, ServerPassword);
request.Method = WebRequestMethods.Ftp.GetFileSize;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
filesize = response.ContentLength;
response.Close();
}
catch (Exception ex)
{
throw ex;
}
return filesize;
}

Remote server error: 150 Opening Binary mode data connection

I am trying to create a small console application that downloads files from a ftp server through Explicit FTP over TLS. I have create these applications before but i am getting an error with this one. I keep Getting this error:
The Remote Server returned an error: 150 Opening BINARY mode data connection fro "filename" <2000 bytes>.
I cant seem to figure out that to do, can anyone help me?
this is my code:
public void DownloadFiles(string fileName)
{
uri.Scheme = "ftp";
uri.Host = ftpUrl;
uri.Port = 21;
uri.UserName = username;
uri.Password = password;
uri.Path = "out";
try
{
FtpWebRequest reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri.ToString() + "/" + fileName));
reqFTP.EnableSsl = true;
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential(username, password);
reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
FtpWebResponse response;
response = (FtpWebResponse)reqFTP.GetResponse();
Stream responseStream = response.GetResponseStream();
FileStream writeStream = new FileStream(localFolder + fileName, FileMode.Create);
long length = response.ContentLength;
int bufferSize = 2048;
Byte[] buffer = new Byte[bufferSize];
int readCount = responseStream.Read(buffer, 0, bufferSize);
while (readCount > 0)
{
writeStream.Write(buffer, 0, readCount);
readCount = responseStream.Read(buffer, 0, bufferSize);
}
AppendLogFile(response, "Downloading Files: ", fileName);
writeStream.Close();
responseStream.Close();
response.Close();
reqFTP.Abort();
}
catch (Exception ex)
{
Console.WriteLine("Error in DownloadFileByFileName method!! " + ex.Message);
}
}
thanks!
I could not find the solution for this so i when over to use Chillkat for this application. works fine. But needed to buy a year license.
this is my code:
public void Connect(string fileName)
{
bool success;
success = ftp.UnlockComponent("license");
if (!success)
{
Console.WriteLine(ftp.LastErrorText);
return;
}
ftp.IdleTimeoutMs = 10000;
ftp.AuthTls = true;
ftp.Ssl = false;
ftp.Hostname = ftpUrl;
ftp.Port = 21;
ftp.Username = username;
ftp.Password = password;
ftp.KeepSessionLog = true;
success = ftp.Connect();
if (success != true)
{
Console.WriteLine(ftp.LastErrorText);
return;
}
ftp.ClearControlChannel();
bool sucess = ftp.GetFile("out/" + fileName, localFolder + fileName);
if (!success)
{
Console.WriteLine(ftp.LastErrorText);
AppendErrorLogFile("Error in downloading file", "Download file method", fileName);
return;
}
else
{
AppendLogFile("Download Success", "Download File Method", fileName);
ftp.DeleteRemoteFile("out/" + fileName);
}
}
long length = response.ContentLength;
Is at fault here I believe, if you use this to download a file that already exists, your request will close before you can get a response from the server and throw the 150 error.

How do I pull the file locations (path and filename) for all files at a given FTP address?

Hi and thanks for looking!
Background
I need to pull the file locations (path and filename) for all files at a given FTP address.
For files on a mapped network or local drive, this code would work:
foreach(string fileName f in Directory.GetFiles("C\\:SomeDirectory"), "*.*",
SearchOption.AllDirectories)
{
//do stuff with each file found
}
But this will NOT work over an FTP connection. I have already found this MS documentation which covers the establishing of an FTPWebRequest, but it does not show me how to loop through each file found (in all nested directories as well).
I am using C# within a forms app.
Question
How do I accomplish this:
foreach(string fileName f in Directory.GetFiles("C\\:SomeDirectory"), "*.*",
SearchOption.AllDirectories)
{
//do stuff with each file found
}
With an FTP connection?
Many thanks!!
UPDATE / Final Answer
Special thanks to #sunk for getting this going. I made a minor tweek to his code that makes it fully recursive so that it can drill into nested folders. Here is the final code:
//A list that holds all file locations in all folders of a given FTP address:
List<string> fnl= new List<string>();
//A string to hold the base FTP address:
string ftpBase = "ftp://[SOME FTP ADDRESS]";
//A button-click event. Can be a stand alone method as well
private void GetFileLocations(object sender, EventArgs e)
{
//Get the file names from the FTP location:
DownloadFileNames(ftpBase);
//Once 'DownloadFileNames' has run, we have populated 'fnl'
foreach(var f in fnl)
{
//do stuff
}
}
//Gets all files in a given FTP address. RECURSIVE METHOD:
public void DownloadFileNames(string ftpAddress)
{
string uri = ftpAddress;
FtpWebRequest reqFTP = (FtpWebRequest)FtpWebRequest.Create(uri);
reqFTP.Credentials = new NetworkCredential("pella", "PellaWA01!");
reqFTP.EnableSsl = false;
reqFTP.KeepAlive = false;
reqFTP.UseBinary = true;
reqFTP.UsePassive = true;
reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
Stream responseStream = response.GetResponseStream();
List<string> files = new List<string>();
StreamReader reader = new StreamReader(responseStream);
while (!reader.EndOfStream)
files.Add(reader.ReadLine());
reader.Close();
responseStream.Dispose();
//Loop through the resulting file names.
foreach (var fileName in files)
{
var parentDirectory = "";
//If the filename has an extension, then it actually is
//a file and should be added to 'fnl'.
if (fileName.IndexOf(".") > 0)
{
fnl.Add(ftpAddress.Replace("ftp://pella.upload.akamai.com/140607/pella/", "http://media.pella.com/") + fileName);
}
else
{
//If the filename has no extension, then it is just a folder.
//Run this method again as a recursion of the original:
parentDirectory += fileName + "/";
try
{
DownloadFileNames(ftpAddress + parentDirectory);
}
catch (Exception)
{
//throw;
}
}
}
}
First of all, you have to get the files name local on your machine using FTPWebRequest.
WebRequestMethods.Ftp.ListDirectory;
then use foreach {};
Here is the code:-
public List<string> DownloadFileNames()
{
string uri = "ftp://" + ftpServerIP + "/";
FtpWebRequest reqFTP = (FtpWebRequest)FtpWebRequest.Create(uri);
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
reqFTP.EnableSsl = true;
reqFTP.KeepAlive = false;
reqFTP.UseBinary = true;
reqFTP.UsePassive = Settings.UsePassive;
reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(AcceptAllCertifications);
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
Stream responseStream = response.GetResponseStream();
List<string> files = new List<string>();
StreamReader reader = new StreamReader(responseStream);
while (!reader.EndOfStream)
files.Add(reader.ReadLine());
reader.Close();
responseStream.Dispose();
return files;
}
Now you have the List:-
List<string> FileNameList = DownloadFileNames();
foreach (var fileName in FileNameList)
{
}
the ListDirectoryDetails command used in the example just returns a string. You will have to manually parse it to build a list of files and subdirectories.
Found at http://social.msdn.microsoft.com/Forums/en/ncl/thread/079fb811-3c55-4959-85c4-677e4b20bea3
string[] files = GetFileList();
foreach (string file in files)
{
Download(file);
}
public string[] GetFileList()
{
string[] downloadFiles;
StringBuilder result = new StringBuilder();
WebResponse response = null;
StreamReader reader = null;
try
{
FtpWebRequest reqFTP;
reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + ftpServerIP + "/"));
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
reqFTP.Proxy = null;
reqFTP.KeepAlive = false;
reqFTP.UsePassive = false;
response = reqFTP.GetResponse();
reader = new StreamReader(response.GetResponseStream());
string line = reader.ReadLine();
while (line != null)
{
result.Append(line);
result.Append("\n");
line = reader.ReadLine();
}
// to remove the trailing '\n'
result.Remove(result.ToString().LastIndexOf('\n'), 1);
return result.ToString().Split('\n');
}
catch (Exception ex)
{
if (reader != null)
{
reader.Close();
}
if (response != null)
{
response.Close();
}
downloadFiles = null;
return downloadFiles;
}
}
private void Download(string file)
{
try
{
string uri = "ftp://" + ftpServerIP + "/" + remoteDir + "/" + file;
Uri serverUri = new Uri(uri);
if (serverUri.Scheme != Uri.UriSchemeFtp)
{
return;
}
FtpWebRequest reqFTP;
reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + ftpServerIP + "/" + remoteDir + "/" + file));
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
reqFTP.KeepAlive = false;
reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
reqFTP.UseBinary = true;
reqFTP.Proxy = null;
reqFTP.UsePassive = false;
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
Stream responseStream = response.GetResponseStream();
FileStream writeStream = new FileStream(localDestnDir + "\" + file, FileMode.Create);
int Length = 2048;
Byte[] buffer = new Byte[Length];
int bytesRead = responseStream.Read(buffer, 0, Length);
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = responseStream.Read(buffer, 0, Length);
}
writeStream.Close();
response.Close();
}
catch (WebException wEx)
{
MessageBox.Show(wEx.Message, "Download Error");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Download Error");
}
}

FtpWebRequest while writing to stream if network goes down then FTP Server gives access denied problem when tried to re upload

I am using FtpWebRequest to upload a file.
Facing a problem of which i am not getting solution.
While uploading a heavy file if network get disconnected then FTP server acquires lock on file being uploaded, now when user tries to re upload the same file then it get access denied error.
I have set TimeOut and ReadWriteTimeOut to 5 secs of FtpWebRequest on FTP Server it is 10 secs.
Even if i try to upload same file after an Hour then also same problem exist.
// Get the object used to communicate with the server.
request = (FtpWebRequest)WebRequest.Create(new Uri("ftp://" + FtpInfo.FtpServer.Trim() + "/" + FtpInfo.FtpFolderPath.Trim() + "/" + FileName.Trim()));
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Proxy = null;
// FTP site uses anonymous logon.
request.Credentials = new NetworkCredential(FtpInfo.UserNameForFTP.Trim(), FtpInfo.PasswordForFTP.Trim());
request.UsePassive = FtpInfo.UsePassive;
request.KeepAlive = FtpInfo.KeepAlive;
request.Timeout = FtpInfo.TimeOut; //Milliseconds
request.UseBinary = true;
request.ReadWriteTimeout = FtpInfo.TimeOut; //Milliseconds
FileInfo fi = new FileInfo(SourceLocation);
long length = fi.Length;
BytesUploaded = length;
long uploadSize = 0;
if (chunks == 0)
{
chunks = 1024;
}
else
{
buffLength = chunks;
}
byte[] buff = new byte[buffLength];
int contentLen;
using (FileStream fs = fi.OpenRead())
{
using (Stream strm = request.GetRequestStream())
{
contentLen = fs.Read(buff, 0, buffLength);
try
{
while (contentLen != 0)
{
Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate() { lblProgress.Content = "Uploading '" + FileName + "'......" + "Bytes Uploaded (" + uploadSize.ToString() + "/" + length.ToString() + ")"; });
strm.Write(buff, 0, contentLen);
uploadSize += contentLen;
contentLen = fs.Read(buff, 0, buffLength);
}
strm.Close();
}
catch (Exception ex)
{
if (strm!=null)
{
try
{
strm.Close();
}
catch
{
throw ex;
}
}
throw ex;
}
}
fs.Close();
}
try
{
//requestStream.Close(); -orignal
fi = null;
request=null;
}
catch { }
I don't know enough about your app (is the FTP upload on a separate thread for example) but try this:
bool DoneOK = false;
FtpWebRequest request = null;
FtpWebResponse response = null;
try {
// Get the object used to communicate with the server.
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri("ftp://" + FtpInfo.FtpServer.Trim() + "/" + FtpInfo.FtpFolderPath.Trim() + "/" + FileName.Trim()));
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Proxy = null;
// FTP site uses anonymous logon.
request.Credentials = new NetworkCredential(FtpInfo.UserNameForFTP.Trim(), FtpInfo.PasswordForFTP.Trim());
request.UsePassive = FtpInfo.UsePassive;
request.KeepAlive = FtpInfo.KeepAlive;
request.Timeout = FtpInfo.TimeOut; //Milliseconds
request.UseBinary = true;
request.ReadWriteTimeout = FtpInfo.TimeOut; //Milliseconds
long length = new FileInfo(SourceLocation).Length;
long uploadSize = 0;
if ( chunks < 1 )
chunks = 1024;
buffLength = chunks;
byte[] buff = new byte[buffLength];
int contentLen = 0;
string MSG = "";
using (FileStream fs = File.OpenRead (SourceLocation))
using (Stream strm = request.GetRequestStream())
{
while ((contentLen = fs.Read(buff, 0, buffLength)) > 0 )
{
MSG = "Uploading '" + FileName + "'......" + "Bytes Uploaded (" + uploadSize.ToString() + "/" + length.ToString() + ")";
string tmp_MSG = MSG;
Dispatcher.Invoke(DispatcherPriority.Normal, () => { lblProgress.Content = tmpMSG; });
strm.Write(buff, 0, contentLen);
uploadSize += contentLen;
};
strm.Close();
// necessary - the upload occurs really here !
response = (FtpWebResponse) request.GetResponse();
// check the response codes... for example FileActionOK...
if ( response.StatusCode == System.Net.FtpStatusCode.FileActionOK )
DoneOK = true;
response.Close(); response = null;
request = null;
}
}
catch
{
};
if ( request != null )
{
if ( response != null )
{
try { response.Close(); } catch {};
response = null;
}
if ( !DoneOK )
{
try { request.Abort (); } catch {};
}
request = null;
}
This was problem related to the FTP Server we were using, when we switched FTP server to IIS then everything worked smoothly.
Thanks for all the help

How to download FTP files with automatic resume in case of disconnect

I can download FTP files, but the download code does not have a resume facility and multi part files download. Because there are file larger than 500 MB, I can't download them continuously, because the connection gets closed and it starts download from beginning. I wanted my code a resume facility if it gets disconnected.
The code I am using is:
public string[] GetFileList()
{
string[] downloadFiles;
StringBuilder result = new StringBuilder();
FtpWebRequest reqFTP;
try
{
reqFTP = (FtpWebRequest)FtpWebRequest.Create(
new Uri("ftp://" + ftpServerIP + "/"));
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
WebResponse response = reqFTP.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
//MessageBox.Show(reader.ReadToEnd());
string line = reader.ReadLine();
while (line != null)
{
result.Append(line);
result.Append("\n");
line = reader.ReadLine();
}
result.Remove(result.ToString().LastIndexOf('\n'), 1);
reader.Close();
response.Close();
//MessageBox.Show(response.StatusDescription);
return result.ToString().Split('\n');
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
downloadFiles = null;
return downloadFiles;
}
}
private void Download(string filePath, string fileName)
{
FtpWebRequest reqFTP;
try
{
//filePath = <<The full path where the file is to be created.>>,
//fileName = <<Name of the file to be created
// (Need not be the name of the file on FTP server).>>
FileStream outputStream =
new FileStream(filePath + "\\" + fileName, FileMode.Create);
reqFTP = (FtpWebRequest)FtpWebRequest.Create(
new Uri("ftp://" + ftpServerIP + "/" + fileName));
reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
Stream ftpStream = response.GetResponseStream();
long cl = response.ContentLength;
int bufferSize = 2048;
int readCount;
byte[] buffer = new byte[bufferSize];
readCount = ftpStream.Read(buffer, 0, bufferSize);
while (readCount > 0)
{
outputStream.Write(buffer, 0, readCount);
readCount = ftpStream.Read(buffer, 0, bufferSize);
}
ftpStream.Close();
outputStream.Close();
response.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Before you start downloading check for the existence of the file on the local filesystem. If it exists, then get the size and use that for the ContentOffset member of the FtpWebRequest object. This functionality may not be supported by the FTP server, though.
A native implementation of FTP download resumption using the FtpWebRequest:
bool resume = false;
do
{
try
{
FileMode mode = resume ? FileMode.Append : FileMode.Create;
resume = false;
using (Stream fileStream = File.Open(#"C:\local\path\file.dat", mode))
{
var url = "ftp://ftp.example.com/remote/path/file.dat";
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
request.Credentials = new NetworkCredential("username", "password");
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.ContentOffset = fileStream.Position;
using (Stream ftpStream = request.GetResponse().GetResponseStream())
{
ftpStream.CopyTo(fileStream);
}
}
}
catch (WebException)
{
resume = true;
}
}
while (resume);
Or use an FTP library that can resume the transfer automatically.
For example WinSCP .NET assembly does. With it, a resumable download is as trivial as:
// Setup session options
var sessionOptions = new WinSCP.SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "user",
Password = "mypassword"
};
using (var session = new Session())
{
// Connect
session.Open(sessionOptions);
// Resumable download
session.GetFileToDirectory("/home/user/file.zip", #"C:\path");
}
(I'm the author of WinSCP)

Categories

Resources