using System;
using System.Net;
using System.IO;
using System.Text;
using Barakbbn.Freeware.Net.Sockets;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 本程式需要使用額外的socket Barakbbn.Freeware.Net.Sockets
//
// 在程式碼上方加入 using Kelp.Opensource.FTPClient;
//
//
// P.S.下達ftp.Gep()後不會等抓完檔案才執行ftp.DisConnect()
// 檔案上傳及下載是透過事件觸發的
// "//檔案下載完成"搜尋此字串可以找到下載完成程式位置 事件為GetFileComplete
// "//檔案上傳完成"搜尋此字串可以找到上傳完成程式位置 事件為PutFileComplete
//
//
// FTPClient ftp = new FTPClient(); //宣告
//
// void Connect() //連線
// void DisConnect() //關閉連線
// void SetTransferType(TransferType ttType) //設定傳輸模式
// TransferType GetTransferType() //取得傳輸模式
// string[] Dir(string strMask) //取得文件列表 (文件名稱~可用*)
// void Delete(string strFileName) //刪除
// void Rename(string strOldFileName, string strNewFileName) //Rename(如果要改的名稱已存在將會覆蓋)
// void Get(string strRemoteFileName, string strLocalFileName) //下載文件
// void Put(string strFileName) //上傳文件
// void MkDir(string strDirName) //建立目錄
// void RmDir(string strDirName) //刪除目錄
// void ChDir(string strDirName) //改變目錄
//
// 來源:打奈特(.net)
// 修改:Kelp
//
// 修改記錄
// 2008/08/16
// 因為要使用.net framework2.0來完成FTP傳輸因此到處尋找利用socket進行FTP的程式碼
//
// 2008/08/18
// 原程式因為不斷掃描socket的緩衝區造成錯誤
// 將System.Net.Socket換成Barakbbn.Freeware.Net.Sockets
//
// 2008/08/19
// 修正不穩定因素
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* 下載範例程式
string[] sdata;
FTPClient ftp = new FTPClient(); 宣告
ftp.GetFileComplete += new EventHandler<LocalGFileAddressData>(ftp_GetFileComplete);
ftp.RemoteHost = "www.test.com"; 設定HOST
ftp.RemoteUser = "XXXXX"; 設定使用者帳號
ftp.RemotePass = "XXXXX"; 設定密碼
ftp.Connect(); 連線
ftp.ChDir("/www/"); 切換目錄
sdata = ftp.Dir(""); 取得目前位置內的資料夾及檔案名稱
ftp.Get("1.jpg", "D:\\1.jpg"); 下載1.jpg另存為F:\\1.jpg
ftp.DisConnect(); 結束連線
private void ftp_GetFileComplete(object sender, LocalGFileAddressData e)
{
MessageBox.Show("Finish " + e.LocalFileAddress);
}
*/
/* 上傳範例程式
string[] sdata;
FTPClient ftp = new FTPClient();
ftp.PutFileComplete += new EventHandler<LocalPFileAddressData>(ftp_PutFileComplete);
ftp.RemoteHost = "www.test.com"; 設定HOST
ftp.RemoteUser = "XXXXX"; 設定使用者帳號
ftp.RemotePass = "XXXXX"; 設定密碼
ftp.Connect(); 連線
ftp.Put("F:\\2.jpg"); 上傳檔案
private void ftp_PutFileComplete(object sender, LocalPFileAddressData e)
{
MessageBox.Show("Finish " + e.LocalFileAddress);
}
*/
namespace Kelp.Opensource.FTPClient
{
public class FTPClient
{
//////////////////////////記憶體宣告/////////////////////////////////////////////////////////////////////////////
#region Field
private bool Received = false; //當基礎連接socket回應結束後為true
private bool DataReceived = false; //當資料連接socket回應結束後為true
private string strMsg; //伺服器回應的訊息
private string strReply; //伺服器回應的答應碼
private int iReplyCode; //伺服器回應的答應碼
private TcpClient socketControl; //socket物件
private TransferType trType; //傳輸模式
byte[] buffer = new byte[0];
Encoding ASCII = Encoding.GetEncoding("big5"); //編碼方式
public enum TransferType { Binary, ASCII }; //傳輸模式
bool Downloading = false;
bool Uploading = false;
string SOutputAddress = ""; //檔案下載路徑
FileStream output; //檔案下載串流
string SInputAddress = ""; //檔案上傳路徑
FileStream input; //檔案上傳串流
int iBytes = 0; //檔案上傳指標
TcpClient socketGetData;
TcpClient socketSendData;
#endregion
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////Client/////////////////////////////////////////////////////////////////////////////////
#region FTPClient
public FTPClient()
{
strRemoteHost = "";
strRemotePath = "";
strRemoteUser = "";
strRemotePass = "";
strRemotePort = 21;
bConnected = false;
}
public FTPClient(string remoteHost, string remotePath, string remoteUser, string remotePass, int remotePort)
{
strRemoteHost = remoteHost;
strRemotePath = remotePath;
strRemoteUser = remoteUser;
strRemotePass = remotePass;
strRemotePort = remotePort;
Connect();
}
#endregion
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////屬性更改對應程式///////////////////////////////////////////////////////////////////////
#region Loging Property
private string strRemoteHost;
public string RemoteHost
{
get
{
return strRemoteHost;
}
set
{
strRemoteHost = value;
}
}
private int strRemotePort;
public int RemotePort
{
get
{
return strRemotePort;
}
set
{
strRemotePort = value;
}
}
private string strRemotePath;
public string RemotePath
{
get
{
return strRemotePath;
}
set
{
strRemotePath = value;
}
}
private string strRemoteUser;
public string RemoteUser
{
set
{
strRemoteUser = value;
}
}
private string strRemotePass;
public string RemotePass
{
set
{
strRemotePass = value;
}
}
private Boolean bConnected;
public bool Connected
{
get
{
return bConnected;
}
}
#endregion
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////事件///////////////////////////////////////////////////////////////////////////////////
public virtual event EventHandler<LocalGFileAddressData> GetFileComplete;
public virtual event EventHandler<LocalPFileAddressData> PutFileComplete;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////Class函數//////////////////////////////////////////////////////////////////////////////
public void Connect() //連線
{
socketControl = new TcpClient();
socketControl.Key = "Bace"; //設定Key為基礎連接socket
WireTcpClient(socketControl); //設定socket對應事件
try
{
Received = false;
socketControl.Connect(RemoteHost, Convert.ToInt32(strRemotePort));
}
catch (Exception)
{
throw new IOException("Couldn't connect to remote server");
}
// 取得回應
while (!Received) ;
if (iReplyCode != 220)
{
DisConnect();
throw new IOException(strReply.Substring(4));
}
// 登入
SendCommand("USER " + strRemoteUser);
if (!(iReplyCode == 331 || iReplyCode == 230))
{
CloseSocketConnect();//如果有錯誤就關閉連線
throw new IOException(strReply.Substring(4));
}
if (iReplyCode != 230)
{
SendCommand("PASS " + strRemotePass);
if (!(iReplyCode == 230 || iReplyCode == 202))
{
CloseSocketConnect();
throw new IOException(strReply.Substring(4));
}
}
bConnected = true;
// 切換到所選的目錄
ChDir(strRemotePath);
}
public void DisConnect() //關閉連線
{
if (socketControl != null)
{
SendCommand("QUIT");
}
if (socketSendData != null)
socketSendData.Close();
if (socketSendData != null)
socketSendData.Close();
CloseSocketConnect();
}
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;
}
}
public TransferType GetTransferType() //取得傳輸模式
{
return trType;
}
public string[] Dir(string strMask) //取得文件列表 (文件名稱~可用*)
{
if (!bConnected) // 先確定是否已連線
Connect();
DataReceived = false;
TcpClient socketData = CreateDataSocket(); //建立並行數據連接的socket
socketData.Key = "Data";
SendCommand("NLST " + strMask); //傳送指令
//分析回應碼
if (!(iReplyCode == 150 || iReplyCode == 125 || iReplyCode == 226))
{
throw new IOException(strReply.Substring(4));
}
//取得結果
while (!DataReceived) ; //等待傳輸結束
while (true)
{
int iBytes = socketData.Receive(buffer, 0, buffer.Length);
if (iBytes < 0)
break;
strMsg += ASCII.GetString(buffer, 0, iBytes);
}
char[] seperator = { '\n' };
string[] strsFileList = strMsg.Split(seperator);
for (int ILoop = 0; ILoop < strsFileList.GetUpperBound(0); ILoop++)
{
strsFileList[ILoop] = strsFileList[ILoop].TrimEnd();
}
socketData.Close();
return strsFileList;
}
public void Delete(string strFileName) //刪除
{
if (!bConnected)
Connect();
SendCommand("DELE " + strFileName);
if (iReplyCode != 250)
{
throw new IOException(strReply.Substring(4));
}
}
public void Rename(string strOldFileName, string strNewFileName) //Rename(如果要改的名稱已存在將會覆蓋)
{
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));
}
}
public void Get(string strRemoteFileName, string strLocalFileName) //下載文件
{
if (!bConnected)
Connect();
SetTransferType(TransferType.Binary);
if (strLocalFileName.Equals(""))
{
strLocalFileName = strRemoteFileName;
}
if (!File.Exists(strLocalFileName))
{
Stream st = File.Create(strLocalFileName);
st.Close();
}
SOutputAddress = strLocalFileName;
Downloading = true;
socketGetData = CreateDataSocket();
socketGetData.Key = "GetFile";
output = new FileStream(SOutputAddress, FileMode.Append);
SendCommand("RETR " + strRemoteFileName);
if (!(iReplyCode == 150 || iReplyCode == 125 || iReplyCode == 226 || iReplyCode == 250))
{
throw new IOException(strReply.Substring(4));
}
}
public void Put(string strFileName) //上傳文件
{
if (!bConnected)
Connect();
socketSendData = CreateDataSocket();
socketSendData.Key = "SendFile";
SendCommand("STOR " + Path.GetFileName(strFileName));
if (!(iReplyCode == 125 || iReplyCode == 150))
{
throw new IOException(strReply.Substring(4));
}
SInputAddress = strFileName;
Uploading = true;
input = new FileStream(strFileName, FileMode.Open);
iBytes = input.Read(buffer, 0, buffer.Length);
socketSendData.Send(buffer, 0, iBytes);
}
public void MkDir(string strDirName) //建立目錄
{
if (!bConnected)
{
Connect();
}
SendCommand("MKD " + strDirName);
if (iReplyCode != 257)
{
throw new IOException(strReply.Substring(4));
}
}
public void RmDir(string strDirName) //刪除目錄
{
if (!bConnected)
{
Connect();
}
SendCommand("RMD " + strDirName);
if (iReplyCode != 250)
{
throw new IOException(strReply.Substring(4));
}
}
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.strRemotePath = strDirName;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////內部函數///////////////////////////////////////////////////////////////////////////////
private TcpClient CreateDataSocket() //建立並行數據連接的Socket
{
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];
TcpClient s = new TcpClient();
WireTcpClient(s);
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;
}
private void CloseSocketConnect() //關閉Socket的連線(用在登入以前)
{
if (socketControl != null)
{
socketControl.Close();
socketControl = null;
}
bConnected = false;
}
private void SendCommand(String strCommand) //傳送命令並取得回應碼和最後一行的回應字串
{
Encoding e = Encoding.GetEncoding("big5");
Byte[] cmdBytes = e.GetBytes((strCommand + "\r\n").ToCharArray());
Received = false;
socketControl.Send(cmdBytes, 0, cmdBytes.Length);
if (strCommand != "QUIT")
while (!Received) ;
}
void WireTcpClient(TcpClient tcpClient) //建立Socket事件
{
tcpClient.Error += new EventHandler<SocketErrorEventArgs>(socket_Error);
tcpClient.SendCompleted += new EventHandler(socket_SendCompleted);
tcpClient.Disconnected += new EventHandler<SocketDisconnectedEventArgs>(socket_Disconnected);
tcpClient.DataAvailable += new EventHandler(socket_DataAvailable);
tcpClient.DataArrived += new EventHandler<SocketDataArrivedEventArgs>(socket_DataArrived);
tcpClient.Connected += new EventHandler(socket_Connected);
tcpClient.DataLost += new EventHandler<SocketDataArrivedEventArgs>(socket_DataLost);
tcpClient.SendProgressChanged += new EventHandler<SocketDataSendProgressChangedEventArgs>(socket_SendProgressChanged);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////Socket事件/////////////////////////////////////////////////////////////////////////////
void socket_DataLost(object sender, SocketDataArrivedEventArgs e)
{
}
void socket_DataArrived(object sender, SocketDataArrivedEventArgs e)
{
TcpClient Socket = sender as TcpClient;
int iBytes;
switch (Socket.Key)
{
case "Bace":
try
{
buffer = (byte[])ReDim(buffer, e.ReceivedSize);
iBytes = socketControl.Receive(buffer, 0, buffer.Length);
strReply = ASCII.GetString(buffer, 0, iBytes);
iReplyCode = Int32.Parse(strReply.Substring(0, 3));
}
catch
{
break;
}
if (iReplyCode == 226)
{
if (Downloading)
{
//檔案下載完成
Downloading = false;
output.Close();
LocalGFileAddressData g = new LocalGFileAddressData(SOutputAddress);
GetFileComplete(socketGetData, g);
}
else if (Uploading)
{
//檔案上傳完成
Uploading = false;
input.Close();
LocalPFileAddressData h = new LocalPFileAddressData(SInputAddress);
PutFileComplete(socketSendData, h);
}
}
Received = true;
break;
case "Data":
DataReceived = true;
break;
case "GetFile":
while (true)
{
iBytes = Socket.Receive(buffer, 0, buffer.Length);
if (iBytes < 0)
{
break;
}
output.Write(buffer, 0, iBytes);
}
break;
}
}
void socket_Connected(object sender, EventArgs e)
{
}
void socket_DataAvailable(object sender, EventArgs e)
{
}
void socket_Disconnected(object sender, SocketDisconnectedEventArgs e)
{
TcpClient Socket = sender as TcpClient;
switch (Socket.Key)
{
case "GetFile":
if (Socket.IsConnected)
{
Received = false;
Socket.Close();
}
break;
}
}
void socket_SendCompleted(object sender, EventArgs e)
{
TcpClient Socket = sender as TcpClient;
switch (Socket.Key)
{
case "SendFile":
iBytes = input.Read(buffer, 0, buffer.Length);
if (iBytes > 0)
Socket.Send(buffer, 0, iBytes);
else
{
if (Socket.IsConnected)
{
Received = false;
Socket.Close();
}
}
break;
}
}
void socket_Error(object sender, SocketErrorEventArgs e)
{
}
void socket_SendProgressChanged(object sender, SocketDataSendProgressChangedEventArgs e)
{
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////通用副程式區域/////////////////////////////////////////////////////////////////////////
public static Array ReDim(Array origArray, Int32 desiredSize) //重新宣告記憶體
{
Type t = origArray.GetType().GetElementType();
Array newArray = Array.CreateInstance(t, desiredSize);
Array.Copy(origArray, 0, newArray, 0, Math.Min(origArray.Length, desiredSize));
return newArray;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
public class LocalGFileAddressData : EventArgs
{
private string sLocalFileAddress;
public LocalGFileAddressData(string sLocalFileAddress)
{
this.sLocalFileAddress = sLocalFileAddress;
}
public string LocalFileAddress
{
get { return this.sLocalFileAddress; }
}
}
public class LocalPFileAddressData : EventArgs
{
private string sLocalFileAddress;
public LocalPFileAddressData(string sLocalFileAddress)
{
this.sLocalFileAddress = sLocalFileAddress;
}
public string LocalFileAddress
{
get { return this.sLocalFileAddress; }
}
}
}