C# - Download files from FTP which have higher last-modified date - c#

I have an FTP server with some files. I have the same files in in a local directory (in C:\).
When I'll run the program, I'd like it searches all files in the FTP server that have a last-modified timestamp later than the same file (equal name) in local directory and download all files founded.
Can someone give me a help or a tip, please? I'll appreciate all answers!

Unfortunately, there's no really reliable and efficient way to retrieve timestamps using features offered by the .NET framework, as it does not support the 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 (the 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 similar to ls *nix command is the most common, drawback is that the format may change over time, as for newer files "May 8 17:48" format is used and for older files "Oct 18 2009" format is used).
Examples:
DOS/Windows format: C# class to parse WebRequestMethods.Ftp.ListDirectoryDetails FTP response
*nix format: Parsing FtpWebRequest ListDirectoryDetails line
the GetDateTimestamp method (an FTP MDTM command) to individually retrieve timestamps for each file. An advantage is that the response is standardized by the RFC 3659 to YYYYMMDDHHMMSS[.sss]. A 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.
For example the WinSCP .NET assembly supports that.
You can use the Session.ListDirectory or the Session.EnumerateRemoteFiles methods and read the RemoteFileInfo.LastWriteTime of the files in returned collection.
Or even easier, you can use the Session.SynchronizeDirectories to have the library automatically download (synchronize) the modified files:
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "user",
Password = "mypassword",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Synchronize files
var localPath = #"d:\www";
var remotePath = "/home/martin/public_html";
session.SynchronizeDirectories(
SynchronizationMode.Local, localPath, remotePath, false).Check();
}
WinSCP GUI can generate a code template for you.
(I'm the author of WinSCP)

List all files : https://msdn.microsoft.com/en-us/library/ms229716(v=vs.110).aspx
Read date : https://msdn.microsoft.com/en-us/library/system.net.ftpwebresponse.lastmodified(v=vs.110).aspx

Related

c# Is it possible to download a particular file from a zip file present in sftp

I'm using the below code(WinScp nuget) to connect to Sftp server, and it works well. I was able to list the files in present in Sftp server.
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Sftp,
HostName = "tst.tst.tt",
UserName = "test",
SshHostKeyFingerprint = "ssh-ed25519 255 xxxxxxxxxxxxxxxxxxxxx=",
SshPrivateKeyPath = #"D:\tst\key.ppk",
PrivateKeyPassphrase = "asdfghwetrtert",
};
sessionOptions.AddRawSettings("FSProtocol", "2");
using (Session session = new Session())
{
session.Open(sessionOptions);
RemoteDirectoryInfo directory = session.ListDirectory("/home/prod/Input");
foreach (RemoteFileInfo fileInfo in directory.Files)
{
Console.WriteLine(
"{0} with size {1}, permissions {2} and last modification at {3}",
fileInfo.Name, fileInfo.Length, fileInfo.FilePermissions,
fileInfo.LastWriteTime);
}
}
I have a zip file present in Sftp, is it possible to download only a particular file from zip instead of downloading the entire zip file ?
In general, it's technically possible to download only specific file from a ZIP archive using the SFTP protocol. But not with WinSCP, as it does not allow partial downloads.
If you use another SFTP library, that does allow partial downloads (like SSH.NET), you can use it to first download ZIP central directory, use it to locate specific file in the archive, then download that part of the archive (in a compressed form) and uncompress locally. But it's not easy, as you will have to dig low level into ZIP archive format.
As a proof of the concept, here a similar question for Python and FTP:
Get files names inside a zip file on FTP server without downloading whole archive
With SFTP it would be bit easier, as SFTP allows a random access to the remote file, what FTP does not.
A way easier, if you have a shell access to the server, is to unzip the file on the server using a shell command and then download the extracted file.

.NET FTP Upload file and preserve original date time

We have a Windows 2008 R2 web server with FTP over SSL. This app uses .NET 4.5 and when I upload files, the date/time on the file changes to the current date/time on the server. Is there a way to have the uploaded file preserve the original (last modified) date?
Here is what I have:
FtpWebRequest clsRequest = (FtpWebRequest)WebRequest.Create(FTPFilePath);
clsRequest.EnableSsl = true;
clsRequest.UsePassive = true;
clsRequest.Credentials = new NetworkCredential(swwwFTPUser, swwwFTPPassword);
clsRequest.Method = WebRequestMethods.Ftp.UploadFile;
Byte[] bFile = File.ReadAllBytes(LocalFilePath);
Stream clsStream = clsRequest.GetRequestStream();
clsStream.Write(bFile, 0, bFile.Length);
clsStream.Close();
clsStream.Dispose();
clsRequest = null;
There's really no standard way to update timestamp of a remote file over an FTP protocol. That's probably why the FtpWebRequest does not support it.
There are two non-standard ways to update the timestamp. Either a non-standard MFMT command:
MFMT yyyymmddhhmmss path
or a non-standard use of (otherwise standard) MDTM command:
MDTM yyyymmddhhmmss path
But the FtpWebRequest does not allow you to send a custom command either.
See for example How to send arbitrary FTP commands in C#.
So you have to use a 3rd party FTP library.
For example WinSCP .NET assembly preserves a timestamp of an uploaded file by default.
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "example.com",
UserName = "user",
Password = "mypassword",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Upload
session.PutFiles(#"c:\toupload\file.txt*", "/home/user/").Check();
}
See a full example.
Note that WinSCP .NET assembly is not a native .NET assembly. It's rather a thin .NET wrapper around a console application.
(I'm the author of WinSCP)
I know that we can assign file attributes:-
//Change the file created time.
File.SetCreationTime(path, dtCreation);
//Change the file modified time.
File.SetLastWriteTime(path, dtModified);
If you can extract the original date before you save it to the server, then you can change file attributes....something like this:-
Sftp sftp = new Sftp();
sftp.Connect(...);
sftp.Login(...);
// upload the file
sftp.PutFile(localFile, remoteFile);
// assign creation and modification time attributes
SftpAttributes attributes = new SftpAttributes();
System.IO.FileInfo info = new System.IO.FileInfo(localFile);
attributes.Created = info.CreationTime;
attributes.Modified = info.LastWriteTime;
// set attributes of the uploaded file
sftp.SetAttributes(remoteFile, attributes);
I hope this points you in the right direction.
This is an older question, but I will add my solution here. I used an approach similar to the solution proposed by #Martin Prikryl, using the MDTM command. His answer shows the DateTime format string as yyyymmddhhmmss which is incorrect since it does not correctly handle the month and 24 hour time format. In this answer I corrected this issue and provided a full solution using C#.
I used the FluentFTP library which handles many other aspects of working with an FTP through C# very well. To set the modified time, this library does not support it but it has an Execute method. Using the FTP command MDTM yyyyMMddHHmmss /path/to/file.txt will set the modified time of the file.
NOTE: in my instance I needed to use the universal time, which may be the case for you.
The code below shows how to connect to the FTP and set the last modified time using the Execute method and sending the MDTM command.
FtpClient client = new FtpClient("ftp-address", "username", "password");
client.Connect();
FtpReply reply = client.Execute($"MDTM {DateTime.UtcNow.ToString("yyyyMMddHHmmss")} /path/to/file.txt");

Get last modified date of a remote file

I have an app with which at startup it downloads a file from a remote location (through the net) and parses it's contents.
I am trying to speed up the process of startup as the bigger the file gets the slower the app starts.
As a way to speed up the process I thought of getting the last modified date of the file and if it is newer from the file on the user's pc then and only then download it.
I have found many ways to do it online but none of them are in C# (for windows store apps). Does anybody here know of a way of doing this without the need to download the file? If I am to download the file then the process is sped up at all.
My C# code for downloading the file currently is this
const string fileLocation = "link to dropbox";
var uri = new Uri(fileLocation);
var downloader = new BackgroundDownloader();
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync("feedlist.txt",CreationCollisionOption.ReplaceExisting);
DownloadOperation download = downloader.CreateDownload(uri, file);
await download.StartAsync();
If it helps the file is stored in dropbox but if any of you guys have a suggestion for another free file hosting service I am open to suggestions
Generally, you can check the file time by sending HEAD request and parsing/looking HTTP header response for a Last-Modified filed. The remote server should support it and DropBox does not support this feature for direct links (only via API). But DropBox have another feature, the headers have the etag field. You should store it and check in the next request. If it changed - the file has been changed too. You can use this tool to check the remote file headers.

c# FTP to FileZilla Server file name save issue

Whenever I use this code it uploads the jpeg, but the jpegs name is STOR with no extension on the server.
Any idea as to why this happens or how I change the file name when saving from my C# desktop application to my FileZilla FTP Server??
Here is the basic code, the names have been changes to protect the innocent ;)
WebClient client = new WebClient();
client.Credentials = new NetworkCredential("username", "password");
client.BaseAddress = "ftp://mysite.com";
client.UploadFile(WebRequestMethods.Ftp.UploadFile, "C:\mypics\pic1.jpg");
Try
client.UploadFile(remoteName, WebRequestMethods.Ftp.UploadFile , #"C:\mypics\pic1.jpg");
WebRequestMethods.Ftp.UploadFile is a string whose value happens to be STOR so the compiler is assuming your are using the client.UploadFile(remoteName, localName) overload which is why your file is named STOR
#sgmoore answered the question. You need just use method correctly:
client.UploadFile("pic1.jpg", "C:\mypics\pic1.jpg");
first argument is remote file name, second is path to local file.
You can also try some other ftp client implementations in .net (anyway FTP is implemented badly in .NET standard library), I've used ftplib and it's working good.

How do I set DateTimeStamp on file after uploading to FTP?

I have an application that deploys game data files to different gaming consoles. If matching files on the users machine and the console have identical size and dates, they must not be re-deployed.
On Xbox, this is easily accomplished because an XDK library used to upload files on the console allows me to set the date on the uploaded files to match the dates on the user's machine.
On Ps3 however, I use an FTP service running on the console. I use WebClient.UploadFileAsync to upload files to the console. However, I cannot figure out how I can set the uploaded file's date timestamp, leaving me with only the file size to determine identical files which is unsafe.
I was wondering if there was a way to set a file's date timestamp through the WebClient interface?
I don't think you can use the WebClient interface for this.
There seem to be various non-standard FTP extension commands implemented by some FTP servers to support the setting of a file's last modified time. The ones I know about are:
MDTM - This is the standard command for getting the a file's last modification time (as used by GetDateTimestamp()). Some servers support a set operation by specifying a timestamp argument to the command. as well as a filename.
MFMT - This was defined in an IETF experimental draft MFMT, to standardise this operation and avoid the non-standard use of the MDTM command described above.
SITE UTIME
If the FTP server running on the PS3 supports any of these extensions (check the result of the FEAT command), then you could use a simple socket FTP connection to issue the appropriate command to the server, after uploading the file.
WebClient will hand off ftp connections to FtpWebRequest. If you use FtpWebRequest directly you can send FTP commands to the server. The commands that are supported are defined as fields of WebRequestMethods.Ftp. One of those commands is GetDateTimestamp.
So if you construct an FtpWebRequest manually (instead of through WebClient) and send either the GateDateTimestamp or the ListDirectoryDetails command, you should be able to get the timestamp of the target file.

Categories