| |
二、通过指令细分析 通过Serv-U的"监视用户"功能,我们可以看到服务器与客户端FTP指令。 当使用UploadRestore()时,客户端发送指令:"REST ****"(*号为函数中指定的Position参数值),服务器响应为:"350 Restarting at ****. Send STORE or RE TRIEVE "然后函数开始上传数据。 当使用UploadAppend()时,客户端发送指令:"APPE ****"(*号为函数中指定的RemoteFile参数值),服务器响应为:"150 Opening BINARY mode data connection for ****."。 如果我们使用CuteFTP进行续传的话,我们可以看到客户端发送的指令首先为"REST ****"(*号为断点所在位置),待服务器响应后,客户端发送的指令变为"APPE ****"(*号为服务器上的文件名)。 通过分析以上的指令,我们可以看到,CuteFTP完成续传主要还是调用Append的方法,"REST"指令仅是获得断点位置,但是由于其内部机制控制了续传从断点处开始而不是从文件起始处开始,而UploadRestore()和UploadAppend()并不能控制续传仅从断点处开始,从而导致续传后的文件无法使用。 三、彻底解决问题 通过前面的分析,我们知道了,NMFTP无法实现真正的续传关键在于无法从断点处传送本地文件。既然如此,我们只要在续传命令前从断点处截取源文件作为临时文件,然后续传这个临时文件即可解决续传问题。事实证明这个方法是有效的。 下面以在BCB6中调用UploadAppend()函数完成续传为例给出源代码,读者也可以使用UploadRestore()函数完成。程序用到的控件见表1,全局变量见表2。
void __fastcall TForm1::btn_Upload Click(TObject *Sender) { int handle=FileOpen(edt1->Text,0); int size=FileSeek(handle,0,2);//获取源文件大小,以便续传前对比 nftp1->List();//通过列表,判定服务器上是否已经有了同名文件及其大小,以便决定是否续传 if (!edt1->Text.IsEmpty()) { if (filesize!=size)//只有当文件大小不同的时候才能上传或续传 { if (filesize!=-1) { ShowMessage("上次已经传送:"+Int ToStr(filesize)+"字节"); //====建立续传临时文件=====// String tempfile=edt1->Text+".tmp"; int iToFileHandle=FileCreate(tempfile);//打开文件 FileSeek(handle,filesize,0);//将文件指针定位在断点处 char *pszBuffer=new char[2049];//缓冲区 int iBytesRead,iBytesWritten;//读写缓冲变量 do { iBytesRead=FileRead(handle,pszBuffer,2048); iBytesWritten=FileWrite(iToFileHandle,pszBuffer,iBytesRead); }while(iBytesRead==2048); delete[] pszBuffer; FileClose(handle); FileClose(iToFileHandle); //========进行续传=======// try {nftp1->UploadAppend(tempfile,Extract FileName(edt1->Text)); } catch (...) //使用"try...catch"是为了避免在上传过程中点击"断开"后出现异常 { ShowMessage("上传操作已被终止!"); } DeleteFile(tempfile);//删除临时文件 } else { ShowMessage("现在开始全新上传!"); FileClose(handle); //关闭文件,否则上传时会因为打不开文件而出错 try { nftp1->Upload(edt1->Text,ExtractFile Name(edt1->Text)); } catch (...) { ShowMessage("上传操作已被终止!"); } } } else ShowMessage("文件已经在服务器上存在,并已经传送完毕!"); nftp1->Disconnect();//传送完毕以后断开连接 i=0; //计数变量清零 filesize=-1; //变量恢复初始值 } else ShowMessage("请选择需要上传的文件!"); } //------------------ void__fastcall TForm1::btn_OpenFile Click(TObject *Sender) { if (Open1->Execute()) edt1->Text=Open1->FileName; } //------------------ void __fastcall TForm1::FormCreate(TObject *Sender) { //====变量初始化========// filesize=-1; i=0; } //------------------ (void __fastcall TForm1::btn_ExitClick(TObject *Sender) { Close(); } //------------------ (void __fastcall TForm1::nftp1Packet Sent(TObject *Sender) { lbl2->Caption=nftp1->BytesSent+file size; } //------------------ void __fastcall TForm1::nftp1ListItem(AnsiString Listing) { if (nftp1->FTPDirectoryList->name->Strings[i]==ExtractFileName(edt1->Text)) filesize=StrToInt(nftp1->FTPDirecto ryList->Size->Strings[i]); else i++; } //------------------ void__fastcall TForm1::btn_Con nectClick(TObject *Sender) { nftp1->Host=lbledt_Host->Text; nftp1->UserID=lbledt_ID->Text; nftp1->Password=lbledt_Pass->Text; nftp1->Connect(); nftp1->Mode(MODE_BYTE); } //------------------ void __fastcall TForm1::btn_DisConnect Click(TObject *Sender) { nftp1->Disconnect(); i=0; //计数变量清零 filesize=-1; //变量恢复初始值 } //------------------ void __fastcall TForm1::nftp1Authenti cationFailed(bool &Handled) { ShowMessage("用户名或密码不正确"); } //------------------ void __fastcall TForm1::nftp1Success(TCmdType Trans_Type) { switch(Trans_Type) { case cmdUpload: ShowMessage("上传完毕"); break; case cmdAppend: ShowMessage("续传完毕"); break; } } |
|