Demo_C#_Winform Ftp异步下载文件,更新ProgressBar

由于研究Winform项目的版本自动检测更新,需要用到Ftp下载更新包文件。
特写出这个小Demo.
我的Ftp是本地用IIS搭建的FTP服务器,下载过一个1.8G的视频文件,亲测有效。
注意点:

  1. Ftp类编写需要参考FTP的帮助文档
  2. 同步更新下载进度状态、下载完成状态的方法:将更新事件的方法绑定到Ftp封装类公布的事件中。
  3. 多线程控制窗体控件的时候不能直接对控件进行赋值等操作,需要将操控UI的代码写在一个委托当中,然后用This.Invoke(操控控件委托)。

运行效果如图:
Demo_C#_Winform Ftp异步下载文件,更新ProgressBar_第1张图片
内容比较多,我会上传Demo附件

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Framework;

namespace DemoFtp
{
     
    public partial class Form1 : Form
    {
     
        public Form1()
        {
     
            InitializeComponent();
            txtLocalPath.Text = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
        }
        /// 
        /// 主线程下载
        /// 
        /// 
        /// 
        private void btnDownLoad_Click(object sender, EventArgs e)
        {
     
            FtpClient ftpClient = new FtpClient("192.168.0.104","/","FtpUser","Chendong144216,");
            string remoteFilePath = txtRemotePath.Text;
            string fileName = Path.GetFileName(remoteFilePath);
            string localFilePath = txtLocalPath.Text + "\\" + fileName;
            ftpClient.DownLoadFile(remoteFilePath, localFilePath);
        }
        /// 
        /// 异步下载
        /// 
        /// 
        /// 
        private void btnDownloadAsync_Click(object sender, EventArgs e)
        {
     
            FtpClient ftpClient = new FtpClient("192.168.0.104", "/", "FtpUser", "Chendong144216,");
            string remoteFilePath = txtRemotePath.Text;
            string fileName = Path.GetFileName(remoteFilePath);
            string localFilePath = txtLocalPath.Text + "\\" + fileName;
            ftpClient.DownloadProgressChanged += DownloadProgressChanged;
            ftpClient.DownloadDataCompleted += DownLoadDataComplete;
            Task<long> task = ftpClient.DownLoadFileAsync(remoteFilePath, txtLocalPath.Text, fileName);
        }
        /// 
        /// 传入委托的方法,用于设置完成状态
        /// 
        /// 
        /// 
        private void DownLoadDataComplete(object sender, FtpClient.UpDownLoadCompletedArgs e)
        {
     
            Action action = ()=> 
            {
     
                pgb.Value = 100;
                lblProgressValue.Text = "下载完毕";
            };
            this.Invoke(action);
        }
        /// 
        /// 传入委托的方法,用于设置下载进度状态
        /// 
        /// 
        /// 
        private void DownloadProgressChanged(object sender, FtpClient.UpDownLoadProcessArgs e)
        {
     
            Action<int> action = v =>
            {
     
                pgb.Value = v;
                lblProgressValue.Text = $"下载进度 {v}%";
            };
            this.Invoke(action, e.ProgressPercentage);
        }
    }
}

Ftp封装类

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Framework
{
     
    /// 
    /// 文件结构信息
    /// 
    public struct FtpFileStruct
    {
     
        /// 
        /// 
        /// 
        public string Flags;
        /// 
        /// 拥有者
        /// 
        public string Owner;
        /// 
        /// 文件组
        /// 
        public string Group;
        /// 
        /// 是否目录
        /// 
        public bool IsDirectory;
        /// 
        /// 创建时间
        /// 
        public DateTime CreateTime;
        /// 
        /// 文件名
        /// 
        public string Name;
        /// 
        /// 文件长度
        /// 
        public double Length;
    }
    /// 
    /// 文件列表类型
    /// 
    public enum FtpFileListStyle
    {
     
        UnixStyle,      //UNIX类型
        WindowsStyle,   //WINDOWS
        Unknown         //未知
    }
    /// 
    /// 传输模式:二进制类型、ASCII类型
    /// 
    public enum FtpTransferType {
      Binary, ASCII };
    /// 
    /// Ftp上传下载
    /// 
    public class FtpClient : IDisposable         //Ftp上传下载
    {
     
        #region Private Fields
        /// 
        /// 用户名
        /// 
        private string _UserName;
        /// 
        /// 密码
        /// 
        private string _Password;
        /// 
        /// 初始创建Ftp类时候制定的根目录
        /// 
        private string _RootDirectory;
        /// 
        /// 工作目录
        /// 
        private string _WorkDirectory;
        /// 
        /// 服务器返回的应答信息(包含应答码)
        /// 
        private string ReplyMessage {
      get; set; }
        /// 
        /// 服务器返回的应答码
        /// 
        private int ReplyCode {
      get; set; }
        /// 
        /// 进行控制连接的socket
        /// 
        private Socket _SocketControl;
        /// 
        /// 传输模式
        /// 
        private FtpTransferType _TransferType = Framework.FtpTransferType.ASCII;
        /// 
        /// 接收和发送数据的缓冲区
        /// 
        private Byte[] _Buffer = new Byte[BUFFER_SIZE];
        /// 
        /// 当前尝试连接ID,用于报错
        /// 
        IPAddress _IPAddress;
        int tryConnectTimes = 0;
        #endregion
        #region Property
        /// 
        /// FTP服务器IP地址
        /// 
        public string Host {
      get; set; }
        /// 
        /// FTP服务器端口
        /// 
        public int Port {
      get; set; }
        /// 
        /// 目录
        /// 
        public string WorkDirectory
        {
     
            get
            {
     
                if (!string.IsNullOrEmpty(_WorkDirectory)) return _WorkDirectory;
                if (!IsConnected) Connect();
                SetCurrentWorkDirectory();
                return _WorkDirectory;
            }
            set
            {
     
                if (string.IsNullOrEmpty(_RootDirectory))//第一次设置Directory的时候,定RootDirectory
                    _RootDirectory = value;
                if (_WorkDirectory != value)
                {
     
                    _WorkDirectory = value;
                    if (IsConnected)//如果正在连接,则更换当前目录
                        ChangeWorkDirectory(_WorkDirectory);
                }

            }
        }
        /// 
        /// 在不知道当前路径的情况下提取并设置对应字段
        /// 
        private void SetCurrentWorkDirectory()
        {
     
            SendCommand("PWD ");
            if (!(ReplyCode == 257)) throw new IOException(ReplyMessage.Substring(4));//return strPath;
            string dir = ReplyMessage.Substring(5);
            _WorkDirectory = dir.Substring(0, dir.IndexOf("\""));
        }

        /// 
        /// 异步事件的间隔时间,毫秒
        /// 
        public int AsyncTriggerInterval {
      get; set; } = 200;
        /// 
        /// 缓冲大小
        /// 
        public static int BUFFER_SIZE {
      get; set; } = 1024 * 8;
        /// 
        /// 编码方式
        /// 
        public Encoding Encoder {
      get; set; } = Encoding.Default;
        /// 
        /// 代理
        /// 
        public WebProxy WebProxy {
      get; }
        /// 
        /// 是否登录
        /// 
        public bool IsConnected {
      get; private set; }
        /// 
        /// 获得传输模式
        /// 
        /// 传输模式
        public FtpTransferType TransferType
        {
     
            get {
      return _TransferType; }
            set 
            {
     
                if (value == _TransferType) return; //如果TransferType已经一样则不重复发送
                if (value == FtpTransferType.Binary)
                    SendCommand("TYPE I");//binary类型传输
                else
                    SendCommand("TYPE A");//ASCII类型传输
                if (ReplyCode != 200)
                    throw new IOException(ReplyMessage.Substring(4));
                else
                    _TransferType = value;
            }
        }
        #endregion
        #region EventArgs
        /// 
        /// 上传下载进度
        /// 
        public class UpDownLoadProcessArgs : EventArgs
        {
     
            private long _TotalBytes;
            private long _CurrentBytes;
            public UpDownLoadProcessArgs(long currentBytes, long totalBytes)
            {
     
                _CurrentBytes = currentBytes;
                _TotalBytes = totalBytes;
            }
            /// 
            /// 完成进度百分比
            /// 
            public int ProgressPercentage
            {
     
                get {
      return (int)(((double)_CurrentBytes / (double)_TotalBytes) * 100); }
            }
            /// 
            /// 总字节数
            /// 
            public long TotalBytes
            {
     
                get {
      return _TotalBytes; }
            }
            /// 
            /// 当前字节数
            /// 
            public long CurrentBytes
            {
     
                get {
      return _CurrentBytes; }
            }
        }
        /// 
        /// 上传下载完成事件
        /// 
        public class UpDownLoadCompletedArgs : EventArgs
        {
     
            private readonly long _TotalBytes;
            private readonly long _CurrentBytes;
            public UpDownLoadCompletedArgs(long curBytes, long totalBytes)
            {
     
                _CurrentBytes = curBytes;
                _TotalBytes = totalBytes;
            }
            /// 
            /// 进度百分比
            /// 
            public int ProgressPercentage
            {
     
                get {
      return (int)(((double)_CurrentBytes / (double)_TotalBytes) * 100); }
            }
            /// 
            /// 是否完成
            /// 
            public bool IsCompleted
            {
     
                get {
      return _CurrentBytes >= _TotalBytes; }
            }
            /// 
            /// 总字节数
            /// 
            public long TotalBytes
            {
     
                get {
      return _TotalBytes; }
            }
            /// 
            /// 当前字节数
            /// 
            public long CurrentBytes
            {
     
                get {
      return _CurrentBytes; }
            }
        }
        /// 
        /// 返回服务器特性
        /// 
        /// 
        public string Feature
        {
     
            get
            {
     
                SendCommand("FEAT");
                return ReplyMessage;
            }
        }
        #endregion
        #region Deletegate
        public delegate void FtpDownloadProgressChanged(object sender, UpDownLoadProcessArgs e);
        public delegate void FtpDownloadDataCompleted(object sender, UpDownLoadCompletedArgs e);
        public delegate void FtpUploadProgressChanged(object sender, UpDownLoadProcessArgs e);
        public delegate void FtpUploadFileCompleted(object sender, UpDownLoadCompletedArgs e);
        #endregion
        #region Event
        /// 
        /// 异步下载进度发生改变触发的事件
        /// 
        public event FtpDownloadProgressChanged DownloadProgressChanged;
        /// 
        /// 异步下载文件完成之后触发的事件
        /// 
        public event FtpDownloadDataCompleted DownloadDataCompleted;
        /// 
        /// 异步上传进度发生改变触发的事件
        /// 
        public event FtpUploadProgressChanged UploadProgressChanged;
        /// 
        /// 异步上传文件完成之后触发的事件
        /// 
        public event FtpUploadFileCompleted UploadFileCompleted;
        #endregion
        #region Constructor
        ~FtpClient()
        {
     
            Dispose(false);
        }
        /// 
        /// 缺省构造函数
        /// 
        public FtpClient()
        {
     
            Host = "";
            WorkDirectory = "/";
            _UserName = "";
            _Password = "";
            Port = 21;
            IsConnected = false;
        }
        /// 
        /// 构造函数
        /// 
        /// 服务器名称
        /// 服务器目录
        /// 用户名
        /// 密码
        /// 端口
        public FtpClient(string remoteHost, string remotePath, string remoteUser, string remotePass, int remotePort = 21, string code = "DEFAULT")
        {
     
            Host = remoteHost;
            _UserName = remoteUser;
            _Password = remotePass;
            Port = remotePort;
            this.Encoder = GetEncoder(code);
            Connect();
            WorkDirectory = remotePath;     //在设置初始化文件夹的时候会自动Connect()
        }
        public FtpClient(Uri FtpUri, string strUserName, string strPassword, int Port, string code = "UNICODE", WebProxy objProxy = null)
        {
     
            this.Host = FtpUri.Host;
            WorkDirectory = FtpUri.AbsolutePath;
            if (!WorkDirectory.EndsWith("/"))
                WorkDirectory += "/";
            _UserName = strUserName;
            _Password = strPassword;
            WebProxy = objProxy;
            this.Port = Port;
            Encoder = GetEncoder(code);
            Connect();
        }
        #endregion
        #region IDisposable Support
        private bool _DisposedValue = false; // 要检测冗余调用

        protected virtual void Dispose(bool disposing)
        {
     
            if (_DisposedValue) return;
            if (disposing)
            {
     
                // TODO: 释放托管状态(托管对象)。
            }
            //CloseConnect();
            // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
            // TODO: 将大型字段设置为 null。
            _DisposedValue = true;
        }


        // 添加此代码以正确实现可处置模式。
        public void Dispose()
        {
     
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。
            GC.SuppressFinalize(this);
        }

        #endregion
        #region Function
        /// 
        /// 获得一个FtpClient副本
        /// 
        /// 
        public FtpClient Clone()
        {
     
            return new FtpClient(Host, WorkDirectory, _UserName, _Password, Port, Encoder.ToString());
        }
        /// 
        /// 建立连接 
        /// 
        public void Connect()
        {
     
            //绑定主机
            IPHostEntry iPHostEntry = Dns.GetHostEntry(Host);
            _IPAddress = iPHostEntry.AddressList[iPHostEntry.AddressList.Length-1];
            IPEndPoint ep = new IPEndPoint(_IPAddress, Port);
            switch (ep.AddressFamily)
            {
     
                //同时支持IP6,IP4
                case AddressFamily.InterNetwork:
                    _SocketControl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    break;
                case AddressFamily.InterNetworkV6:
                    _SocketControl = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
                    break;
                default:
                    _SocketControl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    break;
            }
            // 链接
            try
            {
     
                _SocketControl.Connect(ep);
            }
            catch (Exception)
            {
     
                IsConnected = false;
                return;
            }
            FetchReply();        // 获取应答码
            if (ReplyCode != 220) //在连接到一台FTP服务器的端口21并接收到一个由代码220打头的行,表示服务器已准备好你向它发USER和PASS命令,以登录进此FTP服务器之后,紧跟着发送USER命令。
            {
     
                IsConnected = false;
                return;
            }
            SendCommand("USER " + _UserName);
            if (!(ReplyCode == 331 || ReplyCode == 230))
            {
     
                CloseSocketConnect();//关闭连接
                throw new IOException(ReplyMessage.Substring(4));
            }
            if (ReplyCode != 230)
            {
     
                SendCommand("PASS " + _Password);
                if (!(ReplyCode == 230 || ReplyCode == 202))
                {
     
                    CloseSocketConnect();//关闭连接
                    throw new IOException(ReplyMessage.Substring(4));
                }
            }
            IsConnected = true;   // 切换到目录
            ChangeWorkDirectory(WorkDirectory);
        }
        /// 
        /// 关闭连接
        /// 
        public void CloseConnect()
        {
     
            if (_SocketControl != null && IsConnected)
            {
     
                SendCommand("QUIT");
            }
            CloseSocketConnect();
        }
        /// 
        /// 得到文件信息
        /// 
        /// Ftp服务器接收到LIST命令后,返回的列表字符串。
        /// 
        private FtpFileStruct[] GetFileStructArray(string dataString)
        {
     
            List<FtpFileStruct> FileList = new List<FtpFileStruct>();
            string[] dataRecords = dataString.Split('\n');
            FtpFileListStyle fileStyle = GuessFileListStyle(dataRecords);
            foreach (string dataRecord in dataRecords)
            {
     
                if (fileStyle != FtpFileListStyle.Unknown && dataRecord != "")
                {
     
                    FtpFileStruct newFileStruct = new FtpFileStruct
                    {
     
                        Name = ".."
                    };
                    switch (fileStyle)
                    {
     
                        case FtpFileListStyle.UnixStyle:
                            newFileStruct = ParseFileStructFromUnixStyle(dataRecord);
                            break;
                        case FtpFileListStyle.WindowsStyle:
                            newFileStruct = ParseFileStructFromWindowsStyle(dataRecord);
                            break;
                    }
                    if (!(newFileStruct.Name == "." || newFileStruct.Name == ".."))
                        FileList.Add(newFileStruct);
                }
            }
            return FileList.ToArray();
        }
        /// 
        /// 从Unix格式中返回文件信息
        /// 
        /// 文件信息
        private FtpFileStruct ParseFileStructFromUnixStyle(string dataRecord)
        {
     
            FtpFileStruct newFileStruct = new FtpFileStruct();
            string processstr = dataRecord.Trim();
            newFileStruct.Flags = processstr.Substring(0, 10);
            newFileStruct.IsDirectory = (newFileStruct.Flags[0] == 'd');
            processstr = (processstr.Substring(11)).Trim();
            _cutSubstringFromStringWithTrim(ref processstr, ' ', 0);   //跳过一部分
            newFileStruct.Owner = _cutSubstringFromStringWithTrim(ref processstr, ' ', 0);
            newFileStruct.Group = _cutSubstringFromStringWithTrim(ref processstr, ' ', 0);
            _cutSubstringFromStringWithTrim(ref processstr, ' ', 0);   //跳过一部分
            string yearOrTime = processstr.Split(new char[] {
      ' ' }, StringSplitOptions.RemoveEmptyEntries)[2];
            if (yearOrTime.IndexOf(":") >= 0)  //time
            {
     
                processstr = processstr.Replace(yearOrTime, DateTime.Now.Year.ToString());
            }
            newFileStruct.CreateTime = DateTime.Parse(_cutSubstringFromStringWithTrim(ref processstr, ' ', 8));
            newFileStruct.Name = processstr;   //最后就是名称
            return newFileStruct;
        }
        /// 
        /// 从Windows格式中返回文件信息
        /// 
        /// 文件信息
        private FtpFileStruct ParseFileStructFromWindowsStyle(string dataRecord)
        {
     
            FtpFileStruct newFileStruct = new FtpFileStruct();
            string str = dataRecord.Trim();
            string dateString = str.Substring(0, 8);
            str = (str.Substring(8, str.Length - 8)).Trim();
            string timeString = str.Substring(0, 7);
            str = (str.Substring(7, str.Length - 7)).Trim();
            DateTimeFormatInfo myDTFI = new CultureInfo("en-US", false).DateTimeFormat;
            myDTFI.ShortTimePattern = "t";
            newFileStruct.CreateTime = DateTime.Parse(dateString + " " + timeString, myDTFI);
            if (str.Substring(0, 5) == "")
            {
     
                newFileStruct.IsDirectory = true;
                str = (str.Substring(5, str.Length - 5)).Trim();
            }
            else
            {
     
                newFileStruct.Length = Convert.ToDouble(str.Substring(0, str.IndexOf(" ")));
                str = str.Substring(str.IndexOf(" ") + 1);
                newFileStruct.IsDirectory = false;
            }
            newFileStruct.Name = str;
            return newFileStruct;
        }
        /// 
        /// 获得文件列表
        /// 
        /// 文件名的匹配字符串
        /// 
        public FtpFileStruct[] Dir(string FileFilterString)
        {
     
            if (!IsConnected) Connect();// 建立链接
            Socket dataSocket = CreateDataSocket();
            SendCommand(WebRequestMethods.Ftp.ListDirectoryDetails + " " + FileFilterString);   //传送命令
            if (!(ReplyCode == 150 || ReplyCode == 125 || ReplyCode == 226 )) //分析应答代码NLST
            {
     
                return GetFileStructArray("");
            }   //获得结果
            string strData = string.Empty;
            while (true)
            {
     
                int iBytes = dataSocket.Receive(_Buffer, _Buffer.Length, 0);
                strData += Encoder.GetString(_Buffer, 0, iBytes);
                if (iBytes == 0) break;
            }
            dataSocket.Close();//数据socket关闭时也会有返回码
            if (ReplyCode != 226)
            {
     
                FetchReply();
                if (ReplyCode != 226)
                    return GetFileStructArray("");
            }
            return GetFileStructArray(strData);
        }
        /// 
        /// 判断文件是否存在
        /// 
        /// 文件全路径或文件名
        /// 
        public bool IsFileExists(string filePath)
        {
     
            if (string.IsNullOrEmpty(filePath)) return false;
            string fileName = Path.GetFileName(filePath);
            FtpFileStruct[] fileStructArray = Dir(filePath);
            foreach (FtpFileStruct fileStruct in fileStructArray)
            {
     
                if (fileStruct.Name == fileName && !fileStruct.IsDirectory)
                        return true;
            }
            return false;
        }
        /// 
        /// 判断目录是否存在
        /// 
        /// 文件路径名
        /// 
        public bool IsDirectoryExists(string directoryName)
        {
     
            FtpFileStruct[]  ftpFileStructs= GetDirectorys();//获取子目录
            foreach (var directoryStruct in ftpFileStructs)
            {
     
                if (directoryStruct.Name == directoryName) return true;
            }
            return false;
        }



        /// 
        /// 获得文件大小
        /// 
        /// 文件名称
        /// 
        public long GetFileSize(string strFileName)
        {
     
            FtpFileStruct[] file = Dir(strFileName);
            if (file.Length > 0)
                foreach (FtpFileStruct a in file)
                {
     
                    if (a.Length > 0) return (int)a.Length;
                }
            return 0;
        }
        /// 
        /// 获得文件时间字符串
        /// 
        /// 
        /// 
        public string GetFileCreateTime(string strFileName)
        {
     
            FtpFileStruct[] file = Dir(strFileName);
            if (file.Length > 0)
                foreach (FtpFileStruct a in file)
                {
     
                    return a.CreateTime.ToString("yyyy-MM-dd HH:mm:ss");
                }
            return null;
        }
        /// 
        /// 删除
        /// 
        /// 待删除文件名
        public string DeleteFile(string strFileName)
        {
     
            if (!IsConnected) Connect();
            SendCommand("DELE " + strFileName);
            if (ReplyCode != 250)
                return ReplyMessage;
            return string.Empty;
        }
        /// 
        /// 重命名(如果新文件名与已有文件重名,将覆盖已有文件,需服务器支持)
        /// 
        /// 旧文件名
        /// 新文件名
        /// 是否覆盖己有文件
        public string Rename(string strOldFileName, string strNewFileName, bool overrideFile = true)
        {
     
            if (!IsConnected) Connect();
            if (overrideFile == true)
                if (IsFileExists(strNewFileName)) DeleteFile(strNewFileName);
            SendCommand("RNFR " + strOldFileName);
            if (ReplyCode != 350) return ReplyMessage;
            SendCommand("RNTO " + strNewFileName);// 如果新文件名与原有文件重名,将覆盖原有文件
            if (ReplyCode != 250) return ReplyMessage;
            return string.Empty;
        }
        #endregion
        #region Function_上传和下载
        /// 
        /// 下载文件
        /// 
        /// 远程文件名
        /// 本地文件完整路径
        /// 如果加载字节不等于总字节长度,则返回0表示失败;成功则返回总字节长度,表示成功。
        public long DownLoadFile(string remoteFileName, string LocalFilePath)
        {
     
            string strFolder = new FileInfo(LocalFilePath).DirectoryName;
            return DownLoadFile(remoteFileName, strFolder, Path.GetFileName(LocalFilePath));
        }
        /// 
        /// 异步下载
        /// 
        /// 远程文件名
        /// 本地文件完整路径
        /// CancellationToken
        /// 
        public long DownLoadFile(string remoteFileName, string localFilePath, System.Threading.CancellationToken cancel)
        {
     
            string strFolder = new FileInfo(localFilePath).DirectoryName;
            return DownLoadFile(remoteFileName, strFolder, Path.GetFileName(localFilePath), cancel);
        }
        /// 
        /// 下载文件
        /// 
        /// 远程文件名
        /// 文件夹完整路径
        /// 本地文件名
        /// 如果加载字节不等于总字节长度,则返回0表示失败;成功则返回总字节长度,表示成功。
        public long DownLoadFile(string remoteFileName, string folderPath, string localFileName)
        {
     
            if (!IsConnected) Connect();
            TransferType =FtpTransferType.Binary;
            if (localFileName.Equals("")) localFileName = remoteFileName;
            long fileLength = GetFileSize(remoteFileName);
            Socket dataSocket = CreateDataSocket();
            long currentPrcess = 0;
            SendCommand("RETR " + remoteFileName);
            if (!(ReplyCode == 150 || ReplyCode == 125 || ReplyCode == 226 || ReplyCode == 250))
            {
     
                throw new IOException(ReplyMessage.Substring(4));
            }
            FileStream outputFileStream = new FileStream(folderPath + "\\" + localFileName, FileMode.Create);
            System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
            long elapsedMillisecondsRemark = 0;
            stopWatch.Start();
            while (true)
            {
     
                int intBytes = dataSocket.Receive(_Buffer, _Buffer.Length, 0);
                currentPrcess += intBytes;
                outputFileStream.Write(_Buffer, 0, intBytes);
                if ((stopWatch.ElapsedMilliseconds - elapsedMillisecondsRemark) >= AsyncTriggerInterval)
                {
     
                    DownloadProgressChanged?.Invoke(this, new UpDownLoadProcessArgs(currentPrcess, fileLength));
                    elapsedMillisecondsRemark = stopWatch.ElapsedMilliseconds;
                }
                if (intBytes <= 0)
                {
     
                    DownloadProgressChanged?.Invoke(this, new UpDownLoadProcessArgs(currentPrcess, fileLength));
                    break;
                }
            }
            stopWatch.Stop();
            outputFileStream.Close();
            dataSocket.Close();
            DownloadDataCompleted?.Invoke(this, new UpDownLoadCompletedArgs(currentPrcess, fileLength));
            if (!(ReplyCode == 226 || ReplyCode == 250))
            {
     
                FetchReply();
                if (!(ReplyCode == 226 || ReplyCode == 250))
                    throw new IOException(ReplyMessage.Substring(4));
            }
            return currentPrcess != fileLength ? 0 : currentPrcess;     
        }
        /// 
        /// 异步下载
        /// 
        /// 远程文件名
        /// 文件夹完整路径
        /// 本地文件完整路径
        /// CancellationToken
        /// 如果加载字节不等于总字节长度,则返回0表示失败;成功则返回总字节长度,表示成功。
        public long DownLoadFile(string remoteFileName, string folderPath, string localFileName, System.Threading.CancellationToken cancel)
        {
     
            if (!IsConnected) Connect();
            TransferType = FtpTransferType.Binary;
            if (localFileName.Equals("")) localFileName = remoteFileName;
            long fileLength = GetFileSize(remoteFileName);
            Socket dataSocket = CreateDataSocket();
            long currentPrcess = 0;
            SendCommand("RETR " + remoteFileName);
            if (!(ReplyCode == 150 || ReplyCode == 125 || ReplyCode == 226 || ReplyCode == 250))
                throw new IOException(ReplyMessage.Substring(4));
            FileStream outputFileStream = new FileStream(folderPath + "\\" + localFileName, FileMode.Create);
            System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
            long elapsedMillisecondsRemark = 0;//定义刷新间隔
            stopWatch.Start();
            int intBytes = 0;
            while (!cancel.IsCancellationRequested)
            {
     
                intBytes = dataSocket.Receive(_Buffer, _Buffer.Length, 0);
                currentPrcess += intBytes;
                outputFileStream.Write(_Buffer, 0, intBytes);
                if ((stopWatch.ElapsedMilliseconds - elapsedMillisecondsRemark) >= AsyncTriggerInterval)
                {
     
                    DownloadProgressChanged?.Invoke(this, new UpDownLoadProcessArgs(currentPrcess, fileLength));
                    elapsedMillisecondsRemark = stopWatch.ElapsedMilliseconds;
                }
                if (intBytes <= 0)
                {
     
                    DownloadProgressChanged?.Invoke(this, new UpDownLoadProcessArgs(currentPrcess, fileLength));
                    break;
                }
            }
            stopWatch.Stop();
            outputFileStream.Close();
            dataSocket.Close() ;
            DownloadDataCompleted?.Invoke(this, new UpDownLoadCompletedArgs(currentPrcess, fileLength));
            if (!cancel.IsCancellationRequested)
            {
     //如果没有取消
                if (!(ReplyCode == 226 || ReplyCode == 250))
                {
     
                    FetchReply();
                    if (!(ReplyCode == 226 || ReplyCode == 250))
                        throw new IOException(ReplyMessage.Substring(4));
                }
            }
            return currentPrcess != fileLength ? 0 : currentPrcess;  
        }
        /// 
        /// 下载到文件到字符数组
        /// 
        /// 
        public byte[] DownLoadBytes(string remoteFileName)
        {
     
            if (!IsConnected) Connect();
            TransferType = Framework.FtpTransferType.Binary;
            long fileLength = GetFileSize(remoteFileName);
            Socket dataSocket = CreateDataSocket();
            long currentPrcess = 0;
            SendCommand("RETR " + remoteFileName);
            if (!(ReplyCode == 150 || ReplyCode == 125 || ReplyCode == 226 || ReplyCode == 250))
                throw new IOException(ReplyMessage.Substring(4));
            byte[] fileBytesArray = new byte[fileLength];
            MemoryStream outputMemoryStream = new MemoryStream(fileBytesArray, true);
            System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
            long elapsedMillisecondsRemark = 0;
            stopWatch.Start();
            int intBytes;
            while (true)
            {
     
                intBytes = dataSocket.Receive(_Buffer, _Buffer.Length, 0);
                currentPrcess += intBytes;
                outputMemoryStream.Write(_Buffer, 0, intBytes);
                if ((stopWatch.ElapsedMilliseconds - elapsedMillisecondsRemark) >= AsyncTriggerInterval)
                {
     
                    DownloadProgressChanged?.Invoke(this, new UpDownLoadProcessArgs(currentPrcess, fileLength));
                    elapsedMillisecondsRemark = stopWatch.ElapsedMilliseconds;
                }
                if (intBytes <= 0)
                {
     
                    DownloadProgressChanged?.Invoke(this, new UpDownLoadProcessArgs(currentPrcess, fileLength));
                    break;
                }
            }
            stopWatch.Stop();
            outputMemoryStream.Close();
            DownloadDataCompleted?.Invoke(this, new UpDownLoadCompletedArgs(currentPrcess, fileLength));
            dataSocket.Close();
            if (!(ReplyCode == 226 || ReplyCode == 250))
            {
     
                FetchReply();
                if (!(ReplyCode == 226 || ReplyCode == 250))
                {
     
                    throw new IOException(ReplyMessage.Substring(4));
                }
            }
            return outputMemoryStream.ToArray();
        }

        /// 
        /// 异步下载文件
        /// 
        /// 远程文件名
        /// 本地目录
        /// 本地文件名
        /// 
        public Task<long> DownLoadFileAsync(string remoteFilePath, string localFolder, string localFilePath)
        {
     
            Task<long> DownloadTask = new Task<long>(() =>
            {
     
                FtpClient tempftp = new FtpClient(Host, WorkDirectory, _UserName, _Password, Port, Encoder.ToString());
                tempftp.DownloadDataCompleted += DownloadDataCompleted; 
                tempftp.DownloadProgressChanged += DownloadProgressChanged;
                long size = tempftp.DownLoadFile(remoteFilePath, localFolder, localFilePath);
                tempftp.Dispose();
                return size;
            });
            DownloadTask.Start();
            return DownloadTask;
        }
        public Task<long> DownLoadFileAsync(string strRemoteFileName, string strFolder, string strLocalFileName, System.Threading.CancellationToken cancel)
        {
     
            Task<long> DownloadTask = new Task<long>(() =>
            {
     
                FtpClient tempftp = new FtpClient(Host, WorkDirectory, _UserName, _Password, Port, Encoder.ToString());
                tempftp.DownloadDataCompleted += DownloadDataCompleted;
                tempftp.DownloadProgressChanged += DownloadProgressChanged;
                long size = tempftp.DownLoadFile(strRemoteFileName, strFolder, strLocalFileName, cancel);
                tempftp.Dispose();
                return size;
            }, cancel);
            DownloadTask.Start();
            return DownloadTask;
        }

        /// 
        /// 上传一个文件在当前工作目录下
        /// 
        /// 文件名
        /// 本地文件完整路径
        /// 如果上传字节不等于总字节长度,则返回0表示失败;成功则返回总字节长度,表示成功。
        public long UploadFile(string uploadFileName, string locaFilePath)
        {
     
            if (!IsConnected) Connect();
            Socket dataSocket = CreateDataSocket();
            SendCommand("STOR " + Path.GetFileName(uploadFileName));
            if (!(ReplyCode == 125 || ReplyCode == 150))
                throw new IOException(ReplyMessage.Substring(4));
            FileStream inputFileStream = new FileStream(locaFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
            long fileLength = inputFileStream.Length;
            long currentPrcess = 0;
            System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
            long elapsedMillisecondsRemark = 0;
            stopWatch.Start();
            int intBytes;
            while ((intBytes = inputFileStream.Read(_Buffer, 0, _Buffer.Length)) > 0)
            {
     
                currentPrcess += intBytes;
                if ((stopWatch.ElapsedMilliseconds - elapsedMillisecondsRemark) >= AsyncTriggerInterval)
                {
     
                    UploadProgressChanged?.Invoke(dataSocket, new UpDownLoadProcessArgs(currentPrcess, fileLength));
                    elapsedMillisecondsRemark = stopWatch.ElapsedMilliseconds;
                }
                dataSocket.Send(_Buffer, intBytes, 0);
            }
            inputFileStream.Close();
            dataSocket.Close();
            stopWatch.Stop();
            UploadFileCompleted?.Invoke(this, new UpDownLoadCompletedArgs(currentPrcess, fileLength));
            if (!(ReplyCode == 226 || ReplyCode == 250))
            {
     
                FetchReply();
                if (!(ReplyCode == 226 || ReplyCode == 250))
                    throw new IOException(ReplyMessage.Substring(4));
            }
            return currentPrcess == fileLength ? currentPrcess : 0;
        }
        /// 
        /// 异步上传
        /// 
        /// 本地文件完整路径
        /// 上传为文件完整路径
        /// CancellationToken
        /// 返回当前字节长度
        public long UploadFile(string uploadFileName, string localFilePath, System.Threading.CancellationToken cancel)
        {
     
            if (!IsConnected) Connect();
            Socket dataSocket = CreateDataSocket();
            SendCommand("STOR " + Path.GetFileName(uploadFileName));
            if (!(ReplyCode == 125 || ReplyCode == 150))
                throw new IOException(ReplyMessage.Substring(4));
            FileStream inputFileStream = new FileStream(localFilePath, FileMode.Open, FileAccess.Read);
            long fileLength = inputFileStream.Length;
            long currentPrcess = 0;
            System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
            long elapsedMillisecondsRemark = 0;
            stopWatch.Start();
            int intBytes;
            while ((intBytes = inputFileStream.Read(_Buffer, 0, _Buffer.Length)) > 0)
            {
     
                currentPrcess += intBytes;
                if ((stopWatch.ElapsedMilliseconds - elapsedMillisecondsRemark) >= AsyncTriggerInterval)
                {
     
                    UploadProgressChanged?.Invoke(dataSocket, new UpDownLoadProcessArgs(currentPrcess, fileLength));
                    elapsedMillisecondsRemark = stopWatch.ElapsedMilliseconds;
                }
                dataSocket.Send(_Buffer, intBytes, 0);
                if (cancel.IsCancellationRequested) {
      break; }//取消了进度,不支持继传
            }
            inputFileStream.Close();
            stopWatch.Stop();
            dataSocket.Close();
            UploadFileCompleted?.Invoke(this, new UpDownLoadCompletedArgs(currentPrcess, fileLength));
            if (!cancel.IsCancellationRequested)
            {
     //如果没有取消
                if (!(ReplyCode == 226 || ReplyCode == 250))
                {
     
                    FetchReply();
                    if (!(ReplyCode == 226 || ReplyCode == 250))
                        throw new IOException(ReplyMessage.Substring(4));
                }
            }
            return currentPrcess;
        }
        /// 
        /// 上传文件扩展,strFileName可以为文件名称或文件全路径名称
        /// 该函数是使用安全的,上传失败不会覆盖原来的文件
        /// 
        /// 文件名称或文件全路径名称
        /// 本地文件全路径名称
        /// 取消标记
        /// 返回当前字节长度
        public long UploadFileExt(string uploadFilePath, string locaFileName, System.Threading.CancellationToken cancel)
        {
     
            string folderPath = uploadFilePath.Replace(Path.GetFileName(uploadFilePath), "");
            if (!string.IsNullOrEmpty(folderPath))
                ChangeWorkDirectory(folderPath);
            string tempFileName = SuperCode.GetMD5(Path.GetFileName(uploadFilePath));
            var uploadedFileLength = UploadFile(tempFileName, locaFileName, cancel);
            if (!cancel.IsCancellationRequested)
            {
     
                var error = Rename(tempFileName, uploadFilePath);
                if (!string.IsNullOrEmpty(error))
                    throw new Exception(error);
            }
            DeleteFile(tempFileName);
            ChangeWorkDirectory(WorkDirectory);
            return uploadedFileLength;
        }
        /// 
        /// 上传内存文件到服务器
        /// 
        /// 
        /// 
        /// 
        public long UploadBytes(string fileName, byte[] Bytes)
        {
     
            if (!IsConnected) Connect();
            Socket dataSocket = CreateDataSocket();
            SendCommand("STOR " + Path.GetFileName(fileName));
            if (!(ReplyCode == 125 || ReplyCode == 150))
                throw new IOException(ReplyMessage.Substring(4));
            MemoryStream inputMemoryStream = new MemoryStream(Bytes);
            long fileLength = Bytes.Length;
            long currentProcess = 0;
            System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
            long elapsedMillisecondsRemark = 0;
            stopWatch.Start();
            int intBytes;
            while ((intBytes = inputMemoryStream.Read(_Buffer, 0, _Buffer.Length)) > 0)
            {
     
                currentProcess += intBytes;
                if ((stopWatch.ElapsedMilliseconds - elapsedMillisecondsRemark) >= AsyncTriggerInterval)
                {
     
                    UploadProgressChanged?.Invoke(dataSocket, new UpDownLoadProcessArgs(currentProcess, fileLength));
                    elapsedMillisecondsRemark = stopWatch.ElapsedMilliseconds;
                }
                dataSocket.Send(_Buffer, intBytes, 0);
            }
            inputMemoryStream.Close();
            dataSocket.Close();
            UploadFileCompleted?.Invoke(dataSocket, new UpDownLoadCompletedArgs(currentProcess, fileLength));
            stopWatch.Stop();
            if (!(ReplyCode == 226 || ReplyCode == 250))
            {
     
                FetchReply();
                if (!(ReplyCode == 226 || ReplyCode == 250))
                    throw new IOException(ReplyMessage.Substring(4));
            }
            return currentProcess;
        }
        #endregion
        #region Function_目录操作
        /// 
        /// 得到目录列表
        /// 
        /// 
        public FtpFileStruct[] GetDirectorys()
        {
     
            FtpFileStruct[] fileArray = Dir("");
            List<FtpFileStruct> fileStructList = new List<FtpFileStruct>();
            foreach (FtpFileStruct file in fileArray)
            {
     
                if (file.IsDirectory == true)
                    fileStructList.Add(file);
            }
            return fileStructList.ToArray();
        }
        /// 
        /// 创建目录
        /// 
        /// 目录名
        /// 如果有错误返回错误文本,反之返回空字符串
        public string MakeDirectory(string directoryPath)
        {
     
            if (!IsConnected) Connect();
            SendCommand("MKD " + directoryPath);
            if (!(ReplyCode == 257 || ReplyCode == 250))
                return ReplyMessage;
            return string.Empty;
        }
        /// 
        /// 删除目录
        /// 
        /// 目录名
        /// 如果有错误返回错误文本,反之返回空字符串
        public string RemoveDirectory(string strDirName)
        {
     
            if (!IsConnected) Connect();
            SendCommand("RMD " + strDirName);
            if (ReplyCode != 250)
                return ReplyMessage;
            return string.Empty;
        }
        /// 
        /// 改变目录
        /// 
        /// 新的工作目录名
        private void ChangeWorkDirectory(string newDirectory)
        {
     
            if ( String.IsNullOrEmpty(newDirectory)|| newDirectory.Equals(".") )
                return;
            if (!IsConnected) Connect();
            SendCommand("CWD " + newDirectory);
            if (ReplyCode != 250)
                throw new IOException(ReplyMessage.Substring(4));
        }
        /// 
        /// 改变工作路径为父文件夹
        /// 
        public void ChangeWorkDirectoryToParent()
        {
     
            if (!IsConnected) Connect();
            SendCommand("CDUP");
            if (ReplyCode != 250)
                throw new IOException(ReplyMessage.Substring(4));
            SetCurrentWorkDirectory();       //设置比骄傲村当前工作路径的对应字段
        }
        /// 
        /// 建立进行数据连接的socket
        /// 
        /// 数据连接socket
        private Socket CreateDataSocket()
        {
     
            SendCommand("PASV");
            if (ReplyCode != 227)
                throw new IOException(ReplyMessage.Substring(4));
            int index1 = ReplyMessage.IndexOf('(');
            int index2 = ReplyMessage.IndexOf(')');
            var ipData = ReplyMessage.Substring(index1 + 1, index2 - index1 - 1).Split(',');
            int[] parts = new int[6];
            for (int i = 0; i < ipData.Length; i++)
            {
     
                try
                {
     
                    parts[i] = Int32.Parse(ipData[i]);
                }
                catch (Exception)
                {
     
                    throw new IOException("不能识别的 PASV 应答码: " + ReplyMessage);
                }
            }
            string ipAddress = parts[0] + "." + parts[1] + "." + parts[2] + "." + parts[3];
            int port = (parts[4] << 8) + parts[5];
            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint ep = new IPEndPoint(IPAddress.Parse(ipAddress), port);
            try
            {
     
                s.Connect(ep);
            }
            catch (Exception)
            {
     
                throw new IOException("不能建立数据连接");
            }
            return s;
        }
        /// 
        /// 关闭socket连接(用于登录以前)
        /// 
        private void CloseSocketConnect()
        {
     
            if (_SocketControl != null)
            {
     
                _SocketControl.Close();
                _SocketControl = null;
            }
            IsConnected = false;
        }
        /// 
        /// 读取Socket返回的所有字符串,将一行应答字符串记录在strReply和strMsg,应答码记录在iReplyCode
        /// 
        /// 包含应答码的字符串行
        private void FetchReply()
        {
     
            ReplyMessage = "";
            while (true)
            {
     
                int bytesLength = _SocketControl.Receive(_Buffer, _Buffer.Length, 0);
                ReplyMessage += Encoder.GetString(_Buffer, 0, bytesLength);
                if (bytesLength < _Buffer.Length) break;
            }
            char[] seperator = {
      '\n' };
            string[] messageArray = ReplyMessage.Split(seperator);
            if (messageArray.Length > 2)
                ReplyMessage = messageArray[messageArray.Length - 2];
                //seperator[0]是10,换行符是由13和10组成的,分隔后10后面虽没有字符串,
                //但也会分配为空字符串给后面(也是最后一个)字符串数组,
                //所以最后一个messageArray是没用的空字符串
                //但为什么不直接取mess[0],因为只有最后一行字符串应答码与信息之间有空格
            else
                ReplyMessage = messageArray[0];
            if (string.IsNullOrEmpty(ReplyMessage)||!ReplyMessage.Substring(3, 1).Equals(" "))//返回字符串正确的是以应答码(如220开头, 后面接一空格, 再接问候字符串)
            {
     
                tryConnectTimes++;
                if (tryConnectTimes >= 100)
                {
     
                    IsConnected = false;
                    throw new Exception($"Ftp多次尝试获取返回数据失败!\r\n请见检查DNS解析是否失败!当前尝试连接IP地址为{_IPAddress.ToString()}");
                }
                FetchReply();

            }
            ReplyCode = Int32.Parse(ReplyMessage.Substring(0, 3));
        }
        /// 
        /// 发送命令并获取应答码和最后一行应答字符串
        /// 
        /// 命令
        private void SendCommand(String strCommand)
        {
     
            try
            {
     
                Byte[] cmdBytes = Encoder.GetBytes((strCommand + "\r\n").ToCharArray());
                if (_SocketControl.Connected == true)
                {
     
                    _SocketControl.Send(cmdBytes, cmdBytes.Length, 0);
                    FetchReply();
                }
                else
                    throw new IOException("控制连接己关闭");
            }
            catch (Exception e)
            {
     
                throw e;
            }
        }
        /// 
        /// 判断文件列表的方式Window方式还是Unix方式
        /// 
        /// 文件信息列表
        private FtpFileListStyle GuessFileListStyle(string[] recordArray)
        {
     
            foreach (string s in recordArray)
            {
     
                if (s.Length > 10
                 && Regex.IsMatch(s.Substring(0, 10), "(-|d)(-|r)(-|w)(-|x)(-|r)(-|w)(-|x)(-|r)(-|w)(-|x)"))
                {
     
                    return FtpFileListStyle.UnixStyle;
                }
                else if (s.Length > 8
                 && Regex.IsMatch(s.Substring(0, 8), "[0-9][0-9]-[0-9][0-9]-[0-9][0-9]"))
                {
     
                    return FtpFileListStyle.WindowsStyle;
                }
            }
            return FtpFileListStyle.Unknown;
        }
        /// 
        /// 按照一定的规则进行字符串截取
        /// 
        /// 截取的字符串
        /// 查找的字符
        /// 查找的位置
        private string _cutSubstringFromStringWithTrim(ref string s, char c, int startIndex)
        {
     
            int pos1 = s.IndexOf(c, startIndex);
            string retString = s.Substring(0, pos1);
            s = (s.Substring(pos1)).Trim();
            return retString;
        }
        /// 
        /// 跟据名称得到代码
        /// 
        /// 
        /// 
        private Encoding GetEncoder(string code)
        {
     
            switch (code)
            {
     

                case "UTF7":
                    return Encoding.UTF7;
                case "UTF8":
                    return Encoding.UTF8;
                case "UTF32":
                    return Encoding.UTF32;
                case "ASCII":
                    return Encoding.ASCII;
                case "UNICODE":
                    return Encoding.Unicode;
                case "BIGENDIANUNICODE":
                    return Encoding.BigEndianUnicode;
                case "DEFAULT":
                    return Encoding.Default;
                default:
                    return Encoding.Default;
            }
        }
        #endregion

        #region GetMD5                          获得MD5码
        /// 
        /// 获得MD5码
        /// 
        /// 源字符串
        /// MD5码
        public static string GetMD5(string source, int code = 2)
        {
     
            byte[] sor = Encoding.UTF8.GetBytes(source);
            string mode = "x2";
            MD5 md5 = MD5.Create();
            byte[] result = md5.ComputeHash(sor);
            StringBuilder strbul = new StringBuilder();
            if (code > 2) mode = "x" + code.ToString();
            for (int i = 0; i < result.Length; i++)
            {
     
                strbul.Append(result[i].ToString(mode));//加密结果"x2"结果为32位,"x3"结果为48位,"x4"结果为64位
            }
            return strbul.ToString();
        }
        #endregion
    }
}

你可能感兴趣的:(自学_C#,Winform,c#,winform,ftp,多线程)