FTP Webrequest Multiple Files without session close - c#

I need some help reworking this Method in such a way that it will not create a new FTP session for every file that is uploaded.
The variables are
server (IP or Hostname)
Dictionary(local file path, ftp server path) ex (c:/mydir/test.txt, \incoming)
PASV Use Passive Mode True/False
and the login/password to the FTP itself
I would like this to work in such a way.
1) Connect to server
2) For each file/path pair in dictionary, upload file
3) Disconnect from server
Is it possible to rewrite this method to accomplish that?
Also I know the try/catch should be better implemented, I'd like to have a try/catch block for login to the FTP itself, then a try block for each file uploaded but I need to sort out the structure of the method first.
protected static bool FtpStart(string server, Dictionary<string, string> FilePath, bool PASV, string login, string password)
{
foreach (var Current in FilePath)
{
try
{
//FileInfo for Filename passed.
var ThisFile = new FileInfo(Current.Key);
// Get the object used to communicate with the server.
FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://" + server + Current.Value + ThisFile.Name);
request.UsePassive = PASV;
request.Method = WebRequestMethods.Ftp.UploadFile;
// This example assumes the FTP site uses anonymous logon.
request.Credentials = new NetworkCredential(login, password);
// Copy the contents of the file to the request stream.
StreamReader sourceStream = new StreamReader(Current.Key);
byte[] fileContents = Encoding.UTF8.GetBytes(sourceStream.ReadToEnd());
sourceStream.Close();
request.ContentLength = fileContents.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(fileContents, 0, fileContents.Length);
requestStream.Close();
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
response.Close();
}
catch (Exception Ex)
{
Console.WriteLine(Ex.Message);
return false;
}
}
return true;
}

You can KeepAlive your request to avoid connection closing
request.KeepAlive = true;
This will make a login only the first FTPWebRequest.
Then when you create the last FTPWebRequest
request.KeepAlive = false;
and it will close the connection when done.

Related

Get a list from FTP folder on web (port 80) to array

I'm trying to get a list of files from FTP web folder via port 80 into an array or list and then download specific extensions, but a counter is always zero for some reason.
public void GetFilesFromServer(string url, string extension=".mp3")
{
List<string> files = new List<string>();
try
{
//Create FTP request
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(url);
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential("anonymous", "anonymous");
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = false;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
while (!reader.EndOfStream)
{
files.Add(reader.ReadLine());
}
//Clean-up
reader.Close();
responseStream.Close(); //redundant
response.Close();
}
catch (Exception)
{
// MessageBox.Show("There was an error connecting to the FTP Server");
}
//If the list was successfully received, display it to the user
//through a dialog
if (files.Count != 0)
{
string folderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DownloadedFiles");
WebClient wc = new WebClient();
foreach (string file in files)
{
if(file.EndsWith(extension))
wc.DownloadFile(url+"/"+file, Path.Combine(folderPath, file));
}
}
}
My goal is to put all .ext files into an array and I can't get the list
The folder is http://url.com/folder for example.
But it fails to request
I checked the solutions and it doesn't work for me.
The FtpWebRequest is for FTP protocol.
FTP protocol does not use port 80.
If your URL is an HTML presentation of a folder on the server, you cannot scrape it with FtpWebRequest. You have to parse the HTML.
Though you should better find a way to access the data using a more reliable method (such as using a real FTP).
The MS documentation says that the FtpWebRequest initializes a new WebRequest and according to the documentation here : https://msdn.microsoft.com/en-us/library/bw00b1dc(v=vs.110).aspx
then if the URL starts with "http://" you will get back an HttpWebRequest rather than a FtpWebRequest.
This answer may explain:
Getting directory listing over http

The remote server returned an error: (421) Service not available, closing control connection

I am running this code for alot of files to transfer locally to another server via ftp. The problem is I am periodically getting the error The remote server returned an error: (421) Service not available, closing control connection. Is there a problem with the code, a better way to transfer the files that is quicker more efficient. All the files need to be transfered so I was thinking a while loop and catching the error until all files in the folder have transfered. It is a periodic error I am getting:
foreach (FileInfo file in files)
{
try
{
// Get the object used to communicate with the server.
FtpWebRequest request =
(FtpWebRequest)
WebRequest.Create(String.Format("{0}{1}/{2}", Host, destinationPath,
file.Name));
request.Method = WebRequestMethods.Ftp.UploadFile;
request.KeepAlive = false;
/* 20 mins timeout */
request.Timeout = 1200000;
request.ReadWriteTimeout = 1200000;
// This example assumes the FTP site uses anonymous logon.
request.Credentials = new NetworkCredential(Username, Password);
// Copy the contents of the file to the request stream.
byte[] fileContents = File.ReadAllBytes(file.FullName);
request.ContentLength = fileContents.Length;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(fileContents, 0, fileContents.Length);
}
//using (FtpWebResponse response = (FtpWebResponse) request.GetResponse())
//{
//}
if (deleteSourcePath)
{
File.Delete(file.FullName);
}
}
catch (Exception ex)
{
// Log.Warn("Error Moving Images", ex.Message);
}
}
Call Close on the the FtpWebResponse object. That will release the associated port.
FtpWebResponse response = (FtpWebResponse) request.GetResponse();
// ...
finally
{
if (response != null)
{
response.Close();
}
}

C# FTP Uload Acces denied Error 550

My Class FTP work fine wtih my FTP server, but not with my client FTP server.
public class UploadToFTP
{
public void UploadFTP(string LeSource, string Desti, string CodeClient)
{
Pers_Conf oConf = LeConf.Get_Config(CodeClient);
// Get the object used to communicate with the server.
FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://" + oConf.FtpServer + oConf.FtpChemin +"/" + Desti);
request.Method = WebRequestMethods.Ftp.UploadFile;
// This example assumes the FTP site uses anonymous logon.
request.Credentials = new NetworkCredential(oConf.FtpLogin, oConf.FtpPwd);
request.UseBinary = true;
request.UsePassive = true;
// Copy the contents of the file to the request stream.
StreamReader sourceStream = new StreamReader(LeSource);
byte [] fileContents = Encoding.UTF8.GetBytes(sourceStream.ReadToEnd());
sourceStream.Close();
request.ContentLength = fileContents.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(fileContents, 0, fileContents.Length);
requestStream.Close();
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
//Console.WriteLine("Upload File Complete, status {0}", response.StatusDescription);
response.Close();
}
}
this line is not work : Stream requestStream = request.GetRequestStream();
Anybody have solution ?
i have change the right into 777.
Thanks you in advance,
Stev
The Solutions
/ work only several ftp servers, but // it works perfectly everywhere

Uploading a text box into a text file on an ftp C#

I am trying to stream a multiline textbox into a text file on an ftp server. Can someone tell me where I may be going wrong?
private void btnSave_Click(object sender, EventArgs e)
{
UriBuilder b = new UriBuilder();
b.Host = "ftp.myserver.com";
b.UserName = "user";
b.Password = "pass";
b.Port = 21;
b.Path = "/myserver.com/directories/" + selected + ".txt";
b.Scheme = Uri.UriSchemeFtp;
Uri g = b.Uri;
System.Net.FtpWebRequest c = (System.Net.FtpWebRequest)System.Net.FtpWebRequest.Create(g);
c.Method = System.Net.WebRequestMethods.Ftp.DownloadFile;
System.Net.FtpWebResponse d = (System.Net.FtpWebResponse)c.GetResponse();
System.IO.Stream h = d.GetResponseStream;
System.IO.StreamWriter SW = new System.IO.StreamWriter(h);
String[] contents = textBox1.Lines.ToArray();
for (int i = 0; i < contents.Length; i++)
{
SW.WriteLine(contents[i]);
}
h.Close();
SW.Close();
d.Close();
}
The error I am getting is this line:
System.IO.StreamWriter SW = new System.IO.StreamWriter(h);
Stream was not writable.
Any ideas?
The response stream from an FTP site is data from the site to you. You'd need the request stream... but then you wouldn't want a method of DownloadFile - you're not downloading, you're uploading, so you want the UploadFile method.
Additionally:
You're not closing anything if exceptions are thrown: use using blocks for this.
It's a bad idea to do network access like this on the UI thread; the UI thread will block (so the whole UI will hang) while the FTP request is happening. Use a background thread instead.
To upload a file you need to use the FtpWebRequest class.
Quote:
When using an FtpWebRequest object to
upload a file to a server, you must
write the file content to the request
stream obtained by calling the
GetRequestStream method or its
asynchronous counterparts, the
BeginGetRequestStream and
EndGetRequestStream methods. You must
write to the stream and close the
stream before sending the request.
For an example of uploading a file (which you can change to writing stream content as in your example) see here.
Taken from MSDN and slightly modified:
public static bool UploadFileOnServer(string fileName, Uri serverUri)
{
// The URI described by serverUri should use the ftp:// scheme.
// It contains the name of the file on the server.
// Example: ftp://contoso.com/someFile.txt.
// The fileName parameter identifies the file
// to be uploaded to the server.
if (serverUri.Scheme != Uri.UriSchemeFtp)
{
return false;
}
// Get the object used to communicate with the server.
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(serverUri);
request.Method = WebRequestMethods.Ftp.UploadFile;
StreamReader sourceStream = new StreamReader(fileName);
byte [] fileContents = Encoding.UTF8.GetBytes(sourceStream.ReadToEnd());
sourceStream.Close();
request.ContentLength = fileContents.Length;
// This example assumes the FTP site uses anonymous logon.
request.Credentials = new NetworkCredential ("anonymous","janeDoe#contoso.com");
Stream requestStream = request.GetRequestStream();
requestStream.Write(fileContents, 0, fileContents.Length);
requestStream.Close();
FtpWebResponse response = (FtpWebResponse) request.GetResponse();
Console.WriteLine("Upload status: {0}",response.StatusDescription);
response.Close();
return true;
}

How to check if file exists on FTP before FtpWebRequest

I need to use FtpWebRequest to put a file in a FTP directory. Before the upload, I would first like to know if this file exists.
What method or property should I use to check if this file exists?
var request = (FtpWebRequest)WebRequest.Create
("ftp://ftp.domain.com/doesntexist.txt");
request.Credentials = new NetworkCredential("user", "pass");
request.Method = WebRequestMethods.Ftp.GetFileSize;
try
{
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
}
catch (WebException ex)
{
FtpWebResponse response = (FtpWebResponse)ex.Response;
if (response.StatusCode ==
FtpStatusCode.ActionNotTakenFileUnavailable)
{
//Does not exist
}
}
As a general rule it's a bad idea to use Exceptions for functionality in your code like this, however in this instance I believe it's a win for pragmatism. Calling list on the directory has the potential to be FAR more inefficient than using exceptions in this way.
If you're not, just be aware it's not good practice!
EDIT: "It works for me!"
This appears to work on most ftp servers but not all. Some servers require sending "TYPE I" before the SIZE command will work. One would have thought that the problem should be solved as follows:
request.UseBinary = true;
Unfortunately it is a by design limitation (big fat bug!) that unless FtpWebRequest is either downloading or uploading a file it will NOT send "TYPE I". See discussion and Microsoft response here.
I'd recommend using the following WebRequestMethod instead, this works for me on all servers I tested, even ones which would not return a file size.
WebRequestMethods.Ftp.GetDateTimestamp
Because
request.Method = WebRequestMethods.Ftp.GetFileSize
may fails in some case (550: SIZE not allowed in ASCII mode), you can just check Timestamp instead.
reqFTP.Credentials = new NetworkCredential(inf.LogOn, inf.Password);
reqFTP.UseBinary = true;
reqFTP.Method = WebRequestMethods.Ftp.GetDateTimestamp;
FtpWebRequest (nor any other class in .NET) does not have any explicit method to check a file existence on FTP server. You need to abuse a request like GetFileSize or GetDateTimestamp.
string url = "ftp://ftp.example.com/remote/path/file.txt";
WebRequest request = WebRequest.Create(url);
request.Credentials = new NetworkCredential("username", "password");
request.Method = WebRequestMethods.Ftp.GetFileSize;
try
{
request.GetResponse();
Console.WriteLine("Exists");
}
catch (WebException e)
{
FtpWebResponse response = (FtpWebResponse)e.Response;
if (response.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable)
{
Console.WriteLine("Does not exist");
}
else
{
Console.WriteLine("Error: " + e.Message);
}
}
If you want a more straightforward code, use some 3rd party FTP library.
For example with WinSCP .NET assembly, you can use its Session.FileExists method:
SessionOptions sessionOptions = new SessionOptions {
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "username",
Password = "password",
};
Session session = new Session();
session.Open(sessionOptions);
if (session.FileExists("/remote/path/file.txt"))
{
Console.WriteLine("Exists");
}
else
{
Console.WriteLine("Does not exist");
}
(I'm the author of WinSCP)
You can use WebRequestMethods.Ftp.ListDirectory to check if a file exist, no need for nasty try catch mechanism.
private static bool ExistFile(string remoteAddress)
{
int pos = remoteAddress.LastIndexOf('/');
string dirPath = remoteAddress.Substring(0, pos); // skip the filename only get the directory
NetworkCredential credentials = new NetworkCredential(FtpUser, FtpPass);
FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(dirPath);
listRequest.Method = WebRequestMethods.Ftp.ListDirectory;
listRequest.Credentials = credentials;
using (FtpWebResponse listResponse = (FtpWebResponse)listRequest.GetResponse())
using (Stream listStream = listResponse.GetResponseStream())
using (StreamReader listReader = new StreamReader(listStream))
{
string fileToTest = Path.GetFileName(remoteAddress);
while (!listReader.EndOfStream)
{
string fileName = listReader.ReadLine();
fileName = Path.GetFileName(fileName);
if (fileToTest == fileName)
{
return true;
}
}
}
return false;
}
static void Main(string[] args)
{
bool existFile = ExistFile("ftp://123.456.789.12/test/config.json");
}
I use FTPStatusCode.FileActionOK to check if file exists...
then, in the "else" section, return false.

Categories