本文整理自网络,由于太多文章类似,此处标识其中一处:点击打开链接
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Net.Sockets;
- using System.IO;
- using System.Net;
-
- namespace JianKunKing.Common.Ftp
- {
- /// <summary>
- /// FTP Client(不允许匿名登录)
- /// 上传、下载已测试,其余未测试
- /// </summary>
- public class FTPClient
- {
- #region 构造函数
- /// <summary>
- /// 缺省构造函数
- /// </summary>
- public FTPClient()
- {
- this.ftpServerIP = "";
- this.remoteFilePath = "";
- this.ftpUserID = "";
- this.ftpPassword = "";
- this.ftpServerPort = 21;
- this.bConnected = false;
- }
-
- /// <summary>
- /// 构造函数
- /// </summary>
- /// <param name="remoteHost">FTP服务器IP地址</param>
- /// <param name="remotePath">当前服务器目录</param>
- /// <param name="remoteUser">Ftp 服务器登录用户账号</param>
- /// <param name="remotePass">Ftp 服务器登录用户密码</param>
- /// <param name="remotePort">FTP服务器端口</param>
- public FTPClient(string ftpServerIP, string remoteFilePath, string ftpUserID, string ftpPassword, int ftpServerPort, bool anonymousAccess = false)
- {
- this.ftpServerIP = ftpServerIP;
- this.remoteFilePath = remoteFilePath;
- this.ftpUserID = ftpUserID;
- this.ftpPassword = ftpPassword;
- this.ftpServerPort = ftpServerPort;
- this.Connect();
- }
- #endregion
-
- #region 登陆字段、属性
- /// <summary>
- /// FTP服务器IP地址
- /// </summary>
- private string ftpServerIP;
- public string FtpServerIP
- {
- get
- {
- return ftpServerIP;
- }
- set
- {
- this.ftpServerIP = value;
- }
- }
- /// <summary>
- /// FTP服务器端口
- /// </summary>
- private int ftpServerPort;
- public int FtpServerPort
- {
- get
- {
- return ftpServerPort;
- }
- set
- {
- this.ftpServerPort = value;
- }
- }
- /// <summary>
- /// 当前服务器目录
- /// </summary>
- private string remoteFilePath;
- public string RemoteFilePath
- {
- get
- {
- return remoteFilePath;
- }
- set
- {
- this.remoteFilePath = value;
- }
- }
- /// <summary>
- /// Ftp 服务器登录用户账号
- /// </summary>
- private string ftpUserID;
- public string FtpUserID
- {
- set
- {
- this.ftpUserID = value;
- }
- }
- /// <summary>
- /// Ftp 服务器用户登录密码
- /// </summary>
- private string ftpPassword;
- public string FtpPassword
- {
- set
- {
- this.ftpPassword = value;
- }
- }
-
- /// <summary>
- /// 是否登录
- /// </summary>
- private bool bConnected;
- public bool Connected
- {
- get
- {
- return this.bConnected;
- }
- }
- #endregion
-
- #region 链接
- /// <summary>
- /// 建立连接
- /// </summary>
- public void Connect()
- {
- socketControl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- IPEndPoint ep = new IPEndPoint(IPAddress.Parse(FtpServerIP), ftpServerPort);
- // 链接
- try
- {
- socketControl.Connect(ep);
- }
- catch (Exception)
- {
- throw new IOException("Couldn't connect to remote server");
- }
-
- // 获取应答码
- ReadReply();
- if (iReplyCode != 220)
- {
- DisConnect();
- throw new IOException(strReply.Substring(4));
- }
-
- // 登陆
- SendCommand("USER " + ftpUserID);
- if (!(iReplyCode == 331 || iReplyCode == 230))
- {
- CloseSocketConnect();//关闭连接
- throw new IOException(strReply.Substring(4));
- }
- if (iReplyCode != 230)
- {
- SendCommand("PASS " + ftpPassword);
- if (!(iReplyCode == 230 || iReplyCode == 202))
- {
- CloseSocketConnect();//关闭连接
- throw new IOException(strReply.Substring(4));
- }
- }
-
- bConnected = true;
-
- // 切换到初始目录
- if (!string.IsNullOrEmpty(remoteFilePath))
- {
- ChDir(remoteFilePath);
- }
- }
-
-
- /// <summary>
- /// 关闭连接
- /// </summary>
- public void DisConnect()
- {
- if (socketControl != null)
- {
- SendCommand("QUIT");
- }
- CloseSocketConnect();
- }
-
- #endregion
-
- #region 传输模式
-
- /// <summary>
- /// 传输模式:二进制类型、ASCII类型
- /// </summary>
- public enum TransferType
- {
- Binary,
- ASCII
- };
-
- /// <summary>
- /// 设置传输模式
- /// </summary>
- /// <param name="ttType">传输模式</param>
- public void SetTransferType(TransferType ttType)
- {
- if (ttType == TransferType.Binary)
- {
- SendCommand("TYPE I");//binary类型传输
- }
- else
- {
- SendCommand("TYPE A");//ASCII类型传输
- }
- if (iReplyCode != 200)
- {
- throw new IOException(strReply.Substring(4));
- }
- else
- {
- trType = ttType;
- }
- }
-
-
- /// <summary>
- /// 获得传输模式
- /// </summary>
- /// <returns>传输模式</returns>
- public TransferType GetTransferType()
- {
- return trType;
- }
-
- #endregion
-
- #region 文件操作
- /// <summary>
- /// 获得文件列表
- /// </summary>
- /// <param name="strMask">文件名的匹配字符串</param>
- /// <returns></returns>
- public string[] Dir(string strMask)
- {
- // 建立链接
- if (!bConnected)
- {
- Connect();
- }
-
- //建立进行数据连接的socket
- Socket socketData = CreateDataSocket();
-
- //传送命令
- SendCommand("LIST " + strMask);
-
- //分析应答代码
- if (!(iReplyCode == 150 || iReplyCode == 125 || iReplyCode == 226))
- {
- throw new IOException(strReply.Substring(4));
- }
-
- //获得结果
- strMsg = "";
- while (true)
- {
- int iBytes = socketData.Receive(buffer, buffer.Length, 0);
- strMsg += GB2312.GetString(buffer, 0, iBytes);
- if (iBytes < buffer.Length)
- {
- break;
- }
- }
- char[] seperator = { '\n' };
- string[] strsFileList = strMsg.Split(seperator);
- socketData.Close();//数据socket关闭时也会有返回码
- if (iReplyCode != 226)
- {
- ReadReply();
- if (iReplyCode != 226)
- {
- throw new IOException(strReply.Substring(4));
- }
- }
- return strsFileList;
- }
-
-
- /// <summary>
- /// 获取文件大小
- /// </summary>
- /// <param name="strFileName">文件名</param>
- /// <returns>文件大小</returns>
- public long GetFileSize(string strFileName)
- {
- if (!bConnected)
- {
- Connect();
- }
- SendCommand("SIZE " + Path.GetFileName(strFileName));
- long lSize = 0;
- if (iReplyCode == 213)
- {
- lSize = Int64.Parse(strReply.Substring(4));
- }
- else
- {
- throw new IOException(strReply.Substring(4));
- }
- return lSize;
- }
-
-
- /// <summary>
- /// 删除
- /// </summary>
- /// <param name="strFileName">待删除文件名</param>
- public void Delete(string strFileName)
- {
- if (!bConnected)
- {
- Connect();
- }
- SendCommand("DELE " + strFileName);
- if (iReplyCode != 250)
- {
- throw new IOException(strReply.Substring(4));
- }
- }
-
-
- /// <summary>
- /// 重命名(如果新文件名与已有文件重名,将覆盖已有文件)
- /// </summary>
- /// <param name="strOldFileName">旧文件名</param>
- /// <param name="strNewFileName">新文件名</param>
- public void Rename(string strOldFileName, string strNewFileName)
- {
- if (!bConnected)
- {
- Connect();
- }
- SendCommand("RNFR " + strOldFileName);
- if (iReplyCode != 350)
- {
- throw new IOException(strReply.Substring(4));
- }
- // 如果新文件名与原有文件重名,将覆盖原有文件
- SendCommand("RNTO " + strNewFileName);
- if (iReplyCode != 250)
- {
- throw new IOException(strReply.Substring(4));
- }
- }
- #endregion
-
- #region 上传和下载
- /// <summary>
- /// 下载一批文件
- /// </summary>
- /// <param name="strFileNameMask">文件名的匹配字符串</param>
- /// <param name="strFolder">本地目录(不得以\结束)</param>
- public void Download(string strFileNameMask, string strFolder)
- {
- if (!bConnected)
- {
- Connect();
- }
- string[] strFiles = Dir(strFileNameMask);
- foreach (string strFile in strFiles)
- {
- if (!strFile.Equals(""))//一般来说strFiles的最后一个元素可能是空字符串
- {
- if (strFile.LastIndexOf(".") > -1)
- {
- Download(strFile.Replace("\r", ""), strFolder, strFile.Replace("\r", ""));
- }
- }
- }
- }
-
- /// <summary>
- /// 下载目录
- /// </summary>
- /// <param name="strRemoteFileName">要下载的文件名</param>
- /// <param name="strFolder">本地目录(不得以\结束)</param>
- /// <param name="strLocalFileName">保存在本地时的文件名</param>
- public void Download(string strRemoteFileName, string strFolder, string strLocalFileName)
- {
- if (strLocalFileName.StartsWith("-r"))
- {
- string[] infos = strLocalFileName.Split(' ');
- strRemoteFileName = strLocalFileName = infos[infos.Length - 1];
- if (!this.bConnected)
- {
- this.Connect();
- }
- SetTransferType(TransferType.Binary);
- if (strLocalFileName.Equals(""))
- {
- strLocalFileName = strRemoteFileName;
- }
- if (!File.Exists(strLocalFileName))
- {
- Stream st = File.Create(strLocalFileName);
- st.Close();
- }
- FileStream output = new
- FileStream(strFolder + "\\" + strLocalFileName, FileMode.Create);
- Socket socketData = CreateDataSocket();
- SendCommand("RETR " + strRemoteFileName);
- if (!(iReplyCode == 150 || iReplyCode == 125
- || iReplyCode == 226 || iReplyCode == 250))
- {
- throw new IOException(strReply.Substring(4));
- }
- int receiveBytes = 0;
- while (true)
- {
- int iBytes = socketData.Receive(buffer, buffer.Length, 0);
- receiveBytes = receiveBytes + iBytes;
- output.Write(buffer, 0, iBytes);
- if (iBytes <= 0)
- {
- break;
- }
- }
- output.Close();
- if (socketData.Connected)
- {
- socketData.Close();
- }
- if (!(iReplyCode == 226 || iReplyCode == 250))
- {
- ReadReply();
- if (!(iReplyCode == 226 || iReplyCode == 250))
- {
- throw new IOException(strReply.Substring(4));
- }
- }
- }
- }
-
- /// <summary>
- /// 下载一个文件
- /// </summary>
- /// <param name="strRemoteFileName">要下载的文件名</param>
- /// <param name="strFolder">本地目录(不得以\结束)</param>
- /// <param name="strLocalFileName">保存在本地时的文件名</param>
- public void DownloadFile(string strRemoteFileName, string strFolder, string strLocalFileName)
- {
- if (!bConnected)
- {
- Connect();
- }
- SetTransferType(TransferType.Binary);
- if (strLocalFileName.Equals(""))
- {
- strLocalFileName = strRemoteFileName;
- }
- if (!File.Exists(strLocalFileName))
- {
- Stream st = File.Create(strLocalFileName);
- st.Close();
- }
-
- FileStream output = new
- FileStream(strFolder + "\\" + strLocalFileName, FileMode.Create);
- Socket socketData = CreateDataSocket();
- SendCommand("RETR " + strRemoteFileName);
- if (!(iReplyCode == 150 || iReplyCode == 125
- || iReplyCode == 226 || iReplyCode == 250))
- {
- throw new IOException(strReply.Substring(4));
- }
- while (true)
- {
- int iBytes = socketData.Receive(buffer, buffer.Length, 0);
- output.Write(buffer, 0, iBytes);
- if (iBytes <= 0)
- {
- break;
- }
- }
- output.Close();
- if (socketData.Connected)
- {
- socketData.Close();
- }
- if (!(iReplyCode == 226 || iReplyCode == 250))
- {
- ReadReply();
- if (!(iReplyCode == 226 || iReplyCode == 250))
- {
- throw new IOException(strReply.Substring(4));
- }
- }
- }
-
- /// <summary>
- /// 下载一个文件(断点续传)
- /// </summary>
- /// <param name="strRemoteFileName">要下载的文件名</param>
- /// <param name="strFolder">本地目录(不得以\结束)</param>
- /// <param name="strLocalFileName">保存在本地时的文件名</param>
- /// <param name="size">已下载文件流长度</param>
- public void DownloadBrokenFile(string strRemoteFileName, string strFolder, string strLocalFileName, long size)
- {
- if (!bConnected)
- {
- Connect();
- }
- SetTransferType(TransferType.Binary);
- FileStream output = new
- FileStream(strFolder + "\\" + strLocalFileName, FileMode.Append);
- Socket socketData = CreateDataSocket();
- SendCommand("REST " + size.ToString());
- SendCommand("RETR " + strRemoteFileName);
- if (!(iReplyCode == 150 || iReplyCode == 125
- || iReplyCode == 226 || iReplyCode == 250))
- {
- throw new IOException(strReply.Substring(4));
- }
- while (true)
- {
- int iBytes = socketData.Receive(buffer, buffer.Length, 0);
- output.Write(buffer, 0, iBytes);
- if (iBytes <= 0)
- {
- break;
- }
- }
- output.Close();
- if (socketData.Connected)
- {
- socketData.Close();
- }
- if (!(iReplyCode == 226 || iReplyCode == 250))
- {
- ReadReply();
- if (!(iReplyCode == 226 || iReplyCode == 250))
- {
- throw new IOException(strReply.Substring(4));
- }
- }
- }
-
-
-
- /// <summary>
- /// 上传一批文件
- /// </summary>
- /// <param name="strFolder">本地目录(不得以\结束)</param>
- /// <param name="strFileNameMask">文件名匹配字符(可以包含*和?)</param>
- public void Upload(string strFolder, string strFileNameMask)
- {
- string[] strFiles = Directory.GetFiles(strFolder, strFileNameMask);
- foreach (string strFile in strFiles)
- {
- //strFile是完整的文件名(包含路径)
- Upload(strFile);
- }
- }
-
-
- /// <summary>
- /// 上传一个文件
- /// </summary>
- /// <param name="strFileName">本地文件名</param>
- public void Upload(string strFileName)
- {
- if (!bConnected)
- {
- Connect();
- }
- Socket socketData = CreateDataSocket();
- SendCommand("STOR " + Path.GetFileName(strFileName));
- if (!(iReplyCode == 125 || iReplyCode == 150))
- {
- throw new IOException(strReply.Substring(4));
- }
- FileStream input = new
- FileStream(strFileName, FileMode.Open);
- int iBytes = 0;
- while ((iBytes = input.Read(buffer, 0, buffer.Length)) > 0)
- {
- socketData.Send(buffer, iBytes, 0);
- }
- input.Close();
- if (socketData.Connected)
- {
- socketData.Close();
- }
- if (!(iReplyCode == 226 || iReplyCode == 250))
- {
- ReadReply();
- if (!(iReplyCode == 226 || iReplyCode == 250))
- {
- throw new IOException(strReply.Substring(4));
- }
- }
- }
-
- #endregion
-
- #region 目录操作
- /// <summary>
- /// 创建目录
- /// </summary>
- /// <param name="strDirName">目录名</param>
- public void MkDir(string strDirName)
- {
- if (!bConnected)
- {
- Connect();
- }
- SendCommand("MKD " + strDirName);
- if (iReplyCode != 257)
- {
- throw new IOException(strReply.Substring(4));
- }
- }
-
-
- /// <summary>
- /// 删除目录
- /// </summary>
- /// <param name="strDirName">目录名</param>
- public void RmDir(string strDirName)
- {
- if (!bConnected)
- {
- Connect();
- }
- SendCommand("RMD " + strDirName);
- if (iReplyCode != 250)
- {
- throw new IOException(strReply.Substring(4));
- }
- }
-
-
- /// <summary>
- /// 改变目录
- /// </summary>
- /// <param name="strDirName">新的工作目录名</param>
- public void ChDir(string strDirName)
- {
- if (strDirName.Equals(".") || strDirName.Equals(""))
- {
- return;
- }
- if (!bConnected)
- {
- Connect();
- }
- SendCommand("CWD " + strDirName);
- if (iReplyCode != 250)
- {
- throw new IOException(strReply.Substring(4));
- }
- this.remoteFilePath = strDirName;
- }
-
- #endregion
-
- #region 内部变量
- /// <summary>
- /// 服务器返回的应答信息(包含应答码)
- /// </summary>
- private string strMsg;
- /// <summary>
- /// 服务器返回的应答信息(包含应答码)
- /// </summary>
- private string strReply;
- /// <summary>
- /// 服务器返回的应答码
- /// </summary>
- private int iReplyCode;
- /// <summary>
- /// 进行控制连接的socket
- /// </summary>
- private Socket socketControl;
- /// <summary>
- /// 传输模式
- /// </summary>
- private TransferType trType;
- /// <summary>
- /// 接收和发送数据的缓冲区
- /// </summary>
- private static int BLOCK_SIZE = 512;
- Byte[] buffer = new Byte[BLOCK_SIZE];
- /// <summary>
- /// 编码方式(为防止出现中文乱码采用 GB2312编码方式)
- /// </summary>
- Encoding GB2312 = Encoding.GetEncoding("gb2312");
- #endregion
-
- #region 内部函数
- /// <summary>
- /// 将一行应答字符串记录在strReply和strMsg
- /// 应答码记录在iReplyCode
- /// </summary>
- private void ReadReply()
- {
- strMsg = "";
- strReply = ReadLine();
- iReplyCode = Int32.Parse(strReply.Substring(0, 3));
- }
-
- /// <summary>
- /// 建立进行数据连接的socket
- /// </summary>
- /// <returns>数据连接socket</returns>
- private Socket CreateDataSocket()
- {
- SendCommand("PASV");
- if (iReplyCode != 227)
- {
- throw new IOException(strReply.Substring(4));
- }
- int index1 = strReply.IndexOf('(');
- int index2 = strReply.IndexOf(')');
- string ipData =
- strReply.Substring(index1 + 1, index2 - index1 - 1);
- int[] parts = new int[6];
- int len = ipData.Length;
- int partCount = 0;
- string buf = "";
- for (int i = 0; i < len && partCount <= 6; i++)
- {
- char ch = Char.Parse(ipData.Substring(i, 1));
- if (Char.IsDigit(ch))
- buf += ch;
- else if (ch != ',')
- {
- throw new IOException("Malformed PASV strReply: " +
- strReply);
- }
- if (ch == ',' || i + 1 == len)
- {
- try
- {
- parts[partCount++] = Int32.Parse(buf);
- buf = "";
- }
- catch (Exception)
- {
- throw new IOException("Malformed PASV strReply: " +
- strReply);
- }
- }
- }
- 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("Can't connect to remote server");
- }
- return s;
- }
-
-
- /// <summary>
- /// 关闭socket连接(用于登录以前)
- /// </summary>
- private void CloseSocketConnect()
- {
- if (socketControl != null)
- {
- socketControl.Close();
- socketControl = null;
- }
- bConnected = false;
- }
-
- /// <summary>
- /// 读取Socket返回的所有字符串
- /// </summary>
- /// <returns>包含应答码的字符串行</returns>
- private string ReadLine()
- {
- while (true)
- {
- int iBytes = socketControl.Receive(buffer, buffer.Length, 0);
- strMsg += GB2312.GetString(buffer, 0, iBytes);
- if (iBytes < buffer.Length)
- {
- break;
- }
- }
- char[] seperator = { '\n' };
- string[] mess = strMsg.Split(seperator);
- if (strMsg.Length > 2)
- {
- strMsg = mess[mess.Length - 2];
- //seperator[0]是10,换行符是由13和0组成的,分隔后10后面虽没有字符串,
- //但也会分配为空字符串给后面(也是最后一个)字符串数组,
- //所以最后一个mess是没用的空字符串
- //但为什么不直接取mess[0],因为只有最后一行字符串应答码与信息之间有空格
- }
- else
- {
- strMsg = mess[0];
- }
- if (!strMsg.Substring(3, 1).Equals(" "))//返回字符串正确的是以应答码(如220开头,后面接一空格,再接问候字符串)
- {
- return ReadLine();
- }
- return strMsg;
- }
-
-
- /// <summary>
- /// 发送命令并获取应答码和最后一行应答字符串
- /// </summary>
- /// <param name="strCommand">命令</param>
- private void SendCommand(String strCommand)
- {
- Byte[] cmdBytes =
- GB2312.GetBytes((strCommand + "\r\n").ToCharArray());
- socketControl.Send(cmdBytes, cmdBytes.Length, 0);
- ReadReply();
- }
-
- #endregion
-
- }
-
- }
该代码上传不支持断点续传,不支持匿名下载,有该需求的朋友可以参考: 点击打开链接