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;
}
Related
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 ...
The code i'm using now:
string[] Files = GetFileList();
ArrayList arrDirectories = new ArrayList();
if (Files != null)
{
foreach (string dir in Files)
{
arrDirectories.Add(dir);
}
}
if (!arrDirectories.Contains(dirName))
{
Sync(dirName, reqFTP, response, ftpStream);
}
The method GetFileList:
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://" + f.Host + "/"));
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential(f.Username, f.Password);
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();
}
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;
}
}
What i'm getting in the end in arrDirectories is all the files and directories and sub directories from my ftp server. The problem is that each file,directory,and sub directory are in it's own index.
For example i see in index 0 in arrDirectories: test
Then in index 1 i see: testsub
Then in index 2 i see: test.jpg
But in fact test is under root directory and testsub is under test and test.jpg is under testsub.
The problem is that when i check if directory already exist:
if (!arrDirectories.Contains(dirName))
If it's a single directory for example test then there is no problem.
But if the directory i want to check if exist is a sub directory like test/testsub
Then it will never find it even if it is exist on my ftp server.
So in case i have to check a directories like test/testsub or test/test1/test2/test3...what should i change first in the GEtFileList method ? And then how to loop over the arrDirectories maybe need a recursive ?
try using this code'
first get the directory structure into string array:
string[] Directories = Directory.GetDirectories(#"c:\windows\Temp", "*.*", SearchOption.AllDirectories);
then use linq to checkout if your directory exsists
bool DirectoryExists = !String.IsNullOrEmpty(Directories.Where(d => d.ToLower().Contains(#"Directory Name...")).FirstOrDefault());
Yes, you're right, the simplest way - call GetFileList() recursively
And put results into list, not into string
Here is your modified code
static public void GetFileList(List<string> list,string sroot)
{
WebResponse response = null;
StreamReader reader = null;
try
{
FtpWebRequest reqFTP;
reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + Host+sroot));
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential(Username, Password);
reqFTP.Method = WebRequestMethods.Ftp.ListDirectoryDetails ;
reqFTP.Proxy = null;
reqFTP.KeepAlive = false;
reqFTP.UsePassive = false;
response = reqFTP.GetResponse();
reader = new StreamReader(response.GetResponseStream());
string line = reader.ReadLine();
string file;
while (!reader.EndOfStream )
{
if (line.StartsWith("-"))
{
file = sroot + "/" + line.Substring(57);
list.Add(file);
}
else if (line.StartsWith("d"))
{
file = sroot + "/" + line.Substring(57);
list.Add(file+"/");
GetFileList(list, file);
}
line = reader.ReadLine();
}
}
catch (Exception ex)
{
if (reader != null)
{
reader.Close();
}
if (response != null)
{
response.Close();
}
}
return;
}
Calls method ListDirectoryDetails instead of ListDirectory to
distinguish directories and files
Parses result to get file name from file details (am not sure it is always the end of line starting from pos. 57 :-)
Calls self for each found folder
The criteria of the end of stream is EndOfStream :-)
Here is the call
List<string> filelist = new List<string>();
GetFileList(filelist, "");
foreach (string s in filelist )
Console.WriteLine(s.Substring(1));
All folders have "/" at the end (feel free to do not include folders into list).
It's easy to build local file list using method from previous answer.
I have this code which works perfect when there is a file on FTP site , but when there is no file on FTP site it fails.
The error II get is in foreach (string file in files) and it says there is null reference .
How can I fix this so that if there is no file on FTP site this code will work.
Thanks in advance.
my error message
System.NullReferenceException was unhandled by user code
code
public void Main()
{
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;
WebRequest reqFTP;
reqFTP = (FtpWebRequest)WebRequest.Create(new Uri("ftp://" + Dts.Variables["strHost"].Value+"/"));
//reqFTP.UseBinary = true;
String FTPUser = (String)Dts.Variables["strUserName"].Value;
String FTPPwd = (String)Dts.Variables["strPassword"].Value;
reqFTP.Credentials = new NetworkCredential(FTPUser, FTPPwd);
reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
reqFTP.Proxy = null;
//reqFTP.KeepAlive = true;
//reqFTP.UsePassive = true;
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://" + Dts.Variables["strHost"].Value + "/" + file;
Uri serverUri = new Uri(uri);
if (serverUri.Scheme != Uri.UriSchemeFtp)
{
return;
}
WebRequest reqFTP;
//FtpWebRequest reqFTP;
reqFTP = (FtpWebRequest)WebRequest.Create(new Uri("ftp://" + Dts.Variables["strHost"].Value + "/" + file));
String FTPUser = (String)Dts.Variables["strUserName"].Value;
String FTPPwd = (String)Dts.Variables["strPassword"].Value;
reqFTP.Credentials = new NetworkCredential(FTPUser, FTPPwd);
//reqFTP.KeepAlive = true;
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(Dts.Variables["strLocalFolder"].Value + "\\" + 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");
}
}
You need to test the result of GetFilesList() for null, because you return null in case an error (in catch clause of GetFileList() you set the result - downloadFiles - to null).
public void Main()
{
String[] files = GetFileList();
if (files != null) // add this line
{
foreach (string file in files)
{
Download(file);
}
}
Problem is your call to GetFileList returns null and thus the foreach fails.
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.
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");
}
}