VBGood网站全文搜索 Google

搜索VBGood全站网页(全文搜索)
首页 - 经验之谈 - 用Visual Basic创建FTP组件
发表评论(0)作者:甘翼平, 平台:VB6.0+Win98, 阅读:10944, 日期:2000-09-29
用Visual Basic创建FTP组件
(作者:甘翼平 2000年08月01日 15:10)

何为FTP?



  文件传输协议(FTP)是IP世界的核心协议,网络管理人员,Web 开发人员,以及那些想要恢复他们的祖先照片的人天天都在使用它。但是对于ASP开发人员来说,如果没有第三方服务器组件提供这一功能的话,FTP功能就不存在。



  猜猜看会怎么样?Microsoft的开发人员已经在WinINet.DLL中为我们提供了FTP功能。这个DLL是与Internet Explorer 和其它一些内容集成在一起的,处理有关FTP协议的低级任务。我们所需要做的是将一些提供的功能集合起来以便ASP代码调用方便。



  听起来这是一个将我们的Visual Basic技巧用于实际的好机会。

来自朋友的一点帮助

  对于那些从来没有使用VB编写过ActiveX 组件的人来说,在网上有许多可用的资源。就在15 Seconds上有许多文章可以参考。COM for ASP Programmers

( http://www.15seconds.com/Issue/971214.htm) 覆盖了创建组件的一些基本技巧,如果你对此是个新手的话,这篇文章一定要读。Creating a Server Component with Visual Basic

( http://www.15seconds.com/issue/98930.htm ) 引导你走过创建组件的所有步骤。

  上面已经提到,Microsoft在WinINet.DLL中为我们提供了FTP功能。WinINet API 的说明文件位于http://msdn.microsoft.com/developer/sdk/inetsdk/help/itt/wininet/wininet.htm#book_wininet。同大多数API说明文件一样,这个文件也是针对C++ 编程人员的。但是如果你不理解C++ 或是很容易对这些API说明文件的说教感到厌烦,还继续读下去吗?

一步步地来

  现在开始,对于FTP.MICROSOFT.COM的文件DIRMAP.TXT执行一个FTP GET命令,并将文件存储为C:\DIRMAP.TXT。基本步骤是:



1、用一个InternetOpen调用设置环境。

2、调用InternetConnect 函数与主机连接。

3、调用FtpGetFile 达到文件。

4、关闭第1、2步创建的句柄,用InternetCloseHandle 函数。

现在来仔细看看每一步:

1、通过调用InternetOpen 函数设置环境。下面是VB特定调用这一函数的声明:



Private Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" _

  (ByVal sAgent As String, ByVal lAccessType As Long, ByVal sProxyName As String, _

  ByVal sProxyBypass As String, ByVal lFlags As Long) As Long



  参数sAgent 用来指定调用WinINet 函数的应用程序或实体。为了达到目的,可以设置FTP控制。



  参数lAccessType 指定我们是直接与某一主机相连还是使用代理服务器相连。如果传递值1,就直接与主机连接。如果传递3,就通过代理服务器。如果传递0,连接时就要基于

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings下的注册表数值

ProxyEnable、ProxyServer和 ProxyOverride 。



  我们可以使用参数sProxyName和 sProxyBypass,而不是注册设置来提供代理服务器和不使用代理服务器的IP地址和名字。列出代理的基本格式是rotocol=protocol://proxy_name:access_port?。例如,要指定Proxy1 上的端口21为代理服务器,用Ftp=ftp://Proxy1:21?作为sProxyName。要饶过以ov? 开始的任何主机,sProxyBypass 字符串应为ov*? 。



  最后,lFlags 用来显示影响函数结果的不同选择。在我们的例子中,我们传递0。

  所以,不使用代理而打开一个Internet session 时,我们的调用是这样的:

lngINet = InternetOpen(揗yFTP Control? 1, vbNullString, vbNullString, 0)

  如果函数调用失败,lngINet 为0。不然,lngINet 就保存在下一步中将要传递给InternetConnect

函数的句柄的值。

2、通过调用InternetConnect 函数与主机连接。VB特定调用这一函数的声明是:

Private Declare Function InternetConnect Lib "wininet.dll" Alias "InternetConnectA" _

  (ByVal hInternetSession As Long, ByVal sServerName As String, _

  ByVal nServerPort As Integer, ByVal sUsername As String, _

  ByVal sPassword As String, ByVal lService As Long, _

  ByVal lFlags As Long, ByVal lContext As Long) As Long



  第一个参数hInternetSession 是InternetOpen 调用返回的句柄值。

  sServerName 是我们即将连接的FTP服务器的IP地址或主机名。

  nServerPort 指示与哪一个端口连接。在我们的例子中使用的值为0,它指示的是默认的端口21。

  sUsername 和 sPassword 分别传递用户名和口令。

  lService 用来指示使用的服务类型,如HTTP, FTP等。通常传递值为1,表示FTP服务。

  如果将x8000000传递到 lFlags 参数,连接将使用被动FTP语义。或者,在我们的例子中,传递0来使用非被动语义。



  最后,当使用回叫信号时,lContext 用来识别应用程序的前后关系。因为在我们的例子中不使用回叫信号,所以这个值为0。



  现在使用匿名的电子邮件用户名与主机FTP.MICROSOFT.COM 相连接:

lngINetConn = InternetConnect(lngINet, ftp.microsoft.com, 0, _

  揳anonymous,ally@wallyworld.com, 1, 0, 0)



  如果函数调用失败,则lngINetConn 为0。反之,lngINetConn 就保存在下一步中将传递给FtpGetFile 的句柄的值。



3、现在我们已经实现了连接,然后就需要调用FtpGetFile 。这个函数完成从一个FTP服务器上读取文件并在本地存储时有关的所有管理功能。VB特定调用这一函数的声明是:



Private Declare Function FtpGetFile Lib "wininet.dll" Alias "FtpGetFileA" _

  (ByVal hFtpSession As Long, ByVal lpszRemoteFile As String, _

  ByVal lpszNewFile As String, ByVal fFailIfExists As Boolean, _

  ByVal dwFlagsAndAttributes As Long, ByVal dwFlags As Long, _

  ByVal dwContext As Long) As Boolean



  第一个参数hFtpSession 是InternetConnect 调用返回的句柄值。

  lpszRemoteFile和lpszNewFile 分别是FTP服务器上的文件名和将在本地机上创建的文件名。

  fFailIfExists 标志是0(替换本地文件)或1 (如果本地文件已经存在则取消)。

  dwFlagsAndAttributes 用来指定本地文件的文件属性。在我们的例子中忽略,只传递0。

  dwFlags 参数指定为1是用ASCII 传输文件(A类传输方法),指定为2是用二进制传输文件(1类传输方法)。由于DIRMAP.TXT 是ASCII 文本文件,我们传递值1。

  最后,当使用回叫信号时,lContext 用来识别应用程序前后关系。因为在我们的例子中不使用回叫信号,所以这个值为0。

  所以,以下是得到DIRMAP.TXT文件并将其存在 C:\DIRMAP.TXT的调用。如果本地文件已经存在,就覆盖它。

blnRC = FtpGetFile(lngINetConn, dirmap.txt,c:\dirmap.txt, 0, 0, 1, 0)

  如果函数调用成功,blnRC为 True, 反之为False。

4、现在文件已经被接收,使用InternetCloseHandle 调用来关闭连接和session 句柄。VB特定调用这一函数的声明是:



Private Declare Function InternetCloseHandle Lib "wininet.dll" (ByVal hInet As Long) As Integer.



  如同指明的一样,此函数只有一个参数hInet,是要关闭或抛弃的句柄的值。因为InternetConnection 和InternetOpen 中有句柄,就需要调用这个关闭函数两次。另外因为InternetConnection 句柄是由InternetOpen 句柄决定的,关闭他们时的顺序与创建时相反。

  以下是调用函数:

InternetCloseHandle lngINetConn

InternetCloseHandle lngINet



  恭喜!用这短短的四步就完成了FTP GET。



接下来

  在FTP中,Put、Rename、 Delete是怎样的?这些函数也相当简单。首先来看看Put 函数。



  基本步骤是:



1、调用InternetOpen函数设置环境。

2、调用InternetConnect 函数连接主机。

3、调用FtpPutFile 函数得到文件。

4、用InternetCloseHandle 函数关闭第1、2步的句柄。



  当使用FtpGetFile 时与上面的步骤看起来完全一样。事实上唯一的区别是在第3步中调用了FtpPutFile 。VB特定调用这一函数的声明是:



Private Declare Function FtpPutFile Lib "wininet.dll" Alias "FtpPutFileA" _

  (ByVal hFtpSession As Long, ByVal lpszLocalFile As String, _

  ByVal lpszRemoteFile As String, ByVal dwFlags As Long, _

  ByVal dwContext As Long) As Boolean



  第一个参数hFtpSession 是InternetConnect 调用返回的句柄值。

  lpszNewFile 和lpszRemoteFile 分别是本地机上的文件名和将在远程主机上创建的文件名。

  参数dwFlags 指定为1时,用ASCII 传输文件(A类传输方法),指定为2是用二进制传输文件(1类传输方法)。由于DIRMAP.TXT 是ASCII 文本文件,我们传递值1。

  最后,当使用回叫信号时,lContext 用来识别应用程序前后关系。因为在我们的例子中不使用回叫信号,所以这个值为0。

  以下是得到DIRMAP.TXT文件并将其存在 C:\DIRMAP.TXT的调用。

blnRC = FtpPutFile(lngINetConn, 揷:\dirmap.txt? 揹irmap.txt? 1, 0)

  如果函数调用成功,blnRC为 True, 反之为False。

  你可以看到,把文件放到FTP服务器上与从FTP服务器上得到文件一样简单。有一点要注意,匿名用户无权在FTP服务器上创建文件。所以要确定用来与FTP服务器连接的用户帐号要有创建文件的权限。不然的话,FtpPutFile函数调用就会返回False,说明Put 失败了。

  现在我们用FtpDeleteFile 函数删除一个名为Test.txt的文件。同样只有第三步中的函数调用发生了改变。VB特定调用这一函数的声明是:



Private Declare Function FtpDeleteFile Lib "wininet.dll" Alias "FtpDeleteFileA" _

  (ByVal hFtpSession As Long, ByVal lpszFileName As String) As Boolean

  第一个参数hFtpSession 是InternetConnect 调用返回的句柄值。

  lpszFileName 是FTP服务器上要删除的文件。

  下面是在FTP服务器上删除TEST.TXT 文件的调用:

blnRC = FtpDeleteFile(lngINetConn, Test.txt)

  如果函数调用成功,blnRC为 True, 反之为False。

要注意什么?

  几乎所有的FTP函数都一样:设置环境,连接主机,执行FTP任务,清除。但是也有例外。如何得到一个路径列表或者得到文件之前读文件的内容?这些类型的FTP任务只是有一点点复杂。首先看看路径列表问题。

  列举路径的基本步骤也是一样的。首先还是要设置环境,连接FTP服务器。结束之后还是要清楚连接和句柄。为了实际得到路径内容,需要使用两个新函数:FtpFindFirstFile 和InternetFindNextFile。VB特定调用这一函数的声明是:

Private Declare Function FtpFindFirstFile Lib "wininet.dll" Alias "FtpFindFirstFileA" _

  (ByVal hFtpSession As Long, ByVal lpszSearchFile As String, _

  lpFindFileData As WIN32_FIND_DATA, ByVal dwFlags As Long, _

  ByVal dwContent As Long) As Long



  第一个参数hFtpSession 是InternetConnect 调用返回的句柄值。

  lpszSearchFile 是FTP服务器上的路径或文件名。如果你指定了一个空字符,就使用当前路径。另外还可以指定通配符。例如,要列出以ms开始的根目录下的路径内容,就使用ms*? 。

  lpFindFileData 与我们使用的其它参数有一点不同。数据类型WIN32_FIND_DATA 是用户定义类型,保存关于路径下的文件的信息。类型看起来是这样的:

Private Type WIN32_FIND_DATA

    dwFileAttributes As Long

    ftCreationTime As FILETIME

    ftLastAccessTime As FILETIME

    ftLastWriteTime As FILETIME

    nFileSizeHigh As Long

    nFileSizeLow As Long

    dwReserved0 As Long

    dwReserved1 As Long

    cFileName As String * MAX_PATH

    cAlternate As String * 14

End Type



  注意,有许多参数有不同的用户定义数据类型:FILETIME。下面是它们的类型定义:

Private Type FILETIME

    dwLowDateTime As Long

    dwHighDateTime As Long

End Type

  在我们的例子中,只使用dwFileAttributes的内容,其中包含文件的属性,还有cFileName,其中包含文件名。

  最后,我们的例子不使用dwFlags 和 dsContext ,所以每个都传递0。

  下面在当前路径下开始路径列举的调用:

lngHINet = FtpFindFirstFile(lngINetConn, "*.*", pData, 0, 0)

  如果函数失败,就返回0。否则,用来继续进行路径列举的lngHInet 是一个有效句柄。另外,第一个文件名和属性储存在pData 参数中。

  一旦调用了FtpFindFirstFile 并返回一个有效句柄,我们要调用InternetFindNextFile 函数除非它返回错误值18表示没有可以列举的文件。对InternetFindNextFile的VB特定声明是:

Private Declare Function InternetFindNextFile Lib "wininet.dll" Alias "InternetFindNextFileA" _

  (ByVal hFind As Long, lpvFindData As WIN32_FIND_DATA) As Long

  第一个参数,hFind 是 FtpFindFirstFile 调用所返回的句柄。

  lpvFindData 是同样的用户定义类型,用在FtpFindFirstFile 调用中存储文件信息。

  下面是在路径中得到下一个文件的调用:

blnRC = InternetFindNextFile(lngHINet, pData)

  如果调用成功,blnRC 返回 True,否则 blnRC 为False。Err对象的LastDllError返回18表明再没有文件存在了。

  现在再回来看看基本的四步:第1步和第2步(设置环境和连接服务器)应该已经完成。以下列出的第三步。最后一步,第四步,就清除环境和连接句柄,跟前面一样:

Dim pData As WIN32_FIND_DATA

Dim lngHINet As Long

Dim intError As Integer

Dim strTemp As String

Dim blnRC As Boolean



'init the filename buffer

pData.cFileName = String(260, 0)



'get the first file in the directory...

lngHINet = FtpFindFirstFile(mlngINetConn, "*.*", pData, 0, 0)



'how'd we do?

If lngHINet = 0 Then

  'get the error from the findfirst call

  intError = Err.LastDllError

  

  'is the directory empty?

  If intError < > ERROR_NO_MORE_FILES Then

    'whoa...a real error

    卐rror handler?

  End If

  

Else

  

  'we got some dir info...

  'get the name

  strTemp = Left(pData.cFileName, InStr(1, pData.cFileName, String(1, 0), vbBinaryCompare) - 1)



  卻tore the file info someplace?

      

  'now loop through the rest of the files...

  Do

    'init the filename buffer

    pData.cFileName = String(260, 0)

    

    'get the next item

    blnRC = InternetFindNextFile(lngHINet, pData)

    

    'how'd we do?

    If Not blnRC Then

    

      'get the error from the findnext call

      intError = Err.LastDllError

      

      'no more items

      If intError < > 18 Then

        'whoa...a real error

        卐rror handler?

        

        Exit Do

      

      Else

        

        'no more items...

        Exit Do

        

      End If

      

    Else

      

      'get the last item returned

      strTemp = Left(pData.cFileName, InStr(1, pData.cFileName, String(1, 0), vbBinaryCompare) - 1)

      

      卻tore the file info someplace?



    End If

    

  Loop

  

  'close the handle for the dir listing

  InternetCloseHandle lngHINet

  

End If



  现在可以看到,即使是有一些复杂的任务,例如列举路径,都可以简单地纳入这简单的四步过程:设置环境,连接主机,执行FTP任务,关闭环境和连接句柄。

你自己的FTP ActiveX 服务器组件

  要使这篇文章完整,本文包括一个FTP ActiveX 服务器组件的VB源代码。代码中包含3个类模块:clsITEM.CLS 、colITEM.CLS和 FTP.CLS。clsITEM.CLS 包含的类定义以clsITEM作为类名,在列举一个路径时包含着单独的文件信息。colITEM.CLS 包含的类定义以colItem作为类名,用于使用clsITEM 类的文件集合。最后FTP.CLS 包含的类定义以ASPFTP 作为类名,用于所有的FTP函数。我还要包含一个工程文件TP_CLASSES.VBP)。

  除了源代码,还包含了每个函数的样本ASP文件,一个包含文件(ASPFTP2.INC) ,你可以将它包含在每个ASP文件中使一些函数的调用更简单。例如,这里是使用FTP Get 函数的ASP代码。服务器,电子邮件用户名,口令,远程和本地文件名,使用的传输类型(ASCII 或 Binary),当本地文件存在时是否覆盖,这些都被输入一个表单中(未显示)并用ASP Request 对象引用:

< %@ LANGUAGE=VBScript % >

< !--#Include File="aspftp2.inc"-- >

< %

'check to see if user submitted form

If Request.Form("GetIt") < > "" Then

Dim objFTP

Dim strMsg



'create reference to object

Set objFTP = Server.CreateObject("NIBLACK.ASPFTP")



'set the properties for the connection

objFTP.sServerName = Request.Form("Server")

objFTP.sUserID = Request.Form("User_ID")

objFTP.sPassword = Request.Form("Password")



'connect to the host

If objFTP.bConnect Then

'set the properties for the get function

objFTP.bOverWrite = Request.Form("OverWrite")

objFTP.lTransferType = Request.Form("Transfer_Type")



'now get the file

If objFTP.bGetFile(Request.Form("Remote_File"), Request.Form("Local_File")) Then

  'get was successful

  strMsg = "Get Successful!"

Else

  'get failed...let user know

  strMsg = "Get Failed: " & objFTP.sError

End If

Else

'connection failed...let user know

strMsg = "Connection Failed: " & objFTP.sError

End If

  

'clean up...

Set objFTP = Nothing



Else

'default return msg

strMsg = ""

End If

% >



  我还为许多许多FTP函数创建了uick?方法。基本上,你调用一种uick?方法,用来完成任务的所有参数都包含在方法调用中,而不用在ASP代码中设置每一个属性。举例说,这里的ASP代码使用FTP Get 函数。服务器,电子邮件用户名,口令,远程和本地文件名,使用的传输类型(ASCII 或 Binary),当本地文件存在时是否覆盖,这些都被输入一个表单中(未显示)并用ASP Request 对象引用。



< %@ LANGUAGE=VBScript % >

< !--#Include File="aspftp2.inc"-- >

< %

'check to see if user submitted form

If Request.Form("GetIt") < > "" Then

Dim objFTP

Dim strMsg



'create reference to object

Set objFTP = Server.CreateObject("NIBLACK.ASPFTP")



'now get the file

If objFTP.bQGetFile(Request.Form("Server"), Request.Form("User_ID"), _

  Request.Form("Password"), Request.Form("Remote_File"), Request.Form("Local_File"), _

  Request.Form("Transfer_Type"), Request.Form("OverWrite")) Then

  'get was successful

  strMsg = "Get Successful!"

Else

  'get failed...let user know

  strMsg = "Get Failed: " & objFTP.sError

End If

  

'clean up...

Set objFTP = Nothing



Else

'default return msg

strMsg = ""

End If

% >

下面如何?

  有了提供的源代码,你就已经有了一个FTP函数的ActiveX服务器组件。现在我们可以跟随同样的基本步骤,创建一个客户机侧的OCX ,你可以将其包含在你的HTML页中以允许用户从他们的PC进行FTP。还可以看看WinINet API提供的一些其它函数。例如你可以使用HTTP函数创建一个机器人,将Web 站点的内容拉回来,对所有的页进行索引。或者创建一个探测器,在一个预定的基础上,

用HTTP函数检测Web 站点或Web 页的状态。用WinINet API所提供的功能,这些和更多的想法都可以相当简单地来探索。