[C#]FTP助手

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace FirstSolver
{
    /// <summary>
    /// FTP 助手
    /// </summary>
    public static class FtpHelper
    {
        /// <summary>
        /// 文件上传重试次数
        /// </summary>
        public static int UploadRetry { get; set; } = 2;

        /// <summary>
        /// 判断FTP目录是否存在
        /// </summary>
        /// <param name="requestUriString">目录路径,注意结尾必须带“/”</param>
        /// <returns>true/false</returns>
        public static bool DirectoryExists(string requestUriString)
        {
            WebRequest request = WebRequest.Create(requestUriString.EndsWith("/") ? requestUriString : $"{requestUriString}/");
            request.Method = WebRequestMethods.Ftp.ListDirectory;
            using (var response = (FtpWebResponse)request.GetResponse())
            {
                return response.StatusCode == FtpStatusCode.DataAlreadyOpen;
            }
        }

        /// <summary>
        /// 判断FTP文件是否存在
        /// </summary>
        /// <param name="requestUriString">文件路径</param>
        /// <returns>true/false</returns>
        public static bool FileExists(string requestUriString)
        {
            WebRequest request = WebRequest.Create(requestUriString);
            request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
            using (var response = (FtpWebResponse)request.GetResponse())
            {
                return response.StatusCode == FtpStatusCode.FileStatus;
            }
        }

        /// <summary>
        /// 获取文件最后修改时间
        /// </summary>
        /// <param name="requestUriString">文件路径</param>
        /// <returns>文件最后修改时间</returns>
        /// <remarks>也可以用于判断文件是否存在</remarks>
        public static DateTime GetDateTimestamp(string requestUriString)
        {
            WebRequest request = WebRequest.Create(requestUriString);
            request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
            using (var response = (FtpWebResponse)request.GetResponse())
            {
                return response.LastModified;
            }
        }

        /// <summary>
        /// 在FTP服务器上创建目录
        /// </summary>
        /// <param name="requestUriString">目录路径,结尾可以不带“/”</param>
        /// <returns>true/false</returns>
        /// <remarks>要求上级目录必须存在,否则创建失败</remarks>
        public static bool MakeDirectory(string requestUriString)
        {
            WebRequest request = WebRequest.Create(requestUriString);
            request.Method = WebRequestMethods.Ftp.MakeDirectory;
            using (var response = (FtpWebResponse)request.GetResponse())
            {
                return response.StatusCode == FtpStatusCode.PathnameCreated;
            }
        }

        /// <summary>
        /// 在FTP服务器上创建目录
        /// </summary>
        /// <param name="requestUriString">目录路径,结尾可以不带“/”</param>
        /// <returns>true/false</returns>
        /// <remarks>如果上级目录不存在,将依次创建</remarks>
        public static bool MakeDirectoryRecursive(string requestUriString)
        {
            string[] parts = requestUriString.Split('/', StringSplitOptions.RemoveEmptyEntries);
            StringBuilder sb = new StringBuilder();
            sb.Append($"{parts[0]}//{parts[1]}/");
            for (int i = 2; i < parts.Length - 1; i++)
            {
                sb.Append($"{parts[i]}/");
                MakeDirectory(sb.ToString());
            }
            return MakeDirectory(requestUriString);
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="requestUriString">上传路径</param>
        /// <param name="data">文件内容</param>
        /// <returns>true/false</returns>
        public static bool Upload(string requestUriString, byte[] data)
        {
            using (var client = new WebClient())
            {
                for (int i = 0; i < UploadRetry; i++)
                {
                    try
                    {
                        client.UploadData(requestUriString, data);
                        return true;
                    }
                    catch
                    {   // [失败的原因很可能是目录未创建]错误类型:(553) | (550)
                        MakeDirectoryRecursive(requestUriString.Substring(0, requestUriString.LastIndexOf("/") + 1));
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="requestUriString">上传路径</param>
        /// <param name="localFilename">要上传的文件名称</param>
        /// <returns>true/false</returns>
        public static bool Upload(string requestUriString, string localFilename)
        {
            return Upload(requestUriString, File.ReadAllBytes(localFilename));
        }

        /// <summary>
        /// 异步上传文件
        /// </summary>
        /// <param name="requestUriString">上传路径</param>
        /// <param name="data">文件内容</param>
        /// <returns>true/false</returns>
        public async static Task<bool> UploadAsync(string requestUriString, byte[] data)
        {
            return await Task.Run(() =>
            {
                return Upload(requestUriString, data);
            });
        }

        /// <summary>
        /// 异步上传文件
        /// </summary>
        /// <param name="requestUriString">上传路径</param>
        /// <param name="localFilename">要上传的文件名称</param>
        /// <returns>true/false</returns>
        public async static Task<bool> UploadAsync(string requestUriString, string localFilename)
        {
            return await Task.Run(() =>
            {
                return Upload(requestUriString, localFilename);
            });
        }

        /// <summary>
        /// 读取FTP文件内容
        /// </summary>
        /// <param name="requestUriString">要下载的文件路径</param>
        /// <returns>存储文件内容的字节数组</returns>
        public static byte[] ReadFile(string requestUriString)
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
            request.Method = WebRequestMethods.Ftp.DownloadFile;
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                using (Stream ftpStream = response.GetResponseStream())
                {
                    using (MemoryStream stream = new MemoryStream())
                    {
                        ftpStream.CopyTo(stream);
                        return stream.ToArray();
                    }
                }
            }
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="requestUriString">要下载的文件路径</param>
        /// <param name="localFilename">文件保存路径</param>
        public static void Download(string requestUriString, string localFilename)
        {
            string? folder = Path.GetDirectoryName(localFilename);
            if (!Directory.Exists(folder)) Directory.CreateDirectory(folder!);
            File.WriteAllBytes(localFilename, ReadFile(requestUriString));
        }

        /// <summary>
        /// 获取文件大小
        /// </summary>
        /// <param name="requestUriString">文件路径</param>
        /// <param name="force">是否通过下载方式强制获取文件大小,默认为false</param>
        /// <returns>文件大小</returns>
        /// <remarks>FTP服务器可能不支持GetFileSize操作,但支持DownloadFile操作</remarks>
        public static long GetFileSize(string requestUriString, bool force = false)
        {
            try
            {
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
                request.Method = WebRequestMethods.Ftp.GetFileSize;
                using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
                {
                    return response.ContentLength;
                }
            }
            catch
            {   // FTP服务器不支持GetFileSize操作
                if (!force) throw;

                // 从文件的详细列表中获取文件大小
                string filename = Path.GetFileName(requestUriString);
                foreach (var item in ListDirectoryDetails(requestUriString.Substring(0, requestUriString.Length - filename.Length)))
                {
                    if (item.StartsWith("-") && item.EndsWith($" {filename}"))
                    {   // 倒数第5项为文件大小
                        return int.Parse(item.Split(' ', StringSplitOptions.RemoveEmptyEntries)[^5]);
                    }
                }
                return -1;
            }
        }

        /// <summary>
        /// 重命名文件
        /// </summary>
        /// <param name="requestUriString">要重命名的文件路径</param>
        /// <param name="newFileName">新文件名</param>
        /// <returns>true/false</returns>
        public static bool Rename(string requestUriString, string newFileName)
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
            request.Method = WebRequestMethods.Ftp.Rename;
            request.RenameTo = newFileName;
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                return response.StatusCode == FtpStatusCode.FileActionOK;
            }
        }

        /// <summary>
        /// 获取 FTP 服务器上的文件的简短列表
        /// </summary>
        /// <param name="requestUriString">要获取文件列表的目录路径</param>
        /// <returns>文件的简短列表</returns>
        /// <remarks>注意:列表里也包含子目录</remarks>
        public static IEnumerable<string> ListDirectory(string requestUriString)
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
            request.Method = WebRequestMethods.Ftp.ListDirectory;
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.Default))
                {
                    while (!reader.EndOfStream)
                    {
                        yield return reader.ReadLine();
                    }
                }
            }
        }

        /// <summary>
        /// 获取 FTP 服务器上的文件的详细列表
        /// </summary>
        /// <param name="requestUriString">要获取文件列表的目录路径</param>
        /// <returns>文件的详细列表</returns>
        /// <remarks>注意:列表里也包含子目录</remarks>
        public static IEnumerable<string> ListDirectoryDetails(string requestUriString)
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
            request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.Default))
                {
                    while (!reader.EndOfStream)
                    {
                        yield return reader.ReadLine();
                    }
                }
            }
        }

        /// <summary>
        /// 获取FTP服务器当前路径
        /// </summary>
        /// <param name="requestUriString">文件路径</param>
        /// <returns>当前路径</returns>
        public static string PrintWorkingDirectory(string requestUriString)
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
            request.Method = WebRequestMethods.Ftp.PrintWorkingDirectory;
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                return response.StatusDescription.Split('\"', StringSplitOptions.RemoveEmptyEntries)[1];
            }
        }

        /// <summary>
        /// 删除文件
        /// </summary>
        /// <param name="requestUriString">要删除的文件路径</param>
        /// <returns>true/false</returns>
        /// <remarks>如果文件不存在将引发异常</remarks>
        public static bool DeleteFile(string requestUriString)
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
            request.Method = WebRequestMethods.Ftp.DeleteFile;
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                return response.StatusCode == FtpStatusCode.FileActionOK;
            }
        }

        /// <summary>
        /// 追加数据
        /// </summary>
        /// <param name="requestUriString">文件路径</param>
        /// <param name="data">追加内容</param>
        /// <returns>true/false</returns>
        public static bool AppendFile(string requestUriString, byte[] data)
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
            request.Method = WebRequestMethods.Ftp.AppendFile;
            request.ContentLength = data.Length;
            using (Stream requestStream = request.GetRequestStream())
            {
                requestStream.Write(data, 0, data.Length);
            }
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                return response.StatusCode == FtpStatusCode.FileActionOK;
            }
        }

        /// <summary>
        /// 删除目录
        /// </summary>
        /// <param name="requestUriString">要删除的目录路径</param>
        /// <returns>true/false</returns>
        /// <remarks>如果目录不为空,则删除失败</remarks>
        public static bool RemoveDirectory(string requestUriString)
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
            request.Method = WebRequestMethods.Ftp.RemoveDirectory;
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                return response.StatusCode == FtpStatusCode.FileActionOK;
            }
        }

        /// <summary>
        /// 迭代删除目录
        /// </summary>
        /// <param name="requestUriString">要删除的目录路径</param>
        /// <returns>true/false</returns>
        public static bool RemoveDirectoryRecursive(string requestUriString)
        {
            if (!requestUriString.EndsWith("/")) requestUriString = $"{requestUriString}/";
            foreach (var item in ListDirectoryDetails(requestUriString))
            {
                if (item.StartsWith("d"))
                {   // 倒数第一项为目录名称
                    if (!RemoveDirectoryRecursive($"{requestUriString}{item.Split(' ', StringSplitOptions.RemoveEmptyEntries)[^1]}/"))
                    {
                        return false;
                    }
                }
                else if (item.StartsWith("-"))
                {   // 倒数第一项为文件名称
                    if (!DeleteFile($"{requestUriString}{item.Split(' ', StringSplitOptions.RemoveEmptyEntries)[^1]}"))
                    {
                        return false;
                    }
                }
            }
            return RemoveDirectory(requestUriString);
        }
    }
}

Comments are closed.