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);
Related
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.
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;
}
}
}
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.
I have code what gets a content of some FTP directory. At some servers I've tested it works fine.
But at one server this method throws an exception when we try to get response.
public static List<string> ListDirectory(string dirPath, string ftpUser, string ftpPassword)
{
List<string> res = new List<string>();
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(dirPath);
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential(ftpUser, ftpPassword);
request.KeepAlive = false;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
while (!reader.EndOfStream)
{
res.Add(reader.ReadLine());
}
reader.Close();
response.Close();
return res;
}
At catch section I have something like this
catch (WebException e)
{
FtpWebResponse response = (FtpWebResponse)e.Response;
/*in my case response.Status = ActionNotTakenFileUnavailableOrBusy*/
....
}
It works before but now it fails when folder is empty. If there is something there it works. And I can see this directory with TotalCommander.
Any ideas why?
This is an example on how to get a listing of a remote directory using the free library System.Net.FtpClient available from CodePlex.
I have used it in many occasions and, in my opinion, is more easy to work with
public void GetListing()
{
using (FtpClient conn = new FtpClient())
{
conn.Host = "your_ftp_site_url";
conn.Credentials = new NetworkCredential("your_user_account", "your_user_password");
foreach (FtpListItem item in conn.GetListing(conn.GetWorkingDirectory(), FtpListOption.Modify | FtpListOption.Size))
{
switch (item.Type)
{
case FtpFileSystemObjectType.Directory:
Console.WriteLine("Folder:" + item.Name);
break;
case FtpFileSystemObjectType.File:
Console.WriteLine("File:" + item.Name);
break;
}
}
}
}
You can find the download from this pages
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);
}