C# FTP get files that has been added today - c#

I have an FTP and I want to know the files that has been added today.
(in my business rules, there is no update to the files, so the files could be added and then can't be modified or removed at all).
I tried this:
FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://172.28.4.7/");
request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Console.WriteLine("{0} {1}", "ftp://172.28.4.7/", response.LastModified);
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
But, as expected, in the console I got the date of the last modifying.
Could you help me please to know the last added files?

You have to retrieve timestamps of remote files to select those you want (today's files).
Unfortunately, there's no really reliable and efficient way to retrieve timestamps using features offered by .NET framework as it does not support FTP MLSD command. The MLSD command provides listing of remote directory in a standardized machine-readable format. The command and the format is standardized by the RFC 3659.
Alternatives you can use, that are supported by the .NET framework:
the ListDirectoryDetails method (FTP LIST command) to retrieve details of all files in a directory and then you deal with FTP server specific format of the details
*nix format: Parsing FtpWebRequest ListDirectoryDetails line
DOS/Windows format: C# class to parse WebRequestMethods.Ftp.ListDirectoryDetails FTP response
the GetDateTimestamp method (FTP MDTM command) to individually retrieve timestamps for each file. Advantage is that the response is standardized by RFC 3659 to YYYYMMDDHHMMSS[.sss]. Disadvantage is that you have to send a separate request for each file, what can be quite inefficient.
const string uri = "ftp://example.com/remote/path/file.txt";
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(uri);
request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Console.WriteLine("{0} {1}", uri, response.LastModified);
This is what the answer by #tretom shows.
Alternatively you can use a 3rd party FTP client implementation that supports the modern MLSD command or that can directly download files given time constraint.
For example WinSCP .NET assembly supports both MLSD and time constraints.
There's even an example for your specific task: How do I transfer new/modified files only?
The example is for PowerShell, but translates to C# easily:
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "username",
Password = "password",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Download today's times
TransferOptions transferOptions = new TransferOptions();
transferOptions.FileMask = "*>=" + DateTime.Today.ToString("yyyy-MM-dd");
session.GetFiles(
"/remote/path/*", #"C:\local\path\", false, transferOptions).Check();
}
(I'm the author of WinSCP)

First you have to Get All Directory Details using "ListDirectoryDetails" :
ftpRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails
Get the Response in string[] .
Than Check if the string[] For File or Directory by checking "DIR" text in String[] items.
And after getting the Filenames From string[] , Again Request For "File Creation Date" of Each and Every File using :
ftpRequest.Method = WebRequestMethods.Ftp.GetDateTimestamp;
So, You can Get The File Added Date of your FTP Server.

a possible synchronous solution (it might be useful for someone):
a data container type:
public class Entity
{
public DateTime uploadDate { get; set; }
public string fileName { get; set; }
}
and the Lister lass:
public class FTPLister
{
private List<Entity> fileList = new List<Entity>();
public List<Entity> ListFilesOnFTP(string ftpAddress, string user, string password)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpAddress);
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential(user, password);
List<string> tmpFileList = new List<string>();
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
while (!reader.EndOfStream)
{
tmpFileList.Add(reader.ReadLine());
}
}
Uri ftp = new Uri(ftpAddress);
foreach (var f in tmpFileList)
{
FtpWebRequest req = (FtpWebRequest)WebRequest.Create(new Uri(ftp, f));
req.Method = WebRequestMethods.Ftp.GetDateTimestamp;
req.Credentials = new NetworkCredential(user, password);
using (FtpWebResponse resp = (FtpWebResponse)req.GetResponse())
{
fileList.Add(new Entity() { fileName=f, uploadDate=resp.LastModified });
}
}
fileList = fileList.Where(p => p.uploadDate>=DateTime.Today && p.uploadDate<DateTime.Today.AddDays(1)).ToList();
return fileList;
}
}

Related

Fill ComboBox with names of files from a directory on FTP server

I have a file folder in my FTP server and I want to fill a ComboBox with the contents inside of that folder. How would I go about doing this?
string result = string.Empty;
//Request location and server name---------->
FtpWebRequest request =
(FtpWebRequest)WebRequest.Create("ftp://*******" +"/" + "Products" + "/");
//Lists directory
request.Method = WebRequestMethods.Ftp.ListDirectory;
// set credentials
request.Credentials = new NetworkCredential("user1","1234");
//initialize response
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
//reader to read response
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
combobox1.Text = FTP_Server();
//data from file.
result = reader.ReadToEnd();
reader.Close();
response.Close();
Thanks! I didn't know if this was even possible!
Read the listing by lines:
FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://example.com/remote/path/");
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential("username", "password");
comboBox1.BeginUpdate();
try
{
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
{
comboBox1.Items.Add(reader.ReadLine());
}
}
}
finally
{
comboBox1.EndUpdate();
}
Downloading whole listing to a string and splitting it afterwards (as suggested by the other answer) can be pretty ineffective, if there's lot of entries.
Without knowing the exact format of your response string, my instinct would be to split the response string:
string files[] = result.Split("\r\n");
Then to iterate over the individual files, adding them to your combobox1's Items:
// Depending on how many items you're adding, you may wish to prevent a repaint until the operation is finished
combobox1.BeginUpdate();
foreach(string file in files)
{
combobox1.Items.Add(file);
}
combobox1.EndUpdate();
That should take care of it for you! There is some excellent (and exhaustive) documentation on MSDN as well, which will often contain some usage examples to help you out further: https://msdn.microsoft.com/en-us/library/system.windows.forms.combobox(v=vs.110).aspx#Examples
Note that, if you end up wanting to display information from a different FTP response, you'll have to clear the combobox1 like so first: combobox1.Items.Clear();

c# Upload to ftp with not created directorys on ftp server

This is the path I am trying to upload to the ftp server:
_ftp://ftp-server/products/productxx/versionxx/releasexx/delivery/data.zip
The problem is that the folders "productxx/versionxx/releasexx/delivery/" do not exist on the server.
Can I create them automatically while uploading the .zip file in c#
My coding at the moment is:
FtpWebRequest request =
(FtpWebRequest)WebRequest.Create(pathToFtp);
// Method set to UploadFile
request.Method = WebRequestMethods.Ftp.UploadFile;
// set password and username
request.Credentials = new NetworkCredential(UserName, Password);
// write MemoryStream in ftpStream
using (Stream ftpStream = request.GetRequestStream())
{
memoryStream.CopyTo(ftpStream);
}
I am getting the System.Net.WebException: "Can't connect to FTP: (553) File name not allowed" at "using (Stream ftpStream =request.GetRequestStream())"
but if my pathToFtp is _ftp://ftp-server/products/data.zip it´s working well
One of the request methods available is WebRequestMethods.Ftp.MakeDirectory. You should be able to use that to do what you want.
Something like this (though I've not tested it), should do the trick:
async Task CreateDirectory(string path)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(path);
request.Method = WebRequestMethods.Ftp.MakeDirectory;
using (var response = (FtpWebResponse)(await request.GetResponseAsync()))
{
Console.WriteLine($"Created: {path}");
}
}
It's answered in more detail here How do I create a directory on ftp server using C#?

Download files from FTP server in C# to local folder with modification date greater than specified

I work on C# web application and need to download files using FTP to local folder. Those images need to have modification date greater than date I specify.
Code:
public static List<FTPLineResult> GetFilesListSortedByDate(string ftpPath, Regex nameRegex, DateTime cutoff, System.Security.Cryptography.X509Certificates.X509Certificate cert)
{
List<FTPLineResult> output = new List<FTPLineResult>();
if (cert != null)
{
FtpWebRequest request = FtpWebRequest.Create(ftpPath) as FtpWebRequest;
request.Credentials = new NetworkCredential("unm", "pwd");
request.ClientCertificates.Add(cert);
ConfigureProxy(request);
request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
FtpWebResponse response = request.GetResponse() as FtpWebResponse;
StreamReader directoryReader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.ASCII);
var parser = new FTPLineParser();
while (!directoryReader.EndOfStream)
{
var result = parser.Parse(directoryReader.ReadLine());
if (!result.IsDirectory && result.DateTime > cutoff && nameRegex.IsMatch(result.Name))
{
output.Add(result);
}
}
// need to ensure the files are sorted in ascending date order
output.Sort(
new Comparison<FTPLineResult>(
delegate(FTPLineResult res1, FTPLineResult res2)
{
return res1.DateTime.CompareTo(res2.DateTime);
}
)
);
}
return output;
}
I have to use certificate (.p12).
How can I do this?
You have to retrieve timestamps of remote files to select those you want.
Unfortunately, there's no really reliable and efficient way to retrieve timestamps using features offered by .NET framework as it does not support FTP MLSD command. The MLSD command provides listing of remote directory in a standardized machine-readable format. The command and the format is standardized by the RFC 3659.
Alternatives you can use, that are supported by the .NET framework:
the ListDirectoryDetails method (FTP LIST command) to retrieve details of all files in a directory and then you deal with FTP server specific format of the details
*nix format: Parsing FtpWebRequest ListDirectoryDetails line
DOS/Windows format: C# class to parse WebRequestMethods.Ftp.ListDirectoryDetails FTP response
the GetDateTimestamp method (FTP MDTM command) to individually retrieve timestamps for each file. Advantage is that the response is standardized by RFC 3659 to YYYYMMDDHHMMSS[.sss]. Disadvantage is that you have to send a separate request for each file, what can be quite inefficient.
const string uri = "ftp://example.com/remote/path/file.txt";
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(uri);
request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Console.WriteLine("{0} {1}", uri, response.LastModified);
Alternatively you can use a 3rd party FTP client implementation that supports the modern MLSD command or that can directly download files given time constraint.
For example WinSCP .NET assembly supports both MLSD and time constraints.
There's even an example for your specific task: How do I transfer new/modified files only?
The example is for PowerShell, but translates to C# easily:
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "username",
Password = "password",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Download files created in 2017-06-15 and later
TransferOptions transferOptions = new TransferOptions();
transferOptions.FileMask = "*>=2017-06-15";
session.GetFiles(
"/remote/path/*", #"C:\local\path\", false, transferOptions).Check();
}
Though for web application, WinSCP is probably not the best solution. You maybe able to find another 3rd party library with similar functionality.
WinSCP also supports authentication with a client certificate. See SessionOptions.TlsClientCertificatePath. But that's really for a separate question.
(I'm the author of WinSCP)

Read FTP file with international characters

I am downloading a FTP location file using below code. Its working for all of the files except where the file name contains international character.
I have learned in the URI format such this is not allowed but how can download if there is existing file at mentioned location.
For testing I have setup local FTP server under IIS like below.
http://www.online-tech-tips.com/computer-tips/setup-ftp-server-in-windows-iis/
string mat_address = "ftp://localhost/";
StringBuilder result = new StringBuilder();
FtpWebRequest ftp = (FtpWebRequest)WebRequest.Create(mat_address);
ftp.Credentials = new NetworkCredential("userid", "Password");
ftp.Method = WebRequestMethods.Ftp.ListDirectory;
string[] downloadfile = null;
using (FtpWebResponse response = (FtpWebResponse)ftp.GetResponse())
using (StreamReader reader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.Default, true))
{
downloadfile = reader.ReadToEnd().Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
}
foreach (string d in downloadfile)
{
if (d.Contains("d"))
{
string temp = mat_address + HttpUtility.UrlPathEncode(d);
FtpWebRequest ftp2 = (FtpWebRequest)WebRequest.Create(temp);
ftp2.Credentials = new NetworkCredential("userid", "Password");
ftp2.Method = WebRequestMethods.Ftp.GetDateTimestamp;
ftp2.UseBinary = true;
ftp2.Proxy = null;
ftp2.KeepAlive = false;
ftp2.UsePassive = false;
FtpWebResponse response2 = ftp2.GetResponse() as FtpWebResponse;
DateTime temp1 = response2.LastModified.Date;
if (temp1 > DateTime.Now.AddDays(-10))
{
// Some extra work
}
}
}
I am getting error
The remote server returned an error: (550) File unavailable (e.g., file not found, no access).
Below is my FTP root folder with problematic file name as diá.png
I am using C# for coding and Visual Studio 2013 for development. Whats going wrong can someone help.
Update to Question:
Changes in Encoding to UTF8.
Using the local host everything works fine. But when using the FTP server from international domain like germany and sweden. The name is read like below.
I am getting error for the line below.
FtpWebResponse response2 = ftp2.GetResponse() as FtpWebResponse;
Hex Value for File Name: Suggested include by Martin. Thanks
31,30,31,33,36,2D,49,43,4F,4D,20,50,4A,C4,54,54,45,52,59,44,20,70,69,63,74,20,37‌​,38,78,31,31,38,20,61,6E,6E,69,2D,76,65,72,73,61,72,69,75,73,20,5B,77,31,33,32,31‌​,20,78,20,68,39,32,31,5D,20,6D,6D,20,44,49,46,46,55,53,45,2E,50,4E,47,
Most FTP servers should use UTF-8 encoding. So does your local (IIS) server.
So you need to use the Encoding.UTF8, when parsing the directory listing.
Though your real/production server seems to be broken in some way. It looks like it uses Windows-1252 encoding for the directory listing. Yet it claims (and seems to require) UTF-8 encoding for commands. That clearly (and rightfully) confuses FileZilla. But I do not see, why it does not work with the FtpWebRequest as it should use the UTF-8 (as the server positively responds to OPTS utf8 on command), and you have tried to explicitly use the Windows-1252 encoding, when parsing the listing.
Anyway, as you found (in chat) that WinSCP works, you can try to use the WinSCP .NET assembly. It will also make your code a way simpler:
SessionOptions sessionOptions = new SessionOptions();
sessionOptions.Protocol = Protocol.Ftp;
sessionOptions.HostName = "hostname";
sessionOptions.UserName = "username";
sessionOptions.Password = "password";
using (Session session = new Session())
{
session.Open(sessionOptions);
foreach (RemoteFileInfo fileInfo in session.ListDirectory("/textures").Files)
{
if (fileInfo.Name.Contains("d"))
{
if (fileInfo.LastWriteTime > DateTime.Now.AddDays(-10))
{
string sourcePath =
RemotePath.EscapeFileMask("/textures/" + fileInfo.Name);
session.GetFiles(sourcePath, #"c:\local\path\").Check();
}
}
}
}
Or, even simpler, using file mask with time constraint:
SessionOptions sessionOptions = new SessionOptions();
sessionOptions.Protocol = Protocol.Ftp;
sessionOptions.HostName = "hostname";
sessionOptions.UserName = "username";
sessionOptions.Password = "password";
using (Session session = new Session())
{
session.Open(sessionOptions);
session.GetFiles("/textures/*d*>=10D", #"c:\local\path\").Check();
}
See also WinSCP example How do I transfer new/modified files only?
I'd say, you'll have to transform the encoding of the recieved filename to match the needs of your local file system. Could you post what filename you revieve? I think you get an excaped string containing some illegal characters...

Get Binary File List from Remote server

I'm developing a system that need to download binary files from a server folder. In here I will check before downloading whether they are in my local folder.so I need to get list of the *.bin files.
I have tried code in below, but it generate list all the files that on server folder.
private string[] GetRemoteFileList()
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(_remoteHost));
request.Credentials = new NetworkCredential(_remoteUser, _remotePass);
request.Method = WebRequestMethods.Ftp.ListDirectory;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
string FileNames = reader.ReadToEnd();
string[] Files = Regex.Split(FileNames, "\r\n");
return Files;
}
What I need is filter out only *.bin files. How can I achieve this?
What have you tried?
You have now in Files an array of all files in the current directory. Why don't you filter that list? For example:
return Files.Where(
f => f.EndsWith(".bin", StringComparison.OrdinalIgnoreCase)
).ToList();

Categories

Resources