How to clean up resources if FtpWebRequest goes wrong - c#

I wonder if this FtpWebRequest goes wrong and, it goes to the catch event. I have seen an example code where they posted what I have uncommented in the catch event, - to clean up the resources.
But I don't know what is the proper way to do that in this scenario? Should I just put all of those to: = null; or is this wrong to do? What is the proper way to do it?
cleanUp(sourceStream, ref response, ref requestStream, ref request);
void uploadimage()
{
String sourceimage = "C:/ESD/image_2.jpg";
Task<bool> task = FtpUploadFile(sourceimage);
if (task.IsFaulted == false)
{
MessageBox.Show(task.Result.ToString());
}
}
private Task closeRequestStreamAsync(Stream requestStream) { return Task.Run(() => { requestStream.Close(); }); }
public async Task<bool> FtpUploadFile(string filename)
{
//if exception occurs we want to be able to close these
FtpWebResponse response = null;
FtpWebRequest request = null;
FileStream sourceStream = null;
Stream requestStream = null;
try
{
bool isimage = false; String ext = Path.GetExtension(filename);
if (ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".gif" || ext == ".bmp") { isimage = true; }
request = (FtpWebRequest)WebRequest.Create("ftp://someurl.com/Folder1/test1.jpg");
request.UsePassive = true;
if (isimage == true) { request.UseBinary = true; } //for images
if (isimage == false) { request.UseBinary = false; } //for text
request.KeepAlive = true; //keep the connection open
request.Method = WebRequestMethods.Ftp.UploadFile;
request.ConnectionGroupName = "Group1";
request.ServicePoint.ConnectionLimit = 4;
//These are the credentials.
request.Credentials = new NetworkCredential("username", "password");
sourceStream = File.OpenRead(filename);
byte[] buffer = new byte[sourceStream.Length];
await sourceStream.ReadAsync(buffer, 0, buffer.Length);
sourceStream.Close();
requestStream = await request.GetRequestStreamAsync();
await requestStream.WriteAsync(buffer, 0, buffer.Length);
//MPM This is the call that takes the time
await closeRequestStreamAsync(requestStream);
//response = (FtpWebResponse)request.GetResponse();
WebResponse responseWeb = await request.GetResponseAsync();
response = (FtpWebResponse)responseWeb;
if (response.StatusDescription.Contains("226"))
{
//This means that we successfully have uploaded the file!
}
response.Close();
return true;
}
catch (Exception ex)
{
string errMSG = string.Format("Upload File failed, exception: {0}", ex.Message);
//cleanUp(sourceStream, ref response, ref requestStream, ref request);
return false;
}
}

To ensure the web request, response and stream objects are closed even if an exception occurs, they should be defined in a using block.
The code can be simplified to :
var ext = Path.GetExtension(filename);
var imageExtensions=new[]{".jpg",".jpeg",".png",".gif",".bmp"};
var isimage = imageExtensions.Contains(ext);
var request = (FtpWebRequest)WebRequest.Create("ftp://someurl.com/Folder1/test1.jpg");
request.UseBinary =isimage;
request.Method = WebRequestMethods.Ftp.UploadFile;
request.ConnectionGroupName = "Group1";
request.ServicePoint.ConnectionLimit = 4;
//These are the credentials.
request.Credentials = new NetworkCredential("username", "password");
using(var sourceStream = File.OpenRead(filename))
using(var requestStream = await request.GetRequestStreamAsync())
{
await sourceStream.CopyToAsync(requestStream);
}
using(var responseWeb = await request.GetResponseAsync())
{
var response = (FtpWebResponse)responseWeb;
if (response.StatusDescription.Contains("226"))
{
return true;
}
}
.....
I removed the KeepAlive and UsePassive setters because true is their default value.
A WebRequest by itself doesn't hold any resources so it doesn't implement IDisposable. The connection to the server is made when GetRequestStream() is called. The values that need disposing/closing are sourceStream, requestStream and responseWeb.

Related

FtpWebRequest GetRequestStream() gots time-outed at second call

Currently I'm writing a specific program, and one of it fuctions is download/upload files via ftp protocol. I made a method for upload, but when I calls it second time, my program freezes (and after 100 seconds it shows me an timeout error).
Method:
public static void uploadFile(string FTPAddress, string filePath, string username, string password)
{
try
{
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(FTPAddress + "/" + filePath);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential(username, password);
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = false;
FileStream stream = File.OpenRead(filePath);
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
stream.Flush();
stream.Dispose();
using (Stream reqStream = request.GetRequestStream())
{
reqStream.Write(buffer, 0, buffer.Length);
reqStream.Flush();
reqStream.Dispose();
}
request.Abort();
MessageBox.Show("Uploaded Successfully");
}
catch (Exception e)
{
if (MessageBox.Show("Error", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error) == DialogResult.Cancel)
{
Application.Exit();
}
else
FTPTools.uploadFile({calls this function again (it doesn't matter)});
}
}
I call it when pushing the button:
Application.DoEvents();
FTPTools.uploadFile({my ftp address}, {filename}, {login}, {password});
By using Visual Studio Debugger I've found that freeze happens when
Stream reqStream = request.GetRequestStream()
calls. Now I don't have any ideas how to fix it. Maybe someone there would solve my problem.
UPDATED 11/11/2016:
Network trace log
UPDATED 12/11/2016:
Today I little updated my code, but it doesn't helped me:
public static void uploadFile(string FTPAddress, string filePath, string username, string password)
{
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(FTPAddress + "/" + filePath);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential(username, password);
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = false;
request.Proxy = null;
request.Timeout = 5000;
request.ServicePoint.ConnectionLeaseTimeout = 5000;
request.ServicePoint.MaxIdleTime = 5000;
try
{
using (FileStream stream = File.OpenRead(filePath))
{
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
stream.Close();
using (Stream reqStream = request.GetRequestStream())
{
reqStream.Write(buffer, 0, buffer.Length);
reqStream.Close();
}
using (FtpWebResponse resp = (FtpWebResponse)request.GetResponse())
{
resp.Close();
}
request.Abort();
}
MessageBox.Show("Uploaded Successfully");
}
catch (Exception e)
{
if (MessageBox.Show("Error", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error) == DialogResult.Cancel)
{
Application.Exit();
}
else
FTPTools.uploadFile({calls this function again (it doesn't matter)});
}
finally
{
request.Abort();
}
}
UPDATED 12/12/2016
Today I've found an anomaly with my code: when I call this method (that uploads file) from a method with if/else statements, it gots time-outed at second call. But when I call this method (that uploads file) from a method without if/else statements, it works correctly at every call. I have no idea why it happens, but I cannot call my method without if/else statements.
For example, this code works correctly if I call 'upl()' method:
public void upl()
{
FTPTools.uploadFile(address, fileName, username, password);
}
And this code throws a time-out exception at second call if I call 'checker()' method:
public void upl()
{
FTPTools.uploadFile(address, fileName, username, password);
}
public void checker()
{
int a = 0, b = 0;
if (a == b)
upl();
}

Ftp upload a file as readonly or otherwise prevent overwrite

I need to prevent existing files being overwritten (by this code, don't care about other ftp code overwriting it), I thought one way might be to upload them as readonly files, anyone able to do this, or other suggestion?
This is my simple uploader based on this msdn example:
public class FtpUploader
{
private readonly string _root;
public FtpUploader(string root)
{
_root = root;
Credentials = new NetworkCredential("anonymous", "");
}
public NetworkCredential Credentials { get; set; }
public async Task<bool> UploadAsync(string fileName, byte[] fileContents)
{
var doc = Path.Combine(_root, fileName);
var request = (FtpWebRequest) WebRequest.Create(doc);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = Credentials;
request.ContentLength = fileContents.Length;
using (var requestStream = request.GetRequestStream())
{
await requestStream.WriteAsync(fileContents, 0, fileContents.Length);
}
using (var response = (FtpWebResponse) await request.GetResponseAsync())
{
Console.WriteLine("Upload File Complete, status {0}", response.StatusCode);
return response.StatusCode == FtpStatusCode.ClosingData;
}
}
}
Used like this: I'd like the file to not get overwritten and to return false from upload (or throw some exception)
byte[] fileContents = Encoding.UTF8.GetBytes("Hello world!");
var res1 = await new FtpUploader(root).UploadAsync("hello.txt", fileContents);
fileContents = Encoding.UTF8.GetBytes("Should not get set to this!");
var res2 = await new FtpUploader(root).UploadAsync("hello.txt", fileContents);
I've tried WebRequestMethods.Ftp.UploadFileWithUniqueName which would be fine if there is a second step to rename/move easily? And get fail from rename/move.
I think you should check if the File exists.
You can have a look at this example: Stackoverflow example
I hope this helps you!
I can upload and rename in two steps. This solves another problem; the listener will must not detect the new file before it is completely uploaded.
But still interested in other approaches.
public class FtpUploader
{
private readonly string _root;
public FtpUploader(string root)
{
_root = root;
Credentials = new NetworkCredential("anonymous", "");
}
public NetworkCredential Credentials { get; set; }
public async Task<bool> UploadAsync(string fileName, byte[] fileContents)
{
//var doc = Path.Combine(_root, fileName);
var request = (FtpWebRequest) WebRequest.Create(_root);
request.Method = WebRequestMethods.Ftp.UploadFileWithUniqueName;
request.Credentials = Credentials;
request.ContentLength = fileContents.Length;
using (var requestStream = request.GetRequestStream())
{
await requestStream.WriteAsync(fileContents, 0, fileContents.Length);
}
using (var response = (FtpWebResponse) await request.GetResponseAsync())
{
Console.WriteLine("Upload File Complete, status {0}", response.StatusCode);
Console.WriteLine(response.ResponseUri);
if (response.StatusCode != FtpStatusCode.ClosingData) return false;
try
{
return await Rename(response.ResponseUri, fileName);
}
catch (WebException)
{
return false;
}
}
}
private async Task<bool> Rename(Uri newUri, string newFileName)
{
var request = (FtpWebRequest) FtpWebRequest.Create(newUri);
request.Proxy = null;
request.Credentials = Credentials;
request.Method = WebRequestMethods.Ftp.Rename;
request.RenameTo = newFileName;
using (var response = (FtpWebResponse) await request.GetResponseAsync())
{
Console.WriteLine("Rename File Complete, status {0}", response.StatusCode);
Console.WriteLine(response.ResponseUri);
return response.StatusCode == FtpStatusCode.FileActionOK;
}
}
}

Error 550 FTP Upload of same file at the same time by two threads

I'm having a problem in uploading files to an ftp server.
the function works fine, but if try to use it in multithread, and when two threads tries to write the same file at the same time, I got error 550 in the second thread.
basically the first thread creates the file, while the second one raises the error.
here it is my code
internal bool UploadFtp(IResult result, string directoryPath, string filename, Stream fileContents, bool considerError550=true)
{
Thread.Sleep(3000);
directoryPath = directoryPath.TrimStart('\\');
string completePath = MachineConnectionParam.Url;
fileContents.Seek(0, SeekOrigin.Begin);
if (directoryPath != string.Empty && directoryPath != "\\")
completePath = completePath + directoryPath;
FtpWebResponse response = null;
try
{
completePath = completePath.TrimEnd('/');
filename = filename.TrimStart('/');
completePath = completePath + "/" + filename;
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(completePath);
if (request == null)
{
result.SetError(Translate.InvalidUrl, completePath);
return false;
}
request.Method = WebRequestMethods.Ftp.UploadFile;
request.UseBinary = true;
request.UsePassive = false;
request.KeepAlive = false;
//request.Timeout = 5000;
request.Proxy = null;
request.Credentials = new NetworkCredential(MachineConnectionParam.Username, MachineConnectionParam.Password);
request.ContentLength = fileContents.Length;
Stream requestStream = request.GetRequestStream();
fileContents.CopyTo(requestStream);
requestStream.Close();
response = (FtpWebResponse)request.GetResponse();
}
catch (Exception e)
{
result.SetError(e.Message);
return false;
}
finally
{
if (response != null)
response.Close();
}
return true;
}
any idea to solve it?
thank you

ftp upload is too big

I've written a function in C# to upload a file when copying to a file share does not work. I'm noticing that any uploaded files are about 1.5-2x the size of the original and are invalid files. Here is the code:
public bool save_FTPUpload(FileInfo fi_attachment)
{
bool fileSaved = false;
string filename = fi_attachment.Name;
while (!fileSaved)
{
string file_ftpURI = string.Format("{0}/{1}", ftpURI, filename);
FtpWebRequest file_exist_request = (FtpWebRequest)FtpWebRequest.Create(file_ftpURI);
file_exist_request.Credentials = new NetworkCredential(ftp_user, ftp_pass);
file_exist_request.Method = WebRequestMethods.Ftp.GetFileSize;
try
{
FtpWebResponse response = (FtpWebResponse)file_exist_request.GetResponse();
}
catch (WebException ex)
{
FtpWebResponse response = (FtpWebResponse)ex.Response;
if (response.StatusCode ==
FtpStatusCode.ActionNotTakenFileUnavailable)
{
FtpWebRequest upload_request = (FtpWebRequest)FtpWebRequest.Create(file_ftpURI);
upload_request.Credentials = new NetworkCredential(ftp_user, ftp_pass);
upload_request.Method = WebRequestMethods.Ftp.UploadFile;
upload_request.UsePassive = true;
upload_request.UseBinary = true;
upload_request.KeepAlive = false;
StreamReader attachment = new StreamReader(fi_attachment.FullName);
byte[] attachmentData = Encoding.UTF8.GetBytes(attachment.ReadToEnd());
upload_request.ContentLength = attachmentData.Length;
//Stream upload_request_stream = upload_request.GetRequestStream();
using (Stream upload_request_stream = upload_request.GetRequestStream())
{
upload_request_stream.Write(attachmentData, 0, attachmentData.Length);
upload_request_stream.Close();
}
FtpWebResponse upload_response = (FtpWebResponse)upload_request.GetResponse();
fileSaved = true;
}
}
}
return fileSaved;
}
Any help in figuring this out would be great.
You need to copy the content of the file in binary mode, like this:
var response = (FtpWebResponse)ex.Response;
if (response.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable) {
FtpWebRequest upload_request = (FtpWebRequest)FtpWebRequest.Create(file_ftpURI);
upload_request.Credentials = new NetworkCredential(ftp_user, ftp_pass);
upload_request.Method = WebRequestMethods.Ftp.UploadFile;
upload_request.UsePassive = true;
upload_request.UseBinary = true;
upload_request.KeepAlive = false;
var attachment = File.Open(fi_attachment.FullName, FileMode.Open);
using (Stream upload_request_stream = upload_request.GetRequestStream()) {
attachment.CopyTo(upload_request_stream);
upload_request_stream.Close();
}
var upload_response = (FtpWebResponse)upload_request.GetResponse();
fileSaved = true;
}
Your current program reads it as a very long string in UTF-8 encoding, which probably accounts for the change in file size.
Why all this dance decoding a file then re-encoding it? You've got two streams right? You want the same file you have on disk to exist on the server? Stream.CopyTo would be considerably less error prone.
You could replace
StreamReader attachment = new StreamReader(fi_attachment.FullName);
byte[] attachmentData = Encoding.UTF8.GetBytes(attachment.ReadToEnd());
upload_request.ContentLength = attachmentData.Length;
//Stream upload_request_stream = upload_request.GetRequestStream();
using (Stream upload_request_stream = upload_request.GetRequestStream())
{
upload_request_stream.Write(attachmentData, 0, attachmentData.Length);
upload_request_stream.Close();
}
with (off the top of my head)
using (var fs = File.OpenRead(fi_attachment.FullName))
using (Stream upload_request_stream = upload_request.GetRequestStream())
{
fs.CopyTo(upload_request_stream);
}

How to Moq a FtpDataStream

I code a FTP client Application, testing my functions by nunit tool. I try to mock FTP data stream. GetAllCsvFilesFromFTP method call FtpConnect() method, but while testing I want to get a Stream data without connecting FTP server.
public List<string> GetAllCsvFilesFromFTP()
{
var files = new List<string>();
var responseStream = _ftpConnection.FtpConnect(WebRequestMethods.Ftp.ListDirectory);
if (responseStream != null)
{
var reader = new StreamReader(responseStream);
ReadResponseStream(files, reader);
reader.Close();
responseStream.Close();
}
return files.OrderByDescending(p => p).ToList();
}
public Stream FtpConnect(string connectionType, string fileName = "")
{
try
{
var request = (FtpWebRequest)WebRequest.Create(_readConfigXml.FtpUrl + fileName);
request.Credentials = new NetworkCredential(_readConfigXml.UserName, _readConfigXml.PassWord);
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = true;
request.Method = connectionType;
var response = (FtpWebResponse) request.GetResponse();
var stream = response.GetResponseStream();
return stream;
}
catch (Exception ex)
{
_logWriter.WriteLog(_readConfigXml.LogPath, ex.Message);
return null;
}
}
I try to mock test’s Setup method in below:
_ftpConnection.Setup(m => m.FtpConnect(It.IsAny(), It.IsAny())).Returns(?);
Could you help me about how can i simulate Returns data.
string csvdata = "1,2,3,4,5":
var ms = new MemoryStream(Encoding.UTF8.GetBytes(csvdata));
ms.Position = 0;
_ftpConnection.Setup(m => m.FtpConnect(It.IsAny(), It.IsAny())).Returns(ms);

Categories

Resources