FTP断点续传
一个类... 包括应用方法...
/*
*
* 功能说明:客户端FTP的操作类
*
* FTP Command:
*
* 1.用户名...................(USER)
* 2.口令.....................(PASS)
* 3.认证.....................(ACCT)
* 4.改变工作目录.............(CWD)
* 5.回到上一层目录...........(CDUP)
* 6.结构加载.................(SMNT)
* 7.重新登录.................(REIN)
* 8.退出登录.................(QUIT)
* 9.数据端口.................(PORT)
* 10.被动....................(PASV)
* 11.类型....................(TYPE)
* 12.文件结构................(STRU)
* 13.传输模式................(MODE)
* 14.获得文件................(RETR)
* 15.上传文件................(STOR)
* 16.唯一保存................(STOU)
* 17.附加....................(APPE)
* 18.分配....................(ALLO)
* 19.重新开始................(REST)
* 20.重命名..................(RNFR)
* 21.重命名为................(RNTO)
* 22.放弃....................(ABOR)
* 23.删除....................(DELE)
* 24.删除目录................(RMD)
* 25.创建目录................(MKD)
* 26.打印工作目录............(PWD)
* 27.列目录..................(LIST)
* 28.站点参数................(SITE)
* 29.系统....................(SYST)
* 30.状态....................(STAT)
* 31.帮助....................(HELP)
* 32.等待....................(NOOP)
* */
using System;
using System.Net;
using System.IO;
using System.Text;
using System.Collections;
using System.Net.Sockets;
/// <summary>
/// FtpUtil 的摘要说明。
/// </summary>
public class FtpUtil
{
private Int32 _RemotePort;
private String _RemoteHost;
private String _RemoteAccounts;
private String _RemotePassword;
private Boolean _Logined;
private String _RemotePath;
private Socket mClientSocket;
private String mRemoteMessage;
private Int32 mBytes;
private Byte[] mBuffer;
private Encoding ASCII;
private String mReply;
private Int32 mRetValue;
/// <summary>
/// 设置缓冲块的大小
/// </summary>
private static Int32 BLOCK_SIZE = 512;
public FtpUtil()
{
_RemotePort = 21;
_RemoteAccounts = "Anonymous";
_RemotePassword = "mack_zhu@hotmail.com";
_RemotePath = ".";
_Logined = false;
ASCII = Encoding.GetEncoding("gb2312");
mReply = String.Empty;
mBuffer = new Byte[BLOCK_SIZE];
mRemoteMessage = String.Empty;
}
/// <summary>
/// 目前状态是否登录成功了
/// </summary>
public Boolean Logined
{
get
{
return _Logined;
}
set
{
_Logined = value;
}
}
/// <summary>
/// 路径
/// </summary>
public String RemotePath
{
get
{
return _RemotePath;
}
set
{
_RemotePath = value;
}
}
/// <summary>
/// 地址
/// </summary>
public String RemoteHost
{
get
{
return _RemoteHost;
}
set
{
_RemoteHost = value;
}
}
/// <summary>
/// 端口
/// </summary>
public Int32 RemotePort
{
get
{
return _RemotePort;
}
set
{
_RemotePort = value;
}
}
/// <summary>
/// 帐号
/// </summary>
public String RemoteAccounts
{
get
{
return _RemoteAccounts;
}
set
{
_RemoteAccounts = value;
}
}
/// <summary>
/// 密码
/// </summary>
public String RemotePassword
{
get
{
return _RemotePassword;
}
set
{
_RemotePassword = value;
}
}
/// <summary>
/// 登录指定的地址
/// </summary>
public void Login()
{
mClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint mEPoint = new IPEndPoint(Dns.Resolve(this.RemoteHost).AddressList[0], this.RemotePort);
try
{
mClientSocket.Connect(mEPoint);
}
catch(Exception)
{
throw new IOException("无法联接到指定的IP地址");
}
ReadReply();
if(mRetValue != 220)
{
Close();
throw new IOException(mReply.Substring(4));
}
SendCommand("USER "+this.RemoteAccounts);
if(!(mRetValue == 331 || mRetValue == 230))
{
Cleanup();
throw new IOException(mReply.Substring(4));
}
if(mRetValue != 230)
{
SendCommand("PASS "+this.RemotePassword);
if(!(mRetValue == 230 || mRetValue == 202))
{
Cleanup();
throw new IOException(mReply.Substring(4));
}
}
Logined = true;
Chdir(RemotePath);
}
/// <summary>
/// 获取目录下的所有文件名
/// </summary>
/// <param name="iMask">文件格式,例如 *.* , *.txt , mack.*</param>
/// <returns></returns>
public ArrayList GetFileList(string iMask)
{
if(!Logined)
{
Login();
}
Socket iSocket = CreateDataSocket();
SendCommand("NLST " + iMask);
if(!(mRetValue == 150 || mRetValue == 125))
{
throw new IOException(mReply.Substring(4));
}
mRemoteMessage = String.Empty;
while(true)
{
Int32 iBytes = iSocket.Receive(mBuffer, mBuffer.Length, 0);
mRemoteMessage += ASCII.GetString(mBuffer, 0, iBytes);
if(iBytes < mBuffer.Length)
{
break;
}
}
Char[] iSeperator = {'\n'};
ArrayList FileNameList = new ArrayList();
foreach(String iFileName in mRemoteMessage.Split(iSeperator))
{
if(iFileName.Trim() != "")
{
FileNameList.Add(ChangeUTP8(iFileName));
}
}
iSocket.Close();
ReadReply();
if(mRetValue != 226)
{
throw new IOException(mReply.Substring(4));
}
return FileNameList;
}
/// <summary>
/// 获取服务器端的文件大小
/// </summary>
/// <param name="iFileName"></param>
/// <returns></returns>
public long GetFileSize(String iFileName)
{
if(!Logined)
{
Login();
}
SendCommand("SIZE " + iFileName);
long iSize=0;
if(mRetValue == 213)
{
iSize = Int64.Parse(mReply.Substring(4));
}
else
{
throw new IOException(mReply.Substring(4));
}
return iSize;
}
/// <summary>
/// 设置是否需要二进值传输模式
/// </summary>
/// <param name="iMode"></param>
public void SetBinaryMode(Boolean iMode)
{
if(iMode)
{
SendCommand("TYPE I");
}
else
{
SendCommand("TYPE A");
}
if (mRetValue != 200)
{
throw new IOException(mReply.Substring(4));
}
}
/// <summary>
/// 下载服务器端的文件
/// </summary>
/// <param name="iRemoteFileName">服务端的文件名</param>
/// <param name="iLocationFileName">保存在本地的文件名</param>
public void Download(String iRemoteFileName,String iLocationFileName)
{
Download(iRemoteFileName,iLocationFileName,false);
}
/// <summary>
/// 下载服务器端的文件
/// </summary>
/// <param name="iRemoteFileName">服务端的文件名</param>
/// <param name="iLocationFileName">保存在本地的文件名</param>
/// <param name="iResume">是否需要断点续传</param>
public void Download(String iRemoteFileName,String iLocationFileName,Boolean iResume)
{
if(!Logined)
{
Login();
}
SetBinaryMode(true);
/* Download File RemoteFileName From RemoteHost/RemotePath */
if (iLocationFileName.Trim() == String.Empty)
{
iLocationFileName = iRemoteFileName;
}
if(!File.Exists(iLocationFileName))
{
Stream iSt = File.Create(iLocationFileName);
iSt.Close();
}
FileStream iOutPut = new FileStream(iLocationFileName,FileMode.Open);
Socket iSocket = CreateDataSocket();
long iOffset = 0;
if(iResume)
{
iOffset = iOutPut.Length;
if(iOffset > 0 )
{
SendCommand("REST "+iOffset);
if(mRetValue != 350)
{
//throw new IOException(reply.Substring(4));
//有些FTP服务器端可能不支持断点续传的功能,从0位置开始传输
iOffset = 0;
}
}
if(iOffset > 0)
{
//如果支持断点续传的就从新设置流的起始值
long iNPos = iOutPut.Seek(iOffset,SeekOrigin.Begin);
/*Console.WriteLine("new pos="+npos);*/
}
}
SendCommand("RETR " + iRemoteFileName);
if(!(mRetValue == 150 || mRetValue == 125))
{
throw new IOException(mReply.Substring(4));
}
while(true)
{
mBytes = iSocket.Receive(mBuffer, mBuffer.Length, 0);
iOutPut.Write(mBuffer,0,mBytes);
if(mBytes <= 0)
{
break;
}
}
iOutPut.Close();
if (iSocket.Connected)
{
iSocket.Close();
}
ReadReply();
if(!(mRetValue == 226 || mRetValue == 250))
{
throw new IOException(mReply.Substring(4));
}
}
/// <summary>
/// 将文件上传到服务端器
/// </summary>
/// <param name="iFileName">本地的文件名</param>
public void Upload(String iFileName)
{
Upload(iFileName,false);
}
/// <summary>
/// 将文件上传到服务端器
/// </summary>
/// <param name="iFileName">本地的文件名</param>
/// <param name="iResume">是否需要断点续传</param>
public void Upload(String iFileName,Boolean iResume)
{
if(!Logined)
{
Login();
}
Socket iSocket = CreateDataSocket();
long iOffset=0;
if(iResume)
{
try
{
SetBinaryMode(true);
iOffset = GetFileSize(iFileName);
}
catch(Exception)
{
iOffset = 0;
}
}
if(iOffset > 0 )
{
SendCommand("REST " + iOffset);
if(mRetValue != 350)
{
//throw new IOException(reply.Substring(4));
//有些服务器端是不支持断点续传的
iOffset = 0;
}
}
SendCommand("STOR "+Path.GetFileName(iFileName));
if(!(mRetValue == 125 || mRetValue == 150))
{
throw new IOException(mReply.Substring(4));
}
FileStream iInput = new FileStream(iFileName,FileMode.Open);
if(iOffset != 0)
{
iInput.Seek(iOffset,SeekOrigin.Begin);
}
while((mBytes = iInput.Read(mBuffer,0,mBuffer.Length)) > 0)
{
iSocket.Send(mBuffer, mBytes, 0);
}
iInput.Close();
if (iSocket.Connected)
{
iSocket.Close();
}
ReadReply();
if(!(mRetValue == 226 || mRetValue == 250))
{
throw new IOException(mReply.Substring(4));
}
}
/// <summary>
/// 删除服务器端的文件名
/// </summary>
/// <param name="iFileName">文件名</param>
public void DeleteRemoteFile(String iFileName)
{
if(!Logined)
{
Login();
}
SendCommand("DELE "+iFileName);
if(mRetValue != 250)
{
throw new IOException(mReply.Substring(4));
}
}
/// <summary>
/// 对服务器端的文件重命名
/// </summary>
/// <param name="iOldFileName">现在的文件名</param>
/// <param name="iNewFileName">新的文件名</param>
public void RenameRemoteFile(String iOldFileName,String iNewFileName)
{
if(!Logined)
{
Login();
}
SendCommand("RNFR "+iOldFileName);
if(mRetValue != 350)
{
throw new IOException(mReply.Substring(4));
}
//提示:FTP服务器端的命令RNTO不会提示你新的文件名是否已重复,它将直接复盖原来那个相同的文件
SendCommand("RNTO "+iNewFileName);
if(mRetValue != 250)
{
throw new IOException(mReply.Substring(4));
}
}
/// <summary>
/// 创建一个服务器端的目录
/// </summary>
/// <param name="iDirName">目录名</param>
public void MakeDir(String iDirName)
{
if(!Logined)
{
Login();
}
SendCommand("MKD "+iDirName);
if(mRetValue != 250)
{
throw new IOException(mReply.Substring(4));
}
}
/// <summary>
/// 移除服务器端的目录
/// </summary>
/// <param name="iDirName">目录名</param>
public void RemoveDir(String iDirName)
{
if(!Logined)
{
Login();
}
SendCommand("RMD "+iDirName);
if(mRetValue != 250)
{
throw new IOException(mReply.Substring(4));
}
}
/// <summary>
/// 创建连接接口
/// </summary>
/// <returns></returns>
private Socket CreateDataSocket()
{
SendCommand("PASV");
if(mRetValue != 227)
{
throw new IOException(mReply.Substring(4));
}
Int32 index1 = mReply.IndexOf('(');
Int32 index2 = mReply.IndexOf(')');
String iPData = mReply.Substring(index1+1,index2-index1-1);
Int32[] iParts = new Int32[6];
Int32 iLen = iPData.Length;
String iBuf = String.Empty;
Int32 iPartCount = 0;
for (int i = 0; i < iLen && iPartCount <= 6; i++)
{
Char iCh = Char.Parse(iPData.Substring(i,1));
if (Char.IsDigit(iCh))
{
iBuf += iCh;
}
else if (iCh != ',')
{
throw new IOException("Malformed PASV Reply: " + mReply);
}
if (iCh == ',' || i+1 == iLen)
{
try
{
iParts[iPartCount++] = Int32.Parse(iBuf);
iBuf = String.Empty;
}
catch (Exception)
{
throw new IOException("Malformed PASV Reply: " + mReply);
}
}
}
string IPAddress = iParts[0] + "."+ iParts[1]+ "." + iParts[2] + "." + iParts[3];
int iPort = (iParts[4] << 8) + iParts[5];
Socket iSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
IPEndPoint iEPoint = new IPEndPoint(Dns.Resolve(IPAddress).AddressList[0], iPort);
try
{
iSocket.Connect(iEPoint);
}
catch(Exception)
{
throw new IOException("无法联接到指定的IP地址");
}
return iSocket;
}
/// <summary>
/// 更新当前操作目录
/// </summary>
/// <param name="iDirName">目录名</param>
public void Chdir(String iDirName)
{
if(iDirName.Equals("."))
{
return;
}
if(!Logined)
{
Login();
}
SendCommand("CWD "+iDirName);
if(mRetValue != 250)
{
throw new IOException(mReply.Substring(4));
}
this.RemotePath = iDirName;
}
void ReadReply()
{
mRemoteMessage = String.Empty;
mReply = ReadLine();
mRetValue = Int32.Parse(mReply.Substring(0,3));
}
String ReadLine()
{
while(true)
{
mBytes = mClientSocket.Receive(mBuffer, mBuffer.Length, 0);
mRemoteMessage += ASCII.GetString(mBuffer, 0, mBytes);
if(mBytes < mBuffer.Length)
{
break;
}
}
Char[] iSeperator = {'\n'};
String[] iMess = mRemoteMessage.Split(iSeperator);
if(iMess.Length > 2)
{
mRemoteMessage = iMess[iMess.Length-2];
}
else
{
mRemoteMessage = iMess[0];
}
if(!mRemoteMessage.Substring(3,1).Equals(" "))
{
return ReadLine();
}
/*
if(debug)
{
for(int i=0; i < iMess.Length-1 ; i++)
{
Console.WriteLine(iMess[i]);
}
}
*/
return mRemoteMessage;
}
/// <summary>
/// FTP中的退出命令
/// </summary>
public void Quit()
{
if(mClientSocket != null)
{
SendCommand("QUIT");
}
Cleanup();
}
/// <summary>
/// 关闭方法,和QUIT一样
/// </summary>
public void Close()
{
if(mClientSocket != null)
{
SendCommand("QUIT");
}
Cleanup();
}
/// <summary>
/// 断开联接,资源释放
/// </summary>
private void Cleanup()
{
if(mClientSocket!=null)
{
mClientSocket.Close();
mClientSocket = null;
}
Logined = false;
}
/// <summary>
/// 向FTP服务器端发送FTP指令
/// </summary>
/// <param name="iCommand">指令名称</param>
private void SendCommand(String iCommand)
{
Byte[] iCmdBytes = ASCII.GetBytes((iCommand+"\r\n").ToCharArray());
mClientSocket.Send(iCmdBytes, iCmdBytes.Length, 0);
ReadReply();
}
private string ChangeUTP8(string str)
{
byte[] btCN = System.Text.Encoding.Default.GetBytes(str);
string strUTF8 = System.Text.Encoding.UTF8.GetString(btCN);
return strUTF8;
}
}
下面是应用的方法...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Collections;
namespace UploadTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private string ErrorFile;
private string RootPath;
private string ErrorFileName;
private string User;
private string Pwd ;
private int Port;
private string FTPAddress;
private string RemoteFile;
private const int STime = 1000 * 10;
private const string strDe = "*.*";
#region 写文件
/// <summary>
/// 写文件
/// </summary>
/// <param name="Log"></param>
private void WriteLog(string filename, string filecontent)
{
string path = filename;
try
{
if (!File.Exists(path))
{
Stream ifile = File.Create(filename);
ifile.Close();
}
StreamWriter sw = new StreamWriter(filename, true);
sw.Write("\r\n" + filecontent);
sw.Flush();
sw.Close();
}
catch (IOException ex)
{
throw new IOException("WriteLog Has Error");
}
}
#endregion
#region 下载文件
/// <summary>
/// 下载文件
/// </summary>
/// <param name="PathList"></param>
private void DownFTPFile(ArrayList PathList)
{
//这里好像贴出来有错误... 大家一看便知哈.. FTP地址必须是IP地址
FtpUtil ftp = new FtpUtil();
ftp= User;
ftp = FTPAddress;
ftp= Pwd;
ftp= RemoteFile;
ftp= Port;
Char[] iSeperator = { '\r' };
http://www.cnblogs.com/z2002m/admin/ftp://ftp.login/();
if (http://www.cnblogs.com/z2002m/admin/ftp://ftp.logined/)
{
// ArrayList PathList = http://www.cnblogs.com/z2002m/admin/ftp://ftp.getfilelist(%22*.txt/");
if (PathList != null || PathList.Count > 0)
{
for (int i=0;i< PathList.Count;i++)
{
string FileName = PathList[i].ToString().Split(iSeperator)[0];
http://www.cnblogs.com/z2002m/admin/ftp://ftp.download(filename/, Path.Combine(RootPath, FileName), true);
http://www.cnblogs.com/z2002m/admin/ftp://ftp.close/();
}
}
}
else
{
WriteLog(ErrorFileName, string.Format(ErrorFileName, "{1}-{2}登录未成功 {0}", DateTime.Now.ToString(), http://www.cnblogs.com/z2002m/admin/ftp://ftp.remoteaccounts/, http://www.cnblogs.com/z2002m/admin/ftp://ftp.remotepassword/));
}
}
#endregion
#region 本地文件名与FTP上面的相比较
/// <summary>
/// 本地文件名与FTP上面的相比较,返回未下载的文件名
/// </summary>
/// <param name="FtpFiles">FTP文件。。多个</param>
/// <param name="LocalFiles">本地多个</param>
/// <returns></returns>
private ArrayList UndownFiles(ArrayList FtpFiles, string[] LocalFiles)
{
try
{
ArrayList NoDownFiles = new ArrayList();
ArrayList DownFiles = new ArrayList();
for (int i = 0; i < FtpFiles.Count; i++)
{
bool IsInclude = false;
if (LocalFiles.Length == 0 || LocalFiles == null)
{
DownFiles.Add(FtpFiles[i]);
continue;
}
for (int p = 0; p < LocalFiles.Length; p++)
{
if (FtpFiles[i].ToString() == LocalFiles[p])
{
IsInclude = true;
}
}
if ( !IsInclude )
{
DownFiles.Add(FtpFiles[i]);
}
}
for (int i = 0; i < DownFiles.Count; i++ )
{
NoDownFiles.Add(DownFiles[i]);
}
return NoDownFiles;
}
catch (Exception ex)
{
WriteLog(ErrorFileName,string.Format("Position:LookFiles . ErrorMessage: {0} . Time; {1}", ex.Message, DateTime.Now.ToString()));
return null;
}
}
#endregion
#region 查看一个目录下有多少个文件. 取出它的文件名..
/// <summary>
/// 查看一个目录下有多少个文件. 取出它的文件名.. 格式是 test.txt, cc.txt
/// </summary>
/// <param name="LocalPath"></param>
/// <returns></returns>
private string[] LookFiles(string LocalPath)
{
if (!Directory.Exists(LocalPath))
{
return null;
}
string[] Files = Directory.GetFiles(LocalPath);
string[] RerurnFile = Files;
if (Files != null && Files.Length>0)
{
for (int i = 0; i < Files.Length;i++ )
{
RerurnFile[i] = System.IO.Path.GetFileName(Files[i]);
}
}
return RerurnFile;
}
#endregion
#region 查看FTP下有多少个文件
/// <summary>
/// 查看FTP下有多少个文件
/// </summary>
/// <param name="dir"></param>
/// <returns></returns>
private ArrayList LookFTPfIle(string dir)
{
FtpUtil ftp = new FtpUtil();
http://www.cnblogs.com/z2002m/admin/ftp://ftp.remoteaccounts/ = User;
http://www.cnblogs.com/z2002m/admin/ftp://ftp.remotehost/ = FTPAddress;
http://www.cnblogs.com/z2002m/admin/ftp://ftp.remotepassword/ = Pwd;
http://www.cnblogs.com/z2002m/admin/ftp://ftp.remotepath/ = RemoteFile;
http://www.cnblogs.com/z2002m/admin/ftp://ftp.remoteport/ = Port;
Char[] iSeperator = { '\r' };
http://www.cnblogs.com/z2002m/admin/ftp://ftp.login/();
if (http://www.cnblogs.com/z2002m/admin/ftp://ftp.logined/)
{
ArrayList PathList = http://www.cnblogs.com/z2002m/admin/ftp://ftp.getfilelist(strde/);
for (int i = 0; i < PathList.Count; i++ )
{
string StrName = PathList[i].ToString().Split(iSeperator)[0];
PathList[i] = StrName;
}
http://www.cnblogs.com/z2002m/admin/ftp://ftp.close/();
return PathList;
}
else
{
WriteLog(ErrorFileName, string.Format(ErrorFileName, "{1}-{2}登录未成功 {0}", DateTime.Now.ToString(), http://www.cnblogs.com/z2002m/admin/ftp://ftp.remoteaccounts/, http://www.cnblogs.com/z2002m/admin/ftp://ftp.remotepassword/));
return null;
}
}
#endregion
#region 初始化
/// <summary>
/// 初始化
/// </summary>
private void LoadWinform()
{
ErrorFile = "Error.txt";
RootPath = Path.Combine(this.txtLocal.Text.Trim(), DateTime.Now.ToShortDateString().Replace("-", ""));
ErrorFileName = Path.Combine(this.txtError.Text.Trim(), ErrorFile);
User = this.txtUser.Text.Trim();
Pwd = this.txtPwd.Text.Trim();
int i=21;
bool IsOK= int.TryParse(this.txtPort.Text.Trim(),out i);
Port = IsOK ? i : 21;
FTPAddress = this.txtFTP.Text.Trim();
RemoteFile = this.txtRemote.Text.Trim();
this.timerClick.Interval = string.IsNullOrEmpty(this.txtInt.Text.Trim()) ? STime : int.Parse(this.txtInt.Text.Trim()) * 1000 * 3600 ;
}
#endregion
private void Form1_Load(object sender, EventArgs e)
{
this.timerClick.Stop();
}
private void timer1_Tick(object sender, EventArgs e)
{
try
{
if (!Directory.Exists(RootPath))
{
Directory.CreateDirectory(RootPath);
}
if (!File.Exists(ErrorFileName))
{
File.Create(ErrorFileName);
}
ArrayList list = LookFTPfIle("dd");
if (list != null)
{
// 取到未下载的文件..
string[] LocalPaths = LookFiles(RootPath);
ArrayList unPathList = UndownFiles(list, LocalPaths);
if (unPathList != null)
{
// 开始下载
DownFTPFile(unPathList);
}
}
}
catch(IOException ex)
{
WriteLog(ErrorFileName, ex.Message+"; time: "+DateTime.Now.ToString());
this.timerClick.Stop();
this.timerClick.Start();
}
}
private void btnSave_Click(object sender, EventArgs e)
{
this.timerClick.Stop();
LoadWinform();
this.timerClick.Start();
this.btnSave.Enabled = false;
}
private void btnStop_Click(object sender, EventArgs e)
{
this.timerClick.Stop();
this.btnSave.Enabled = true;
}
}
}