首页  编辑  

用 Windows Internet 函数进行 FTP 操作

Tags: /超级猛料/Network.网络通讯/FTP和拨号/   Date Created:
用 Windows Internet 函数进行 FTP 操作的简介?
--------------------------------------------------------------------------------
目录:
初步内容与INET句柄
打开进程取得操作句柄
开始连接
获取当前路径
获取文件/目录信息
文件的传输
范例
--------------------------------------------------------------------------------
1、初步内容与INET句柄...
让我们先来对您在FTP部分使用WININETDLL时需要编写的代码作一个做一个概括的了解。这并不是一个详尽的学习,但却能够让您进门。为了知晓这项技术,您要做的第一件事情是明白WININET.PAS中的一些函数返回的是一个叫做HINTERNET类的指针变量:
var
 HINTERNET : Pointer;
这个指针扮演一个您正在使用的不同的因特网服务的句柄的角色。获得了这个句柄之后,你应当把它作为第一个参数传递给在这个进程周期[注:指FTP的整个存在时间(译者)]中调用的其他WININET函数。
您要记住的适当您在使用它的时间内要把句柄返回给系统,通常是通过调用WININET函数InternetCloseHandle来实现:
function InternetCloseHandle(hInet : HINTERNET) : BOOL; stdcall;
2、打开进程取得操作句柄...
为了让一个WININET进程开始,您可以调用InternetOpen:
function InternetOpen(
 lpszCallerName : PChar;
 dwAccessType : DWORD;
 lpszServerName : PChar;
 nServerPort : INTERNET_PORT;
 dwFlags : DWORD) : HINTERNET; stdcall;
第一个参数时打开这个进程的应用程序的名字。您可以在这个参数中传递任何您所要的任意符串。微软公司的文献声称"这个名字作为HTTP协议中的用户代理器的名字而被使用"。这个保留的参数可以设为0或空。
var
 MyHandle : HINTERNET;

begin
 MyHandle := InternetOpen('MyApp', 0, nil, 0, 0);
end;
如果您想要关于这个函数的更多信息,从 www.microsoft.com 那里下载 WININET.HLP。
3、开始连接...
打开了这这个进程之后,下一步是通过 InternetConnect 函数来连接到服务器上。
function InternetConnect(
 hInet : HINTERNET;   //Handle from InternetOpen
 lpszServerName : PChar;   //Server, i.e. www.borland.com
 nServerPort : INTERNET_PORT;   //Usually 0
 lpszUsername : PChar;   //usually anonymous
 lpszPassword : PChar;   //usually your email address
 dwService : DWORD;   //FTP, HTTP, or Gopher?
 dwFlags : DWORD;   //Usually 0
 dwContext : DWORD):   //User defined number for callback
HINTERNET; stdcall;
这里有三个可能的可以通过dwService参数传递的自说明旗标,它们是互斥的:
INTERNET_SERVICE_FTP
INTERNET_SERVICE_GOPHER
INTERNET_SERVICE_HTTP
下面是dwFlags参数的选择:
INTERNET_CONNECT_FLAG_PASSIVE
这个选项仅当您在前一个参数中传递了 INTERNET_SERVER_FTP 才有效。这时候这个参数没有其他有效的选项。
如果这个进程成功的话会返回一个有效的指针,否则它返回空。
4、获取当前路径...
当您连接上之后,您可以调用来 GetCurrentDirectory 获得当前的路径的名字:
function TMyFtp.GetCurrentDirectory : string;
var
 Len:Integer;
 S:string;
begin
 Len:=0;
 ftpGetCurrentDirectory(FFTPHandle, PChar(S), Len);
 SetLength(S, Len);
 ftpGetCurrentDirectory(FFTPHandle, PChar(S), Len);
 Result := S;
end;
这个函数声明如下:
function FtpGetCurrentDirectory(
 hFtpSession : HINTERNET;  //handle from InternetConnect
 lpszCurrentDirectory : PChar;   //directory returned here
 varlpdwCurrentDirectory : DWORD):  //buf size of 2nd parameter
BOOL; stdcall;  //Trueonsuccess
如果您把最后一个参数设为 0,那么 WININET 会使用这个参数来返回路径字符串的长度。接着您可以为您的字符串分配内存,也可以在调用一次这个函数来获得路径的名字。这个过程在上面的方法中已经演示过了。(注意到*设定长度*的那个调用,Delphi 要求您在类似这样的情况下为新的长字符串分配内存!这是因为这个字符串必须在操作系统中指定值,而不是在 Delphi 应用程序中指定。结果就是Delphi不能在类似的情况下像它通常那样悄悄地为字符串分配内存)
5、获取文件/目录信息...
下面是返回在特定路径下当前可用的文件的一系列函数:
function GetFindDataStr(FindData:TWin32FindData) : string;
var
 S : string;
 Temp : string;
begin
 case FindData.dwFileAttributes of
         FILE_ATTRIBUTE_ARCHIVE : S := 'A';
         // FILE_ATTRIBUTE_COMPRESSED : S := 'C';
         FILE_ATTRIBUTE_DIRECTORY : S := 'D';
         FILE_ATTRIBUTE_HIDDEN : S := 'H';
         FILE_ATTRIBUTE_NORMAL : S := 'N';
         FILE_ATTRIBUTE_READONLY : S := 'R';
         FILE_ATTRIBUTE_SYSTEM : S := 'S';
         FILE_ATTRIBUTE_TEMPORARY : S := 'T';
     else
         S := IntToStr(FindData.dwFileAttributes);
     end;
 S := S+GetDots(75);
 Move(FindData.CFilename[0], S[6], StrLen(FindData.CFileName));
 Temp := IntToStr(FindData.nFileSizeLow);
 Move(Temp[1], S[25], Length(Temp));
 Result := S;
end;
function TMyFtp.FindFiles : TStringList;
var
 FindData : TWin32FindData;
 FindHandle : HInternet;
begin
 FindHandle := FtpFindFirstFile(FFtphandle, '*.*', FindData, 0, 0);
 if FindHandle = nil then
    begin
    Result := nil;
    Exit;
    end;
 FCurFiles.Clear;
 FCurFiles.Add(GetFindDataStr(FindData));
 while InternetFindnextFile(FindHandle,@FindData) do
       FCurFiles.Add(GetFindDataStr(FindData));
 InternetCloseHandle(Findhandle);
 GetCurrentDirectory;
 Result := FCurFiles;
end;
这里需要注意的关键函数是 ftpFindFirstFile, InternetFindNextFile & InternetCloseHandle。您可以像调用 Delphi 函数FindFirst、FindNext & FinClose 一样调用这些函数。特别的是,您使用函数ftpFindFirstFile来取得这个路径下的第一个函数。您可以不断地调用 InternetFindNextFile,直到函数返回"False"为止。当这个进程结束时,调用InternetCloseHandle来通知操作系统回收与这个进程相关的内存。
如果您想要更多的信息,您可以在 Delphi 帮助中查找 FindFirst。最后提醒一句:并不向前文提及的函数,TWin32FindData 并不是在 WININET.PAS 中定义的,但可以在随 Delphi 分发的 WIN32 帮助文件中找到它。它在随 Delphi 分发的WINDOWS.PAS文件中被定义。
6、文件的传输...
您可以使用 WININET.PAS 文件中的 ftpGetFile 函数来从 FTP 取回一个文件:
function FtpGetFile(
 hFtpSession : HINTERNET;  //Returned by InternetConnect
 lpszRemoteFile : PChar;  //File to get
 lpszNewFile : PChar;  //Where to put it on your PC
 fFailIfExists : BOOL;  //Over write existing files?
 dwFlagsAndAttributes : DWORD;  //File attribute-See Create File.
 dwFlags : DWORD;  //Binaryor ASCII transfer
 dwContext : DWORD):  //Usually zero
BOOL; stdcall;  //Trueonsuccess
下面是一个如何使用该函数的例子:
function TMyFtp.GetFile(FTPFile, NewFile : string) : Boolean;
begin
 Result := FtpGetFile(
         FFTPHandle,
         PChar(FTPFile),
         PChar(NewFile),
         False,
         File_Attribute_Normal,
         Ftp_Transfer_Type_Binary,
         0);
end;
如果要知道 dwFlagsAndAttributes 参数中的变量是怎样传递的,请查阅随 Delphi 附送的WIN32帮助文件。
7、范例...
下面的 Delphi 控制给了你一个通过 WININETFTP 部分建立可视工具的起点。只是因为,这个控制可以让您是用 ObjectInspector 来定义远程服务器(RemoteServer)、用户身份(UserID)和密码(Password)。
unit Ftp1;
{ FTP example using WININET.PAS rather than  
 an ACTIVEX control. Requires WININET.PAS and  
 WININET.DLL. WININET.DLL you can get from  
 Microsoft, WININET.PAS is available from  
  www.borland.com , or with some versions of  
 Delphi 2.0.  
   
 You might Respond to OnNewDir events as follows:  
 
 procedure TForm1.FTP1NewDir(Sender: TObject);  
 begin  
   ListBox1.Items := MyFtp1.FindFiles; // Get the directory list  
 end;    
}
interface
uses
 Windows,Classes,WinINet,SysUtils;
   
type
 TMyFtp = class(TComponent)
 private
   FContext: Integer;
   FINet: HInternet;
   FFtpHandle: HInternet;
   FCurFiles: TStringList;
   FServer: string;
   FOnNewDir: TNotifyEvent;
   FCurDir: string;
   FUserID: string;
   FPassword: string;
   function GetCurrentDirectory: string;
   procedure SetUpNewDir;
 protected
   destructor Destroy; override;
 public
   constructor Create(AOwner: TComponent); override;
   function Connect: Boolean;
   function FindFiles: TStringList;
   function ChangeDirExact(S: string): Boolean;
   function ChangeDirCustom(S: string): Boolean;
   function BackOneDir: Boolean;
   function GetFile(FTPFile, NewFile: string): Boolean;
   function SendFile1(FTPFile, NewFile: string): Boolean;
   function SendFile2(FTPFile, NewFile: string): Boolean;
   function CustomToFileName(S: string): string;
 published
   property CurFiles: TStringList read FCurFiles;
   property CurDir: string read FCurDir;
   property UserID: string read FUserID write FUserID;
   property Password: string read FPassword write FPassword;
   property Server: string read FServer write FServer;
   property OnNewDir: TNotifyEvent read FOnNewDir write FOnNewDir;
 end;
procedure Register;  
implementation
uses
 Dialogs;
// A few utility functions
function GetFirstToken(S: string; Token: Char): string;
var
 Temp: string;
 Index: INteger;
begin
 Index := Pos(Token, S);
 if Index < 1 then begin
   GetFirstToken := '';
   Exit;
 end;
 Dec(Index);
 SetLength(Temp, Index);
 Move(S[1], Temp[1], Index);
 GetFirstToken := Temp;
end;
function StripFirstToken(S: string; Ch: Char): string;
var
 i, Size: Integer;
begin
 i := Pos(Ch, S);
 if i = 0 then begin
   StripFirstToken := S;
   Exit;
 end;
 Size := (Length(S) - i);
 Move(S[i + 1], S[1], Size);
 SetLength(S, Size);
 StripFirstToken := S;
end;
function ReverseStr(S: string): string;
var
 Len: Integer;
 Temp: String;
 i,j: Integer;
begin
 Len := Length(S);
 SetLength(Temp, Len);
 j := Len;
 for i := 1 to Len do begin
   Temp[i] := S[j];
   dec(j);
 end;
 ReverseStr := Temp;
end;
function StripLastToken(S: string; Token: Char): string;
var
 Temp: string;
 Index: INteger;
begin
 SetLength(Temp, Length(S));
 S := ReverseStr(S);
 Index := Pos(Token, S);
 Inc(Index);
 Move(S[Index], Temp[1], Length(S) - (Index - 1));
 SetLength(Temp, Length(S) - (Index - 1));
 StripLastToken := ReverseStr(Temp);
end;
procedure Register;
begin
 RegisterComponents('Unleash', [TMyFtp]);
end;
constructor TMyFtp.Create(AOwner: TComponent);
begin
 inherited Create(AOwner);
 FCurFiles := TStringList.Create;
 FINet := InternetOpen('WinINet1', 0, nil, 0, 0);
end;
destructor TMyFtp.Destroy;
begin
 if FINet <> nil then
   InternetCloseHandle(FINet);
 if FFtpHandle <> nil then
   InternetCloseHandle(FFtpHandle);
 inherited Destroy;
end;
function TMyFtp.Connect: Boolean;
begin
 FContext := 255;
 FftpHandle := InternetConnect(FINet, PChar(FServer), 0,
  PChar(FUserID), PChar(FPassWord),
  Internet_Service_Ftp, 0, FContext);
 if FFtpHandle = nil then
   Result := False
 else begin
   SetUpNewDir;
   Result := True;
 end;
end;
function TMyFtp.GetCurrentDirectory: string;
var
 Len: Integer;
 S: string;
begin
 Len := 0;
 ftpGetCurrentDirectory(FFTPHandle, PChar(S), Len);
 SetLength(S, Len);
 ftpGetCurrentDirectory(FFTPHandle, PChar(S), Len);
 Result := S;
end;
procedure TMyFtp.SetUpNewDir;
begin
 FCurDir := GetCurrentDirectory;
 if Assigned(FOnNewDir) then
   FOnNewDir(Self);            
end;
function GetDots(NumDots: Integer): string;
var
 S: string;
 i: Integer;
begin
 S := '';
 for i := 1 to NumDots do
   S := S + ' ';
 Result := S;
end;
function GetFindDataStr(FindData: TWin32FindData): string;
var
 S: string;
 Temp: string;
begin
 case FindData.dwFileAttributes of
   FILE_ATTRIBUTE_ARCHIVE: S := 'A';
//  FILE_ATTRIBUTE_COMPRESSED: S := 'C';
   FILE_ATTRIBUTE_DIRECTORY: S := 'D';
   FILE_ATTRIBUTE_HIDDEN: S := 'H';
   FILE_ATTRIBUTE_NORMAL: S := 'N';
   FILE_ATTRIBUTE_READONLY: S := 'R';
   FILE_ATTRIBUTE_SYSTEM: S := 'S';
   FILE_ATTRIBUTE_TEMPORARY: S := 'T';
 else
   S := IntToStr(FindData.dwFileAttributes);
 end;
 S := S + GetDots(75);
 Move(FindData.CFilename[0], S[6], StrLen(FindData.CFileName));
 Temp := IntToStr(FindData.nFileSizeLow);
 Move(Temp[1], S[25], Length(Temp));
 Result := S;
end;
function TMyFtp.FindFiles: TStringList;
var
 FindData: TWin32FindData;
 FindHandle: HInternet;
begin
  FindHandle := FtpFindFirstFile(FFtphandle, '*.*',
    FindData, 0, 0);
  if FindHandle = nil then begin
    Result := nil;
    Exit;
  end;
  FCurFiles.Clear;
  FCurFiles.Add(GetFindDataStr(FindData));
  while InternetFindnextFile(FindHandle, @FindData) do
    FCurFiles.Add(GetFindDataStr(FindData));
  InternetCloseHandle(Findhandle);
  GetCurrentDirectory;
  Result := FCurFiles;
end;
function TMyFtp.CustomToFileName(S: string): string;
const
 PreSize = 6;
var
 Temp: string;
 TempSize: Integer;
begin
 Temp := '';
 TempSize := Length(S) - PreSize;
 SetLength(Temp, TempSize);
 Move(S[PreSize], Temp[1], TempSize);
 Temp := GetFirstToken(Temp, ' ');
 Result := Temp;
end;
function TMyFtp.BackOneDir: Boolean;
var
 S: string;
begin
 S := FCurDir;
 S := StripLastToken(S, '/');
 if S = '/' then begin
   Result := False;
   Exit;
 end;
 if S <> '' then begin
   ChangeDirExact(S);
   Result := True;
 end else begin
   ChangeDirExact('/');
   Result := True;
 end;
end;
// Changes to specific directory in S
function TMyFtp.ChangeDirExact(S: string): Boolean;
begin
 if S <> '' then
   FtpSetCurrentDirectory(FFTPHandle, PChar(S));
 Result := True;
 FindFiles;
 SetUpNewDir;
end;
// Assumes S has been returned by GetFindDataString;
function TMyFtp.ChangeDirCustom(S: string): Boolean;
begin
 S := CustomToFileName(S);
 if S <> '' then
   FtpSetCurrentDirectory(FFTPHandle, PChar(S));
 Result := True;
 FindFiles;
 SetUpNewDir;
end;
function TMyFtp.GetFile(FTPFile, NewFile: string): Boolean;
begin
 Result := FtpGetFile(FFTPHandle, PChar(FTPFile), PChar(NewFile),
              False, File_Attribute_Normal, Ftp_Transfer_Type_Binary, 0);
end;
function TMyFtp.SendFile1(FTPFile, NewFile: string): Boolean;
const
 Size:DWord = 3000;
var
 Transfer: Bool;
 Error: DWord;
 S: string;
begin
 Transfer := FtpPutFile(FFTPHandle, PChar(FTPFile),
                        PChar(NewFile), Ftp_Transfer_Type_Binary, 0);
 if not Transfer then begin
   Error := GetLastError;
   ShowMessage(Format('Error Number: %d. Hex: %x',
                      [Error, Error]));
   SetLength(S, Size);
   if not InternetGetLastResponseInfo(Error, PChar(S), Size) then
   begin
     Error := GetLastError;
     ShowMessage(Format('Error Number: %d. Hex: %x', [Error, Error]));
   end;
   ShowMessage(Format('Error Number: %d. Hex: %x Info: %s', [Error, Error, S]));
 end else
   ShowMessage('Success');
 Result := Transfer;
end;
function TMyFtp.SendFile2(FTPFile, NewFile: string): Boolean;
var
 FHandle: HInternet;
begin
 FHandle :=  FtpOpenFile(FFTPHandle, 'sam.txt', GENERIC_READ, FTP_TRANSFER_TYPE_BINARY, 0);
 if FHandle <> nil then
 InternetCloseHandle(FHandle)
 else
   ShowMessage('Failed');
 Result := True;
end;
end