用INDY9开发FTP客户端_05
-- 实现FTP断点续传和下载限速的例程
Roger Yang
1. TIdFTP的GET()方法实现断点续传的例程:
procedure TMainForm.DirectoryListBoxDblClick(Sender: TObject);
Var
Name{, Line}: String;
begin
// 如果未与FTP服务器连接,则退出
if not IdFTP1.Connected then exit;
//Line := DirectoryListBox.Items[DirectoryListBox.ItemIndex];
// 获得文件名
Name := IdFTP1.DirectoryListing.Items[DirectoryListBox.ItemIndex].FileName;
// 判断用户所选是文件还是目录
if IdFTP1.DirectoryListing.Items[DirectoryListBox.ItemIndex].ItemType = ditDirectory then begin
// Change directory
// 如果选择的是目录,则进入这个目录
SetFunctionButtons(false);
ChageDir(Name);
SetFunctionButtons(true);
end
else begin
try
// 服务器上的文件名作为保存对话框的默认文件名.
SaveDialog1.FileName := Name;
// 判断用户在保存对话框中是否单机了确认键.
if SaveDialog1.Execute then begin
SetFunctionButtons(false);
// 以二进制方式进行传输
IdFTP1.TransferType := ftBinary;
// 默认需要传输的字节数就是文件的大小
BytesToTransfer := IdFTP1.Size(Name);
// 判断文件是否存在
if FileExists(Name) then begin
// 如果文件已存在,就要询问用户是断点续传,还是覆盖.
case MessageDlg('File aready exists. Do you want to resume the download operation?',
mtConfirmation, mbYesNoCancel, 0) of
mrYes: begin
// 如果续传,那么传输的字节数就是文件总大小减去已传输的字节.
BytesToTransfer := BytesToTransfer - FileSizeByName(Name);
IdFTP1.Get(Name, SaveDialog1.FileName, false, true);
end;
mrNo: begin
IdFTP1.Get(Name, SaveDialog1.FileName, true);
end;
mrCancel: begin
exit;
end;
end;
end
else begin
// 文件不存在的情况
IdFTP1.Get(Name, SaveDialog1.FileName, false);
end;
end;
finally
SetFunctionButtons(true);
end;
end;
end;
2. 计算传输平均速度(单位:千字节)的例程:
procedure TMainForm.IdFTP1Work(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCount: Integer);
Var
S: String;
TotalTime: TDateTime; // 已传输时间
// RemainingTime: TDateTime;
H, M, Sec, MS: Word;
DLTime: Double;
begin
// 已传输时间等于当前时间减去传输开始时间
TotalTime := Now - STime;
// 把已传输时间拆开成时,分,秒,毫秒
DecodeTime(TotalTime, H, M, Sec, MS);
// 把已传输时间转换成以秒作为单位
Sec := Sec + M * 60 + H * 3600;
// 毫秒作为小数部分
DLTime := Sec + MS / 1000;
if DLTime > 0 then
AverageSpeed := {(AverageSpeed + }(AWorkCount / 1024) / DLTime{) / 2};
if AverageSpeed > 0 then begin
// 根据剩余字节数和平均速度,计算剩余时间
Sec := Trunc(((ProgressBar1.Max - AWorkCount) / 1024) / AverageSpeed);
S := Format('%2d:%2d:%2d', [Sec div 3600, (Sec div 60) mod 60, Sec mod 60]);
S := 'Time remaining ' + S;
end
else S := '';
S := FormatFloat('0.00 KB/s', AverageSpeed) + '; ' + S;
case AWorkMode of
wmRead: StatusBar1.Panels[1].Text := 'Download speed ' + S;
wmWrite: StatusBar1.Panels[1].Text := 'Uploade speed ' + S;
end;
if AbortTransfer then IdFTP1.Abort;
ProgressBar1.Position := AWorkCount;
AbortTransfer := false;
end;
3. 限速的例程(收集自INDY的DEMO)
function TMyDebugger.Recv(var ABuf; ALen: integer): integer;
var
LWaitTime: Cardinal;
LRecvTime: Cardinal;
begin
if FBytesPerSecond > 0 then begin
LRecvTime := IdGlobal.GetTickCount;
Result := inherited Recv(ABuf, ALen);
LRecvTime := GetTickDiff(LRecvTime, IdGlobal.GetTickCount); //得到按实际速度传输所需的毫秒数
LWaitTime := (Result * 1000) div FBytesPerSecond; //得到按规定速度传输所需的毫秒数
if LWaitTime > LRecvTime then begin
// 实际传输速度快于规定的传输速度,SLEEP相差的毫秒数
IdGlobal.Sleep(LWaitTime - LRecvTime);
end;
end else begin
// 实际传输速度慢于规定的传输速度,继续传输
Result := inherited Recv(ABuf, ALen);
end;
end;
4. 结合了计算平均速度和限速的例程:
void __fastcall TfrmDllMain::idFtpWork(TObject *Sender,
TWorkMode AWorkMode, const int AWorkCount)
{
char strDebugString[1024];
TDateTime dtTotalTime; // 当前文件的实际已传输时间
Word H,M,Sec,MSec;
double dTotalTime; // 当前文件的实际已传输时间,以秒作为单位
long lCurrentFileTotalTime; //当前文件的实际已传输时间,以毫秒作为单位
long lRatedTotalTime; // 当前文件按额定速度传输,已传输的时间,以毫秒作为单位.
/*
memset(strDebugString, 0, sizeof(strDebugString));
sprintf(strDebugString, "--- lStartTransferSize=[%d]", lStartTransferSize);
OutputDebugString(strDebugString);
*/
lTransferCompleted = lStartTransferSize + AWorkCount;
dtTotalTime = Now() - dtCurrentFileStartTime; // 当前文件的实际已传输时间等于当前时间减去当前文件传输开始时间
DecodeTime(dtTotalTime, H, M, Sec, MSec); // 把已传输时间拆开成时,分,秒,毫秒
Sec = Sec + M * 60 + H * 3600;
dTotalTime = Sec + MSec / 1000; // 毫秒作为小数部分
lCurrentFileTotalTime = Sec * 1000 + MSec; // 以毫秒作为单位
/*
memset(strDebugString, 0, sizeof(strDebugString));
sprintf(strDebugString, "--- AWorkCount=[%d]", AWorkCount);
OutputDebugString(strDebugString);
memset(strDebugString, 0, sizeof(strDebugString));
sprintf(strDebugString, "--- lTransferCompleted=[%d]", lTransferCompleted);
OutputDebugString(strDebugString);
*/
if(dTotalTime > 0)
{
dAverageSpeed = (dAverageSpeed + AWorkCount / dTotalTime) / 2;
}
// 对传输速度进行限制.如果额定速度为0,就不限速
if(lBytesPerSec != 0)
{
// 计算以额定速度来传输这些已传输的字节,需要多少时间(单位:毫秒)
lRatedTotalTime = ((lTransferCompleted - lStartTransferSize) / lBytesPerSec) * 1000;
/*
memset(strDebugString, 0, sizeof(strDebugString));
sprintf(strDebugString, "--- lRatedTotalTime=[%ld]", lRatedTotalTime);
OutputDebugString(strDebugString);
memset(strDebugString, 0, sizeof(strDebugString));
sprintf(strDebugString, "--- lCurrentFileTotalTime=[%ld]", lCurrentFileTotalTime);
OutputDebugString(strDebugString);
*/
// 判断实际下载速度是否小于额定下载速度
if(lRatedTotalTime > lCurrentFileTotalTime)
{
// 实际下载速度小于额定下载速度,Sleep相差的毫秒数
Sleep(lRatedTotalTime - lCurrentFileTotalTime);
}
}
/*
switch(AWorkMode)
{
case wmRead:
memset(strDebugString, 0, sizeof(strDebugString));
sprintf(strDebugString, "↓↓↓ 下载速度=[%ld]", (long)dAverageSpeed);
OutputDebugString(strDebugString);
break;
case wmWrite:
memset(strDebugString, 0, sizeof(strDebugString));
sprintf(strDebugString, "↑↑↑ 上传速度=[%ld]", (long)dAverageSpeed);
OutputDebugString(strDebugString);
break;
}
*/
if(blnAbortTransfer)
{
idFtp->Abort();
}
blnAbortTransfer = false;
}