現在很多應用都需要上傳與下載大型文件
通過HTTP方式上傳大文件有一定的局限性
幸好FTP作為一個非常老而且非常成熟的協議可以高效穩定地完成大文件的上傳下載
並且可以完美地實現續傳
就拿我寫的電影服務器管理端程序來說
各種方案比較後
發現使用FTP可以完美地實現要求
但是要通過WinSocket庫實現FTP比較麻煩
幸好有Indy
一個包裝了大多數網絡協議的組件包
通過Indy程序設計人員可以通過阻塞方式進行編程可以拋開蹩腳的Winsocket異步模式采用與Unix系統上等同的阻塞編程模式進行這樣程序員就可以很好的處理程序的運行流程 下面我們進入到Indy的TIdFtp世界
控件的說明
使用Indy 中的TIdFtp控件可以實現通過FTP方式進行文件的上傳與下載
控件的具體使用
()控件屬性設置
默認屬性即可與服務器連接直接相關的屬性如主機名與用戶等在建立連接時進行設定需要設定的是RecvBufferSize和SendBufferSize兩屬性的值另外需要根據要傳輸的文件類型指定TransferType屬性而其他屬性按默認值設定即可
RecvBufferSize說明(默認值為字節)該屬性為整型變量用於指定連接所用的接受緩沖區大小
SendBufferSize說明(默認值為字節)該屬性也為整型變量用於指定連接所用的發送緩沖區的最大值該屬性在WriteStream方法中時可用於TStream指定要發送內容的塊數如果要發送的內容大於本屬性值則發送內容被分為多個塊發送
TransferType說明(默認值為ftBinary)該屬性為TIdFTPTransferType型變量用於指定傳輸內容是二進制文件(ftBinary )還是ASCII文件(ftASCII)應用程序需要使用二進制方式傳輸可執行文件壓縮文件和多媒體文件等而使用ASCII方式傳輸文本或超文本等文本型數據
()控件的事件響應
OnDisconnected響應TNotifyEvent類用於響應斷開(disconnect)事件當Disconnect方法被調用用來關閉Socket的時候觸發該響應應用程序必須指定該事件響應的過程以便對該斷開事件進行響應
OnStatus響應TIdStatusEvent類該響應在當前連接的狀態變化時被觸發該事件可由DoStatus方法觸發並提供給事件控制器屬性axStatus是當前連接的TIdStatus值aaArgs是一個可選的參數用於格式化函數它將用於構造表現當前連接狀態的文本消息
OnWork響應OnWord是TWorkEvent類事件的響應控制器OnWork用於關聯DoWork方法當緩沖區讀寫操作被調用時通知Indy組件和類它一般被用於控制進度條和視窗元素的更新AWorkMode表示當前操作的模式其中wmRead組件正在讀取數據wmWrite組件正在發送數據AWorkCount指示當前操作的字節計數
OnWorkBegin響應TWorkBeginEvent類當緩沖區讀寫操作初始化時該事件關聯BeginWork方法用於通知Indy組件和類它一般被用於控制進度條和視窗元素的更新AWorkMode表示當前操作的模式其中wmRead組件正在讀取數據wmWrite組件正在發送數據AWorkCountMax用於指示發送到OnWorkBegin事件的操作的最大字節數值代表未知
OnWorkEnd響應TWorkEndEvent類當緩沖區讀寫操作終止時該事件關聯EndWork方法用於通知Indy組件和類AWorkMode表示當前操作的模式其中wmRead組件正在讀取數據wmWrite組件正在發送數據AWorkCount表示操作的字節數
在事件響應中主要通過上述五種事件響應來控制程序在一般情況下在OnDisconnected中設定連接斷開的界面通知在OnStatus中設定當前操作的狀態在OnWork中實現傳輸中狀態條和其他參數的顯示而在OnWorkBegin和OnWorkEnd中分別設定開始傳輸和傳輸結束時的界面
()連接遠程服務器
完成了設定控件屬性和實現了控件的事件響應後就可以與服務器進行交互和傳輸了在連接之前應首先判斷IdFtp是否處於連接狀態如果Connected為False則通過界面控件或其他方式指定與服務器連接相關的一些TCP類屬性的設置分別是Host(主機名):StringUsername(用戶名):StringPassword(密碼):String也可以指定Port(端口)之後調用Connect方法連接遠程服務器如果無異常出現則連接成功建立
過程說明procedure Connect(AAutoLogin: boolean; const ATimeout: Integer);
該過程連接遠程FTP服務器
屬性AAutoLogin: boolean = True
連接後自動登錄該參數默認為True
const ATimeout: Integer = IdTimeoutDefault
超時時間單位秒
示例代碼
if IdFTPConnected then
try
if TransferrignData then IdFTPAbort;
IdFTPQuit;
finally
end
else
with IdFTP do try
Username := UserIDEditText;
Password := PasswordEditText;
Host := FtpServerEditText;
Connect;
ChangeDir(CurrentDirEditText);
finally
end;
()改變目錄
連接建立後可以改變當前FTP會話所在的目錄對於已知絕對路徑的情況下可以直接調用ChangeDir(const ADirName: string)方法來轉換目錄ADirName表示服務器上的文件系統目錄另外還可以調用ChangeDirUp回到上級目錄
如果未知路徑則可以通過List(ADest: TStrings; const ASpecifier: string; const ADetails: boolean)過程獲取遠程服務器的當前目錄結構此時必須設定TransferType為ftASCII(ASCII模式)其中ADest保存當前目錄結構可以在後續程序中調用該列表另外可以通過RetrieveCurrentDir方法獲取當前目錄名
過程說明
procedure ChangeDir(const ADirName: string);
改變工作目錄
屬性
const ADirName: string
遠程服務器的目錄描述
說明該過程實際上是實現了FTP CWD命令
procedure ChangeDirUp;
到上一級目錄
function RetrieveCurrentDir: string;
該函數返回當前目錄名
procedure List(ADest: TStrings; const ASpecifier: string; const ADetails: boolean);
列出當前目錄所有文件和子目錄及其屬性
參數
ADest: TStrings
保存文件及子目錄的返回結果
const ASpecifier: string =
文件掩碼用於列出符合條件的文件
const ADetails: boolean = true
包含文件和子目錄屬性
property DirectoryListing: TIdFTPListItems;
返回文件及目錄結構的列表
示例代碼
LS := TStringListCreate;
try
IdFTPChangeDir(DirName);
IdFTPTransferType := ftASCII;
CurrentDirEditText := IdFTPRetrieveCurrentDir;
DirectoryListBoxItemsClear;
IdFTPList(LS);
DirectoryListBoxItemsAssign(LS);
if DirectoryListBoxItemsCount > then
if AnsiPos(total DirectoryListBoxItems[]) > then DirectoryListBoxItemsDelete();
finally
LSFree;
end;
()實現下載
在下載之前必須查看DirectoryListingItems[sCurrFile]ItemType是否為文件如返回為ditDirectory則代表當前文件名為目錄不能下載必須導向到文件才可如為文件則可以進行下載在下載前設定傳輸的類型為二進制文件並且指定本地要保存的路徑通過調用Get方法實現文件的下載下載過程較慢可以考慮將其放到線程中實現
過程說明
procedure Get(const ASourceFile: string; ADest: TStream; AResume: Boolean);
overload;
procedure Get(const ASourceFile: string; const ADestFile: string;
const ACanOverwrite: boolean; AResume: Boolean);
overload;
從遠程服務器上獲取文件
屬性說明
const ASourceFile: string
遠程服務器上的源文件名
const ADestFile: string
保存到客戶機上的文件名
const ACanOverwrite: boolean = false
重寫同名文件
AResume: Boolean = false;
是否進行斷點續傳
示例代碼
SaveDialogFileName := Name;
if SaveDialogExecute then begin
SetFunctionButtons(false);
IdFTPTransferType := ftBinary;
BytesToTransfer := IdFTPSize(Name);
if FileExists(Name) then begin
case MessageDlg(File aready exists Do you want to resume the download operation?
mtConfirmation mbYesNoCancel ) of
mrYes: begin
BytesToTransfer := BytesToTransfer FileSizeByName(Name);
IdFTPGet(Name SaveDialogFileName false true);
end;
mrNo: begin
IdFTPGet(Name SaveDialogFileName true);
end;
mrCancel: begin
exit;
end;
end;
end
else begin
IdFTPGet(Name SaveDialogFileName false);
end;
()上傳的實現
上傳的實現與下載類似通過put方法即可
過程說明
procedure Put(const ASource: TStream; const ADestFile: string; const AAppend: boolean); overload;
procedure Put(const ASourceFile: string; const ADestFile: string; const AAppend: boolean); overload;
上傳文件至服務器
屬性說明
const ASourceFile: string
將要被上傳的文件
const ADestFile: string =
服務器上的目標文件名
const AAppend: boolean = false
是否繼續上傳
代碼示例
if IdFTPConnected then begin
if UploadOpenDialogExecute then try
IdFTPTransferType := ftBinary;
IdFTPPut(UploadOpenDialogFileName ExtractFileName(UploadOpenDialogFileName));
//可以在此添加改變目錄的代碼;
finally
//完成清除工作
end;
end;
()刪除的實現
刪除文件使用Delete方法該方法刪除指定的文件刪除對象必須為文件如果要刪除目錄則使用RemoveDir方法
過程說明
procedure Delete(const AFilename: string);
刪除文件
procedure RemoveDir(const ADirName: string);
刪除文件夾根據不同的服務器刪除文件夾有不同的要求有些服務器不允許刪除非空文件夾程序員需要添加清空目錄的代碼
上述兩個過程的參數均為目標名稱
代碼示例
if not IdFTPConnected then exit;
Name := IdFTPDirectoryListingItems[iCurrSelect]FileName;
if IdFTPDirectoryListingItems[iCurrSelect]ItemType = ditDirectory then try
idftpRemoveDir(Name);
finally
end
else
try
idftpDelete(Name);
finally
end;
()後退的實現
後退在實際上是目錄操作的一種可以簡單的改變當前目錄為來實現也可以通過回到上級目錄來實現
()取消的實現
在IdFtp的傳輸過程中可以隨時使用abort方法取消當前操作可以的OnWork事件的實現中來確定何時取消操作
代碼示例
//取消按鈕的OnClick響應
procedure TMainFormAbortButtonClick(Sender: TObject);
begin
AbortTransfer := true;
end;
//IdFTP的OnWork事件響應
procedure TMainFormIdFTPWork(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCount: Integer);
begin
if AbortTransfer then IdFTPAbort;
AbortTransfer := false;
end;
()斷點續傳的實現
斷點續傳就是在上傳或下載過程開始時判斷已經傳輸過的文件是否上傳輸完畢如果傳輸沒有成功完成則在上次中斷處繼續進行傳輸工作實現該功能需要兩個重要的操作首先是判斷文件的大小信息其次是在傳輸過程Get和Put中指定上傳的行為
判斷服務器上文件的大小使用函數Size(FileName)在下載過程中比較本地文件和遠程文件的信息然後在Get中指定AResume := True即可而上傳也一樣指定Put的AAppend := True就可以了
在前面我們講過Indy的網絡操作大部分是阻塞模式的TIdFtp也不例外這樣在上述各個操作運行過程的時候用戶界面被暫時凍結必須時要等待調用返回才能繼續用戶操作界面響應所以在實際編程中需要使用多線程的方式來保證戶界面的響應Windows系統可以使用CreateThread系統調用來創建線程但是在使用的時候需要開發人員做很多額外的工作來保證線程的同步等問題而Indy中也包含了實現多線程的控件TIdThreadComponent相對比之下該控件實現多線程時更加方便也更容易控制
From:http://tw.wingwit.com/Article/program/Delphi/201311/25075.html