Im trying to make a program that lets you check the availibility of a specific file extention on that ftp server and then sort the ones out that have the files on.
this is how i tried to do it so far:
string path;
path = "ftp://" + textBox1.Text + "/";
string[] files = System.IO.Directory.GetFiles(path, "*.txt", SearchOption.AllDirectories);
FtpWebRequest ftpRequest =
(FtpWebRequest)WebRequest.Create("ftp://ftp.freebsd.org/pub/FreeBSD/");
ftpRequest.Credentials = new NetworkCredential("anonymous", "k3rnel31#k.com");
ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
FtpWebResponse response = (FtpWebResponse)ftpRequest.GetResponse();
StreamReader streamReader = new StreamReader(response.GetResponseStream());
List<string> filestxt = new List<string>();
string line = streamReader.ReadLine();
while (!string.IsNullOrEmpty(line))
{
if (line.Contains(".txt"))
{
MessageBox.Show(line);
line = streamReader.ReadLine();
filestxt.Add(line);
}
else
{
line = streamReader.ReadLine();
}
}
streamReader.Close();
Related
I want to write a C# that:
Access the FTP with username and password
Goes to a certain folder
Sees what files aren't there from my PC folder and upload them.
My idea:
I have a folder called "mods" in my PC and another folder called "mods" in the FTP, so instead of opening the "FileZilla" software I want to write a C# that connects to the FTP and check what files aren't there.
Thank you so much!
you can list your file inftp server like this and do a comparaiosn with list file of mod dir
FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create(uri);
ftpRequest.Credentials =new NetworkCredential("anonymous","janeDoe#contoso.com");
ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
FtpWebResponse response = (FtpWebResponse)ftpRequest.GetResponse();
StreamReader streamReader = new StreamReader(response.GetResponseStream());
List<string> directories = new List<string>();
string line = streamReader.ReadLine();
while (!string.IsNullOrEmpty(line))
{
directories.Add(line);
line = streamReader.ReadLine();
}
streamReader.Close();
There's no magic way, with a pure .NET framework (its FtpWebRequest class). You have to code it.
List the FTP folder
List the local folder
Find the missing files
Upload them one by one
void SynchronizeLocalAndFtpDirectory(
string localPath, string remoteUri, NetworkCredential credentials)
{
List<string> remoteFiles = new List<string>();
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(remoteUri);
request.Credentials = credentials;
request.Method = WebRequestMethods.Ftp.ListDirectory;
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
while (!reader.EndOfStream)
{
remoteFiles.Add(reader.ReadLine());
}
}
IEnumerable<string> localFiles =
Directory.GetFiles(localPath).Select(path => Path.GetFileName(path));
IEnumerable<string> missingFiles = localFiles.Except(remoteFiles);
foreach (string filename in missingFiles)
{
Console.WriteLine("Uploading missing file {0}", filename);
string remoteFileUri = remoteUri + filename;
string localFilePath = Path.Combine(localPath, filename);
FtpWebRequest uploadRequest = (FtpWebRequest)WebRequest.Create(remoteFileUri);
uploadRequest.Method = WebRequestMethods.Ftp.UploadFile;
uploadRequest.Credentials = credentials;
using (Stream targetStream = uploadRequest.GetRequestStream())
using (Stream sourceStream = File.OpenRead(localFilePath))
{
byte[] buffer = new byte[10240];
int read;
while ((read = sourceStream.Read(buffer, 0, buffer.Length)) > 0)
{
targetStream.Write(buffer, 0, read);
}
}
}
}
Use the function like:
SynchronizeLocalAndFtpDirectory(
#"C:\local\mods", "ftp://ftp.example.com/remote/mods/",
new NetworkCredential("username", "password"));
(Do not forget the trailing slash in the URI).
Or use 3rd party FTP library that supports synchronization.
For example with the WinSCP .NET assembly, this is as easy as a single call to Session.SynchronizeDirectories.
And it will not only upload missing file, but also update out-of-the-date files and optionally delete orphan files.
void SynchronizeLocalAndFtpDirectory(
string localPath, string remotePath, SessionOptions sessionOptions)
{
using (Session session = new Session())
{
session.Open(sessionOptions);
session.SynchronizeDirectories(
SynchronizationMode.Remote, localPath, remotePath, false).Check();
}
}
Use it like:
SessionOptions sessionOptions = new SessionOptions()
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "username",
Password = "password",
};
SynchronizeLocalAndFtpDirectory(#"C:\local\mods", "/remote/mods", sessionOptions);
(I'm the author of WinSCP)
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.
how to make function supports parameters for this
public List<string> getfiles(string FTPhostname, string FTPpath,string FTPusername,string FTPpassword,string extension)
{
'request to ftp hostname
'get response from
'list all directories and files
'search for spesified extension files
}
any way for this ?
here is the solution a function that return List
public List<string> getfiles(string FTPhostname, string FTPpath,string FTPusername,string FTPpassword,string extension)
{
FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create("ftp://" + FTPhostname + "/" + FTPpath );
ftpRequest.Credentials = new NetworkCredential(FTPusername , FTPpassword );
ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
FtpWebResponse response = (FtpWebResponse)ftpRequest.GetResponse();
StreamReader streamReader = new StreamReader(response.GetResponseStream());
List<string> results = new List<string>();
string line = streamReader.ReadLine();
while (!string.IsNullOrEmpty(line))
{
if (line.Contains(extension)) {
line = streamReader.ReadLine();
results.Add(line);}
else {line = streamReader.ReadLine(); }
}
streamReader.Close();
return results;
}
then call this function for ex: png files
String[] txtfounds= getfiles("ftp.piacton.com", "/Public/Software/Official/LightField/Archives/", "anonymous", "k3tnel31#k.com", ".png").ToArray();
if you want to add this result to lisbox :
listBox1.DataSource = txtfounds;
There is an error, you need to do the
results.Add(line);
before you call the
results.Add(line);
to get the correct files.
I have created a function that find files on a FTP folder.
public static List<string> GetFileList(NetworkCredential credential, string FTPSite, string FTPFolder, string extension)
{
string LSOutput = "";
List<string> files = new List<string>();
//Fetch LS
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(#"ftp://" + FTPSite + FTPFolder);
request.Credentials = credential;
request.UseBinary = true;
request.EnableSsl = true;
request.Method = WebRequestMethods.Ftp.ListDirectory;
FtpWebResponse response = null;
response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
LSOutput = reader.ReadToEnd();
reader.Close();
response.Close();
PrepareLog("LS output while finding files:");
PrepareLog(LSOutput);
//Parse the LS
string[] LSOutputLines = LSOutput.Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
foreach (string LSOutputLine in LSOutputLines)
files.Add(LSOutputLine);
//Filter files
files = files.Where(f => f.ToLower().EndsWith(extension.ToLower())).ToList();
PrepareLogAndEmail("Total " + extension.ToLower() + " files found: " + files.Count, LogMessageType.Simple);
return files;
}
Exception handling is done at the place where the function is being called.
Now the problem is that sometimes the FTP faces some issues(like timeout) and then we have to rerun the program.
I have been asked to add 3 times retry mechanism and here is what I did to retry it once.
public static List<string> GetFileList(NetworkCredential credential, string FTPSite, string FTPFolder, string extension)
{
string LSOutput = "";
List<string> files = new List<string>();
try
{
//Fetch LS
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(#"ftp://" + FTPSite + FTPFolder);
request.Credentials = credential;
request.UseBinary = true;
request.EnableSsl = true;
request.Method = WebRequestMethods.Ftp.ListDirectory;
FtpWebResponse response = null;
response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
LSOutput = reader.ReadToEnd();
reader.Close();
response.Close();
PrepareLog("LS output while finding files:");
PrepareLog(LSOutput);
}
catch (Exception ex)
{
PrepareLogAndEmail("First attempt failed: Details: " + ex.Message, LogMessageType.AttemptFailed);
PrepareLogAndEmail("Second attempt started" + ex.Message, LogMessageType.Simple);
//Fetch LS
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(#"ftp://" + FTPSite + FTPFolder);
request.Credentials = credential;
request.UseBinary = true;
request.EnableSsl = true;
request.Method = WebRequestMethods.Ftp.ListDirectory;
FtpWebResponse response = null;
response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
LSOutput = reader.ReadToEnd();
reader.Close();
response.Close();
PrepareLog("LS output while finding files(second attempt):");
PrepareLog(LSOutput);
}
//Parse the LS
string[] LSOutputLines = LSOutput.Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
foreach (string LSOutputLine in LSOutputLines)
files.Add(LSOutputLine);
//Filter files
files = files.Where(f => f.ToLower().EndsWith(extension.ToLower())).ToList();
PrepareLogAndEmail("Total " + extension.ToLower() + " files found: " + files.Count, LogMessageType.Simple);
return files;
}
Now if I have to add 2 more tries, will I need to do this sort of daiys chaining or is there any other cleaner option available ?
I would go with a retry loop, something like below
You will notice that I make use of a bolean to flag out of a successfull download, or the retries count to terminate.
I would even go as far as changing the last section to check if the download was successfull, and notify the user if it was not, Maybe even logging the error messages.
public static List<string> GetFileList(NetworkCredential credential, string FTPSite, string FTPFolder, string extension)
{
List<string> files = new List<string>();
int retries = 0;
int maxRetries = 5;
bool downloaded = false;
string LSOutput = "";
while (!downloaded && retries < maxRetries)
{
try
{
LSOutput = "";
//Fetch LS
FtpWebRequest request = (FtpWebRequest) WebRequest.Create(#"ftp://" + FTPSite + FTPFolder);
request.Credentials = credential;
request.UseBinary = true;
request.EnableSsl = true;
request.Method = WebRequestMethods.Ftp.ListDirectory;
FtpWebResponse response = null;
response = (FtpWebResponse) request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
LSOutput = reader.ReadToEnd();
reader.Close();
response.Close();
PrepareLog("LS output while finding files:");
PrepareLog(LSOutput);
downloaded = true;
}
catch (Exception ex)
{
retries++;
}
}
if (downloaded)
{
//Parse the LS
string[] LSOutputLines = LSOutput.Trim().Split(new string[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);
foreach (string LSOutputLine in LSOutputLines)
files.Add(LSOutputLine);
//Filter files
files = files.Where(f => f.ToLower().EndsWith(extension.ToLower())).ToList();
PrepareLogAndEmail("Total " + extension.ToLower() + " files found: " + files.Count, LogMessageType.Simple);
return files;
}
else
{
PrepareLogAndEmail("Failed to download file", LogMessageType.Simple);
return null;
}
}
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");
}
}