I have created a process in C# that is intended to move files around by FTP. One part is working - my GetFileFTP. I can retrieve a file from the FTP server and place a copy on my local machine. I also want to copy a file to an archive folder on the FTP server and then delete the original file on the FTP server. I created the functions CopyFileFTP and DeleteFileFTP shown below. They run without error, but the file does not get copied or deleted. I also check the StatusCode in the delete function and everything looks happy. Do you know why this isn't working? Could it be that I only have read-only permission on the server? Would it throw an error if I try to delete and don't have permission or would it just process the command with no action? My supervisor doesn't seem to want to ask the supplier who owns the FTP server if we have the necessary permissions, so I will try posting this question.
public static bool CopyFileFTP(string sFileURI1, string sFileURI2, string sUserName, string sPassword)
{
bool bSuccess = false;
try
{
FtpWebRequest oRequest1 = (FtpWebRequest)WebRequest.Create(sFileURI1);
oRequest1.Credentials = new NetworkCredential(sUserName, sPassword);
oRequest1.Method = WebRequestMethods.Ftp.DownloadFile;
FtpWebResponse oResponse = (FtpWebResponse)oRequest1.GetResponse();
Stream oStream1 = oResponse.GetResponseStream();
MemoryStream oMemoryStream = new MemoryStream();
byte[] aChunk = new byte[4096];
int iBytesRead = 0;
do
{
iBytesRead = oStream1.Read(aChunk, 0, aChunk.Length);
if (iBytesRead > 0)
{
oMemoryStream.Write(aChunk, 0, iBytesRead);
}
else
{
break;
}
} while (iBytesRead > 0);
byte[] aBuffer = oMemoryStream.ToArray();
FtpWebRequest oRequest2 = (FtpWebRequest)WebRequest.Create(sFileURI2);
oRequest2.Credentials = new NetworkCredential(sUserName, sPassword);
oRequest2.Method = WebRequestMethods.Ftp.UploadFile;
Stream oStream2 = oRequest2.GetRequestStream();
oStream2.Write(aBuffer, 0, aBuffer.Length);
oStream2.Close();
oStream2.Dispose();
oStream1.Close();
oStream1.Dispose();
bSuccess = true;
}
catch (Exception oException)
{
LogEvent(oException.Message);
}
return bSuccess;
}
public static bool DeleteFileFTP(string sFileURI, string sUserName, string sPassword)
{
bool bSuccess = false;
try
{
FtpWebRequest oRequest = (FtpWebRequest)WebRequest.Create(sFileURI);
oRequest.Credentials = new NetworkCredential(sUserName, sPassword);
oRequest.Method = WebRequestMethods.Ftp.DeleteFile;
FtpWebResponse oResponse = (FtpWebResponse)oRequest.GetResponse();
string sWelcomeMessage = oResponse.WelcomeMessage;
string sStatusDescription = oResponse.StatusDescription;
bool bIsMutuallyAuthenticated = oResponse.IsMutuallyAuthenticated;
string sBannerMessage = oResponse.BannerMessage;
FtpStatusCode eStatusCode = oResponse.StatusCode;
string sExitMessage = oResponse.ExitMessage;
oResponse.Close();
bSuccess = true;
}
catch (Exception oException)
{
LogEvent(oException.Message);
}
return bSuccess;
}
It looks fine to me; the code is almost exactly as MSDN documents FtpWebRequest.
I would agree that it is credentials most likely.
Incidentally, have you tried using a traditional FTP client with the same credentials and see if you can perform these actions manually?
Additionally, it appears as you suspected that there is no exception thrown for not having permissions; you can catch an exception but that will mostly be from actual connection errors. You need to process the response's status to find out what the FTP server is telling you.
Related
General Info
I'm still in the process of learning C#. To help myself out, I'm trying to create a program that will automatically synchronise all of my local projects with a folder on my FTP server. This so that whether I'm at school or at home, I always have the same projects available to me.
I know there are programs like Dropbox that already do this for me, but I figured creating something like that myself will teach me a lot along the way.
The problem
My first step towards my goal was to just download all files, subdirectories and subfiles from my FTP server. I've managed to download all files from a directory with the code below. However, my code only lists the folder names and the files in the main directory. Subfolders and subfiles are never returned and never downloaded. Aside from that, the server returns a 550 error because I'm trying to download the folders as if they are files. I've been on this for 4+ hours now, but I just can't find anything on how to fix these problems and make it work. Therefor I'm hoping you guys will help me out :)
Code
public string[] GetFileList()
{
string[] downloadFiles;
StringBuilder result = new StringBuilder();
WebResponse response = null;
StreamReader reader = null;
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
request.UseBinary = true;
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord);
request.KeepAlive = false;
request.UsePassive = false;
response = request.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;
}
}
private void Download(string file)
{
try
{
string uri = url + "/" + file;
Uri serverUri = new Uri(uri);
if (serverUri.Scheme != Uri.UriSchemeFtp)
{
return;
}
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url + "/" + file);
request.UseBinary = true;
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord);
request.KeepAlive = false;
request.UsePassive = false;
FtpWebResponse response = (FtpWebResponse)request.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");
}
}
The FtpWebRequest does not have any explicit support for recursive file operations (including downloads). You have to implement the recursion yourself:
List the remote directory
Iterate the entries, downloading files and recursing into subdirectories (listing them again, etc.)
Tricky part is to identify files from subdirectories. There's no way to do that in a portable way with the FtpWebRequest. The FtpWebRequest unfortunately does not support the MLSD command, which is the only portable way to retrieve directory listing with file attributes in FTP protocol. See also Checking if object on FTP server is file or directory.
Your options are:
Do an operation on a file name that is certain to fail for file and succeeds for directories (or vice versa). I.e. you can try to download the "name". If that succeeds, it's a file, if that fails, it's a directory.
You may be lucky and in your specific case, you can tell a file from a directory by a file name (i.e. all your files have an extension, while subdirectories do not)
You use a long directory listing (LIST command = ListDirectoryDetails method) and try to parse a server-specific listing. Many FTP servers use *nix-style listing, where you identify a directory by the d at the very beginning of the entry. But many servers use a different format. The following example uses this approach (assuming the *nix format)
void DownloadFtpDirectory(
string url, NetworkCredential credentials, string localPath)
{
FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url);
listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
listRequest.Credentials = credentials;
List<string> lines = new List<string>();
using (var listResponse = (FtpWebResponse)listRequest.GetResponse())
using (Stream listStream = listResponse.GetResponseStream())
using (var listReader = new StreamReader(listStream))
{
while (!listReader.EndOfStream)
{
lines.Add(listReader.ReadLine());
}
}
foreach (string line in lines)
{
string[] tokens =
line.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
string name = tokens[8];
string permissions = tokens[0];
string localFilePath = Path.Combine(localPath, name);
string fileUrl = url + name;
if (permissions[0] == 'd')
{
if (!Directory.Exists(localFilePath))
{
Directory.CreateDirectory(localFilePath);
}
DownloadFtpDirectory(fileUrl + "/", credentials, localFilePath);
}
else
{
FtpWebRequest downloadRequest =
(FtpWebRequest)WebRequest.Create(fileUrl);
downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile;
downloadRequest.Credentials = credentials;
using (FtpWebResponse downloadResponse =
(FtpWebResponse)downloadRequest.GetResponse())
using (Stream sourceStream = downloadResponse.GetResponseStream())
using (Stream targetStream = File.Create(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:
NetworkCredential credentials = new NetworkCredential("user", "mypassword");
string url = "ftp://ftp.example.com/directory/to/download/";
DownloadFtpDirectory(url, credentials, #"C:\target\directory");
If you want to avoid troubles with parsing the server-specific directory listing formats, use a 3rd party library that supports the MLSD command and/or parsing various LIST listing formats; and recursive downloads.
For example with WinSCP .NET assembly you can download whole directory with a single call to the Session.GetFiles:
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "user",
Password = "mypassword",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Download files
session.GetFiles("/directory/to/download/*", #"C:\target\directory\*").Check();
}
Internally, WinSCP uses the MLSD command, if supported by the server. If not, it uses the LIST command and supports dozens of different listing formats.
The Session.GetFiles method is recursive by default.
In most cases, Session.GetFilesToDirectory is a more straightforward equivalent to Session.GetFiles:
session.GetFiles("/directory/to/download", #"C:\target\directory").Check();
(I'm the author of WinSCP)
General Info
I'm still in the process of learning C#. To help myself out, I'm trying to create a program that will automatically synchronise all of my local projects with a folder on my FTP server. This so that whether I'm at school or at home, I always have the same projects available to me.
I know there are programs like Dropbox that already do this for me, but I figured creating something like that myself will teach me a lot along the way.
The problem
My first step towards my goal was to just download all files, subdirectories and subfiles from my FTP server. I've managed to download all files from a directory with the code below. However, my code only lists the folder names and the files in the main directory. Subfolders and subfiles are never returned and never downloaded. Aside from that, the server returns a 550 error because I'm trying to download the folders as if they are files. I've been on this for 4+ hours now, but I just can't find anything on how to fix these problems and make it work. Therefor I'm hoping you guys will help me out :)
Code
public string[] GetFileList()
{
string[] downloadFiles;
StringBuilder result = new StringBuilder();
WebResponse response = null;
StreamReader reader = null;
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
request.UseBinary = true;
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord);
request.KeepAlive = false;
request.UsePassive = false;
response = request.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;
}
}
private void Download(string file)
{
try
{
string uri = url + "/" + file;
Uri serverUri = new Uri(uri);
if (serverUri.Scheme != Uri.UriSchemeFtp)
{
return;
}
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url + "/" + file);
request.UseBinary = true;
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord);
request.KeepAlive = false;
request.UsePassive = false;
FtpWebResponse response = (FtpWebResponse)request.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");
}
}
The FtpWebRequest does not have any explicit support for recursive file operations (including downloads). You have to implement the recursion yourself:
List the remote directory
Iterate the entries, downloading files and recursing into subdirectories (listing them again, etc.)
Tricky part is to identify files from subdirectories. There's no way to do that in a portable way with the FtpWebRequest. The FtpWebRequest unfortunately does not support the MLSD command, which is the only portable way to retrieve directory listing with file attributes in FTP protocol. See also Checking if object on FTP server is file or directory.
Your options are:
Do an operation on a file name that is certain to fail for file and succeeds for directories (or vice versa). I.e. you can try to download the "name". If that succeeds, it's a file, if that fails, it's a directory.
You may be lucky and in your specific case, you can tell a file from a directory by a file name (i.e. all your files have an extension, while subdirectories do not)
You use a long directory listing (LIST command = ListDirectoryDetails method) and try to parse a server-specific listing. Many FTP servers use *nix-style listing, where you identify a directory by the d at the very beginning of the entry. But many servers use a different format. The following example uses this approach (assuming the *nix format)
void DownloadFtpDirectory(
string url, NetworkCredential credentials, string localPath)
{
FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url);
listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
listRequest.Credentials = credentials;
List<string> lines = new List<string>();
using (var listResponse = (FtpWebResponse)listRequest.GetResponse())
using (Stream listStream = listResponse.GetResponseStream())
using (var listReader = new StreamReader(listStream))
{
while (!listReader.EndOfStream)
{
lines.Add(listReader.ReadLine());
}
}
foreach (string line in lines)
{
string[] tokens =
line.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
string name = tokens[8];
string permissions = tokens[0];
string localFilePath = Path.Combine(localPath, name);
string fileUrl = url + name;
if (permissions[0] == 'd')
{
if (!Directory.Exists(localFilePath))
{
Directory.CreateDirectory(localFilePath);
}
DownloadFtpDirectory(fileUrl + "/", credentials, localFilePath);
}
else
{
FtpWebRequest downloadRequest =
(FtpWebRequest)WebRequest.Create(fileUrl);
downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile;
downloadRequest.Credentials = credentials;
using (FtpWebResponse downloadResponse =
(FtpWebResponse)downloadRequest.GetResponse())
using (Stream sourceStream = downloadResponse.GetResponseStream())
using (Stream targetStream = File.Create(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:
NetworkCredential credentials = new NetworkCredential("user", "mypassword");
string url = "ftp://ftp.example.com/directory/to/download/";
DownloadFtpDirectory(url, credentials, #"C:\target\directory");
If you want to avoid troubles with parsing the server-specific directory listing formats, use a 3rd party library that supports the MLSD command and/or parsing various LIST listing formats; and recursive downloads.
For example with WinSCP .NET assembly you can download whole directory with a single call to the Session.GetFiles:
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "user",
Password = "mypassword",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Download files
session.GetFiles("/directory/to/download/*", #"C:\target\directory\*").Check();
}
Internally, WinSCP uses the MLSD command, if supported by the server. If not, it uses the LIST command and supports dozens of different listing formats.
The Session.GetFiles method is recursive by default.
In most cases, Session.GetFilesToDirectory is a more straightforward equivalent to Session.GetFiles:
session.GetFiles("/directory/to/download", #"C:\target\directory").Check();
(I'm the author of WinSCP)
General Info
I'm still in the process of learning C#. To help myself out, I'm trying to create a program that will automatically synchronise all of my local projects with a folder on my FTP server. This so that whether I'm at school or at home, I always have the same projects available to me.
I know there are programs like Dropbox that already do this for me, but I figured creating something like that myself will teach me a lot along the way.
The problem
My first step towards my goal was to just download all files, subdirectories and subfiles from my FTP server. I've managed to download all files from a directory with the code below. However, my code only lists the folder names and the files in the main directory. Subfolders and subfiles are never returned and never downloaded. Aside from that, the server returns a 550 error because I'm trying to download the folders as if they are files. I've been on this for 4+ hours now, but I just can't find anything on how to fix these problems and make it work. Therefor I'm hoping you guys will help me out :)
Code
public string[] GetFileList()
{
string[] downloadFiles;
StringBuilder result = new StringBuilder();
WebResponse response = null;
StreamReader reader = null;
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
request.UseBinary = true;
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord);
request.KeepAlive = false;
request.UsePassive = false;
response = request.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;
}
}
private void Download(string file)
{
try
{
string uri = url + "/" + file;
Uri serverUri = new Uri(uri);
if (serverUri.Scheme != Uri.UriSchemeFtp)
{
return;
}
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url + "/" + file);
request.UseBinary = true;
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord);
request.KeepAlive = false;
request.UsePassive = false;
FtpWebResponse response = (FtpWebResponse)request.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");
}
}
The FtpWebRequest does not have any explicit support for recursive file operations (including downloads). You have to implement the recursion yourself:
List the remote directory
Iterate the entries, downloading files and recursing into subdirectories (listing them again, etc.)
Tricky part is to identify files from subdirectories. There's no way to do that in a portable way with the FtpWebRequest. The FtpWebRequest unfortunately does not support the MLSD command, which is the only portable way to retrieve directory listing with file attributes in FTP protocol. See also Checking if object on FTP server is file or directory.
Your options are:
Do an operation on a file name that is certain to fail for file and succeeds for directories (or vice versa). I.e. you can try to download the "name". If that succeeds, it's a file, if that fails, it's a directory.
You may be lucky and in your specific case, you can tell a file from a directory by a file name (i.e. all your files have an extension, while subdirectories do not)
You use a long directory listing (LIST command = ListDirectoryDetails method) and try to parse a server-specific listing. Many FTP servers use *nix-style listing, where you identify a directory by the d at the very beginning of the entry. But many servers use a different format. The following example uses this approach (assuming the *nix format)
void DownloadFtpDirectory(
string url, NetworkCredential credentials, string localPath)
{
FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url);
listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
listRequest.Credentials = credentials;
List<string> lines = new List<string>();
using (var listResponse = (FtpWebResponse)listRequest.GetResponse())
using (Stream listStream = listResponse.GetResponseStream())
using (var listReader = new StreamReader(listStream))
{
while (!listReader.EndOfStream)
{
lines.Add(listReader.ReadLine());
}
}
foreach (string line in lines)
{
string[] tokens =
line.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
string name = tokens[8];
string permissions = tokens[0];
string localFilePath = Path.Combine(localPath, name);
string fileUrl = url + name;
if (permissions[0] == 'd')
{
if (!Directory.Exists(localFilePath))
{
Directory.CreateDirectory(localFilePath);
}
DownloadFtpDirectory(fileUrl + "/", credentials, localFilePath);
}
else
{
FtpWebRequest downloadRequest =
(FtpWebRequest)WebRequest.Create(fileUrl);
downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile;
downloadRequest.Credentials = credentials;
using (FtpWebResponse downloadResponse =
(FtpWebResponse)downloadRequest.GetResponse())
using (Stream sourceStream = downloadResponse.GetResponseStream())
using (Stream targetStream = File.Create(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:
NetworkCredential credentials = new NetworkCredential("user", "mypassword");
string url = "ftp://ftp.example.com/directory/to/download/";
DownloadFtpDirectory(url, credentials, #"C:\target\directory");
If you want to avoid troubles with parsing the server-specific directory listing formats, use a 3rd party library that supports the MLSD command and/or parsing various LIST listing formats; and recursive downloads.
For example with WinSCP .NET assembly you can download whole directory with a single call to the Session.GetFiles:
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "user",
Password = "mypassword",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Download files
session.GetFiles("/directory/to/download/*", #"C:\target\directory\*").Check();
}
Internally, WinSCP uses the MLSD command, if supported by the server. If not, it uses the LIST command and supports dozens of different listing formats.
The Session.GetFiles method is recursive by default.
In most cases, Session.GetFilesToDirectory is a more straightforward equivalent to Session.GetFiles:
session.GetFiles("/directory/to/download", #"C:\target\directory").Check();
(I'm the author of WinSCP)
General Info
I'm still in the process of learning C#. To help myself out, I'm trying to create a program that will automatically synchronise all of my local projects with a folder on my FTP server. This so that whether I'm at school or at home, I always have the same projects available to me.
I know there are programs like Dropbox that already do this for me, but I figured creating something like that myself will teach me a lot along the way.
The problem
My first step towards my goal was to just download all files, subdirectories and subfiles from my FTP server. I've managed to download all files from a directory with the code below. However, my code only lists the folder names and the files in the main directory. Subfolders and subfiles are never returned and never downloaded. Aside from that, the server returns a 550 error because I'm trying to download the folders as if they are files. I've been on this for 4+ hours now, but I just can't find anything on how to fix these problems and make it work. Therefor I'm hoping you guys will help me out :)
Code
public string[] GetFileList()
{
string[] downloadFiles;
StringBuilder result = new StringBuilder();
WebResponse response = null;
StreamReader reader = null;
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
request.UseBinary = true;
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord);
request.KeepAlive = false;
request.UsePassive = false;
response = request.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;
}
}
private void Download(string file)
{
try
{
string uri = url + "/" + file;
Uri serverUri = new Uri(uri);
if (serverUri.Scheme != Uri.UriSchemeFtp)
{
return;
}
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url + "/" + file);
request.UseBinary = true;
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord);
request.KeepAlive = false;
request.UsePassive = false;
FtpWebResponse response = (FtpWebResponse)request.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");
}
}
The FtpWebRequest does not have any explicit support for recursive file operations (including downloads). You have to implement the recursion yourself:
List the remote directory
Iterate the entries, downloading files and recursing into subdirectories (listing them again, etc.)
Tricky part is to identify files from subdirectories. There's no way to do that in a portable way with the FtpWebRequest. The FtpWebRequest unfortunately does not support the MLSD command, which is the only portable way to retrieve directory listing with file attributes in FTP protocol. See also Checking if object on FTP server is file or directory.
Your options are:
Do an operation on a file name that is certain to fail for file and succeeds for directories (or vice versa). I.e. you can try to download the "name". If that succeeds, it's a file, if that fails, it's a directory.
You may be lucky and in your specific case, you can tell a file from a directory by a file name (i.e. all your files have an extension, while subdirectories do not)
You use a long directory listing (LIST command = ListDirectoryDetails method) and try to parse a server-specific listing. Many FTP servers use *nix-style listing, where you identify a directory by the d at the very beginning of the entry. But many servers use a different format. The following example uses this approach (assuming the *nix format)
void DownloadFtpDirectory(
string url, NetworkCredential credentials, string localPath)
{
FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url);
listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
listRequest.Credentials = credentials;
List<string> lines = new List<string>();
using (var listResponse = (FtpWebResponse)listRequest.GetResponse())
using (Stream listStream = listResponse.GetResponseStream())
using (var listReader = new StreamReader(listStream))
{
while (!listReader.EndOfStream)
{
lines.Add(listReader.ReadLine());
}
}
foreach (string line in lines)
{
string[] tokens =
line.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
string name = tokens[8];
string permissions = tokens[0];
string localFilePath = Path.Combine(localPath, name);
string fileUrl = url + name;
if (permissions[0] == 'd')
{
if (!Directory.Exists(localFilePath))
{
Directory.CreateDirectory(localFilePath);
}
DownloadFtpDirectory(fileUrl + "/", credentials, localFilePath);
}
else
{
FtpWebRequest downloadRequest =
(FtpWebRequest)WebRequest.Create(fileUrl);
downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile;
downloadRequest.Credentials = credentials;
using (FtpWebResponse downloadResponse =
(FtpWebResponse)downloadRequest.GetResponse())
using (Stream sourceStream = downloadResponse.GetResponseStream())
using (Stream targetStream = File.Create(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:
NetworkCredential credentials = new NetworkCredential("user", "mypassword");
string url = "ftp://ftp.example.com/directory/to/download/";
DownloadFtpDirectory(url, credentials, #"C:\target\directory");
If you want to avoid troubles with parsing the server-specific directory listing formats, use a 3rd party library that supports the MLSD command and/or parsing various LIST listing formats; and recursive downloads.
For example with WinSCP .NET assembly you can download whole directory with a single call to the Session.GetFiles:
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "user",
Password = "mypassword",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Download files
session.GetFiles("/directory/to/download/*", #"C:\target\directory\*").Check();
}
Internally, WinSCP uses the MLSD command, if supported by the server. If not, it uses the LIST command and supports dozens of different listing formats.
The Session.GetFiles method is recursive by default.
In most cases, Session.GetFilesToDirectory is a more straightforward equivalent to Session.GetFiles:
session.GetFiles("/directory/to/download", #"C:\target\directory").Check();
(I'm the author of WinSCP)
I am trying to upload images to my ftp server hosted by a web hosting to store users profile images for when they close and re open my application
Note If there is any other way I can store it please suggest it
I have tryed the following code below but I keep receiving a error saying
An unhandled exception of type 'System.Net.WebException' occurred in App.exe Additional information: The remote server returned an error: (500) Syntax error, command unrecognized
A comment a person said to me was "That error is quite generic. It could mean you have a firewall or something blocking something or it can mean that SSL is not supported on the server" Could any of you help towards this comment. Because I don't understand how i can block the firewall or stop it or excreter excreter (Not that important help if you can)
Carrying on to the main problem ... My code - (FTP Part) In a public static class
public static void UpLoadImage(string source)
{
try
{
String sourcefilepath = source;
String ftpurl = "ftp://www.locu.site90.com/public_html/";
String ftpusername = "a4670620";
String ftppassword = "********";
string filename = Path.GetFileName(source);
string ftpfullpath = ftpurl + filename;
FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(ftpfullpath);
ftp.Credentials = new NetworkCredential(ftpusername, ftppassword);
ftp.KeepAlive = false;
ftp.EnableSsl = true;
ftp.Method = WebRequestMethods.Ftp.UploadFile;
FileStream fs = File.OpenRead(source);
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
fs.Close();
Stream ftpstream = ftp.GetRequestStream();
ftpstream.Write(buffer, 0, buffer.Length);
ftpstream.Close();
}
catch (Exception ex)
{
throw ex;
}
}
And here is where I call the void when selecting a image
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog open = new OpenFileDialog();
if (open.ShowDialog() == DialogResult.OK)
{
Bitmap bit = new Bitmap(open.FileName);
pictureBox1.Image = bit;
pictureBox2.Image = bit;
bit.Dispose();
string fullPath = open.FileName;
string fileName = open.SafeFileName;
string path = fullPath.Replace(fileName, "");
User.Details.UpLoadImage(fullPath);
}
}
Any help given is 100% appreciated from me myself!
I am using the following code to create the path:
System.Net.FtpWebRequest ftpReq = null;
System.Net.FtpWebResponse ftpRes = null;
try
{
ftpReq = System.Net.WebRequest.Create(path) as System.Net.FtpWebRequest;
ftpReq.Credentials = new System.Net.NetworkCredential(user, password);
ftpReq.Method = System.Net.WebRequestMethods.Ftp.MakeDirectory;
ftpReq.KeepAlive = false;
ftpRes = ftpReq.GetResponse() as System.Net.FtpWebResponse;
ftpRes.Close();
}
catch (WebException we)
{
//do something with the error
}
catch (Exception e)
{
//do something with the error
}
and to upload the file why you don't use
public byte[] UploadFile(string address, string fileName):
Code for uploading to ftp:
WebClient wc = new WebClient();
wc.Credentials = new NetworkCredential(tabFolder.FldUser, tabFolder.FldPassword);
wc.UploadFile(folder.TrimEnd(#"\".ToCharArray()) + #"\" + fn, jpgFile);