I have 1 issues when i try too send file using FTP from Azure WebJobs.
This throw alternatly 'The remote server returned an error: (530) Not logged in.', my code work great in localhost (on dev computer).
I have read all off thoses Post but i didn't find a way :
FTPWebRequest 530 Error: Not Logged in issue
FTP The remote server returned an error: (530) Not logged in
Fetching files from FTP server via Azure webjobs
Ftp to external server in azure webjob not working
Other but i have only 1 webjobs in my app service and the CPU was at 30% when the jobs running ..
File download from FTP fails only in Azure web app
Edit :
Code Make Directory
FtpWebRequest reqFTP = null;
Stream ftpStream = null;
string[] subDirs = directory.Split('/');
string currentDir = publishUrl;
foreach (string subDir in subDirs)
{
try
{
currentDir = currentDir + "/" + subDir;
reqFTP = (FtpWebRequest)FtpWebRequest.Create(currentDir);
reqFTP.Method = WebRequestMethods.Ftp.MakeDirectory;
reqFTP.UseBinary = true;
//reqFTP.UsePassive = true;
//reqFTP.KeepAlive = true;
reqFTP.Credentials = new NetworkCredential(userName, userPWD);
FtpWebResponse response = (FtpWebResponse)await reqFTP.GetResponseAsync();
ftpStream = response.GetResponseStream();
ftpStream.Close();
response.Close();
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
//directory already exist I know that is weak but there is no way to check if a folder exist on ftp...
}
}
Code Send File :
try
{
using (WebClient client = new WebClient())
{
client.Credentials = new NetworkCredential(userName, userPWD);
client.UploadFile(publishUrl, "STOR", localFileName);
return true;
}
}
catch (Exception exception)
{
//Console.Error.Write(exception.Message);
Console.WriteLine(exception.Message);
return false;
}
And log when i run code in Azure WebApp WebJobs (Failed) :
https://pastebin.com/vgTxqT5p
And log when i run code in local machine (Work Great) :
https://pastebin.com/hBpum8T0
I think the workaround was on WebJobs app style and normal function wasn't waiting. I'm going to change my code to use Async Await method for all my WebJobs program.
Any have a way ?
Thx in advance.
Bad way (and i didn't Like this) but it's work..... :
Change the Make Directory Function like this :
public static void MakeFTPDir(string publishUrl, string userName, string userPWD, string directory)
{
FtpWebRequest reqFTP = null;
Stream ftpStream = null;
string[] subDirs = directory.Split('/');
string currentDir = publishUrl;
foreach (string subDir in subDirs)
{
bool isNotCreated = true;
int iTentative = 0;
currentDir = currentDir + "/" + subDir;
while (isNotCreated)
{
iTentative++;
try
{
reqFTP = (FtpWebRequest)FtpWebRequest.Create(currentDir);
reqFTP.Method = WebRequestMethods.Ftp.MakeDirectory;
reqFTP.UseBinary = true;
reqFTP.UsePassive = true;
reqFTP.KeepAlive = true;
reqFTP.Credentials = new NetworkCredential(userName, userPWD);
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
ftpStream = response.GetResponseStream();
ftpStream.Close();
response.Close();
}
catch(WebException webException)
{
FtpWebResponse excWebResponse = (FtpWebResponse)webException.Response;
if(excWebResponse.StatusCode == FtpStatusCode.NotLoggedIn)
{
Console.WriteLine("WebException ==> NotLoggedIn >> Tentative :" + iTentative);
isNotCreated = true;
}
else
{
Console.WriteLine(webException.Message);
isNotCreated = false;
}
}
catch (Exception exception)
{
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
if (response.StatusCode == FtpStatusCode.NotLoggedIn)
{
Console.WriteLine("Exception ==> NotLoggedIn >> Tentative :" + iTentative);
isNotCreated = true;
}
else
{
Console.WriteLine(exception.Message);
isNotCreated = false;
}
}
}
}
}
Change the send file function like this
public static bool SendFtpFile(string publishUrl, string userName, string userPWD, string localFileName)
{
bool isNotCreated = true;
int iTentative = 0;
while (isNotCreated)
{
iTentative++;
try
{
using (WebClient client = new WebClient())
{
client.Credentials = new NetworkCredential(userName, userPWD);
client.UploadFile(publishUrl, "STOR", localFileName);
return true;
}
}
catch (WebException webException)
{
FtpWebResponse excWebResponse = (FtpWebResponse)webException.Response;
if (excWebResponse.StatusCode == FtpStatusCode.NotLoggedIn)
{
Console.WriteLine("WebException ==> NotLoggedIn >> Tentative :" + iTentative);
isNotCreated = true;
}
else
{
Console.WriteLine(webException.Message);
return false;
}
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
return false;
}
}
return true;
}
Change the IfFileExistOnServer :
public static bool CheckIfFileExistsOnServer(string publishUrl, string userName, string userPWD, string fileName)
{
bool isNoCheck = true;
int iTentative = 0;
string azureBotUrl = publishUrl + "/" + fileName;
while (isNoCheck)
{
iTentative++;
try
{
var request = (FtpWebRequest)WebRequest.Create(azureBotUrl);
request.Credentials = new NetworkCredential(userName, userPWD);
request.UseBinary = true;
request.UsePassive = true;
request.KeepAlive = true;
request.Method = WebRequestMethods.Ftp.GetFileSize;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
return true;
}
catch (WebException webException)
{
FtpWebResponse excWebResponse = (FtpWebResponse)webException.Response;
if (excWebResponse.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable)
return false;
if (excWebResponse.StatusCode == FtpStatusCode.NotLoggedIn)
{
Console.WriteLine("WebException ==> NotLoggedIn >> Tentative :" + iTentative);
isNoCheck = true;
}
else
{
return false;
}
}
}
return false;
}
And Change RenameFileOnServer :
public static bool RenameFileOnServer(string publishUrl, string userName, string userPWD, string sourceFileName, string newFileName)
{
bool isNoRenameFile = true;
int iTentative = 0;
FtpWebRequest ftpRequest = null;
FtpWebResponse ftpResponse = null;
string azureBotUrl = publishUrl + "/" + sourceFileName;
while (isNoRenameFile)
{
iTentative++;
try
{
ftpRequest = (FtpWebRequest)WebRequest.Create(azureBotUrl);
ftpRequest.Credentials = new NetworkCredential(userName, userPWD);
ftpRequest.UseBinary = true;
ftpRequest.UsePassive = true;
ftpRequest.KeepAlive = true;
ftpRequest.Method = WebRequestMethods.Ftp.Rename;
ftpRequest.RenameTo = newFileName.Split('\\')[newFileName.Split('\\').Length - 1];
ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
ftpResponse.Close();
ftpRequest = null;
return true;
}
catch (WebException webException)
{
FtpWebResponse excWebResponse = (FtpWebResponse)webException.Response;
if (excWebResponse.StatusCode == FtpStatusCode.NotLoggedIn)
{
Console.WriteLine("WebException ==> NotLoggedIn >> Tentative :" + iTentative);
isNoRenameFile = true;
}
else
{
return false;
}
Console.WriteLine(webException.Message);
}
catch (Exception)
{
return false;
}
}
return false;
}
I'm waiting a call from Ms Azure Support ...
Related
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;
}
i'm using the below method to upload files from local server to FTP server, here i'm creating a new connection and initiating a new session each and every file uploading and closing the same. how to achieve this in single initiated session in c#.
this is my code
public bool UploadTempFilesToFTP()
{
string[] fileList;
try
{
ConfiguredValues conObj = new ConfiguredValues();
conObj.PickTheValuesFromConfigFile();
fileList = Directory.GetFiles(conObj.tempPath);
foreach (string FileName in fileList)
{
FtpWebRequest upldrequest = (FtpWebRequest)FtpWebRequest.Create(conObj.tempOutboundURL + FileName);
upldrequest.UseBinary = true;
upldrequest.KeepAlive = false;
upldrequest.Timeout = -1;
upldrequest.UsePassive = true;
upldrequest.Credentials = new NetworkCredential(conObj.user, conObj.pass);
upldrequest.Method = WebRequestMethods.Ftp.UploadFile;
string destinationAddress = conObj.tempPath;
FileStream fs = File.OpenRead(destinationAddress + FileName);
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
fs.Close();
Stream requestStr = upldrequest.GetRequestStream();
requestStr.Write(buffer, 0, buffer.Length);
requestStr.Close();
requestStr.Flush();
FtpWebResponse response = (FtpWebResponse)upldrequest.GetResponse();
response.Close();
File.Delete(destinationAddress + FileName);
}
Console.WriteLine("Uploaded Successfully to Temp folder");
return true;
}
catch (Exception ex)
{
Console.WriteLine("Upload failed. {0}", ex.Message);
return false;
}
}
it's weird that i answer an old question but i try almost everything to upload multiple files to ftp with no luck while the solution is very simple and effective, using LOOPING - foreach solved the issue for me i use the below function to Upload the files in one simple step..
public void Uploadbulkftpfiles(string[] list)
{
bool ife;// is folder exists
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://ftpsite.com/folder");
request.Credentials = new NetworkCredential("Username", "Password");
request.Method = WebRequestMethods.Ftp.ListDirectory;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
ife = true;
}
catch (Exception)
{
ife = false;
}
/////////////////////////////////////////////begin of upload process
if (ife)//the folder is already exists
{
foreach (var str in list)
{
try
{
FtpWebRequest requestUP2 = (FtpWebRequest)WebRequest.Create("ftp://ftpsite.com/folder" + str);
requestUP2.Credentials = new NetworkCredential("UserName", "Password");
requestUP2.Method = WebRequestMethods.Ftp.UploadFile;
requestUP2.KeepAlive = false;
requestUP2.UsePassive = true;
using (Stream fileStream = File.OpenRead("ftp://ftpsite.com/folder" + str))
using (Stream ftpStream = requestUP2.GetRequestStream())
{
fileStream.CopyTo(ftpStream);
}
}
catch (Exception ex1)
{
MessageBox.Show(ex1.Message);
}
}
}
else if (!ife)
{
//CREATE THE FOLDER
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp:ftpsite/folder");
request.Credentials = new NetworkCredential("UserName", "Password");
request.Method = WebRequestMethods.Ftp.MakeDirectory;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
}
catch (Exception excr) { MessageBox.Show(excr.Message); }
//UPLOAD THE FILES
foreach (var str in list)
{
try
{
FtpWebRequest requestUP2 = (FtpWebRequest)WebRequest.Create("ftp://ftpsite.com/folder" + str);
requestUP2.Credentials = new NetworkCredential("UserName", "Password");
requestUP2.Method = WebRequestMethods.Ftp.UploadFile;
requestUP2.KeepAlive = false;
requestUP2.UsePassive = true;
using (Stream fileStream = File.OpenRead("ftp://ftpsite.com/folder" + str))
using (Stream ftpStream = requestUP2.GetRequestStream())
{
fileStream.CopyTo(ftpStream);
}
}
catch (Exception ex1)
{
MessageBox.Show(ex1.Message);
}
}
}
}
The ftp protocol is intended to works on request basis.
You start a request with a method (in your case UploadFile).
The only thing you can do is to KeepAlive your request to avoid connection closing
upldrequest.KeepAlive = true;
on every request you create except the last one. This will make a login only the first FTPWebRequest.
Then when you create the last FTPWebRequest, set
upldrequest.KeepAlive = false;
and it will close the connection when done.
I'm using FtpWebRequest to make directory Now, and I would get this exception like ftp error 550: File unavailable. Though I can make directory successfully sometimes, I always got this Exception.
And Below is my CheckDir function:
protected string CheckDir(string fullpath, string ip, string acc, string pwd)
{
string[] path = fullpath.Split(slash[1]);
bool result = false;
FtpWebRequest request = (FtpWebRequest)(WebRequest.Create(ip + path[2]));
request.Credentials = new NetworkCredential(acc, pwd);
request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
request.Timeout = 10000;
try
{
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
result = true;
}
}
catch (WebException ex)
{
FtpWebResponse response = (FtpWebResponse)ex.Response;
if (response != null && response.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable)
{
request = (FtpWebRequest)WebRequest.Create(ip + path[2]);
request.Credentials = new NetworkCredential(acc, pwd);
request.Method = WebRequestMethods.Ftp.MakeDirectory;
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = false;
request.GetResponse();
result = true;
}
else
{
result = false;
}
}
if (result == true)
return path[2];
else
return null;
}
I found that I got the way to check existed Directory. Then, I use Contains with ListDirecotyDetails.
Here is my function:
protected string CheckDir(string fullpath, string c_ip, string c_acc, string c_pwd)
{
string[] path = fullpath.Split(slash[1]);
bool result = false;
try
{
if (WebRequestMethods.Ftp.ListDirectoryDetails.Contains(c_ip + path[2]))
{
result = true;
}
else
{
FtpWebRequest request = (FtpWebRequest)(WebRequest.Create(c_ip + path[2]));
request.Credentials = new NetworkCredential(c_acc, c_pwd);
request.Method = WebRequestMethods.Ftp.MakeDirectory;
request.KeepAlive = false;
try
{
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
response.Close();
}
catch (Exception)
{
request.Abort();
result = false;
}
request.Abort();
result = true;
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
if (result == true)
return path[2];
else
return null;
}
When I want to get the size of a file on my ftp server I gets nothing.
Anyone see a problem in the code?
it seems to me that method is good
the class :
public string getFileSize(string fileName)
{
try
{
ftpRequest = (FtpWebRequest)FtpWebRequest.Create(host + "/" + fileName);
ftpRequest.Credentials = new NetworkCredential(user, pass);
ftpRequest.UseBinary = true;
ftpRequest.UsePassive = true;
ftpRequest.KeepAlive = true;
ftpRequest.Method = WebRequestMethods.Ftp.GetFileSize;
ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
ftpStream = ftpResponse.GetResponseStream();
StreamReader ftpReader = new StreamReader(ftpStream);
string fileInfo = null;
try { while (ftpReader.Peek() != -1) { fileInfo = ftpReader.ReadToEnd(); } }
catch (Exception ex) { Console.WriteLine(ex.ToString()); }
ftpReader.Close();
ftpStream.Close();
ftpResponse.Close();
ftpRequest = null;
return fileInfo;
}
catch (Exception ex) { Console.WriteLine(ex.ToString()); }
return "";
}
the process :
ftp ftpClientCheckFile = new ftp(#"ftp://******.******.fr", "***********", "********");
string ftpfileSize = ftpClientCheckFile.getFileSize(fileName);
if (ftpfileSize == localfilesize)
{
this.Invoke(new Action(() => { MessageBox.Show(this, "*********", "***", MessageBoxButtons.OK, MessageBoxIcon.Information); }));
ftpClientCheckFile = null;
}
else
{
this.Invoke(new Action(() => { MessageBox.Show(this, "***** ", "*******", MessageBoxButtons.OK, MessageBoxIcon.Information); }));
}
Thanks for help.
You need to use the ContentLength property to get the file size
Console.WriteLine(ftpResponse.ContentLength.ToString());
Some ftp servers don't support getfilesize so you will have to use ListDirectoryDetails
The following code works well 99% of the time.
However, when I use it to copy a directory with a large number of files (some of the individual files are also large) it hangs (no exceptions thrown) and any further FTP requests hang until I recycle the IIS 7.5 application pool that the code is running under. (This is used in a web-based file browser.)
It does not hang on the same file each time and actually lets me completely copy the directory once successfully, but then if I try and do it again it hangs after only copying some of the files and sub-directories.
My question is, can anyone see an obvious problem with the code? Is there a connection object that isn't closed properly or something?
Incidentally, I have tried (in the FtpCopyFile method) flushing and closing the "uploadStream" object as well as instantiating the FtpWebResponse object and subsequently closing it. Neither of those changes made any difference.
If there is nothing obvious with the code, can anyone recommend a method for tracking down the problem? Since no exception is thrown and I can't find anything in the server logs (at least the ones I know to look at), I am at a loss.
Any help would be greatly appreciated!
fodder
public string FtpCopy(string fromUrl, string toUrl, bool isDirectory)
{
string copyResult = "";
// COPY ENTIRE DIRECTORY
if (isDirectory) {
// MAKE SURE TOP DIRECTORY IS CREATED
if (!FtpDirectoryExists(toUrl)) { copyResult += FtpMakeDirectory(toUrl); }
// ITERATE TROUGH ALL FILES AND FOLDERS AND COPY TO LIVE LOCATION
Dictionary<string,Dictionary<string,string>> newItems = FtpRecursiveFileList(fromUrl);
foreach (KeyValuePair<string,Dictionary<string,string>> item in newItems) {
string currentFromUrl = item.Key;
string currentToUrl = currentFromUrl.Replace(fromUrl, toUrl);
if(item.Value["isdirectory"] == "true") { copyResult += FtpMakeDirectory(currentToUrl); }
else { copyResult += FtpCopyFile(currentFromUrl, currentToUrl); }
}
// COPY SINGLE FILE
} else { copyResult = FtpCopyFile(fromUrl, toUrl); }
string returnString = "";
if (copyResult == "") { returnString = "Success"; }
else { returnString = "Error: " + copyResult; }
return returnString;
}
private string FtpMakeDirectory(string url) {
string returnString = "";
// PARSE URL
url = url.TrimEnd('/') + "/";
string[] urlPath = Jbu.Util.UrlToStringArray(url, FTP_PATH_PREFIX);
string currentPath = FTP_PATH_PREFIX + urlPath[0];
// LOOP THROUGH EACH DIRECTORY LEVEL OF PATH
for (int i = 1; i < (urlPath.Length - 1); i++) {
currentPath = currentPath + "/" + urlPath[i];
string[] currentFiles = FtpListDirectoryArray(currentPath);
bool found = false;
if (currentFiles != null) {
// LOOK IN CURRENT DIRECTORY FOR DIRECTORY THAT HAS SAME NAME AS NEXT LOOP'S DIRECTORY
for (int j = 0; j < currentFiles.Length; j++) {
if (currentFiles[j] == urlPath[i + 1]) { found = true; }
}
}
// IF NAME NOT FOUND, CREATE DIRECTORY
if(!found) { returnString += FtpResponseAsString(CreateFtpRequest(currentPath + "/" + urlPath[i + 1], "makedirectory")); }
}
return returnString;
}
private string FtpCopyFile(string fromUrl, string toUrl)
{
string returnString = "";
try {
// GET FILE TO BE COPIED
FtpWebRequest ftpDownloadRequest = CreateFtpRequest(fromUrl, "downloadfile");
System.Net.FtpWebResponse downloadResponse = (System.Net.FtpWebResponse)ftpDownloadRequest.GetResponse();
Stream ftpDownloadStream = downloadResponse.GetResponseStream();
byte[] fileByteArray = Jbu.Util.StreamToByteArray(ftpDownloadStream);
ftpDownloadStream.Close();
// CREATE DIRECTORY, IF NEEDED
string containingDirectory = toUrl.Substring(0,toUrl.LastIndexOf('/'));
if (!FtpDirectoryExists(containingDirectory)) { returnString += FtpMakeDirectory(containingDirectory); }
// UPLOAD FILE TO NEW LOCATION
FtpWebRequest ftpUploadRequest = CreateFtpRequest(toUrl, "uploadfile");
ftpUploadRequest.ContentLength = fileByteArray.Length;
using (Stream uploadStream = ftpUploadRequest.GetRequestStream()) { uploadStream.Write(fileByteArray, 0, fileByteArray.Length); }
} catch (Exception ex) { returnString += "Error: " + ex.ToString(); }
return returnString;
}
private FtpWebRequest CreateFtpRequest(string url, string method)
{
// CREATE REQUEST OBJECT
ServicePointManager.ServerCertificateValidationCallback = (Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) => (certificate.Subject.Contains("CN=" + Jbu.Constant.FTP_CERT_DOMAIN));
FtpWebRequest ftpRequest = (FtpWebRequest)FtpWebRequest.Create(new Uri(url));
ftpRequest.EnableSsl = true;
ftpRequest.Credentials = new NetworkCredential(Jbu.Constant.FTP_USER, Jbu.Constant.FTP_PASSWORD);
ftpRequest.UseBinary = true;
ftpRequest.KeepAlive = false;
// SET METHOD
switch(method) {
case "listdirectory": ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory; break;
case "listdirectorydetails": ftpRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails; break;
case "makedirectory": ftpRequest.Method = WebRequestMethods.Ftp.MakeDirectory; break;
case "removedirectory": ftpRequest.Method = WebRequestMethods.Ftp.RemoveDirectory; break;
case "downloadfile": ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile; break;
case "uploadfile": ftpRequest.Method = WebRequestMethods.Ftp.UploadFile; break;
case "deletefile": ftpRequest.Method = WebRequestMethods.Ftp.DeleteFile; break;
case "getdatetimestamp": ftpRequest.Method = WebRequestMethods.Ftp.GetDateTimestamp; break;
default: break;
}
return ftpRequest;
}
private bool FtpDirectoryExists(string url)
{
bool dirExists = true;
try {
FtpWebRequest ftpRequest = CreateFtpRequest(url + "/", "listdirectory");
FtpWebResponse ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
} catch { dirExists = false; }
return dirExists;
}
private Dictionary<string,Dictionary<string,string>> FtpRecursiveFileList(string url)
{
Dictionary<string,Dictionary<string,string>> returnList = new Dictionary<string,Dictionary<string,string>>();
List<string> files = new List<string>();
Queue<string> folders = new Queue<string>();
folders.Enqueue(url);
while (folders.Count > 0) {
string fld = folders.Dequeue();
Dictionary<string,Dictionary<string,string>> newItems = FtpListDirectoryDetailsArray(fld);
foreach(KeyValuePair<string,Dictionary<string,string>> item in newItems) {
returnList.Add(fld + "/" + item.Key, item.Value);
if(item.Value["isdirectory"] == "true") {
folders.Enqueue(fld + "/" + item.Key);
}
}
}
return returnList;
}
private string[] FtpListDirectoryArray(string ftpPath)
{
FtpWebRequest ftpRequest = CreateFtpRequest(ftpPath, "listdirectory");
List<string> items = new List<string>();
try {
FtpWebResponse ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
Stream responseStream = ftpResponse.GetResponseStream();
using (StreamReader responseReader = new StreamReader(responseStream)) {
string line;
while ((line = responseReader.ReadLine()) != null) { items.Add(line); }
}
} catch { return null; }
string[] itemData = new string[items.Count];
for (int i = 0; i < items.Count; i++) { itemData[i] = items[i]; }
return itemData;
}
private Dictionary<string,Dictionary<string,string>> FtpListDirectoryDetailsArray(string ftpPath)
{
Dictionary<string,Dictionary<string,string>> items = new Dictionary<string,Dictionary<string,string>>();
FtpWebRequest ftpRequest = CreateFtpRequest(ftpPath, "listdirectorydetails");
try {
FtpWebResponse ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
Stream responseStream = ftpResponse.GetResponseStream();
using (StreamReader responseReader = new StreamReader(responseStream)) {
string line;
while ((line = responseReader.ReadLine()) != null) {
Dictionary<string,string> item = new Dictionary<string,string>();
line = System.Text.RegularExpressions.Regex.Replace(line, #"\s+", " "); // REMOVE EXTRA SPACES
string[] itemDetails = line.Split(' ');
item.Add("datetime", itemDetails[0] + " " + itemDetails[1]);
// FOLDERS
if (itemDetails[2] == "<DIR>") {
item.Add("isdirectory", "true");
item.Add("size", "-1");
item.Add("name", itemDetails[3]);
} else {
item.Add("isdirectory", "false");
item.Add("size", itemDetails[2]);
item.Add("name", itemDetails[3]);
}
items.Add(itemDetails[3], item);
}
}
// IF DIRECTORY DOES NOT EXIST, RETURN EMPTY DICT
} catch {};
return items;
}
private string FtpResponseAsString(FtpWebRequest ftpRequest)
{
try {
FtpWebResponse ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
Stream responseStream = ftpResponse.GetResponseStream();
StreamReader responseReader = new StreamReader(responseStream);
return responseReader.ReadToEnd();
} catch (Exception ex) { return "Error: " + ftpRequest.RequestUri + "\n\n" + ex.ToString(); }
}
Finally found the problem!
"FtpWebRequest.KeepAlive = false;" does work, but it takes a little time to clear out closed connections.
So what was happening is that my maximum number of connections was being hit. (Apparently there is a .NET max, as well, because my maxconnections in IIS was already set very high.)
Adding this line to the CreateFtpConnection method solves the issue by giving IIS enough time to close the old connections and make room for more: ftpRequest.ServicePoint.ConnectionLimit = 1000;
Your specific mileage will most likely vary based on your file sizes but hopefully this will help someone.
For the record, here is what CreateFtpConnection looks like now:
private FtpWebRequest CreateFtpRequest(string url, string method)
{
// CREATE REQUEST OBJECT
ServicePointManager.ServerCertificateValidationCallback = (Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) => (certificate.Subject.Contains("CN=" + Jbu.Constant.FTP_CERT_DOMAIN));
FtpWebRequest ftpRequest = (FtpWebRequest)FtpWebRequest.Create(new Uri(url));
ftpRequest.EnableSsl = true;
ftpRequest.Credentials = new NetworkCredential(Jbu.Constant.FTP_USER, Jbu.Constant.FTP_PASSWORD);
ftpRequest.UseBinary = true;
ftpRequest.KeepAlive = false;
ftpRequest.ServicePoint.ConnectionLimit = 1000;
// SET METHOD
switch(method) {
case "listdirectory": ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory; break;
case "listdirectorydetails": ftpRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails; break;
case "makedirectory": ftpRequest.Method = WebRequestMethods.Ftp.MakeDirectory; break;
case "removedirectory": ftpRequest.Method = WebRequestMethods.Ftp.RemoveDirectory; break;
case "downloadfile": ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile; break;
case "uploadfile": ftpRequest.Method = WebRequestMethods.Ftp.UploadFile; break;
case "deletefile": ftpRequest.Method = WebRequestMethods.Ftp.DeleteFile; break;
case "getdatetimestamp": ftpRequest.Method = WebRequestMethods.Ftp.GetDateTimestamp; break;
default: break;
}
return ftpRequest;
}