csharp: FTP Client Library using System.Net.FtpClient and FluentFTP,测试中存在的BUG修正

任小云
2023-12-01

https://netftp.codeplex.com/

 

        /// <summary>
        /// Gets a file listing from the server. Each FtpListItem object returned
        /// contains information about the file that was able to be retrieved. If
        /// a DateTime property is equal to DateTime.MinValue then it means the 
        /// date in question was not able to be retrieved. If the Size property
        /// is equal to 0 then it means the size of the object could also not
        /// be retrieved.
        /// 读取文件或文件夹
        /// </summary>
        /// <param name="path">The path of the directory to list</param>
        /// <param name="options">Options that dictacte how a list is performed and what information is gathered.根据选项读取文件或文件夹或其文件的属性</param>
        /// <returns>An array of FtpListItem objects</returns>
        /// <example><code source="..\Examples\GetListing.cs" lang="cs" /></example>
        public FtpListItem[] GetListing(string path, FtpListOption options) {
            FtpListItem item = null;
            List<FtpListItem> lst = new List<FtpListItem>();
            List<string> rawlisting = new List<string>();
            string listcmd = null;
            string pwd = GetWorkingDirectory();
            string buf = null;

            if (path == null || path.Trim().Length == 0) {
                pwd = GetWorkingDirectory();
                if (pwd != null && pwd.Trim().Length > 0)
                    path = pwd;
                else
                    path = "./";
            }
            else if (!path.StartsWith("/") && pwd != null && pwd.Trim().Length > 0) {
                if (path.StartsWith("./"))
                    path = path.Remove(0, 2);
                path = string.Format("{0}/{1}", pwd, path).GetFtpPath();
            }

            // MLSD provides a machine parsable format with more
            // accurate information than most of the UNIX long list
            // formats which translates to more effcient file listings
            // so always prefer MLSD over LIST unless the caller of this
            // method overrides it with the ForceList option 
            // !=
            if ((options & FtpListOption.ForceList) == FtpListOption.ForceList && HasFeature(FtpCapability.MLSD)) 
            {
                listcmd = "MLSD";
            }
            else 
            {
                if ((options & FtpListOption.UseLS) == FtpListOption.UseLS) 
                {
                    listcmd = "LS";
                }
                else if ((options & FtpListOption.NameList) == FtpListOption.NameList) 
                {
                    listcmd = "NLST"; //读取文件名
                }
                else 
                {
                    string listopts = "";
                    listcmd = "LIST"; //读取所有文件夹文件
                    if ((options & FtpListOption.AllFiles) == FtpListOption.AllFiles)
                        listopts += "a";

                    if ((options & FtpListOption.Recursive) == FtpListOption.Recursive)
                        listopts += "R";

                    if (listopts.Length > 0)
                        listcmd += " -" + listopts;
                }
            }

            if((options & FtpListOption.NoPath) != FtpListOption.NoPath) 
            {
                listcmd = string.Format("{0} {1}", listcmd, path.GetFtpPath());
            }

            lock (m_lock)
            {
                Execute("TYPE I");
                //NLST /htdocs 读文件
                // read in raw file listing     MLSD /htdocs
                using (FtpDataStream stream = OpenDataStream(listcmd, 0)) {
                    try {
                        while ((buf = stream.ReadLine(Encoding)) != null) {
                            if (buf.Length > 0)
                            {
                                rawlisting.Add(buf);
                                FtpTrace.WriteLine(buf);
                            }
                            //rawlisting.Add(buf);
                            //FtpTrace.WriteLine(buf);
                        }
                    }
                    finally {
                        stream.Close();
                    }
                }
            }


            //根据上一个循环来读文件或文件夹的属性
            for (int i = 0; i < rawlisting.Count; i++) {
                buf = rawlisting[i];
                if ((options & FtpListOption.NameList) == FtpListOption.NameList) {
                    // if NLST was used we only have a file name so
                    // there is nothing to parse.
                    item = new FtpListItem() {
                        FullName = buf
                    };

                    if (DirectoryExists(item.FullName))
                        item.Type = FtpFileSystemObjectType.Directory;
                    else
                        item.Type = FtpFileSystemObjectType.File;

                    lst.Add(item);
                }
                else {
                    // if this is a result of LIST -R then the path will be spit out
                    // before each block of objects
                    if (listcmd.StartsWith("LIST") && (options & FtpListOption.Recursive) == FtpListOption.Recursive) {
                        if (buf.StartsWith("/") && buf.EndsWith(":")) {
                            path = buf.TrimEnd(':');
                            continue;
                        }
                    }

                    // if the next line in the listing starts with spaces
                    // it is assumed to be a continuation of the current line
                    if (i + 1 < rawlisting.Count && (rawlisting[i + 1].StartsWith("\t") || rawlisting[i + 1].StartsWith(" ")))
                        buf += rawlisting[++i];

                    item = FtpListItem.Parse(path, buf, m_caps); //重复了文件夹名称
                    // FtpListItem.Parse() returns null if the line
                    // could not be parsed
                    if (item != null && (item.Name != "." && item.Name != ".."))
                        lst.Add(item);
                    else
                        FtpTrace.WriteLine("Failed to parse file listing: " + buf);
                }

                // load extended information that wasn't available if the list options flags say to do so.
                if (item != null) {
                    // try to dereference symbolic links if the appropriate list
                    // option was passed
                    if (item.Type == FtpFileSystemObjectType.Link && (options & FtpListOption.DerefLinks) == FtpListOption.DerefLinks) {
                        item.LinkObject = DereferenceLink(item);
                    }

                    if ((options & FtpListOption.Modify) == FtpListOption.Modify && HasFeature(FtpCapability.MDTM)) {
                        // if the modified date was not loaded or the modified date is more than a day in the future 
                        // and the server supports the MDTM command, load the modified date.
                        // most servers do not support retrieving the modified date
                        // of a directory but we try any way.
                        if (item.Modified == DateTime.MinValue || listcmd.StartsWith("LIST")) {
                            DateTime modify;

                            if (item.Type == FtpFileSystemObjectType.Directory)
                                FtpTrace.WriteLine("Trying to retrieve modification time of a directory, some servers don't like this...");

                            if ((modify = GetModifiedTime(item.FullName)) != DateTime.MinValue)
                                item.Modified = modify;
                        }
                    }

                    if ((options & FtpListOption.Size) == FtpListOption.Size && HasFeature(FtpCapability.SIZE)) {
                        // if no size was parsed, the object is a file and the server
                        // supports the SIZE command, then load the file size
                        if (item.Size == -1) {
                            if (item.Type != FtpFileSystemObjectType.Directory) {
                                item.Size = GetFileSize(item.FullName);
                            }
                            else {
                                item.Size = 0;
                            }
                        }
                    }
                }
            }

            return lst.ToArray();
        }

       /// <summary>
        /// Creates a valid FTP path by appending the specified segments to this string
        /// </summary>
        /// <param name="path">This string</param>
        /// <param name="segments">The path segments to append</param>
        /// <returns>A valid FTP path</returns>
        public static string GetFtpPath(this string path, params string[] segments) {
            if (String.IsNullOrEmpty(path))
                path = "./";

            foreach (string part in segments) {
                if (part != null) {
                    if (path.Length > 0 && !path.EndsWith("/"))
                        path += "/";
                    //path += Regex.Replace(part.Replace('\\', '/'), "[/]+", "/").TrimEnd('/'); //变成重复
                }
            }

            path = Regex.Replace(path.Replace('\\', '/'), "[/]+", "/").TrimEnd('/');
            if (path.Length == 0)
                path = "/";

            /*if (!path.StartsWith("/") || !path.StartsWith("./"))
                path = "./" + path;*/

            return path;
        }

  测试

       /// <summary>
        /// 涂聚文
        /// Geovin Du
        /// 2017-08-02
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            FtpClient client = new FtpClient();
            client.Host = ftpServer;
            client.Encoding = System.Text.Encoding.GetEncoding("GB2312");
            // if you don't specify login credentials, we use the "anonymous" user account
            client.Credentials = new NetworkCredential(user, pwd);

            // begin connecting to the server
            client.Connect();
            //client.SetWorkingDirectory("/htdocs/");

            //文件夹和文件(未成功)
            //FtpListOption.NameList 当前文件夹下的文件
            //FtpListOption.AllFiles 当前文件夹下文件和文件夹
            FtpListItem[] items = client.GetListing("/htdocs/", FtpListOption.AllFiles);//根目录下可以。 AllFiles
            this.dataGridView1.DataSource = items;

            //获取的是所有文件名
            //client.SetWorkingDirectory("/htdocs/");
            //string[] items = client.GetNameListing();
            //List<FileInfoList> list = new List<FileInfoList>();
            //foreach (string str in items)
            //{
            //    FileInfoList info = new FileInfoList();
            //    info.Name = str;
            //    info.Date = DateTime.Now;
            //    list.Add(info);
            //}
            //this.dataGridView1.DataSource = list;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public class FileInfoList
    {
        public string Name { set; get; }

        public DateTime Date { set; get; }

    }

 https://github.com/hgupta9/FluentFTP

		/// <summary>
		/// Gets a file listing from the server. Each <see cref="FtpListItem"/> object returned
		/// contains information about the file that was able to be retrieved. 
        /// 读取文件和文件夹
        /// 涂聚文 2017-08-02 小小修改
		/// </summary>
		/// <remarks>
		/// If a <see cref="DateTime"/> property is equal to <see cref="DateTime.MinValue"/> then it means the 
		/// date in question was not able to be retrieved. If the <see cref="FtpListItem.Size"/> property
		/// is equal to 0, then it means the size of the object could also not
		/// be retrieved.
		/// </remarks>
		/// <param name="path">The path of the directory to list</param>
		/// <param name="options">Options that dictacte how a list is performed and what information is gathered.</param>
		/// <returns>An array of FtpListItem objects</returns>
		/// <example><code source="..\Examples\GetListing.cs" lang="cs" /></example>
		public FtpListItem[] GetListing(string path, FtpListOption options) {

			FtpTrace.WriteFunc("GetListing", new object[] { path, options });

			FtpListItem item = null;
			List<FtpListItem> lst = new List<FtpListItem>();
			List<string> rawlisting = new List<string>();
			string listcmd = null;
			string buf = null;

			// read flags
			bool isIncludeSelf = (options & FtpListOption.IncludeSelfAndParent) == FtpListOption.IncludeSelfAndParent;
			bool isForceList = (options & FtpListOption.ForceList) == FtpListOption.ForceList;
			bool isNoPath = (options & FtpListOption.NoPath) == FtpListOption.NoPath;
			bool isNameList = (options & FtpListOption.NameList) == FtpListOption.NameList;
			bool isUseLS = (options & FtpListOption.UseLS) == FtpListOption.UseLS;
			bool isAllFiles = (options & FtpListOption.AllFiles) == FtpListOption.AllFiles;
			bool isRecursive = (options & FtpListOption.Recursive) == FtpListOption.Recursive && RecursiveList;
			bool isDerefLinks = (options & FtpListOption.DerefLinks) == FtpListOption.DerefLinks;
			bool isGetModified = (options & FtpListOption.Modify) == FtpListOption.Modify;
			bool isGetSize = (options & FtpListOption.Size) == FtpListOption.Size;

			// calc path to request
			path = GetAbsolutePath(path);

			// MLSD provides a machine readable format with 100% accurate information
			// so always prefer MLSD over LIST unless the caller of this method overrides it with the ForceList option
			bool machineList = false; //||
            if ((!isForceList & m_parser == FtpParser.Machine) && HasFeature(FtpCapability.MLSD)) {
				listcmd = "MLSD";
				machineList = true;
			} else {
				if (isUseLS) {
					listcmd = "LS";
				} else if (isNameList) {
					listcmd = "NLST";
				} else {
					string listopts = "";

					listcmd = "LIST";

					if (isAllFiles)
						listopts += "a"; //读取所有文件夹和文件

                    if (isRecursive)
						listopts += "R";

					if (listopts.Length > 0)
						listcmd += " -" + listopts;
				}
			}

			if (!isNoPath) {
				listcmd = (listcmd + " " + path.GetFtpPath());
			}

#if !CORE14
			lock (m_lock) {
#endif
				Execute("TYPE I");
                //读取出文件夹或文件 读取所有文件夹和文件 LIST a
                // read in raw file listing
                using (FtpDataStream stream = OpenDataStream(listcmd, 0)) {
					try {
						FtpTrace.WriteLine(FtpTraceLevel.Verbose, "+---------------------------------------+");

						if (this.BulkListing) {

							// increases performance of GetListing by reading multiple lines of the file listing at once
							foreach (var line in stream.ReadAllLines(Encoding, this.BulkListingLength)) {
								if (!FtpExtensions.IsNullOrWhiteSpace(line)) {
									rawlisting.Add(line);
									FtpTrace.WriteLine(FtpTraceLevel.Verbose, "Listing:  " + line);
								}
							}

						} else {

							// GetListing will read file listings line-by-line (actually byte-by-byte)
							while ((buf = stream.ReadLine(Encoding)) != null) {
								if (buf.Length > 0) {
									rawlisting.Add(buf);
									FtpTrace.WriteLine(FtpTraceLevel.Verbose, "Listing:  " + buf);
								}
							}
						}

						FtpTrace.WriteLine(FtpTraceLevel.Verbose, "-----------------------------------------");

					} finally {
						stream.Close();
					}
				}
#if !CORE14
			}
#endif
            //在上循环中读取文件或文件夹属性
			for (int i = 0; i < rawlisting.Count; i++) {
				buf = rawlisting[i];

				if (isNameList) {

					// if NLST was used we only have a file name so
					// there is nothing to parse.
					item = new FtpListItem() {
						FullName = buf
					};

					if (DirectoryExists(item.FullName))
						item.Type = FtpFileSystemObjectType.Directory;
					else
						item.Type = FtpFileSystemObjectType.File;

					lst.Add(item);

				} else {

					// if this is a result of LIST -R then the path will be spit out
					// before each block of objects
					if (listcmd.StartsWith("LIST") && isRecursive) {
						if (buf.StartsWith("/") && buf.EndsWith(":")) {
							path = buf.TrimEnd(':');
							continue;
						}
					}

					// if the next line in the listing starts with spaces
					// it is assumed to be a continuation of the current line
					if (i + 1 < rawlisting.Count && (rawlisting[i + 1].StartsWith("\t") || rawlisting[i + 1].StartsWith(" ")))
						buf += rawlisting[++i];

					try {
						item = m_listParser.ParseSingleLine(path, buf, m_caps, machineList);
					} catch (FtpListParser.CriticalListParseException) {
						FtpTrace.WriteStatus(FtpTraceLevel.Verbose, "Restarting parsing from first entry in list");
						i = -1;
						lst.Clear();
						continue;
					}

					// FtpListItem.Parse() returns null if the line
					// could not be parsed
					if (item != null) {
						if (isIncludeSelf || !(item.Name == "." || item.Name == "..")) {
							lst.Add(item);
						} else {
							//FtpTrace.WriteStatus(FtpTraceLevel.Verbose, "Skipped self or parent item: " + item.Name);
						}
					} else {
						FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Failed to parse file listing: " + buf);
					}
				}

				// load extended information that wasn't available if the list options flags say to do so.
				if (item != null) {

					// try to dereference symbolic links if the appropriate list
					// option was passed
					if (item.Type == FtpFileSystemObjectType.Link && isDerefLinks) {
						item.LinkObject = DereferenceLink(item);
					}

					// if need to get file modified date
					if (isGetModified && HasFeature(FtpCapability.MDTM)) {

						// if the modified date was not loaded or the modified date is more than a day in the future 
						// and the server supports the MDTM command, load the modified date.
						// most servers do not support retrieving the modified date
						// of a directory but we try any way.
						if (item.Modified == DateTime.MinValue || listcmd.StartsWith("LIST")) {
							DateTime modify;

							if (item.Type == FtpFileSystemObjectType.Directory)
								FtpTrace.WriteStatus(FtpTraceLevel.Verbose, "Trying to retrieve modification time of a directory, some servers don't like this...");

							if ((modify = GetModifiedTime(item.FullName)) != DateTime.MinValue)
								item.Modified = modify;
						}
					}

					// if need to get file size
					if (isGetSize && HasFeature(FtpCapability.SIZE)) {

						// if no size was parsed, the object is a file and the server
						// supports the SIZE command, then load the file size
						if (item.Size == -1) {
							if (item.Type != FtpFileSystemObjectType.Directory) {
								item.Size = GetFileSize(item.FullName); //尺寸要换算一下
							} else {
								item.Size = 0;
							}
						}
					}
				}
			}

			return lst.ToArray();
		}

		/// <summary>
		/// Creates a valid FTP path by appending the specified segments to this string
        /// 涂聚文 2017-08-02 小小修改
		/// </summary>
		/// <param name="path">This string</param>
		/// <param name="segments">The path segments to append</param>
		/// <returns>A valid FTP path</returns>
		public static string GetFtpPath(this string path, params string[] segments) {
			if (String.IsNullOrEmpty(path))
				path = "./";

			foreach (string part in segments) {
				if (part != null) {
					if (path.Length > 0 && !path.EndsWith("/"))
						path += "/";
					//path = Regex.Replace(part.Replace('\\', '/'), "[/]+", "/").TrimEnd('/');  //这里重复 path +=
                }
			}

			path = Regex.Replace(path.Replace('\\', '/'), "[/]+", "/").TrimEnd('/');
			if (path.Length == 0)
				path = "/";

			/*if (!path.StartsWith("/") || !path.StartsWith("./"))
				path = "./" + path;*/

			return path;
		}

  

 

 

转载于:https://www.cnblogs.com/geovindu/p/7274642.html

 类似资料: