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.
Related
I have problem with my C# program. I have an big zip archive (about 4Gb) which is split in small pieces, in fact each piece is 100 Mb. So, when my program start to upload files it working perfectly until 14th piece is starting uploaded. my program upload only ~90 Mb of that piece and then I receive error "The remote server returned an error: (451) Local error in processing". My C# program detect if file was not uploaded successfully and trying to upload it, so I got a never ending loop.
private static void SendToFtp(AppConfig cfg, string zipName)
{
Console.WriteLine("Starting FTP upload");
NetworkCredential creds = new NetworkCredential(cfg.FtpUserName, cfg.FtpPassword);
try
{
StringBuilder sb = new StringBuilder();
DirectoryInfo di = new DirectoryInfo(cfg.ZipFolder);
byte[] buffer = null;
foreach (var file in di.EnumerateFiles())
{
using (Stream st = File.OpenRead(file.FullName))
using (BinaryReader br = new BinaryReader(st))
{
bool success = false;
while (!success)
{
br.BaseStream.Seek(0, SeekOrigin.Begin);
buffer = br.ReadBytes((int)file.Length);
sb.Append($"ftp://{cfg.FtpHost}/{file.Name}");
request = (FtpWebRequest)WebRequest.Create(sb.ToString());
request.Method = WebRequestMethods.Ftp.UploadFile;
request.UseBinary = false;
request.UsePassive = false;
request.KeepAlive = false;
request.ServicePoint.ConnectionLimit = 1000;
request.Credentials = new NetworkCredential(cfg.FtpUserName, cfg.FtpPassword);
using (Stream requestStream = request.GetRequestStream())
{
try
{
requestStream.Write(buffer, 0, buffer.Length);
requestStream.Flush();
requestStream.Close();
var resp = (FtpWebResponse)request.GetResponse();
success = !success;
Console.WriteLine($"======================{file.Name}=======================");
Console.WriteLine($"Message:{resp.StatusDescription}");
Console.WriteLine($" Status code: {resp.StatusCode}");
Console.WriteLine($"========================================================");
}
catch (Exception ex)
{
request.Abort();
Console.WriteLine($"MSG: {ex.Message}");
}
}
sb.Clear();
}
}
}
Console.WriteLine("FTP upload finished");
}
catch (Exception ex)
{
Console.WriteLine($"{ex.Message}; {ex.Source}");
}
}
So, can someone help me or point me where I'm wrong.
Thank you!
I have 1 issues when i try too send file using FTP from Azure WebJobs.
This throw alternatly 'The remote server returned an error: (530) Not logged in.', my code work great in localhost (on dev computer).
I have read all off thoses Post but i didn't find a way :
FTPWebRequest 530 Error: Not Logged in issue
FTP The remote server returned an error: (530) Not logged in
Fetching files from FTP server via Azure webjobs
Ftp to external server in azure webjob not working
Other but i have only 1 webjobs in my app service and the CPU was at 30% when the jobs running ..
File download from FTP fails only in Azure web app
Edit :
Code Make Directory
FtpWebRequest reqFTP = null;
Stream ftpStream = null;
string[] subDirs = directory.Split('/');
string currentDir = publishUrl;
foreach (string subDir in subDirs)
{
try
{
currentDir = currentDir + "/" + subDir;
reqFTP = (FtpWebRequest)FtpWebRequest.Create(currentDir);
reqFTP.Method = WebRequestMethods.Ftp.MakeDirectory;
reqFTP.UseBinary = true;
//reqFTP.UsePassive = true;
//reqFTP.KeepAlive = true;
reqFTP.Credentials = new NetworkCredential(userName, userPWD);
FtpWebResponse response = (FtpWebResponse)await reqFTP.GetResponseAsync();
ftpStream = response.GetResponseStream();
ftpStream.Close();
response.Close();
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
//directory already exist I know that is weak but there is no way to check if a folder exist on ftp...
}
}
Code Send File :
try
{
using (WebClient client = new WebClient())
{
client.Credentials = new NetworkCredential(userName, userPWD);
client.UploadFile(publishUrl, "STOR", localFileName);
return true;
}
}
catch (Exception exception)
{
//Console.Error.Write(exception.Message);
Console.WriteLine(exception.Message);
return false;
}
And log when i run code in Azure WebApp WebJobs (Failed) :
https://pastebin.com/vgTxqT5p
And log when i run code in local machine (Work Great) :
https://pastebin.com/hBpum8T0
I think the workaround was on WebJobs app style and normal function wasn't waiting. I'm going to change my code to use Async Await method for all my WebJobs program.
Any have a way ?
Thx in advance.
Bad way (and i didn't Like this) but it's work..... :
Change the Make Directory Function like this :
public static void MakeFTPDir(string publishUrl, string userName, string userPWD, string directory)
{
FtpWebRequest reqFTP = null;
Stream ftpStream = null;
string[] subDirs = directory.Split('/');
string currentDir = publishUrl;
foreach (string subDir in subDirs)
{
bool isNotCreated = true;
int iTentative = 0;
currentDir = currentDir + "/" + subDir;
while (isNotCreated)
{
iTentative++;
try
{
reqFTP = (FtpWebRequest)FtpWebRequest.Create(currentDir);
reqFTP.Method = WebRequestMethods.Ftp.MakeDirectory;
reqFTP.UseBinary = true;
reqFTP.UsePassive = true;
reqFTP.KeepAlive = true;
reqFTP.Credentials = new NetworkCredential(userName, userPWD);
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
ftpStream = response.GetResponseStream();
ftpStream.Close();
response.Close();
}
catch(WebException webException)
{
FtpWebResponse excWebResponse = (FtpWebResponse)webException.Response;
if(excWebResponse.StatusCode == FtpStatusCode.NotLoggedIn)
{
Console.WriteLine("WebException ==> NotLoggedIn >> Tentative :" + iTentative);
isNotCreated = true;
}
else
{
Console.WriteLine(webException.Message);
isNotCreated = false;
}
}
catch (Exception exception)
{
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
if (response.StatusCode == FtpStatusCode.NotLoggedIn)
{
Console.WriteLine("Exception ==> NotLoggedIn >> Tentative :" + iTentative);
isNotCreated = true;
}
else
{
Console.WriteLine(exception.Message);
isNotCreated = false;
}
}
}
}
}
Change the send file function like this
public static bool SendFtpFile(string publishUrl, string userName, string userPWD, string localFileName)
{
bool isNotCreated = true;
int iTentative = 0;
while (isNotCreated)
{
iTentative++;
try
{
using (WebClient client = new WebClient())
{
client.Credentials = new NetworkCredential(userName, userPWD);
client.UploadFile(publishUrl, "STOR", localFileName);
return true;
}
}
catch (WebException webException)
{
FtpWebResponse excWebResponse = (FtpWebResponse)webException.Response;
if (excWebResponse.StatusCode == FtpStatusCode.NotLoggedIn)
{
Console.WriteLine("WebException ==> NotLoggedIn >> Tentative :" + iTentative);
isNotCreated = true;
}
else
{
Console.WriteLine(webException.Message);
return false;
}
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
return false;
}
}
return true;
}
Change the IfFileExistOnServer :
public static bool CheckIfFileExistsOnServer(string publishUrl, string userName, string userPWD, string fileName)
{
bool isNoCheck = true;
int iTentative = 0;
string azureBotUrl = publishUrl + "/" + fileName;
while (isNoCheck)
{
iTentative++;
try
{
var request = (FtpWebRequest)WebRequest.Create(azureBotUrl);
request.Credentials = new NetworkCredential(userName, userPWD);
request.UseBinary = true;
request.UsePassive = true;
request.KeepAlive = true;
request.Method = WebRequestMethods.Ftp.GetFileSize;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
return true;
}
catch (WebException webException)
{
FtpWebResponse excWebResponse = (FtpWebResponse)webException.Response;
if (excWebResponse.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable)
return false;
if (excWebResponse.StatusCode == FtpStatusCode.NotLoggedIn)
{
Console.WriteLine("WebException ==> NotLoggedIn >> Tentative :" + iTentative);
isNoCheck = true;
}
else
{
return false;
}
}
}
return false;
}
And Change RenameFileOnServer :
public static bool RenameFileOnServer(string publishUrl, string userName, string userPWD, string sourceFileName, string newFileName)
{
bool isNoRenameFile = true;
int iTentative = 0;
FtpWebRequest ftpRequest = null;
FtpWebResponse ftpResponse = null;
string azureBotUrl = publishUrl + "/" + sourceFileName;
while (isNoRenameFile)
{
iTentative++;
try
{
ftpRequest = (FtpWebRequest)WebRequest.Create(azureBotUrl);
ftpRequest.Credentials = new NetworkCredential(userName, userPWD);
ftpRequest.UseBinary = true;
ftpRequest.UsePassive = true;
ftpRequest.KeepAlive = true;
ftpRequest.Method = WebRequestMethods.Ftp.Rename;
ftpRequest.RenameTo = newFileName.Split('\\')[newFileName.Split('\\').Length - 1];
ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
ftpResponse.Close();
ftpRequest = null;
return true;
}
catch (WebException webException)
{
FtpWebResponse excWebResponse = (FtpWebResponse)webException.Response;
if (excWebResponse.StatusCode == FtpStatusCode.NotLoggedIn)
{
Console.WriteLine("WebException ==> NotLoggedIn >> Tentative :" + iTentative);
isNoRenameFile = true;
}
else
{
return false;
}
Console.WriteLine(webException.Message);
}
catch (Exception)
{
return false;
}
}
return false;
}
I'm waiting a call from Ms Azure Support ...
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);
}
I can download FTP files, but the download code does not have a resume facility and multi part files download. Because there are file larger than 500 MB, I can't download them continuously, because the connection gets closed and it starts download from beginning. I wanted my code a resume facility if it gets disconnected.
The code I am using is:
public string[] GetFileList()
{
string[] downloadFiles;
StringBuilder result = new StringBuilder();
FtpWebRequest reqFTP;
try
{
reqFTP = (FtpWebRequest)FtpWebRequest.Create(
new Uri("ftp://" + ftpServerIP + "/"));
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
WebResponse response = reqFTP.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
//MessageBox.Show(reader.ReadToEnd());
string line = reader.ReadLine();
while (line != null)
{
result.Append(line);
result.Append("\n");
line = reader.ReadLine();
}
result.Remove(result.ToString().LastIndexOf('\n'), 1);
reader.Close();
response.Close();
//MessageBox.Show(response.StatusDescription);
return result.ToString().Split('\n');
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
downloadFiles = null;
return downloadFiles;
}
}
private void Download(string filePath, string fileName)
{
FtpWebRequest reqFTP;
try
{
//filePath = <<The full path where the file is to be created.>>,
//fileName = <<Name of the file to be created
// (Need not be the name of the file on FTP server).>>
FileStream outputStream =
new FileStream(filePath + "\\" + fileName, FileMode.Create);
reqFTP = (FtpWebRequest)FtpWebRequest.Create(
new Uri("ftp://" + ftpServerIP + "/" + fileName));
reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
Stream ftpStream = response.GetResponseStream();
long cl = response.ContentLength;
int bufferSize = 2048;
int readCount;
byte[] buffer = new byte[bufferSize];
readCount = ftpStream.Read(buffer, 0, bufferSize);
while (readCount > 0)
{
outputStream.Write(buffer, 0, readCount);
readCount = ftpStream.Read(buffer, 0, bufferSize);
}
ftpStream.Close();
outputStream.Close();
response.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Before you start downloading check for the existence of the file on the local filesystem. If it exists, then get the size and use that for the ContentOffset member of the FtpWebRequest object. This functionality may not be supported by the FTP server, though.
A native implementation of FTP download resumption using the FtpWebRequest:
bool resume = false;
do
{
try
{
FileMode mode = resume ? FileMode.Append : FileMode.Create;
resume = false;
using (Stream fileStream = File.Open(#"C:\local\path\file.dat", mode))
{
var url = "ftp://ftp.example.com/remote/path/file.dat";
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
request.Credentials = new NetworkCredential("username", "password");
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.ContentOffset = fileStream.Position;
using (Stream ftpStream = request.GetResponse().GetResponseStream())
{
ftpStream.CopyTo(fileStream);
}
}
}
catch (WebException)
{
resume = true;
}
}
while (resume);
Or use an FTP library that can resume the transfer automatically.
For example WinSCP .NET assembly does. With it, a resumable download is as trivial as:
// Setup session options
var sessionOptions = new WinSCP.SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "user",
Password = "mypassword"
};
using (var session = new Session())
{
// Connect
session.Open(sessionOptions);
// Resumable download
session.GetFileToDirectory("/home/user/file.zip", #"C:\path");
}
(I'm the author of WinSCP)