金融网站开发公司,seo资讯,网站建设不包括哪个阶段,wordpress用什么系统好2019独角兽企业重金招聘Python工程师标准 delphi中利用Indy的TIdFtp控件实现FTP协议版权声明#xff1a;本文为博主原创文章#xff0c;未经博主允许不得转载。现在很多应用都需要上传与下载大型文件#xff0c;通过HTTP方式上传大文件有一定的局限性。幸好FT… 2019独角兽企业重金招聘Python工程师标准 delphi中利用Indy的TIdFtp控件实现FTP协议版权声明本文为博主原创文章未经博主允许不得转载。现在很多应用都需要上传与下载大型文件通过HTTP方式上传大文件有一定的局限性。幸好FTP作为一个非常老而且非常成熟的协议可以高效稳定地完 成大文件的上传下载并且可以完美地实现续传。就拿我写的电影服务器管理端程序来说各种方案比较后发现使用FTP可以完美地实现要求。但是要通过 WinSocket库实现FTP比较麻烦幸好有Indy--一个包装了大多数网络协议的组件包。通过Indy程序设计人员可以通过阻塞方式进行编程可以抛开蹩脚的Winsocket异步模式采用与Unix系统上等同的阻塞编程模式进行。这样程序员就可以很好的处理程序的运行流程。下面我们进入到Indy的TIdFtp世界。1.控件的说明使用Indy 9中的TIdFtp控件可以实现通过FTP方式进行文件的上传与下载。2.控件的具体使用1控件属性设置默 认属性即可与服务器连接直接相关的属性如主机名与用户等在建立连接时进行设定。需要设定的是RecvBufferSize和 SendBufferSize两属性的值。另外需要根据要传输的文件类型指定TransferType属性而其他属性按默认值设定即可。RecvBufferSize说明默认值为8192字节该属性为整型变量用于指定连接所用的接受缓冲区大小。SendBufferSize 说明默认值为32768字节该属性也为整型变量用于指定连接所用的发送缓冲区的最大值。该属性在WriteStream方法中时可用于 TStream指定要发送内容的块数。如果要发送的内容大于本属性值则发送内容被分为多个块发送。TransferType说明默认 值为ftBinary该属性为TIdFTPTransferType型变量。用于指定传输内容是二进制文件ftBinary 还是ASCII文件ftASCII。应用程序需要使用二进制方式传输可执行文件、压缩文件和多媒体文件等而使用ASCII方式传输文本或超文本等 文本型数据。2控件的事件响应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事 件的操作的最大字节数0值代表未知。OnWorkEnd响应TWorkEndEvent类。当缓冲区读写操作终止时该事件 关联EndWork方法用于通知Indy组件和类。AWorkMode表示当前操作的模式其中wmRead-组件正在读取数据wmWrite-组件 正在发送数据。AWorkCount表示操作的字节数。在事件响应中主要通过上述五种事件响应来控制程序。在一般情况下在 OnDisconnected中设定连接断开的界面通知在OnStatus中设定当前操作的状态在OnWork中实现传输中状态条和其他参数的显示 而在OnWorkBegin和OnWorkEnd中分别设定开始传输和传输结束时的界面。3连接远程服务器完 成了设定控件属性和实现了控件的事件响应后就可以与服务器进行交互和传输了。在连接之前应首先判断IdFtp是否处于连接状态如果 Connected为False则通过界面控件或其他方式指定与服务器连接相关的一些TCP类属性的设置分别是Host主机名:String、 Username用户名:String、Password密码:String也可以指定Port(端口)。之后调用Connect方法连接远程 服务器如果无异常出现则连接成功建立。过程说明procedure Connect(AAutoLogin: boolean; const ATimeout: Integer);该过程连接远程FTP服务器属性AAutoLogin: boolean True连接后自动登录该参数默认为True。const ATimeout: Integer IdTimeoutDefault超时时间单位秒。示例代码if IdFTP1.Connected then tryif TransferrignData then IdFTP1.Abort;IdFTP1.Quit;finallyendelse with IdFTP1 do tryUsername : UserIDEdit.Text;Password : PasswordEdit.Text;Host : FtpServerEdit.Text;Connect;ChangeDir(CurrentDirEdit.Text);finallyend;4改变目录连 接建立后可以改变当前FTP会话所在的目录。对于已知绝对路径的情况下可以直接调用ChangeDir(const ADirName: string)方法来转换目录ADirName表示服务器上的文件系统目录另外还可以调用ChangeDirUp回到上级目录。如 果未知路径则可以通过List(ADest: TStrings; const ASpecifier: string; const ADetails: boolean)过程获取远程服务器的当前目录结构此时必须设定TransferType为ftASCIIASCII模式其中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 : TStringList.Create;tryIdFTP1.ChangeDir(DirName);IdFTP1.TransferType : ftASCII;CurrentDirEdit.Text : IdFTP1.RetrieveCurrentDir;DirectoryListBox.Items.Clear;IdFTP1.List(LS);DirectoryListBox.Items.Assign(LS);if DirectoryListBox.Items.Count 0 thenif AnsiPos(total, DirectoryListBox.Items[0]) 0 then DirectoryListBox.Items.Delete(0);finallyLS.Free;end;5下载的实现在 下载之前必须查看DirectoryListing.Items[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是否进行断点续传示例代码SaveDialog1.FileName : Name;if SaveDialog1.Execute then beginSetFunctionButtons(false);IdFTP1.TransferType : ftBinary;BytesToTransfer : IdFTP1.Size(Name);if FileExists(Name) then begincase MessageDlg(File aready exists. Do you want to resume the download operation?,mtConfirmation, mbYesNoCancel, 0) ofmrYes: beginBytesToTransfer : BytesToTransfer - FileSizeByName(Name);IdFTP1.Get(Name, SaveDialog1.FileName, false, true);end;mrNo: beginIdFTP1.Get(Name, SaveDialog1.FileName, true);end;mrCancel: beginexit;end;end;endelse beginIdFTP1.Get(Name, SaveDialog1.FileName, false);end;6上传的实现上传的实现与下载类似通过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 IdFTP1.Connected then beginif UploadOpenDialog1.Execute then tryIdFTP1.TransferType : ftBinary;IdFTP1.Put(UploadOpenDialog1.FileName, ExtractFileName(UploadOpenDialog1.FileName));//可以在此添加改变目录的代码;finally//完成清除工作end;end;7删除的实现删除文件使用Delete方法该方法删除指定的文件删除对象必须为文件。如果要删除目录则使用RemoveDir方法。过程说明procedure Delete(const AFilename: string);删除文件procedure RemoveDir(const ADirName: string);删除文件夹根据不同的服务器删除文件夹有不同的要求。有些服务器不允许删除非空文件夹程序员需要添加清空目录的代码。上述两个过程的参数均为目标名称代码示例if not IdFTP1.Connected then exit;Name : IdFTP1.DirectoryListing.Items[iCurrSelect].FileName;if IdFTP1.DirectoryListing.Items[iCurrSelect].ItemType ditDirectory then tryidftp1.RemoveDir(Name);finallyendelsetryidftp1.Delete(Name);finallyend;8后退的实现后退在实际上是目录操作的一种可以简单的改变当前目录为..来实现也可以通过回到上级目录来实现。9取消的实现在IdFtp的传输过程中可以随时使用abort方法取消当前操作。可以的OnWork事件的实现中来确定何时取消操作。代码示例//取消按钮的OnClick响应procedure TMainForm.AbortButtonClick(Sender: TObject);beginAbortTransfer : true;end;//IdFTP的OnWork事件响应procedure TMainForm.IdFTP1Work(Sender: TObject; AWorkMode: TWorkMode;const AWorkCount: Integer);begin... if AbortTransfer then IdFTP1.Abort;AbortTransfer : false;end;10断点续传的实现断点续传就是在上传或下载过程开始时判断已经传输过的文件是否上传输完毕如果传输没有成功完成则在上次中断处继续进行传输工作。实现该功能需要两个重要的操作首先是判断文件的大小信息其次是在传输过程Get和Put中指定上传的行为。判断服务器上文件的大小使用函数Size(FileName)。在下载过程中比较本地文件和远程文件的信息然后在Get中指定AResume : True即可。而上传也一样指定Put的AAppend : True就可以了。 在 前面我们讲过Indy的网络操作大部分是阻塞模式的TIdFtp也不例外。这样在上述各个操作运行过程的时候用户界面被暂时冻结必须要等待调用返回 才能继续用户操作界面响应。所以在实际编程中需要使用多线程的方式来保证户界面的响应。Windows系统可以使用CreateThread系统调用来 创建线程但是在使用的时候需要开发人员做很多额外的工作来保证线程的同步等问题。而Indy中也包含了实现多线程的控件 TIdThreadComponent相对比之下该控件实现多线程时更加方便也更容易控制。我将在后续的文章里为大家介绍 TIdThreadCOmponent的使用方法。下载的一些代码接下来我们来写最主要的代码也就是下载部分了首先来看HTTP协议的
procedure TForm1.HttpDownLoad(aURL, aFile: string; bResume: Boolean);
vartStream: TFileStream;
begin //Http方式下载if FileExists(aFile) then //如果文件已经存在tStream : TFileStream.Create(aFile, fmOpenWrite) elsetStream : TFileStream.Create(aFile, fmCreate);if bResume then //续传方式beginIdHTTP1.Request.ContentRangeStart : tStream.Size - 1;tStream.Position : tStream.Size - 1; //移动到最后继续下载IdHTTP1.Head(aURL);IdHTTP1.Request.ContentRangeEnd : IdHTTP1.Response.ContentLength;end else //覆盖或新建方式beginIdHTTP1.Request.ContentRangeStart : 0;end;tryIdHTTP1.Get(aURL, tStream); //开始下载finallytStream.Free;end;
end;
这里我们同样使用IdHTTP的Get过程函数的aURL是网址aFile是保存的文件名bResume确定是否续传需要注意的就是续传方式时的代码IdHTTP1.Request.ContentRangeStart : tStream.Size - 1;tStream.Position : tStream.Size - 1; //移动到最后继续下载IdHTTP1.Head(aURL);IdHTTP1.Request.ContentRangeEnd : IdHTTP1.Response.ContentLength;
第 一行我们将下载开始位置设置为读入文件流的末尾也就是设置为已经下载了的那部分文件的大小第二行我们将文件流本身也指向自己的末尾第三行我们通过 Head过程得到网址头信息在第四行将头信息的文件总大小赋值给下载的结束的位置至于这里为什么第一行和第二行代码最后都要-1我当时没有加-1的 时候在续下载一个完整的已经下载的文件的时候总是提示错误最后跟踪IdHTTP的代码发现他在处理下载范围的时候如果开始的位置和结束位置一样时会引发 将浮点数转为整数的错误因而这里加上-1防止这种错误发生另外一种处理方法就是比较如果开始位置等于结束位置就退出也是可以的。
再来看FTP协议的下载过程
procedure TForm1.FtpDownLoad(aURL, aFile: string; bResume: Boolean);
vartStream: TFileStream;sName, sPass, sHost, sPort, sDir: string;
begin //ftp方式下载if FileExists(aFile) then //建立文件流tStream : TFileStream.Create(aFile, fmOpenWrite) elsetStream : TFileStream.Create(aFile, fmCreate);GetFTPParams(aURL, sName, sPass, sHost, sPort, sDir);with IdFTP1 dotryif Connected then Disconnect; //重新连接Username : sName;Password : sPass;Host : sHost;Port : StrToInt(sPort);Connect;exceptexit;end;IdFTP1.ChangeDir(sDir); //改变目录BytesToTransfer : IdFTP1.Size(aFile);tryif bResume then //续传begintStream.Position : tStream.Size;IdFTP1.Get(aFile, tStream, True);end elsebeginIdFTP1.Get(aFile, tStream, False);end;finallytStream.Free;end;
end;
这 个过程中我们就用到了GetFTPParams()函数将网址的用户名、密码、主机地址、端口、路径等信息分离出来IdFTP利用这些信息登陆服务器并 到相应目录最后利用Get()过程就很容易实现下载了它的续传就比HTTP协议要简单很多因为IdFTP的Get()本身就支持续传。
这里我简单穿插一点的内容一个服务器是否支持断点续传我们可以通过发送REST 1FTP指令来检测如果返回350则表示支持。
最后我们根据网址来确定使用什么协议来下载
function TForm1.GetProt(aURL: string): Byte;
begin //检测下载的地址是http还是ftpResult : 0;if Pos(http, LowerCase(aURL)) 1 thenResult : 1; //http协议if Pos(ftp, LowerCase(aURL)) 1 thenResult : 2; //ftp协议
end;
这个函数根据网址返回整数供我们使用。
procedure TForm1.MyDownLoad(aURL, aFile: string; bResume: Boolean);
begincase GetProt(aURL) of0: ShowMessage(不可识别的地址);1: HttpDownLoad(aURL, aFile, bResume);2: FtpDownLoad(aURL, aFile, bResume);end;
end;
这个过程就利用GetProt()函数返回的整数执行相应的协议下载过程。
2 接下来看看每个按钮的代码有了上面的函数按钮的代码就简单多了
下载按钮
procedure TForm1.Button1Click(Sender: TObject);
varaURL, aFile: string;
beginaURL : ComboBox1.Text; //下载地址例如http://www.2ccc.com/update/demo.exe;aFile : GetURLFileName(aURL); //得到文件名例如demo.exeif FileExists(aFile) thenbegincase MessageDlg(文件已经存在是否续传, mtConfirmation, mbYesNoCancel, 0) ofmrYes: MyDownLoad(aURL, aFile, True); //续传mrNo: MyDownLoad(aURL, aFile, False); //覆盖mrCancel: Exit; //取消end;end else MyDownLoad(aURL, aFile, False); //建立新文件下载
end;
MessageDlg()函数弹出一个对话框让用户选择续传、覆盖还是取消下载。
中断按钮
procedure TForm1.Button2Click(Sender: TObject);
beginAbortTransfer : True;
end;
前 面忘了介绍所以这里大家看不明白AbortTransfer是我们定义的一个私有变量在开始下载的时候将它设为False下载的过程中随时监测这 个变量一旦变为True就利用IdHTTP的Disconnect和IdFTP1的Abort方法中断下载如果没有下载完就中断那程序的目录中就会 有一个下载不完整的程序或者其他东西下次再下载的时候我们就可以选择续传来完成剩下的下载过程。
procedure TForm1.IdHTTP1WorkBegin(Sender: TObject; AWorkMode: TWorkMode;const AWorkCountMax: Integer);
beginAbortTransfer : False;
……
end;
在IdHTTP1和IdFTP的OnWorkBegin事件我们就将AbortTransfer设置为False了在他们的Work事件中我们检测AbortTransfer变量来完成是否中断的操作。
procedure TForm1.IdHTTP1Work(Sender: TObject; AWorkMode: TWorkMode;const AWorkCount: Integer);
beginif AbortTransfer thenbegin //中断下载IdHTTP1.Disconnect;IdFTP1.Abort;end;ProgressBar1.Position : AWorkCount;Application.ProcessMessages;
end;
3 最后是连接状态等信息的代码
在IdHTTP和IdFTP的OnStatus事件写入
procedure TForm1.IdHTTP1Status(ASender: TObject; const AStatus: TIdStatus;const AStatusText: string);
beginListBox1.ItemIndex : ListBox1.Items.Add(AStatusText);
end;
因为IdHTTP和IdFTP在OnWork、OnStatus等事件上执行的代码都是一样的所以我们只用写其中一个的代码然后另外一个选择相同的事件就OK了。图8.3.4
3.全部代码写完收工F9运行一下看看效果是不是能断点续传。
【程序小结】
本程序主要的功能由IdHTTP和IdFTP组件完成主要掌握他们的Get过程实现断点续传的方法以及字符串的分析分解方法这里我们同样使用了流格式不过这次不是内存流而是文件流。通过本例读者应该初步掌握调试程序时断点的使用事件代码的共用等。
【作者后话】
在写完这篇文章不久作者偶然间察看了Indy系列组件的帮助发现一个封装了分析URL结构的类TIdURI在IdURI单元这个类可以很轻松的将我们上面的GetFTPParams()函数的功能实现例如
varURI: TIdURI;
beginURI : TIdURI.Create(aURL); //建立trysProtocol : URI.Protocol; //协议sHost : URI.Host; //主机//……等等都可以通过URI的属性得到finallyURI.Free;end;
end;
使用此类我们的程序可以变得更简单如何修改就留给读者自己去完善吧。 转载于:https://my.oschina.net/u/582827/blog/872827