该代码源自互联网,并经过修改:
解决了中文文件名会出现乱码的情况;
改善了上传不稳定的问题(但没有从根本上解决,目前只知道在接收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
}

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
}
}

FtpReply.cs

/**//// <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;
}
}
}

FtpTransferType.cs

/**//// <summary>
/// 传输模式。
/// </summary>
public enum FtpTransferType

...{

/**//// <summary>
/// 二进制模式。
/// </summary>
Binary,

/**//// <summary>
/// ASCII 模式。
/// </summary>
ASCII
};