using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Collections; using Microsoft.VisualBasic; using System.Net; using System.IO; using System.Text.RegularExpressions; using System.Windows.Forms; //https://www.codeproject.com/Articles/56321/A-Windows-FTP-Application //https://www.codeproject.com/Articles/4472/Enhanced-BrowseForFolder-styled-TreeView //https://github.com/ChrisRichner/TreeViewFolderBrowser //https://www.codeproject.com/Articles/11991/An-FTP-client-library-for-NET //https://github.com/aybe/Windows-API-Code-Pack-1.1 //https://github.com/dbarros/WindowsAPICodePack namespace FTPLibrary { #region "FTP client class" /// <summary> /// A wrapper class for .NET 2.0 FTP /// </summary> /// <remarks> /// This class does not hold open an FTP connection but /// instead is stateless: for each FTP request it /// connects, performs the request and disconnects. /// </remarks> public class FTPclient { #region Delegated & Events //Download Progress Changed Event public delegate void DownloadProgressChangedHandler(object sender, DownloadProgressChangedArgs e); public event DownloadProgressChangedHandler OnDownloadProgressChanged; //Download Completed Event public delegate void DownloadCompletedHandler(object sender, DownloadCompletedArgs e); public event DownloadCompletedHandler OnDownloadCompleted; //New Server Message Event public delegate void NewMessageHandler(object sender, NewMessageEventArgs e); public event NewMessageHandler OnNewMessageReceived; //Upload Progress Changed Event //Download Progress Changed Event public delegate void UploadProgressChangedHandler(object sender, UploadProgressChangedArgs e); public event UploadProgressChangedHandler OnUploadProgressChanged; //Upload Completed Event public delegate void UploadCompletedHandler(object sender, UploadCompletedArgs e); public event UploadCompletedHandler OnUploadCompleted; #endregion #region "CONSTRUCTORS" /// <summary> /// Blank constructor /// </summary> /// <remarks>Hostname, username and password must be set manually</remarks> public FTPclient() { } /// <summary> /// Constructor just taking the hostname /// </summary> /// <param name="Hostname">in either ftp://ftp.host.com or ftp.host.com form</param> /// <remarks></remarks> public FTPclient(string Hostname) { _hostname = Hostname; } /// <summary> /// Constructor taking hostname, username and password /// </summary> /// <param name="Hostname">in either ftp://ftp.host.com or ftp.host.com form</param> /// <param name="Username">Leave blank to use 'anonymous' but set password to your email</param> /// <param name="Password"></param> /// <remarks></remarks> public FTPclient(string Hostname, string Username, string Password) { _hostname = Hostname; _username = Username; _password = Password; } #endregion #region "Directory functions" /// <summary> /// Return a simple directory listing /// </summary> /// <param name="directory">Directory to list, e.g. /pub</param> /// <returns>A list of filenames and directories as a List(of String)</returns> /// <remarks>For a detailed directory listing, use ListDirectoryDetail</remarks> public List<string> ListDirectory(string directory) { //return a simple list of filenames in directory System.Net.FtpWebRequest ftp = GetRequest(GetDirectory(directory)); //Set request to do simple list ftp.Method = System.Net.WebRequestMethods.Ftp.ListDirectory; //Give Message of Command NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "List Directory", "NLST"); OnNewMessageReceived(this, e); string str = GetStringResponse(ftp); //replace CRLF to CR, remove last instance str = str.Replace(" ", " ").TrimEnd(' '); //split the string into a list List<string> result = new List<string>(); result.AddRange(str.Split(' ')); return result; } /// <summary> /// Return a detailed directory listing /// </summary> /// <param name="directory">Directory to list, e.g. /pub/etc</param> /// <returns>An FTPDirectory object</returns> public FTPdirectory ListDirectoryDetail(string directory) { System.Net.FtpWebRequest ftp = GetRequest(GetDirectory(directory)); //Set request to do simple list ftp.Method = System.Net.WebRequestMethods.Ftp.ListDirectoryDetails; ftp.UseBinary = true; //Give Message of Command NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "List Directory Details", "LIST"); OnNewMessageReceived(this, e); string str = GetStringResponse(ftp); //replace CRLF to CR, remove last instance str = str.Replace(" ", " ").TrimEnd(' '); //split the string into a list return new FTPdirectory(str, _lastDirectory); } #endregion #region "Upload: File transfer TO ftp server" /// <summary> /// Copy a local file to the FTP server /// </summary> /// <param name="localFilename">Full path of the local file</param> /// <param name="targetFilename">Target filename, if required</param> /// <returns></returns> /// <remarks>If the target filename is blank, the source filename is used /// (assumes current directory). Otherwise use a filename to specify a name /// or a full path and filename if required.</remarks> public bool Upload(string localFilename, string targetFilename) { //1. check source if (!File.Exists(localFilename)) { throw (new ApplicationException("File " + localFilename + " not found")); } //copy to FI FileInfo fi = new FileInfo(localFilename); return Upload(fi, targetFilename); } #region Upload Variables System.Net.FtpWebRequest UploadFTPRequest = null; FileStream UploadFileStream = null; Stream UploadStream = null; bool UploadCanceled = false; FileInfo UploadFileInfo = null; #endregion /// <summary> /// Upload a local file to the FTP server /// </summary> /// <param name="fi">Source file</param> /// <param name="targetFilename">Target filename (optional)</param> /// <returns></returns> public bool Upload(FileInfo fi, string targetFilename) { //copy the file specified to target file: target file can be full path or just filename (uses current dir) //1. check target string target; if (targetFilename.Trim() == "") { //Blank target: use source filename & current dir target = this.CurrentDirectory + fi.Name; } else if (targetFilename.Contains("/")) { //If contains / treat as a full path target = AdjustDir(targetFilename); } else { //otherwise treat as filename only, use current directory target = CurrentDirectory + targetFilename; } string URI = Hostname + target; //perform copy UploadFTPRequest = GetRequest(URI); //Set request to upload a file in binary UploadFTPRequest.Method = System.Net.WebRequestMethods.Ftp.UploadFile; UploadFTPRequest.UseBinary = true; //Notify FTP of the expected size UploadFTPRequest.ContentLength = fi.Length; UploadFileInfo = fi; //create byte array to store: ensure at least 1 byte! const int BufferSize = 2048; byte[] content = new byte[BufferSize - 1 + 1]; int dataRead; //open file for reading using (UploadFileStream = fi.OpenRead()) { try { //open request to send using (UploadStream = UploadFTPRequest.GetRequestStream()) { //Give Message of Command NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "Upload File", "STOR"); OnNewMessageReceived(this, e); //Get File Size Int64 TotalBytesUploaded = 0; Int64 FileSize = fi.Length; do { if (UploadCanceled) { NewMessageEventArgs CancelMessage = new NewMessageEventArgs("RESPONSE", "Upload Canceled.", "CANCEL"); OnNewMessageReceived(this, CancelMessage); UploadCanceled = false; return false; } dataRead = UploadFileStream.Read(content, 0, BufferSize); UploadStream.Write(content, 0, dataRead); TotalBytesUploaded += dataRead; //Declare Event UploadProgressChangedArgs DownloadProgress = new UploadProgressChangedArgs(TotalBytesUploaded, FileSize); //Progress changed, Raise the event. OnUploadProgressChanged(this, DownloadProgress); System.Windows.Forms.Application.DoEvents(); } while (!(dataRead < BufferSize)); //Get Message and Raise Event NewMessageEventArgs UPloadResponse = new NewMessageEventArgs("RESPONSE", "File Uploaded!", "STOR"); OnNewMessageReceived(this, UPloadResponse); //Declare Event UploadCompletedArgs Args = new UploadCompletedArgs("Successful", true); //Raise Event OnUploadCompleted(this, Args); UploadStream.Close(); } } catch (Exception ex) { //Declare Event UploadCompletedArgs Args = new UploadCompletedArgs("Error: " + ex.Message, false); //Raise Event OnUploadCompleted(this, Args); } finally { //ensure file closed UploadFileStream.Close(); } } UploadFTPRequest = null; return true; } public void CancelUpload(string UploadFileName) { if (UploadFileStream != null) { UploadFileStream.Close(); UploadFTPRequest.Abort(); //UploadFileInfo.Delete(); UploadCanceled = true; UploadFTPRequest = null; this.FtpDelete(UploadFileName); MessageBox.Show("Upload Canceled"); } } #endregion #region "Download: File transfer FROM ftp server" /// <summary> /// Copy a file from FTP server to local /// </summary> /// <param name="sourceFilename">Target filename, if required</param> /// <param name="localFilename">Full path of the local file</param> /// <returns></returns> /// <remarks>Target can be blank (use same filename), or just a filename /// (assumes current directory) or a full path and filename</remarks> public bool Download(string sourceFilename, string localFilename, bool PermitOverwrite) { //2. determine target file FileInfo fi = new FileInfo(localFilename); return this.Download(sourceFilename, fi, PermitOverwrite); } //Version taking an FtpFileInfo public bool Download(FTPfileInfo file, string localFilename, bool PermitOverwrite) { return this.Download(file.FullName, localFilename, PermitOverwrite); } //Another version taking FtpFileInfo and FileInfo public bool Download(FTPfileInfo file, FileInfo localFI, bool PermitOverwrite) { return this.Download(file.FullName, localFI, PermitOverwrite); } #region Download Variables System.Net.FtpWebRequest DownloadFTPRequest = null; FtpWebResponse DownloadResponse = null; Stream DownloadResponseStream = null; //中文亂碼 FileStream DownloadFileStream = null; FileInfo TargetFileInfo = null; bool DownloadCanceled = false; #endregion //Version taking string/FileInfo public bool Download(string sourceFilename, FileInfo targetFI, bool PermitOverwrite) { //1. check target if (targetFI.Exists && !(PermitOverwrite)) { throw (new ApplicationException("Target file already exists")); } //2. check source string target; if (sourceFilename.Trim() == "") { throw (new ApplicationException("File not specified")); } else if (sourceFilename.Contains("/")) { //treat as a full path target = AdjustDir(sourceFilename); } else { //treat as filename only, use current directory target = CurrentDirectory + sourceFilename; } string URI = Hostname + target; //3. perform copy DownloadFTPRequest = GetRequest(URI); //Set request to download a file in binary mode DownloadFTPRequest.Method = System.Net.WebRequestMethods.Ftp.DownloadFile; DownloadFTPRequest.UseBinary = true; TargetFileInfo = targetFI; //open request and get response stream using (DownloadResponse = (FtpWebResponse)DownloadFTPRequest.GetResponse()) { using (DownloadResponseStream = DownloadResponse.GetResponseStream()) { //System.Security.AccessControl.FileSecurity fileSecurity = new System.Security.AccessControl.FileSecurity(targetFI.FullName, System.Security.AccessControl.AccessControlSections.All); //targetFI.SetAccessControl(fileSecurity); //loop to read & write to file using (DownloadFileStream = new FileStream(targetFI.FullName, FileMode.OpenOrCreate, FileAccess.ReadWrite)) { try { //Give Message of Command NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "Download File", "RETR"); OnNewMessageReceived(this, e); byte[] buffer = new byte[2048]; int read = 0; Int64 TotalBytesRead = 0; Int64 FileSize = this.GetFileSize(sourceFilename); DownloadCanceled = false; do { if (DownloadCanceled) { NewMessageEventArgs CancelMessage = new NewMessageEventArgs("RESPONSE", "Download Canceled.", "CANCEL"); DownloadCanceled = false; OnNewMessageReceived(this, CancelMessage); return false; } read = DownloadResponseStream.Read(buffer, 0, buffer.Length); DownloadFileStream.Write(buffer, 0, read); TotalBytesRead += read; //Declare Event DownloadProgressChangedArgs DownloadProgress = new DownloadProgressChangedArgs(TotalBytesRead, FileSize); //Progress changed, Raise the event. OnDownloadProgressChanged(this, DownloadProgress); System.Windows.Forms.Application.DoEvents(); } while (!(read == 0)); //Get Message and Raise Event NewMessageEventArgs NewMessageArgs = new NewMessageEventArgs("RESPONSE", DownloadResponse.StatusDescription, DownloadResponse.StatusCode.ToString()); OnNewMessageReceived(this, NewMessageArgs); //Declare Event DownloadCompletedArgs Args = new DownloadCompletedArgs("Successful", true); //Raise Event OnDownloadCompleted(this, Args); DownloadResponseStream.Close(); DownloadFileStream.Flush(); DownloadFileStream.Close(); DownloadFileStream = null; DownloadResponseStream = null; } catch (Exception ex) { //catch error and delete file only partially downloaded DownloadFileStream.Close(); //delete target file as it's incomplete targetFI.Delete(); //Decalre Event for Error DownloadCompletedArgs DownloadCompleted = new DownloadCompletedArgs("Error: " + ex.Message, false); //Raise Event OnDownloadCompleted(this, DownloadCompleted); } } if (DownloadFileStream != null) DownloadResponseStream.Close(); } if (DownloadFileStream != null) DownloadResponse.Close(); } return true; } public void CancelDownload() { if (DownloadFileStream != null) { DownloadFileStream.Close(); DownloadFTPRequest.Abort(); DownloadResponse.Close(); DownloadResponseStream.Close(); //DownloadFileStream = null; //DownloadResponseStream = null; TargetFileInfo.Delete(); DownloadCanceled = true; MessageBox.Show("Download Canceled"); } } #endregion #region "Other functions: Delete rename etc." /// <summary> /// Delete remote file /// </summary> /// <param name="filename">filename or full path</param> /// <returns></returns> /// <remarks></remarks> public bool FtpDelete(string filename) { //Determine if file or full path string URI = this.Hostname + GetFullPath(filename); System.Net.FtpWebRequest ftp = GetRequest(URI); //Set request to delete ftp.Method = System.Net.WebRequestMethods.Ftp.DeleteFile; try { //get response but ignore it string str = GetStringResponse(ftp); //Give Message of Command NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "Delete File", "DELE"); OnNewMessageReceived(this, e); } catch (Exception) { return false; } return true; } /// <summary> /// Determine if file exists on remote FTP site /// </summary> /// <param name="filename">Filename (for current dir) or full path</param> /// <returns></returns> /// <remarks>Note this only works for files</remarks> public bool FtpFileExists(string filename) { //Try to obtain filesize: if we get error msg containing "550" //the file does not exist try { long size = GetFileSize(filename); return true; } catch (Exception ex) { //only handle expected not-found exception if (ex is System.Net.WebException) { //file does not exist/no rights error = 550 if (ex.Message.Contains("550")) { //clear return false; } else { System.Windows.Forms.MessageBox.Show(ex.Message); return false; } } else { System.Windows.Forms.MessageBox.Show(ex.Message); return false; } } } /// <summary> /// Determine size of remote file /// </summary> /// <param name="filename"></param> /// <returns></returns> /// <remarks>Throws an exception if file does not exist</remarks> public long GetFileSize(string filename) { string path; if (filename.Contains("/")) { path = AdjustDir(filename); } else { path = this.CurrentDirectory + filename; } string URI = this.Hostname + path; System.Net.FtpWebRequest ftp = GetRequest(URI); //Try to get info on file/dir? ftp.Method = System.Net.WebRequestMethods.Ftp.GetFileSize; string tmp = this.GetStringResponse(ftp); //Give Message of Command NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "Get File Size", "SIZE"); OnNewMessageReceived(this, e); return GetSize(ftp); } public bool FtpRename(string sourceFilename, string newName) { //Does file exist? string source = GetFullPath(sourceFilename); if (!FtpFileExists(source)) { throw (new FileNotFoundException("File " + source + " not found")); } //build target name, ensure it does not exist string target = GetFullPath(newName); if (target == source) { throw (new ApplicationException("Source and target are the same")); } else if (FtpFileExists(target)) { throw (new ApplicationException("Target file " + target + " already exists")); } //perform rename string URI = this.Hostname + source; System.Net.FtpWebRequest ftp = GetRequest(URI); //Set request to delete ftp.Method = System.Net.WebRequestMethods.Ftp.Rename; ftp.RenameTo = target; ftp.UseBinary = true; try { //get response but ignore it string str = GetStringResponse(ftp); //Give Message of Command NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "File Rename", "RENAME"); OnNewMessageReceived(this, e); } catch (Exception) { return false; } return true; } public bool FtpCreateDirectory(string dirpath) { //perform create string URI = this.Hostname + AdjustDir(dirpath); System.Net.FtpWebRequest ftp = GetRequest(URI); //Set request to MkDir ftp.Method = System.Net.WebRequestMethods.Ftp.MakeDirectory; try { //get response but ignore it string str = GetStringResponse(ftp); //Give Message of Command NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "Make Directory", "MKD"); OnNewMessageReceived(this, e); } catch (Exception) { return false; } return true; } public bool FtpDeleteDirectory(string dirpath) { //perform remove string URI = this.Hostname + AdjustDir(dirpath); System.Net.FtpWebRequest ftp = GetRequest(URI); ftp.UseBinary = true; //Set request to RmDir ftp.Method = System.Net.WebRequestMethods.Ftp.RemoveDirectory; try { //get response but ignore it string str = GetStringResponse(ftp); //Give Message of Command NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "Remove Directory", "RMD"); OnNewMessageReceived(this, e); } catch (Exception) { return false; } return true; } #endregion #region "private supporting fns" //Get the basic FtpWebRequest object with the //common settings and security private FtpWebRequest GetRequest(string URI) { //create request FtpWebRequest result = (FtpWebRequest)FtpWebRequest.Create(URI); //Set the login details result.Credentials = GetCredentials(); //Do not keep alive (stateless mode) result.KeepAlive = false; result.UseBinary = true; return result; } /// <summary> /// Get the credentials from username/password /// </summary> private System.Net.ICredentials GetCredentials() { return new System.Net.NetworkCredential(Username, Password); } /// <summary> /// returns a full path using CurrentDirectory for a relative file reference /// </summary> private string GetFullPath(string file) { if (file.Contains("/")) { return AdjustDir(file); } else { return this.CurrentDirectory + file; } } /// <summary> /// Amend an FTP path so that it always starts with / /// </summary> /// <param name="path">Path to adjust</param> /// <returns></returns> /// <remarks></remarks> private string AdjustDir(string path) { return ((path.StartsWith("/")) ? "" : "/").ToString() + path; } private string GetDirectory(string directory) { string URI; if (directory == "") { //build from current URI = Hostname + this.CurrentDirectory; _lastDirectory = this.CurrentDirectory; } else { if (!directory.StartsWith("/")) { throw (new ApplicationException("Directory should start with /")); } URI = this.Hostname + directory; _lastDirectory = directory; } return URI; } //stores last retrieved/set directory private string _lastDirectory = ""; /// <summary> /// Obtains a response stream as a string /// </summary> /// <param name="ftp">current FTP request</param> /// <returns>String containing response</returns> /// <remarks>FTP servers typically return strings with CR and /// not CRLF. Use respons.Replace(vbCR, vbCRLF) to convert /// to an MSDOS string</remarks> private string GetStringResponse(FtpWebRequest ftp) { //Get the result, streaming to a string string result = ""; using (FtpWebResponse response = (FtpWebResponse)ftp.GetResponse()) { long size = response.ContentLength; using (Stream datastream = response.GetResponseStream()) { // 一中文亂碼問題 涂聚文 20180801 using (StreamReader sr = new StreamReader(datastream, System.Text.Encoding.GetEncoding("GB2312"))) { _WelcomeMessage = response.WelcomeMessage; _ExitMessage = response.ExitMessage; result = sr.ReadToEnd(); sr.Close(); } try { //Declare Event NewMessageEventArgs e = new NewMessageEventArgs("RESPONSE", response.StatusDescription, response.StatusCode.ToString()); //Raise Event OnNewMessageReceived(this, e); } catch { } datastream.Close(); } response.Close(); } return result; } /// <summary> /// Gets the size of an FTP request /// </summary> /// <param name="ftp"></param> /// <returns></returns> /// <remarks></remarks> private long GetSize(FtpWebRequest ftp) { long size; using (FtpWebResponse response = (FtpWebResponse)ftp.GetResponse()) { size = response.ContentLength; response.Close(); } return size; } #endregion #region "Properties" private string _hostname; /// <summary> /// Hostname /// </summary> /// <value></value> /// <remarks>Hostname can be in either the full URL format /// ftp://ftp.myhost.com or just ftp.myhost.com /// </remarks> public string Hostname { get { if (_hostname.StartsWith("ftp://")) { return _hostname; } else { return "ftp://" + _hostname; } } set { _hostname = value; } } private string _username; /// <summary> /// Username property /// </summary> /// <value></value> /// <remarks>Can be left blank, in which case 'anonymous' is returned</remarks> public string Username { get { return (_username == "" ? "anonymous" : _username); } set { _username = value; } } private string _password; public string Password { get { return _password; } set { _password = value; } } /// <summary> /// The CurrentDirectory value /// </summary> /// <remarks>Defaults to the root '/'</remarks> private string _currentDirectory = "/"; public string CurrentDirectory { get { //return directory, ensure it ends with / return _currentDirectory + ((_currentDirectory.EndsWith("/")) ? "" : "/").ToString(); } set { if (!value.StartsWith("/")) { throw (new ApplicationException("Directory should start with /")); } _currentDirectory = value; } } #endregion #region Server Messages string _WelcomeMessage, _ExitMessage; public string WelcomeMessage { get { return _WelcomeMessage; } set { _WelcomeMessage = value; } } public string ExitMessage { get { return _ExitMessage; } set { _ExitMessage = value; } } #endregion } #endregion #region "FTP file info class" /// <summary> /// Represents a file or directory entry from an FTP listing /// </summary> /// <remarks> /// This class is used to parse the results from a detailed /// directory list from FTP. It supports most formats of /// </remarks> public class FTPfileInfo { //Stores extended info about FTP file #region "Properties" public string FullName { get { return Path + Filename; } } public string Filename { get { return _filename; } } public string Path { get { return _path; } } public DirectoryEntryTypes FileType { get { return _fileType; } } public long Size { get { return _size; } } public DateTime FileDateTime { get { return _fileDateTime; } } public string Permission { get { return _permission; } } public string Extension { get { int i = this.Filename.LastIndexOf("."); if (i >= 0 && i < (this.Filename.Length - 1)) { return this.Filename.Substring(i + 1); } else { return ""; } } } public string NameOnly { get { int i = this.Filename.LastIndexOf("."); if (i > 0) { return this.Filename.Substring(0, i); } else { return this.Filename; } } } private string _filename; private string _path; private DirectoryEntryTypes _fileType; private long _size; private DateTime _fileDateTime; private string _permission; #endregion /// <summary> /// Identifies entry as either File or Directory /// </summary> public enum DirectoryEntryTypes { File, Directory } /// <summary> /// Constructor taking a directory listing line and path /// </summary> /// <param name="line">The line returned from the detailed directory list</param> /// <param name="path">Path of the directory</param> /// <remarks></remarks> public FTPfileInfo(string line, string path) { //parse line Match m = GetMatchingRegex(line); if (m == null) { //failed throw (new ApplicationException("Unable to parse line: " + line)); } else { _filename = m.Groups["name"].Value; _path = path; Int64.TryParse(m.Groups["size"].Value, out _size); //_size = System.Convert.ToInt32(m.Groups["size"].Value); _permission = m.Groups["permission"].Value; string _dir = m.Groups["dir"].Value; if (_dir != "" && _dir != "-") { _fileType = DirectoryEntryTypes.Directory; } else { _fileType = DirectoryEntryTypes.File; } try { _fileDateTime = DateTime.Parse(m.Groups["timestamp"].Value); } catch (Exception) { _fileDateTime = Convert.ToDateTime(null); } } } private Match GetMatchingRegex(string line) { Regex rx; Match m; for (int i = 0; i <= _ParseFormats.Length - 1; i++) { rx = new Regex(_ParseFormats[i]); m = rx.Match(line); if (m.Success) { return m; } } return null; } #region "Regular expressions for parsing LIST results" /// <summary> /// List of REGEX formats for different FTP server listing formats /// </summary> /// <remarks> /// The first three are various UNIX/LINUX formats, fourth is for MS FTP /// in detailed mode and the last for MS FTP in 'DOS' mode. /// I wish VB.NET had support for Const arrays like C# but there you go /// </remarks> private static string[] _ParseFormats = new string[] { "(?<dir>[\-d])(?<permission>([\-r][\-w][\-xs]){3})\s+\d+\s+\w+\s+\w+\s+(?<size>\d+)\s+(?<timestamp>\w+\s+\d+\s+\d{4})\s+(?<name>.+)", "(?<dir>[\-d])(?<permission>([\-r][\-w][\-xs]){3})\s+\d+\s+\d+\s+(?<size>\d+)\s+(?<timestamp>\w+\s+\d+\s+\d{4})\s+(?<name>.+)", "(?<dir>[\-d])(?<permission>([\-r][\-w][\-xs]){3})\s+\d+\s+\d+\s+(?<size>\d+)\s+(?<timestamp>\w+\s+\d+\s+\d{1,2}:\d{2})\s+(?<name>.+)", "(?<dir>[\-d])(?<permission>([\-r][\-w][\-xs]){3})\s+\d+\s+\w+\s+\w+\s+(?<size>\d+)\s+(?<timestamp>\w+\s+\d+\s+\d{1,2}:\d{2})\s+(?<name>.+)", "(?<dir>[\-d])(?<permission>([\-r][\-w][\-xs]){3})(\s+)(?<size>(\d+))(\s+)(?<ctbit>(\w+\s\w+))(\s+)(?<size2>(\d+))\s+(?<timestamp>\w+\s+\d+\s+\d{2}:\d{2})\s+(?<name>.+)", "(?<timestamp>\d{2}\-\d{2}\-\d{2}\s+\d{2}:\d{2}[Aa|Pp][mM])\s+(?<dir>\<\w+\>){0,1}(?<size>\d+){0,1}\s+(?<name>.+)" }; #endregion } #endregion #region "FTP Directory class" /// <summary> /// Stores a list of files and directories from an FTP result /// </summary> /// <remarks></remarks> public class FTPdirectory : List<FTPfileInfo> { public FTPdirectory() { //creates a blank directory listing } /// <summary> /// Constructor: create list from a (detailed) directory string /// </summary> /// <param name="dir">directory listing string</param> /// <param name="path"></param> /// <remarks></remarks> public FTPdirectory(string dir, string path) { foreach (string line in dir.Replace(" ", "").Split(System.Convert.ToChar(' '))) { //parse if (line != "") { this.Add(new FTPfileInfo(line, path)); } } } /// <summary> /// Filter out only files from directory listing /// </summary> /// <param name="ext">optional file extension filter</param> /// <returns>FTPdirectory listing</returns> public FTPdirectory GetFiles(string ext) { return this.GetFileOrDir(FTPfileInfo.DirectoryEntryTypes.File, ext); } /// <summary> /// Returns a list of only subdirectories /// </summary> /// <returns>FTPDirectory list</returns> /// <remarks></remarks> public FTPdirectory GetDirectories() { return this.GetFileOrDir(FTPfileInfo.DirectoryEntryTypes.Directory, ""); } //internal: share use function for GetDirectories/Files private FTPdirectory GetFileOrDir(FTPfileInfo.DirectoryEntryTypes type, string ext) { FTPdirectory result = new FTPdirectory(); foreach (FTPfileInfo fi in this) { if (fi.FileType == type) { if (ext == "") { result.Add(fi); } else if (ext == fi.Extension) { result.Add(fi); } } } return result; } public bool FileExists(string filename) { foreach (FTPfileInfo ftpfile in this) { if (ftpfile.Filename == filename) { return true; } } return false; } private const char slash = '/'; public static string GetParentDirectory(string dir) { string tmp = dir.TrimEnd(slash); int i = tmp.LastIndexOf(slash); if (i > 0) { return tmp.Substring(0, i - 1); } else { throw (new ApplicationException("No parent for root")); } } } #endregion #region Events public class DownloadProgressChangedArgs : EventArgs { //Private Members private Int64 _BytesDownload; private Int64 _TotalBytes; //Constructor public DownloadProgressChangedArgs(Int64 BytesDownload, Int64 TotleBytes) { this._BytesDownload = BytesDownload; this._TotalBytes = TotleBytes; } //Public Members public Int64 BytesDownloaded { get { return _BytesDownload; } } public Int64 TotleBytes { get { return _TotalBytes; } } } public class DownloadCompletedArgs : EventArgs { //Private Members private bool _DownloadedCompleted; private string _DownloadStatus; //Constructor public DownloadCompletedArgs(string Status, bool Completed) { this._DownloadedCompleted = Completed; this._DownloadStatus = Status; } //Public Members public String DownloadStatus { get { return _DownloadStatus; } } public bool DownloadCompleted { get { return _DownloadedCompleted; } } } public class NewMessageEventArgs : EventArgs { //Private Members private string _Message; private string _StatusCode; private string _Type; //Constructor public NewMessageEventArgs(string Type, string Status, string Code) { this._Message = Status; this._StatusCode = Code; this._Type = Type; } //Public Members public string StatusMessage { get { return _Message; } } public string StatusCode { get { return _StatusCode; } } public string StatusType { get { return _Type; } } } public class UploadProgressChangedArgs : EventArgs { //Private Members private Int64 _BytesUpload; private Int64 _TotalBytes; //Constructor public UploadProgressChangedArgs(Int64 BytesUpload, Int64 TotleBytes) { this._BytesUpload = BytesUpload; this._TotalBytes = TotleBytes; } //Public Members public Int64 BytesUploaded { get { return _BytesUpload; } } public Int64 TotleBytes { get { return _TotalBytes; } } } public class UploadCompletedArgs : EventArgs { //Private Members private bool _UploadCompleted; private string _UploadStatus; //Constructor public UploadCompletedArgs(string Status, bool Completed) { this._UploadCompleted = Completed; this._UploadStatus = Status; } //Public Members public String UploadStatus { get { return _UploadStatus; } } public bool UploadCompleted { get { return _UploadCompleted; } } } #endregion }
测试代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Net; using FTPLibrary; using System.Diagnostics; using System.Reflection; using System.IO; namespace ftpdemo { /// <summary> /// 涂聚文 2017-08-01 /// </summary> public partial class Form6 : Form { public FTPclient FtpClient; ListViewItem Message; string remotingFolder = System.Configuration.ConfigurationManager.AppSettings["remotingFolder"]; //远程ftp文件目录 string localFolder = System.Configuration.ConfigurationManager.AppSettings["localFolder"]; //要下载到的本地目录 string ftpServer = System.Configuration.ConfigurationManager.AppSettings["ftpServer"]; //ftp服务器 string user = System.Configuration.ConfigurationManager.AppSettings["user"]; //用户名 string pwd = System.Configuration.ConfigurationManager.AppSettings["pwd"]; //密码 string port = System.Configuration.ConfigurationManager.AppSettings["port"]; //端口 /// <summary> /// /// </summary> /// <param name="byteCount"></param> /// <returns></returns> private string GetFileSize(double byteCount) { string size = "0 Bytes"; if (byteCount >= 1073741824.0) size = String.Format("{0:##.##}", byteCount / 1073741824.0) + " GB"; else if (byteCount >= 1048576.0) size = String.Format("{0:##.##}", byteCount / 1048576.0) + " MB"; else if (byteCount >= 1024.0) size = String.Format("{0:##.##}", byteCount / 1024.0) + " KB"; else if (byteCount > 0 && byteCount < 1024.0) size = byteCount.ToString() + " Bytes"; return size; } /// <summary> /// Sets up the FTPClient for this Form. Called from frmLogin. /// </summary> /// <param name="client">FTPclient on frmLogin is used to refrence the FtpClient here.</param> public void SetFtpClient(FTPclient client) { //Set FtpClient FtpClient = client; //Display the Welcome Message Message = new ListViewItem(); Message.Text = DateTime.Now.ToLongTimeString() + " " + DateTime.Now.ToLongDateString(); Message.SubItems.Add("Welcome Message"); Message.SubItems.Add(FtpClient.WelcomeMessage); Message.SubItems.Add("No Code"); Message.SubItems.Add("/"); lstMessages.Items.Add(Message); //Setup OnMessageReceived Event FtpClient.OnNewMessageReceived += new FTPclient.NewMessageHandler(FtpClient_OnNewMessageReceived); //Open and Display Root Directory and Files/Folders in it foreach (FTPfileInfo folder in FtpClient.ListDirectoryDetail("/")) { ListViewItem item = new ListViewItem(); item.Text = folder.Filename; if (folder.FileType == FTPfileInfo.DirectoryEntryTypes.Directory) item.SubItems.Add("Folder"); else item.SubItems.Add("File"); item.SubItems.Add(folder.FullName); item.SubItems.Add(folder.Permission); item.SubItems.Add(folder.FileDateTime.ToShortTimeString() + folder.FileDateTime.ToShortDateString()); item.SubItems.Add(GetFileSize(folder.Size)); lstRemoteSiteFiles.Items.Add(item); } } /// <summary> /// /// </summary> /// <param name="myObject"></param> /// <param name="e"></param> private void FtpClient_OnNewMessageReceived(object myObject, NewMessageEventArgs e) { //Display Meesage in lstMessages Message = new ListViewItem(); Message.Text = DateTime.Now.ToLongTimeString() + " " + DateTime.Now.ToLongDateString(); Message.SubItems.Add(e.StatusType); Message.SubItems.Add(e.StatusMessage); Message.SubItems.Add(e.StatusCode); Message.SubItems.Add(txtRemoteDirectory.Text); lstMessages.Items.Add(Message); this.lstMessages.EnsureVisible(this.lstMessages.Items.Count - 1); } /// <summary> /// Reload all Directories and Files in Current Directory /// </summary> private void RefreshDirectory() { //Clear all items lstRemoteSiteFiles.Items.Clear(); //Open and Display Root Directory foreach (FTPfileInfo folder in FtpClient.ListDirectoryDetail(txtRemoteDirectory.Text)) { ListViewItem item = new ListViewItem(); item.Text = folder.Filename; if (folder.FileType == FTPfileInfo.DirectoryEntryTypes.Directory) item.SubItems.Add("Folder"); else item.SubItems.Add("File"); item.SubItems.Add(folder.FullName); item.SubItems.Add(folder.Permission); item.SubItems.Add(folder.FileDateTime.ToShortTimeString() + folder.FileDateTime.ToShortDateString()); item.SubItems.Add(folder.Size.ToString()); lstRemoteSiteFiles.Items.Add(item); } } /// <summary> /// /// </summary> public Form6() { InitializeComponent(); //Set Selected Directory txtRemoteDirectory.Text = "/"; lstRemoteSiteFiles.FullRowSelect = true; } /// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Form6_Load(object sender, EventArgs e) { //Set FTP FTPclient objFtp = new FTPclient(ftpServer, user,pwd); objFtp.CurrentDirectory = "/"; SetFtpClient(objFtp); } /// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void lstRemoteSiteFiles_SelectedIndexChanged(object sender, EventArgs e) { } /// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void lstRemoteSiteFiles_MouseDoubleClick(object sender, MouseEventArgs e) { if (lstRemoteSiteFiles.Items.Count != 0) { try { if (lstRemoteSiteFiles.SelectedItems[0].SubItems[1].Text == "File") { //Enable the buttons that are related to the FILE // btnRename.Enabled = true; // btnDownload.Enabled = true; //Set Current Directory for Download FtpClient.CurrentDirectory = txtRemoteDirectory.Text; //Its a File, so Ask them if they want to Save it... if (MessageBox.Show("Do you want to save this file: " + txtRemoteDirectory.Text + lstRemoteSiteFiles.SelectedItems[0].Text + "/" + "?", "Download File?", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Yes) { //Save the File to location //downloadToolStripMenuItem_Click(this, e); } } else if (lstRemoteSiteFiles.SelectedItems[0].SubItems[1].Text == "Folder") // Its a Directory { //Set Directory to txtRemoteDirectory.Text + selectedItem + "/" //Result - /SelectedDirecotory/ -- good for navigation, keeping user informed and code :) txtRemoteDirectory.Text += lstRemoteSiteFiles.SelectedItems[0].Text + "/"; lstRemoteSiteFiles.Items.Clear(); //Set Current Dir FtpClient.CurrentDirectory = txtRemoteDirectory.Text; //Get Files and Folders from Selected Direcotry foreach (FTPfileInfo folder in FtpClient.ListDirectoryDetail(txtRemoteDirectory.Text)) { ListViewItem item = new ListViewItem(); item.Text = folder.Filename; if (folder.FileType == FTPfileInfo.DirectoryEntryTypes.Directory) item.SubItems.Add("Folder"); else item.SubItems.Add("File"); item.SubItems.Add(folder.FullName); item.SubItems.Add(folder.Permission); item.SubItems.Add(folder.FileDateTime.ToShortDateString()+" "+folder.FileDateTime.ToShortTimeString()); item.SubItems.Add(GetFileSize(folder.Size)); lstRemoteSiteFiles.Items.Add(item); } } } catch { } } } /// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void lstRemoteSiteFiles_ItemDrag(object sender, ItemDragEventArgs e) { } } }