[转]利用FtpClient类实现文件的上传下载功能
该代码源自互联网,并经过修改:
解决了中文文件名会出现乱码的情况;
改善了上传不稳定的问题(但没有从根本上解决,目前只知道在接收Socket时有时会收到一半就退出,结果造成接收数据不完整,经常出现在Dir时。尚未仔细研究。)
FtpClient.cs
/**//// <summary>
/// FtpClient 的摘要说明。
/// </summary>
public class FtpClient : IDisposable
...{
私有变量#region 私有变量
private string _serverAddress;
private int _serverPort;
private string _remotePath;
private string _loginUser;
private string _loginPassword;
private bool _connected;
private FtpTransferType _transferType;
private int _blockSize = 512;
/**//// <summary>
/// 服务器返回的应答信息。
/// </summary>
private FtpReply _reply;
/**//// <summary>
/// 进行控制连接的socket
/// </summary>
private Socket _socketControl;
#endregion
// 构造方法
构造方法#region 构造方法
/**//// <summary>
/// 构造方法。
/// </summary>
public FtpClient() : this("localhost", "anonymous", "anonymous@anonymous.net", 21)
...{
}
/**//// <summary>
/// 构造方法。
/// </summary>
/// <param name="serverAddress"></param>
/// <param name="loginUser"></param>
/// <param name="loginPassword"></param>
/// <param name="serverPort"></param>
public FtpClient(string serverAddress, string loginUser, string loginPassword, int serverPort)
...{
_serverAddress = serverAddress;
_loginUser = loginUser;
_loginPassword = loginPassword;
_serverPort = serverPort;
this._remotePath = string.Empty;
}
#endregion
// 属性
BlockSize 接收和发送数据的缓冲区大小#region BlockSize 接收和发送数据的缓冲区大小
/**//// <summary>
/// 获取或设置接收和发送数据的缓冲区大小,默认为 1024 。
/// </summary>
public int BlockSize
...{
get
...{
return this._blockSize;
}
set
...{
this._blockSize = value;
}
}
#endregion
ServerAddress Ftp 服务器地址#region ServerAddress Ftp 服务器地址
/**//// <summary>
/// Ftp 服务器地址。默认为 localhost 。
/// </summary>
public string ServerAddress
...{
get
...{
return _serverAddress;
}
set
...{
_serverAddress = value;
}
}
#endregion
ServerPort Ftp 服务器端口#region ServerPort Ftp 服务器端口
/**//// <summary>
/// Ftp 服务器端口。默认为 21 。
/// </summary>
public int ServerPort
...{
get
...{
return _serverPort;
}
set
...{
_serverPort = value;
}
}
#endregion
RemotePath 当前服务器目录#region RemotePath 当前服务器目录
/**//// <summary>
/// 当前服务器目录。
/// </summary>
public string RemotePath
...{
get
...{
return _remotePath;
}
set
...{
_remotePath = value;
}
}
#endregion
LoginUser 登录用户账号#region LoginUser 登录用户账号
/**//// <summary>
/// 登录用户账号。默认为 anonymous 。
/// </summary>
public string LoginUser
...{
set
...{
_loginUser = value;
}
}
#endregion
LoginPassword 用户登录密码#region LoginPassword 用户登录密码
/**//// <summary>
/// 用户登录密码。默认为 anonymous@anonymous.net 。
/// </summary>
public string LoginPassword
...{
set
...{
_loginPassword = value;
}
}
#endregion
Connected 是否登录#region Connected 是否登录
/**//// <summary>
/// 是否登录
/// </summary>
public bool Connected
...{
get
...{
return _connected;
}
}
#endregion
// 接口
IDisposable 成员#region IDisposable 成员
private bool disposed = false;
/**//// <summary>
/// <see cref="IDisposable"/> 接口的实现方法。
/// </summary>
void IDisposable.Dispose()
...{
this.Dispose(true);
GC.SuppressFinalize(this);
}
/**//// <summary>
/// 释放由 <see cref="FtpClient"/> 使用的非托管资源,并可根据需要处置托管资源。
/// </summary>
/// <param name="isDisposing">指示当前是否是被 Dispose() 方法调用。</param>
protected virtual void Dispose(bool isDisposing)
...{
if(!this.disposed)
...{
if(isDisposing)
...{
// 释放托管的资源
if(this._socketControl!= null)
...{
this.Close();
}
}
// 释放非托管的资源
// 标记资源已经释放
this.disposed = true;
}
}
#endregion
// 私有方法
ReadReply#region ReadReply
/**//// <summary>
/// 将一行应答字符串记录在this._reply.Message和this._strMsg
/// 应答码记录在this._reply.Code
/// </summary>
private FtpReply ReadReply()
...{
this._reply = null;
string message = ReadLine();
this._reply = message.Length < 3 ? null : new FtpReply(Int32.Parse(message.Substring(0, 3)), message);
return this._reply;
}
#endregion
CreateDataSocket 建立进行数据连接的socket#region CreateDataSocket 建立进行数据连接的socket
/**//// <summary>
/// 建立进行数据连接的socket
/// </summary>
/// <returns>数据连接socket</returns>
private Socket CreateDataSocket()
...{
SendCommand("PASV");
if(this._reply.Code != 227)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
int index1 = this._reply.Message.IndexOf('(');
int index2 = this._reply.Message.IndexOf(')');
string ipData = this._reply.Message.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 FtpClientException("Malformed PASV result: " + this._reply.Message);
}
if(ch == ',' || i + 1 == len)
...{
try
...{
parts[partCount++] = Int32.Parse(buf);
buf = "";
}
catch (Exception)
...{
throw new FtpClientException("Malformed PASV result (not supported?): " + this._reply.Message);
}
}
}
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 ex)
...{
throw new FtpClientException(string.Format("Can't connect to remote server {0} .", ipAddress), ex);
}
return s;
}
#endregion
CloseSocketConnect 关闭socket连接(用于登录以前)#region CloseSocketConnect 关闭socket连接(用于登录以前)
/**//// <summary>
/// 关闭socket连接(用于登录以前)
/// </summary>
private void CloseSocketConnect()
...{
if(this._socketControl!= null)
...{
this._socketControl.Close();
this._socketControl = null;
}
_connected = false;
}
#endregion
ReadLine 读取Socket返回的所有字符串#region ReadLine 读取Socket返回的所有字符串
/**//// <summary>
/// 读取Socket返回的所有字符串
/// </summary>
/// <returns>包含应答码的字符串行</returns>
private string ReadLine()
...{
string message = string.Empty;
byte[] buffer = new byte[this._blockSize];
while(true)
...{
Thread.Sleep(10);
int iBytes = this._socketControl.Receive(buffer, buffer.Length, 0);
message += Encoding.Default.GetString(buffer, 0, iBytes);
if(iBytes < buffer.Length)
...{
break;
}
}
string[] mess = StringHelper.Split(StringHelper.TrimEnd(message, true, " "), true, " ");
if(mess.Length > 2)
...{
message = mess[mess.Length-2];
//seperator[0]是10,换行符是由13和0组成的,分隔后10后面虽没有字符串,
//但也会分配为空字符串给后面(也是最后一个)字符串数组,
//所以最后一个mess是没用的空字符串
//但为什么不直接取mess[0],因为只有最后一行字符串应答码与信息之间有空格
}
else
...{
message = mess[0];
}
if(message.Length > 3 && message.Substring(3, 1) != " ")//返回字符串正确的是以应答码(如220开头,后面接一空格,再接问候字符串)
...{
return ReadLine();
}
return message;
}
#endregion
SendCommand 发送命令并获取应答码和最后一行应答字符串#region SendCommand 发送命令并获取应答码和最后一行应答字符串
/**//// <summary>
/// 发送命令并获取应答码和最后一行应答字符串。
/// </summary>
/// <param name="command">命令</param>
public FtpReply SendCommand(string command)
...{
char[] charList = (command + " ").ToCharArray();
Byte[] cmdBytes = Encoding.Default.GetBytes(charList);
this._socketControl.Send(cmdBytes, cmdBytes.Length, 0);
return this.ReadReply();
}
#endregion
// 公共方法
Connect 建立连接#region Connect 建立连接
/**//// <summary>
/// 建立连接
/// </summary>
public void Connect()
...{
IPAddress address = null;
if(RegexUtility.IsIP(this._serverAddress))
...{
address = IPAddress.Parse(this._serverAddress);
}
else
...{
try
...{
IPHostEntry host = Dns.GetHostByName(this._serverAddress);
if(host.AddressList.Length != 0)
...{
address = host.AddressList[0];
}
}
catch(Exception ex)
...{
throw new FtpClientException(string.Format("获取 Ftp 服务器主机地址出错:{0}", ex.Message), ex);
}
}
if(address == null)
...{
throw new FtpClientException(string.Format("无法获取远程主机 {0} 的 IP 地址。", this._serverAddress));
}
this._socketControl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ep = new IPEndPoint(address, _serverPort);
// 链接
try
...{
this._socketControl.Connect(ep);
}
catch(Exception ex)
...{
throw new FtpClientException(string.Format("无法连接到远程服务器 {0} 。", address.ToString()), ex);
}
// 获取应答码
ReadReply();
if(this._reply.Code != 220)
...{
Close();
throw new FtpClientException(this._reply.Message.Substring(4));
}
// 登陆
SendCommand("USER " + _loginUser);
if(!(this._reply.Code == 331 || this._reply.Code == 230))
...{
//关闭连接
CloseSocketConnect();
throw new FtpClientException(this._reply.Message.Substring(4));
}
if(this._reply.Code != 230)
...{
SendCommand("PASS " + _loginPassword);
if(!(this._reply.Code == 230 || this._reply.Code == 202))
...{
//关闭连接
CloseSocketConnect();
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
_connected = true;
// 切换到目录
ChangeDirectory(_remotePath);
}
#endregion
EnsureConnected 确保已经连接到服务器#region EnsureConnected 确保已经连接到服务器
/**//// <summary>
/// 确保已经连接到服务器。
/// </summary>
protected void EnsureConnected()
...{
if(!_connected)
...{
Connect();
}
}
#endregion
Close 关闭连接#region Close 关闭连接
/**//// <summary>
/// 关闭连接
/// </summary>
public void Close()
...{
if(this._socketControl != null)
...{
SendCommand("QUIT");
}
CloseSocketConnect();
}
#endregion
TransferType 获取或设置传输模式#region TransferType 获取或设置传输模式
/**//// <summary>
/// 获取或设置传输模式
/// </summary>
public FtpTransferType TransferType
...{
get
...{
return this._transferType;
}
set
...{
if(value != this._transferType)
...{
if(value == FtpTransferType.Binary)
...{
SendCommand("TYPE I");
}
else
...{
SendCommand("TYPE A");
}
if(this._reply.Code != 200)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
else
...{
this._transferType = value;
}
}
}
}
#endregion
Dir 获得文件列表#region Dir 获得文件列表
/**//// <summary>
/// 获得文件列表。
/// </summary>
/// <returns></returns>
public string[] Dir()
...{
// 确保已连接上服务器
this.EnsureConnected();
//建立进行数据连接的socket
Socket socketData = CreateDataSocket();
//传送命令
SendCommand("NLST");
//分析应答代码
if(this._reply.Code == 550)
...{
return new string[0];
}
if(!(this._reply.Code == 150 || this._reply.Code == 125 || this._reply.Code == 226))
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
//获得结果
string message = string.Empty;
byte[] buffer = new byte[this._blockSize];
while(true)
...{
int iBytes = socketData.Receive(buffer, buffer.Length, 0);
message += Encoding.Default.GetString(buffer, 0, iBytes);
if(iBytes < buffer.Length)
...{
break;
}
}
string[] strsFileList = StringHelper.Split(StringHelper.TrimEnd(message, false, " "), false, " ");
socketData.Close();//数据socket关闭时也会有返回码
if(this._reply.Code != 226)
...{
ReadReply();
if(this._reply.Code != 226)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
return strsFileList;
}
#endregion
GetFileSize 获取文件大小#region GetFileSize 获取文件大小
/**//// <summary>
/// 获取文件大小
/// </summary>
/// <param name="fileName">文件名</param>
/// <returns>文件大小</returns>
private long GetFileSize(string fileName)
...{
// 确保已连接上服务器
this.EnsureConnected();
SendCommand("SIZE " + fileName);
long lSize = 0;
if(this._reply.Code == 213)
...{
lSize = Int64.Parse(this._reply.Message.Substring(4));
}
else
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
return lSize;
}
#endregion
Delete 删除#region Delete 删除
/**//// <summary>
/// 删除
/// </summary>
/// <param name="fileName">待删除文件名</param>
public void Delete(string fileName)
...{
// 确保已连接上服务器
this.EnsureConnected();
SendCommand("DELE " + fileName);
if(this._reply.Code != 250)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
#endregion
Rename 重命名文件(如果新文件名与已有文件重名,将覆盖已有文件)#region Rename 重命名文件(如果新文件名与已有文件重名,将覆盖已有文件)
/**//// <summary>
/// 重命名(如果新文件名与已有文件重名,将覆盖已有文件)。
/// </summary>
/// <param name="oldFileName">旧文件名</param>
/// <param name="newFileName">新文件名</param>
public void Rename(string oldFileName,string newFileName)
...{
// 确保已连接上服务器
this.EnsureConnected();
SendCommand("RNFR " + oldFileName);
if(this._reply.Code != 350)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
// 如果新文件名与原有文件重名,将覆盖原有文件
SendCommand("RNTO " + newFileName);
if(this._reply.Code != 250)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
#endregion
Download 下载文件#region Download 下载文件
/**//// <summary>
/// 下载一个文件
/// </summary>
/// <param name="remoteFileName">远程服务器上的文件路径名。</param>
/// <param name="localFileName">将文件保存到的本地路径名。</param>
public void DownloadFile(string remoteFileName, string localFileName)
...{
// 确保已连接上服务器
this.EnsureConnected();
this.TransferType = FtpTransferType.Binary;
if(localFileName == string.Empty)
...{
localFileName = remoteFileName;
}
string path = Path.GetDirectoryName(localFileName);
if(!Directory.Exists(path))
...{
Directory.CreateDirectory(path);
}
FileStream output = new FileStream(localFileName, FileMode.Create);
Socket socketData = CreateDataSocket();
SendCommand("RETR " + remoteFileName);
if(!(this._reply.Code == 150 || this._reply.Code == 125 || this._reply.Code == 226 || this._reply.Code == 250))
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
byte[] buffer = new byte[this._blockSize];
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(!(this._reply.Code == 226 || this._reply.Code == 250))
...{
ReadReply();
if(!(this._reply.Code == 226 || this._reply.Code == 250))
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
}
#endregion
Upload 上传文件#region Upload 上传文件
/**//// <summary>
/// 上传一批文件
/// </summary>
/// <param name="folder">本地目录(不得以结束)</param>
/// <param name="fileNameMask">文件名匹配字符(可以包含*和?)</param>
public void UploadDirectory(string folder,string fileNameMask)
...{
string[] strFiles = Directory.GetFiles(folder,fileNameMask);
foreach(string strFile in strFiles)
...{
//strFile是完整的文件名(包含路径)
UploadFile(strFile);
}
}
/**//// <summary>
/// 上传一个文件
/// </summary>
/// <param name="fileName">本地文件名</param>
public void UploadFile(string fileName)
...{
FileStream input = new FileStream(fileName, FileMode.Open);
this.UploadFile(input, Path.GetFileName(fileName));
input.Close();
}
/**//// <summary>
/// 上传一个文件
/// </summary>
/// <param name="input">流。</param>
/// <param name="fileName">要保存为的文件名。</param>
public void UploadFile(Stream input, string fileName)
...{
// 确保已连接上服务器
this.EnsureConnected();
Socket socketData = CreateDataSocket();
SendCommand("STOR " + fileName);
if(!(this._reply.Code == 125 || this._reply.Code == 150))
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
int iBytes = 0;
byte[] buffer = new byte[this._blockSize];
while ((iBytes = input.Read(buffer,0,buffer.Length)) > 0)
...{
socketData.Send(buffer, iBytes, 0);
}
if(socketData.Connected)
...{
socketData.Close();
}
if(!(this._reply.Code == 226 || this._reply.Code == 250))
...{
ReadReply();
if(!(this._reply.Code == 226 || this._reply.Code == 250))
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
}
#endregion
MakeDirectory 创建目录#region MakeDirectory 创建目录
/**//// <summary>
/// 创建目录
/// </summary>
/// <param name="directory">目录名</param>
public void MakeDirectory(string directory)
...{
// 确保已连接上服务器
this.EnsureConnected();
SendCommand("MKD " + directory);
if(this._reply.Code != 257)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
#endregion
RenameDirectory 删除目录#region RenameDirectory 删除目录
/**//// <summary>
/// 删除目录
/// </summary>
/// <param name="directory">目录名</param>
public void RenameDirectory(string directory)
...{
// 确保已连接上服务器
this.EnsureConnected();
SendCommand("RMD " + directory);
if(this._reply.Code != 250)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
#endregion
ChangeDirectory 改变当前目录#region ChangeDirectory 改变当前目录
/**//// <summary>
/// 改变当前目录
/// </summary>
/// <param name="directory">新的工作目录名</param>
public void ChangeDirectory(string directory)
...{
if(directory == null || directory == string.Empty || directory == ".")
...{
return;
}
// 确保已连接上服务器
this.EnsureConnected();
SendCommand("CWD " + directory);
if(this._reply.Code != 250)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
this._remotePath = directory;
}
#endregion
}
/// FtpClient 的摘要说明。
/// </summary>
public class FtpClient : IDisposable
...{
私有变量#region 私有变量
private string _serverAddress;
private int _serverPort;
private string _remotePath;
private string _loginUser;
private string _loginPassword;
private bool _connected;
private FtpTransferType _transferType;
private int _blockSize = 512;
/**//// <summary>
/// 服务器返回的应答信息。
/// </summary>
private FtpReply _reply;
/**//// <summary>
/// 进行控制连接的socket
/// </summary>
private Socket _socketControl;
#endregion
// 构造方法
构造方法#region 构造方法
/**//// <summary>
/// 构造方法。
/// </summary>
public FtpClient() : this("localhost", "anonymous", "anonymous@anonymous.net", 21)
...{
}
/**//// <summary>
/// 构造方法。
/// </summary>
/// <param name="serverAddress"></param>
/// <param name="loginUser"></param>
/// <param name="loginPassword"></param>
/// <param name="serverPort"></param>
public FtpClient(string serverAddress, string loginUser, string loginPassword, int serverPort)
...{
_serverAddress = serverAddress;
_loginUser = loginUser;
_loginPassword = loginPassword;
_serverPort = serverPort;
this._remotePath = string.Empty;
}
#endregion
// 属性
BlockSize 接收和发送数据的缓冲区大小#region BlockSize 接收和发送数据的缓冲区大小
/**//// <summary>
/// 获取或设置接收和发送数据的缓冲区大小,默认为 1024 。
/// </summary>
public int BlockSize
...{
get
...{
return this._blockSize;
}
set
...{
this._blockSize = value;
}
}
#endregion
ServerAddress Ftp 服务器地址#region ServerAddress Ftp 服务器地址
/**//// <summary>
/// Ftp 服务器地址。默认为 localhost 。
/// </summary>
public string ServerAddress
...{
get
...{
return _serverAddress;
}
set
...{
_serverAddress = value;
}
}
#endregion
ServerPort Ftp 服务器端口#region ServerPort Ftp 服务器端口
/**//// <summary>
/// Ftp 服务器端口。默认为 21 。
/// </summary>
public int ServerPort
...{
get
...{
return _serverPort;
}
set
...{
_serverPort = value;
}
}
#endregion
RemotePath 当前服务器目录#region RemotePath 当前服务器目录
/**//// <summary>
/// 当前服务器目录。
/// </summary>
public string RemotePath
...{
get
...{
return _remotePath;
}
set
...{
_remotePath = value;
}
}
#endregion
LoginUser 登录用户账号#region LoginUser 登录用户账号
/**//// <summary>
/// 登录用户账号。默认为 anonymous 。
/// </summary>
public string LoginUser
...{
set
...{
_loginUser = value;
}
}
#endregion
LoginPassword 用户登录密码#region LoginPassword 用户登录密码
/**//// <summary>
/// 用户登录密码。默认为 anonymous@anonymous.net 。
/// </summary>
public string LoginPassword
...{
set
...{
_loginPassword = value;
}
}
#endregion
Connected 是否登录#region Connected 是否登录
/**//// <summary>
/// 是否登录
/// </summary>
public bool Connected
...{
get
...{
return _connected;
}
}
#endregion
// 接口
IDisposable 成员#region IDisposable 成员
private bool disposed = false;
/**//// <summary>
/// <see cref="IDisposable"/> 接口的实现方法。
/// </summary>
void IDisposable.Dispose()
...{
this.Dispose(true);
GC.SuppressFinalize(this);
}
/**//// <summary>
/// 释放由 <see cref="FtpClient"/> 使用的非托管资源,并可根据需要处置托管资源。
/// </summary>
/// <param name="isDisposing">指示当前是否是被 Dispose() 方法调用。</param>
protected virtual void Dispose(bool isDisposing)
...{
if(!this.disposed)
...{
if(isDisposing)
...{
// 释放托管的资源
if(this._socketControl!= null)
...{
this.Close();
}
}
// 释放非托管的资源
// 标记资源已经释放
this.disposed = true;
}
}
#endregion
// 私有方法
ReadReply#region ReadReply
/**//// <summary>
/// 将一行应答字符串记录在this._reply.Message和this._strMsg
/// 应答码记录在this._reply.Code
/// </summary>
private FtpReply ReadReply()
...{
this._reply = null;
string message = ReadLine();
this._reply = message.Length < 3 ? null : new FtpReply(Int32.Parse(message.Substring(0, 3)), message);
return this._reply;
}
#endregion
CreateDataSocket 建立进行数据连接的socket#region CreateDataSocket 建立进行数据连接的socket
/**//// <summary>
/// 建立进行数据连接的socket
/// </summary>
/// <returns>数据连接socket</returns>
private Socket CreateDataSocket()
...{
SendCommand("PASV");
if(this._reply.Code != 227)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
int index1 = this._reply.Message.IndexOf('(');
int index2 = this._reply.Message.IndexOf(')');
string ipData = this._reply.Message.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 FtpClientException("Malformed PASV result: " + this._reply.Message);
}
if(ch == ',' || i + 1 == len)
...{
try
...{
parts[partCount++] = Int32.Parse(buf);
buf = "";
}
catch (Exception)
...{
throw new FtpClientException("Malformed PASV result (not supported?): " + this._reply.Message);
}
}
}
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 ex)
...{
throw new FtpClientException(string.Format("Can't connect to remote server {0} .", ipAddress), ex);
}
return s;
}
#endregion
CloseSocketConnect 关闭socket连接(用于登录以前)#region CloseSocketConnect 关闭socket连接(用于登录以前)
/**//// <summary>
/// 关闭socket连接(用于登录以前)
/// </summary>
private void CloseSocketConnect()
...{
if(this._socketControl!= null)
...{
this._socketControl.Close();
this._socketControl = null;
}
_connected = false;
}
#endregion
ReadLine 读取Socket返回的所有字符串#region ReadLine 读取Socket返回的所有字符串
/**//// <summary>
/// 读取Socket返回的所有字符串
/// </summary>
/// <returns>包含应答码的字符串行</returns>
private string ReadLine()
...{
string message = string.Empty;
byte[] buffer = new byte[this._blockSize];
while(true)
...{
Thread.Sleep(10);
int iBytes = this._socketControl.Receive(buffer, buffer.Length, 0);
message += Encoding.Default.GetString(buffer, 0, iBytes);
if(iBytes < buffer.Length)
...{
break;
}
}
string[] mess = StringHelper.Split(StringHelper.TrimEnd(message, true, " "), true, " ");
if(mess.Length > 2)
...{
message = mess[mess.Length-2];
//seperator[0]是10,换行符是由13和0组成的,分隔后10后面虽没有字符串,
//但也会分配为空字符串给后面(也是最后一个)字符串数组,
//所以最后一个mess是没用的空字符串
//但为什么不直接取mess[0],因为只有最后一行字符串应答码与信息之间有空格
}
else
...{
message = mess[0];
}
if(message.Length > 3 && message.Substring(3, 1) != " ")//返回字符串正确的是以应答码(如220开头,后面接一空格,再接问候字符串)
...{
return ReadLine();
}
return message;
}
#endregion
SendCommand 发送命令并获取应答码和最后一行应答字符串#region SendCommand 发送命令并获取应答码和最后一行应答字符串
/**//// <summary>
/// 发送命令并获取应答码和最后一行应答字符串。
/// </summary>
/// <param name="command">命令</param>
public FtpReply SendCommand(string command)
...{
char[] charList = (command + " ").ToCharArray();
Byte[] cmdBytes = Encoding.Default.GetBytes(charList);
this._socketControl.Send(cmdBytes, cmdBytes.Length, 0);
return this.ReadReply();
}
#endregion
// 公共方法
Connect 建立连接#region Connect 建立连接
/**//// <summary>
/// 建立连接
/// </summary>
public void Connect()
...{
IPAddress address = null;
if(RegexUtility.IsIP(this._serverAddress))
...{
address = IPAddress.Parse(this._serverAddress);
}
else
...{
try
...{
IPHostEntry host = Dns.GetHostByName(this._serverAddress);
if(host.AddressList.Length != 0)
...{
address = host.AddressList[0];
}
}
catch(Exception ex)
...{
throw new FtpClientException(string.Format("获取 Ftp 服务器主机地址出错:{0}", ex.Message), ex);
}
}
if(address == null)
...{
throw new FtpClientException(string.Format("无法获取远程主机 {0} 的 IP 地址。", this._serverAddress));
}
this._socketControl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ep = new IPEndPoint(address, _serverPort);
// 链接
try
...{
this._socketControl.Connect(ep);
}
catch(Exception ex)
...{
throw new FtpClientException(string.Format("无法连接到远程服务器 {0} 。", address.ToString()), ex);
}
// 获取应答码
ReadReply();
if(this._reply.Code != 220)
...{
Close();
throw new FtpClientException(this._reply.Message.Substring(4));
}
// 登陆
SendCommand("USER " + _loginUser);
if(!(this._reply.Code == 331 || this._reply.Code == 230))
...{
//关闭连接
CloseSocketConnect();
throw new FtpClientException(this._reply.Message.Substring(4));
}
if(this._reply.Code != 230)
...{
SendCommand("PASS " + _loginPassword);
if(!(this._reply.Code == 230 || this._reply.Code == 202))
...{
//关闭连接
CloseSocketConnect();
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
_connected = true;
// 切换到目录
ChangeDirectory(_remotePath);
}
#endregion
EnsureConnected 确保已经连接到服务器#region EnsureConnected 确保已经连接到服务器
/**//// <summary>
/// 确保已经连接到服务器。
/// </summary>
protected void EnsureConnected()
...{
if(!_connected)
...{
Connect();
}
}
#endregion
Close 关闭连接#region Close 关闭连接
/**//// <summary>
/// 关闭连接
/// </summary>
public void Close()
...{
if(this._socketControl != null)
...{
SendCommand("QUIT");
}
CloseSocketConnect();
}
#endregion
TransferType 获取或设置传输模式#region TransferType 获取或设置传输模式
/**//// <summary>
/// 获取或设置传输模式
/// </summary>
public FtpTransferType TransferType
...{
get
...{
return this._transferType;
}
set
...{
if(value != this._transferType)
...{
if(value == FtpTransferType.Binary)
...{
SendCommand("TYPE I");
}
else
...{
SendCommand("TYPE A");
}
if(this._reply.Code != 200)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
else
...{
this._transferType = value;
}
}
}
}
#endregion
Dir 获得文件列表#region Dir 获得文件列表
/**//// <summary>
/// 获得文件列表。
/// </summary>
/// <returns></returns>
public string[] Dir()
...{
// 确保已连接上服务器
this.EnsureConnected();
//建立进行数据连接的socket
Socket socketData = CreateDataSocket();
//传送命令
SendCommand("NLST");
//分析应答代码
if(this._reply.Code == 550)
...{
return new string[0];
}
if(!(this._reply.Code == 150 || this._reply.Code == 125 || this._reply.Code == 226))
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
//获得结果
string message = string.Empty;
byte[] buffer = new byte[this._blockSize];
while(true)
...{
int iBytes = socketData.Receive(buffer, buffer.Length, 0);
message += Encoding.Default.GetString(buffer, 0, iBytes);
if(iBytes < buffer.Length)
...{
break;
}
}
string[] strsFileList = StringHelper.Split(StringHelper.TrimEnd(message, false, " "), false, " ");
socketData.Close();//数据socket关闭时也会有返回码
if(this._reply.Code != 226)
...{
ReadReply();
if(this._reply.Code != 226)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
return strsFileList;
}
#endregion
GetFileSize 获取文件大小#region GetFileSize 获取文件大小
/**//// <summary>
/// 获取文件大小
/// </summary>
/// <param name="fileName">文件名</param>
/// <returns>文件大小</returns>
private long GetFileSize(string fileName)
...{
// 确保已连接上服务器
this.EnsureConnected();
SendCommand("SIZE " + fileName);
long lSize = 0;
if(this._reply.Code == 213)
...{
lSize = Int64.Parse(this._reply.Message.Substring(4));
}
else
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
return lSize;
}
#endregion
Delete 删除#region Delete 删除
/**//// <summary>
/// 删除
/// </summary>
/// <param name="fileName">待删除文件名</param>
public void Delete(string fileName)
...{
// 确保已连接上服务器
this.EnsureConnected();
SendCommand("DELE " + fileName);
if(this._reply.Code != 250)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
#endregion
Rename 重命名文件(如果新文件名与已有文件重名,将覆盖已有文件)#region Rename 重命名文件(如果新文件名与已有文件重名,将覆盖已有文件)
/**//// <summary>
/// 重命名(如果新文件名与已有文件重名,将覆盖已有文件)。
/// </summary>
/// <param name="oldFileName">旧文件名</param>
/// <param name="newFileName">新文件名</param>
public void Rename(string oldFileName,string newFileName)
...{
// 确保已连接上服务器
this.EnsureConnected();
SendCommand("RNFR " + oldFileName);
if(this._reply.Code != 350)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
// 如果新文件名与原有文件重名,将覆盖原有文件
SendCommand("RNTO " + newFileName);
if(this._reply.Code != 250)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
#endregion
Download 下载文件#region Download 下载文件
/**//// <summary>
/// 下载一个文件
/// </summary>
/// <param name="remoteFileName">远程服务器上的文件路径名。</param>
/// <param name="localFileName">将文件保存到的本地路径名。</param>
public void DownloadFile(string remoteFileName, string localFileName)
...{
// 确保已连接上服务器
this.EnsureConnected();
this.TransferType = FtpTransferType.Binary;
if(localFileName == string.Empty)
...{
localFileName = remoteFileName;
}
string path = Path.GetDirectoryName(localFileName);
if(!Directory.Exists(path))
...{
Directory.CreateDirectory(path);
}
FileStream output = new FileStream(localFileName, FileMode.Create);
Socket socketData = CreateDataSocket();
SendCommand("RETR " + remoteFileName);
if(!(this._reply.Code == 150 || this._reply.Code == 125 || this._reply.Code == 226 || this._reply.Code == 250))
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
byte[] buffer = new byte[this._blockSize];
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(!(this._reply.Code == 226 || this._reply.Code == 250))
...{
ReadReply();
if(!(this._reply.Code == 226 || this._reply.Code == 250))
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
}
#endregion
Upload 上传文件#region Upload 上传文件
/**//// <summary>
/// 上传一批文件
/// </summary>
/// <param name="folder">本地目录(不得以结束)</param>
/// <param name="fileNameMask">文件名匹配字符(可以包含*和?)</param>
public void UploadDirectory(string folder,string fileNameMask)
...{
string[] strFiles = Directory.GetFiles(folder,fileNameMask);
foreach(string strFile in strFiles)
...{
//strFile是完整的文件名(包含路径)
UploadFile(strFile);
}
}
/**//// <summary>
/// 上传一个文件
/// </summary>
/// <param name="fileName">本地文件名</param>
public void UploadFile(string fileName)
...{
FileStream input = new FileStream(fileName, FileMode.Open);
this.UploadFile(input, Path.GetFileName(fileName));
input.Close();
}
/**//// <summary>
/// 上传一个文件
/// </summary>
/// <param name="input">流。</param>
/// <param name="fileName">要保存为的文件名。</param>
public void UploadFile(Stream input, string fileName)
...{
// 确保已连接上服务器
this.EnsureConnected();
Socket socketData = CreateDataSocket();
SendCommand("STOR " + fileName);
if(!(this._reply.Code == 125 || this._reply.Code == 150))
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
int iBytes = 0;
byte[] buffer = new byte[this._blockSize];
while ((iBytes = input.Read(buffer,0,buffer.Length)) > 0)
...{
socketData.Send(buffer, iBytes, 0);
}
if(socketData.Connected)
...{
socketData.Close();
}
if(!(this._reply.Code == 226 || this._reply.Code == 250))
...{
ReadReply();
if(!(this._reply.Code == 226 || this._reply.Code == 250))
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
}
#endregion
MakeDirectory 创建目录#region MakeDirectory 创建目录
/**//// <summary>
/// 创建目录
/// </summary>
/// <param name="directory">目录名</param>
public void MakeDirectory(string directory)
...{
// 确保已连接上服务器
this.EnsureConnected();
SendCommand("MKD " + directory);
if(this._reply.Code != 257)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
#endregion
RenameDirectory 删除目录#region RenameDirectory 删除目录
/**//// <summary>
/// 删除目录
/// </summary>
/// <param name="directory">目录名</param>
public void RenameDirectory(string directory)
...{
// 确保已连接上服务器
this.EnsureConnected();
SendCommand("RMD " + directory);
if(this._reply.Code != 250)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
}
#endregion
ChangeDirectory 改变当前目录#region ChangeDirectory 改变当前目录
/**//// <summary>
/// 改变当前目录
/// </summary>
/// <param name="directory">新的工作目录名</param>
public void ChangeDirectory(string directory)
...{
if(directory == null || directory == string.Empty || directory == ".")
...{
return;
}
// 确保已连接上服务器
this.EnsureConnected();
SendCommand("CWD " + directory);
if(this._reply.Code != 250)
...{
throw new FtpClientException(this._reply.Message.Substring(4));
}
this._remotePath = directory;
}
#endregion
}
FtpClientException.cs
/**//// <summary>
/// FtpClientException 的摘要说明。
/// </summary>
[Serializable]
public class FtpClientException : Exception
...{
构造方法#region 构造方法
public FtpClientException() : base()
...{
}
public FtpClientException(string message) : base(message)
...{
}
public FtpClientException(string message, Exception innerException) : base(message, innerException)
...{
}
protected FtpClientException(SerializationInfo info, StreamingContext context) : base(info, context)
...{
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
...{
base.GetObjectData (info, context);
}
#endregion
}
}
/// FtpClientException 的摘要说明。
/// </summary>
[Serializable]
public class FtpClientException : Exception
...{
构造方法#region 构造方法
public FtpClientException() : base()
...{
}
public FtpClientException(string message) : base(message)
...{
}
public FtpClientException(string message, Exception innerException) : base(message, innerException)
...{
}
protected FtpClientException(SerializationInfo info, StreamingContext context) : base(info, context)
...{
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
...{
base.GetObjectData (info, context);
}
#endregion
}
}
/**//// <summary>
/// FtpReply 的摘要说明。
/// </summary>
[Serializable]
public class FtpReply
...{
private int _code;
private string _message;
public FtpReply() : this(0, string.Empty)
...{
}
public FtpReply(int code, string message)
...{
this._code = code;
this._message = message;
}
public int Code
...{
get
...{
return this._code;
}
set
...{
this._code = value;
}
}
public string Message
...{
get
...{
return this._message;
}
set
...{
this._message = value;
}
}
}
/// FtpReply 的摘要说明。
/// </summary>
[Serializable]
public class FtpReply
...{
private int _code;
private string _message;
public FtpReply() : this(0, string.Empty)
...{
}
public FtpReply(int code, string message)
...{
this._code = code;
this._message = message;
}
public int Code
...{
get
...{
return this._code;
}
set
...{
this._code = value;
}
}
public string Message
...{
get
...{
return this._message;
}
set
...{
this._message = value;
}
}
}
/**//// <summary>
/// 传输模式。
/// </summary>
public enum FtpTransferType
...{
/**//// <summary>
/// 二进制模式。
/// </summary>
Binary,
/**//// <summary>
/// ASCII 模式。
/// </summary>
ASCII
};
/// 传输模式。
/// </summary>
public enum FtpTransferType
...{
/**//// <summary>
/// 二进制模式。
/// </summary>
Binary,
/**//// <summary>
/// ASCII 模式。
/// </summary>
ASCII
};