FTP Sessions
使用WinINet 能够很容易实现对ftp server 的目录和文件的操作。在开始连接FTP server 时你首先需要使用 InternetConnect 去创建session handle。
使用WinINet 能够完成以下对FTP server 的操作:
① 浏览目录
② 枚举,创建,移动和重命名目录
③ 重命名,上传,下载和删除文件
通过方法FtpGetCurrentDirectory和 FtpSetCurrentDirectory 实现对目录的操作。这两个方法需要使用InternetConnect返回的handle,这样应用才能够确定是哪一个目录或者要是哪一个子目录。
目录的枚举是通过使用方法FtpFindFirstFile 和 InternetFindNextFile。FtpFindFirstFile 需要使用InternetConnect的handle去FTP server 中查找到满足条件的目录并返回一个句柄用于继续枚举。InternetFindNextFile 使用FtpFindFirstFile 返回的句柄去查找满足条件的目录。应用通过不断的调用InternetFindNextFile 直到FTP server 中没有任何的目录。
使用FtpCreateDirectory 方法在FTP server 上创建新的目录。这个方法需要使用InternetConnect返回的handle 并且创建目录的名称需要自己传入。
使用FtpRenameFile 方法重命名FTP server 中的目录,这个方法将用你定义的名称取代原目录的名称。
可以使用FtpPutFile 或者FtpOpenFile (这个方法要与方法InternetWriteFile 一同使用)将文件上传到FTP server。如果文件已经在本地存在,那么你可以使用FtpPutFile;而当数据需要被写到FTP server 中的某一个文件中时,你需要使用FtpOpenFile 和InternetWriteFile 。
可以使用FtpGetFile 或者FtpOpenFile (这个方法要与方法InternetReadFile一同使用)下载FTP server 中的文件到本地。FtpGetFile 能够直接从FTP server 中下载文件到本地;而当数据需要直接被显示出来,例如显示在编辑框中时你需要使用FtpOpenFile 和InternetReadFile 。
可以使用FtpDeleteFile 方法删除FTP server 中的文件。这个方法需要InternetConnect 返回中的handle。这个方法需要传入具体的文件的路径。
FTP Function Handles
为了正常的工作FTP 的方法需要具体的HINTERNET handles,而使用到的handles 必须按一定的顺序被构建出来,最先被构建的是通过调用方法InternetOpen 返回的handle。InternetConnect 能够返回一个FTP session 的handle。下图展示的哪些方法需要使用到InternetConnect 返回的handle。阴影的方框表示这些方法返回HINTERNET handles,而白方框表示这些方法需要使用阴影方法返回的HINTERNET handles。
下图展示了会返回HINTERNET handles 和需要使用HINTERNET handles 的方法。阴影的方框代表这些方法会返回HINTERNET handles,而白方框代表这些方法需要使用阴影方法返回的HINTERNET handles。
Using the WinINet Functions for FTP Sessions
下面的方法是在FTP sessions 中常用的。这些方法并不会被CERN 代理识别。应用必须使用CERN 代理的应该使用InternetOpenUrl 直接连接资源。更多的相关信息请查看文章Accessing URLs Directly。
方法 |
描述 |
|
在FTP server 中创建一个目录,这个方法需要使用 InternetConnect返回的handle |
|
|
删除FTP server 中文件,这个方法需要使用 InternetConnect返回的handle |
|
|
开始在当前目录下枚举文件或者搜索文件,这个方法需要使用 InternetConnect返回的handle |
|
|
返回FTP server 的当前目录,这个方法需要使用 InternetConnect返回的handle |
|
|
检索FTP server 中的文件,这个方法需要使用 InternetConnect返回的handle |
|
|
开始连接FTP server 进行读或者写,这个方法需要使用 InternetConnect返回的handle |
|
|
上传文件到FTP server,这个方法需要使用 InternetConnect返回的handle |
|
|
删除FTP server中的一个目录,这个方法需要使用 InternetConnect返回的handle |
|
|
重命名FTP server 中的文件名称,这个方法需要使用 InternetConnect返回的handle |
|
|
改变在FTP server 中访问的目录,这个方法需要使用 InternetConnect返回的handle |
|
|
将内容追加到FTP server 中的一个打开文件中,这个方法需要使用 InternetConnect返回的handle |
Starting an FTP Session
FTP client 连接FTP server 有两个模式:PORT 模式和PASV 模式:
① PORT 模式:PORT是主动模式。当client 选择这种模式与server进行连接的时候,它需要向server提供一个IP 地址和一个端口号
② PASV 模式:PASV 是被动模式。当选择这种模式连接时,server需要提供给client 一个IP 地址和一个端口号。用户平时从网上一个指定的FTP 地址和端口下载文件就是这种模式的一种实际应用。相反则为PORT 模式。
以下的代码是建立在已经调用方法InternetOpen和InternetConnect 连接了FTP session。InternetConnect 需要server 名字,端口,账号,密码和service type(这个必须是INTERNET_SERVER_FTP)。对于被动的FTP 语义,这个应用必须设置INTERNET_FLAG_PASSIVE 标志。INTERNET_DEFAULT_FTP_PORT可以用来当缺省的FTP 端口,但是service type 必须被设定。INTERNET_INVALID_PORT_NUMBER 可以被当做指定的service type 的值。
账号和密码可以被设为NULL,如果两个值都被设为NULL,那么InternetConnect 将使用“anonymous”作为账号,并且将用户的email将被作为密码。如果仅仅当密码设为NULL,那么账号是用户传入的账号,但是密码将为NULL。如果两者都不为NULL,那么用户输入的账号和密码将被使用。
1 /* 2 初始化要访问的ftp 的信息 3 */ 4 void WINAPI CFTPClientSeal::InitInternet() 5 { 6 // HINTERNET m_hOpen = NULL; 7 m_hOpen = InternetOpen(_T("Testing"), NULL, 8 NULL, NULL, NULL); 9 // ftp 的IP地址 10 TCHAR serverName[] = _T("192.168.1.2"); 11 // 访问ftp 的端口号 12 int serverPort = 21; 13 CString userName = _T("view"); 14 CString psw = _T("123456"); 15 m_hConnection = InternetConnect(m_hOpen, serverName, serverPort, userName, psw, 16 INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, NULL); 17 }
Enumerating Directories
枚举FTP server 中的目录需要用到FtpFindFirstFile 返回的handle。这个handle 是session handle (由InternetConnect 创建)的一个分支。FtpFindFirstFile 定位FTP server 中的第一个目录并且返回一个WIN32_FIND_DATA 结构体。使用InternetFindNextFile 直到这个方法返回ERROR_NO_MORE_FILES。这个方法找到在FTP server 中的所有随后的目录。想知道更多的头于InternetFindNextFile的信息,请查看文章Finding the Next File。
想要知道被FtpFindFirstFile 或者InternetFindNextFile 检索到的内容是不是一个目录可以检查WIN32_FIND_DATA 结构体中的成员dwFileAttributes 是不是等于FILE_ATTRIBUTE_DIRECTORY。
如果应用使得FTP server 改变或者FTP server 的内容频繁的改变,INTERNET_FLAG_NO_CACHE_WRITE 和 INTERNET_FLAG_RELOAD 标志应该在方法FtpFindFirstFile 中被设定。这些标志确保被检索到的目录信息是当前FTP server 的。
在应用完成目录的枚举之后必须启用InternetCloseHandle 关闭FtpFindFirstFile 返回的handle。如果handle 没有被关闭,那么FtpFindFirstFile 不能够被调用。如果没有关闭handle,并再次调用FtpFindFirstFile 那么将会调用失败,返回错误信息ERROR_FTP_TRANSFER_IN_PROGRESS。
以下的例子枚举FTP server 中的目录信息到list box 控件中。参数hConnection 是方法InternetConnect 返回的handle。方法InternetErrorOut的简单的代码示例能够在Handling Errors 话题中被找到。
1 #include <windows.h> 2 #include <strsafe.h> 3 #include <wininet.h> 4 5 #pragma comment(lib, "wininet.lib") 6 #pragma comment(lib, "user32.lib") 7 8 #define FTP_FUNCTIONS_BUFFER_SIZE MAX_PATH+8 9 10 /* 11 获得FTP server 中的目录并显示到List Box 控件中 12 input: 13 hDlg —— 对话框的handle 14 dwFindFlags —— FtpFindFirstFile 方法的行为参数 15 nListBoxId —— List Box 控件的ID 16 output: 17 true —— 读取FTP server 目录成功 18 false —— 读取FTP server 目录失败 19 */ 20 BOOL WINAPI CFTPClientSeal::DisplayFtpDir( 21 HWND hDlg, // 对话框的handle 22 // HINTERNET hConnection, // InternetConnect 返回的handle 23 DWORD dwFindFlags, // FtpFindFirstFile 方法的行为参数 24 int nListBoxId) // List Box 控件的ID 25 { 26 WIN32_FIND_DATA dirInfo; 27 HINTERNET hFind; 28 DWORD dwError; 29 BOOL retVal = FALSE; 30 TCHAR szMsgBuffer[FTP_FUNCTIONS_BUFFER_SIZE]; 31 TCHAR szFName[FTP_FUNCTIONS_BUFFER_SIZE]; 32 33 // List Box 控件清空 34 SendDlgItemMessage(hDlg, nListBoxId, LB_RESETCONTENT, 0, 0); 35 hFind = FtpFindFirstFile(m_hConnection, TEXT("*.*"), 36 &dirInfo, dwFindFlags, 0); 37 if (hFind == NULL) 38 { 39 dwError = GetLastError(); 40 // FTP server 中没有文件 41 if (dwError == ERROR_NO_MORE_FILES) 42 { 43 StringCchCopy(szMsgBuffer, FTP_FUNCTIONS_BUFFER_SIZE, 44 TEXT("No files found at FTP location specified.")); 45 retVal = TRUE; 46 goto DisplayDirError_1; 47 } 48 StringCchCopy(szMsgBuffer, FTP_FUNCTIONS_BUFFER_SIZE, 49 TEXT("FtpFindFirstFile failed.")); 50 goto DisplayDirError_1; 51 } 52 // 通过循环获得FTP server 中的所有文件 53 do 54 { 55 // 获得文件的名字放置在字符串szFName 中 56 if (FAILED(StringCchCopy(szFName, FTP_FUNCTIONS_BUFFER_SIZE, 57 dirInfo.cFileName)) || // 判断是文件还是目录,如果是目录需要加上“<DIR>” 58 ((dirInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && 59 (FAILED(StringCchCat(szFName, FTP_FUNCTIONS_BUFFER_SIZE, 60 TEXT(" <DIR> ")))))) 61 { 62 StringCchCopy(szMsgBuffer, FTP_FUNCTIONS_BUFFER_SIZE, 63 TEXT("Failed to copy a file or directory name.")); 64 retVal = FALSE; 65 goto DisplayDirError_2; 66 } 67 // 将获得的文件名称添加到List Box 控件中 68 SendDlgItemMessage(hDlg, nListBoxId, LB_ADDSTRING, 69 0, (LPARAM)szFName); 70 } while (InternetFindNextFile(hFind, (LPVOID)&dirInfo)); 71 72 // 遍历到最后没有文件返回ERROR_NO_MORE_FILES 错误信息 73 if ((dwError = GetLastError()) == ERROR_NO_MORE_FILES) 74 { 75 InternetCloseHandle(hFind); 76 return(TRUE); 77 } 78 StringCchCopy(szMsgBuffer, FTP_FUNCTIONS_BUFFER_SIZE, 79 TEXT("FtpFindNextFile failed.")); 80 81 DisplayDirError_2: 82 InternetCloseHandle(hFind); 83 DisplayDirError_1: 84 MessageBox(hDlg, 85 (LPCTSTR)szMsgBuffer, 86 TEXT("DisplayFtpDir( ) Problem"), 87 MB_OK | MB_ICONERROR); 88 return(retVal); 89 }
Navigating Directories
FtpGetCurrentDirectory 和FtpSetCurrentDirectory 方法可以对FTP server 中的目录进行操作。
FtpGetCurrentDirectory 返回FTP server 中当前目录到客户端。这个目录的路径包括FTP server 中根目录的路径。
FtpSetCurrentDirectory 改变你所访问的FTP server 中的目录。传递给这个方法的路径可以是部分的(相对路径)或者是完整的路径(绝对路径)。
下面的例子使用FTP session 的handle hConnection(通过调用InternetConnect 获得),这个新的目录名从编辑框中获得,这个编辑框的ID 为nDirNameId。你可以在这个函数的最后调用上面的方法DisplayFtpDir。
1 /* 2 改变所访问的FTP server 的目录 3 input: 4 hDlg —— 对话框的handle 5 nDirNameId —— 编辑框的ID 6 nListBoxId —— List Box 的ID 7 output: 8 true —— 改变目录成功 9 false —— 改变目录失败 10 */ 11 BOOL WINAPI CFTPClientSeal::ChangeFtpDir(HWND hDlg, // 对话框的handle 12 int nDirNameId, // 编辑框的ID 13 int nListBoxId) // List Box 的ID 14 { 15 DWORD dwSize; 16 TCHAR szNewDirName[FTP_FUNCTIONS_BUFFER_SIZE]; 17 TCHAR szOldDirName[FTP_FUNCTIONS_BUFFER_SIZE]; 18 TCHAR* szFailedFunctionName = NULL; 19 20 dwSize = FTP_FUNCTIONS_BUFFER_SIZE; 21 22 // 获得编辑框中的文本,即需要访问FTP server 的新目录路径 23 if (!GetDlgItemText(hDlg, nDirNameId, szNewDirName, dwSize)) 24 { 25 szFailedFunctionName = TEXT("GetDlgItemText"); 26 goto ChangeFtpDirError; 27 } 28 29 // 获得当前目录的名称并保存到字符串szOldDirName 中 30 if (!FtpGetCurrentDirectory(m_hConnection, szOldDirName, &dwSize)) 31 { 32 szFailedFunctionName = TEXT("FtpGetCurrentDirectory"); 33 goto ChangeFtpDirError; 34 } 35 36 // 将当前目录显示到编辑框中 37 if (!SetDlgItemText(hDlg, nDirNameId, szOldDirName)) 38 { 39 szFailedFunctionName = TEXT("SetDlgItemText"); 40 goto ChangeFtpDirError; 41 } 42 43 // 改变FTP server 中的目录路径为szNewDirName 44 if (!FtpSetCurrentDirectory(m_hConnection, szNewDirName)) 45 { 46 szFailedFunctionName = TEXT("FtpSetCurrentDirectory"); 47 goto ChangeFtpDirError; 48 } 49 50 // 显示改变目录后,该目录下的文件 51 return(DisplayFtpDir(hDlg, 0, nListBoxId)); 52 53 ChangeFtpDirError: 54 InternetErrorOut(hDlg, GetLastError(), szFailedFunctionName); 55 DisplayFtpDir(hDlg, INTERNET_FLAG_RELOAD, nListBoxId); 56 return(FALSE); 57 }
Manipulating Directories on an FTP Server
WinINet 能够很方便的在FTP server 中创建和删除目录,当然你必须给予你的FTP 的账号这样的权力。
FtpCreateDirectory 方法需要一个FTP session 的handle 和一个包括部分目录信息(绝对路径)或完整目录信息(相对路径)的非空字符串。
下面是FtpCreateDirectory 方法的使用,其中hFtpSession 是InternetConnect 方法返回的handle。
1 // 在当前目录创建一个叫"test" 的目录 2 FtpCreateDirectory( hFtpSession, "test" ); 3 4 // 在“test” 目录下创建"example" 子目录 5 FtpCreateDirectory( hFtpSession, "\\test\\example" );
FtpRemoveDirectory 方法需要一个FTP session 的handle 和一个包括部分目录信息(绝对路径)或完整目录信息(相对路径)的非空字符串。这个方法会删除FTP server 指定的目录。
以下的例子展示了FtpRemoveDirectory 方法的使用,其中hFtpSession 是InternetConnect 方法返回的handle,并且当前目录是根目录。在根目录下有一个叫“test” 目录,并且在“test”下有一个叫“example”的子目录。
1 // 删除“test” 下的“example” 子目录(会删除它底下的所有内容) 2 FtpRemoveDirectory(hFtpSession,TEXT("\\test\\example")); 3 4 // 删除“test” 目录(会删除它底下的所有内容) 5 FtpRemoveDirectory(hFtpSession, TEXT("test"));
下面这个例子在FTP server 中创建一个新的目录。这个新的目录的名称从编辑框中获得,它的ID 为nDirNameId。hConnection 是方法InternetConnect 返回的handle。这个方法的最后调用了本文章上边接到的DisplayFtpDir 方法。
1 /* 2 在FTP server 中创建一个新的目录 3 input: 4 hDlg —— 对话框的handle 5 nDirNameId —— 编辑框的ID 6 nListBoxId —— List Box 的ID 7 output: 8 true —— 创建一个新的目录成功 9 false —— 创建一个新的目录失败 10 */ 11 BOOL WINAPI CFTPClientSeal::CreateFtpDir(HWND hDlg, int nDirNameId, int nListBoxId) 12 { 13 TCHAR szNewDirName[FTP_FUNCTIONS_BUFFER_SIZE]; 14 15 // 获得编辑框中要创建的目录 16 if (!GetDlgItemText(hDlg, nDirNameId, 17 szNewDirName, 18 FTP_FUNCTIONS_BUFFER_SIZE)) 19 { 20 MessageBox(hDlg, 21 TEXT("Error: Directory Name Must Be Specified"), 22 TEXT("Create FTP Directory"), 23 MB_OK | MB_ICONERROR); 24 return(FALSE); 25 } 26 27 // 在FTP server 中创建一个新的目录 28 if (!FtpCreateDirectory(m_hConnection, szNewDirName)) 29 { 30 InternetErrorOut(hDlg, GetLastError(), 31 TEXT("FtpCreateDirectory")); 32 return(FALSE); 33 } 34 35 return(DisplayFtpDir(hDlg, 36 INTERNET_FLAG_RELOAD, 37 nListBoxId)); 38 }
下面这个例子实现了删除FTP server 中的一个目录的功能。删除的目录的名称是从编辑框中获得的,它的ID 为nDirNameId,hConnection 是方法InternetConnect 返回的handle。这个方法的最后调用了本文章上边接到的DisplayFtpDir 方法。
1 /* 2 删除FTP server 中的一个目录 3 input: 4 hDlg —— 对话框的handle 5 nDirNameId —— 编辑框的ID 6 nListBoxId —— List Box 的ID 7 output: 8 true —— 删除一个目录成功 9 false —— 删除一个目录失败 10 11 */ 12 BOOL WINAPI CFTPClientSeal::RemoveFtpDir(HWND hDlg, int nDirNameId, int nListBoxId) 13 { 14 TCHAR szDelDirName[FTP_FUNCTIONS_BUFFER_SIZE]; 15 16 // 获得编辑框中要删除的目录的名称并保存到szDelDirName 17 if (!GetDlgItemText(hDlg, nDirNameId, szDelDirName, 18 FTP_FUNCTIONS_BUFFER_SIZE)) 19 { 20 MessageBox(hDlg, 21 TEXT("Error: Directory Name Must Be Specified"), 22 TEXT("Remove FTP Directory"), 23 MB_OK | MB_ICONERROR); 24 return(FALSE); 25 } 26 27 // 删除FTP serer 中的目录szDelDirName 28 if (!FtpRemoveDirectory(m_hConnection, szDelDirName)) 29 { 30 InternetErrorOut(hDlg, GetLastError(), 31 TEXT("FtpRemoveDirectory")); 32 return(FALSE); 33 } 34 35 return(DisplayFtpDir(hDlg, 36 INTERNET_FLAG_RELOAD, nListBoxId)); 37 }
Getting Files on an FTP Server
有三种方法可以下载FTP server 的文件:
① 使用InternetOpenUrl 和InternetReadFile
② 使用FtpOpenFile 和InternetReadFile
③ 使用FtpGetFile
关于方法InternetReadFile 的更多信息请阅读文章Reading Files。如果文件的URL 是可能的,那么应用可以调用InternetOpenUrl 去连接URL,使用InternetReadFile 去下载文件。这种方法是对下载的一种强控制,当不需要对FTP server 进行其他的操作时可以使用这种方法。关于直接连接资源的更多详细信息请阅读文章Accessing URLs Directly。
如果应用已经、使用方法InternetConnect 建立FTP serssion handle,那么这个应用可以调用FtpOpenFile 去下载文件,这个方法需要一个FTP server 中存在的文件名和一个保存的路径。这种方法是对下载的强控制。
如果应用不需要对下载的强控制,那么你可以采用第二种方法。使用FtpGetFile 需要一个FTP session handle(就是InternetConnect 的返回handle),FTP server 上存在的文件名,我一个本地保存的路径。这种方法可以实现下载的现时将其内容显示,例如将下载的文本的内容显示到编辑框中。
以下的例子是从FTP file 中下载文件并保存到本地。下载的文件的名字人编辑框中获得,它的ID 为nFtpFileNameId 。保存的本地路径通过编辑框获得,它的ID 为nLocalFileNameId。 hConnection 是方法InternetConnect 返回的handle。
1 /* 2 从FTP server 中下载文件 3 input: 4 hDlg —— 对话框的handle 5 nFtpFileNameId —— 需要下载的文件的名字的编辑框的ID 6 nLocalFileNameId —— 要保存到本地的路径的编辑框ID 7 output: 8 true —— 文件下载成功 9 false —— 文件下载失败 10 */ 11 BOOL WINAPI CFTPClientSeal::GetFtpFile(HWND hDlg, 12 int nFtpFileNameId, int nLocalFileNameId) 13 { 14 // 要下载的文件的名称:例如 1.doc 15 TCHAR szFtpFileName[FTP_FUNCTIONS_BUFFER_SIZE]; 16 // 保存的路径:例如 D:\kan\2.doc(必须要有保存的名称哦,要不然会报错) 17 TCHAR szLocalFileName[FTP_FUNCTIONS_BUFFER_SIZE]; 18 DWORD dwTransferType; 19 TCHAR szBoxTitle[] = TEXT("Download FTP File"); 20 TCHAR szAsciiQuery[] = 21 TEXT("Do you want to download as ASCII text?(Default is binary)"); 22 TCHAR szAsciiDone[] = 23 TEXT("ASCII Transfer completed successfully..."); 24 TCHAR szBinaryDone[] = 25 TEXT("Binary Transfer completed successfully..."); 26 27 // 获得编辑框中要下载的文件的名称保存到szFtpFileName 中 28 if (!GetDlgItemText(hDlg, nFtpFileNameId, szFtpFileName, 29 FTP_FUNCTIONS_BUFFER_SIZE) || // 获得编辑框中保存到本地的路径 30 // 保存到szLocalFileName中 31 !GetDlgItemText(hDlg, nLocalFileNameId, szLocalFileName, 32 FTP_FUNCTIONS_BUFFER_SIZE)) 33 { 34 MessageBox(hDlg, 35 TEXT("Target File or Destination File Missing"), 36 szBoxTitle, 37 MB_OK | MB_ICONERROR); 38 return(FALSE); 39 } 40 41 dwTransferType = (MessageBox(hDlg, 42 szAsciiQuery, 43 szBoxTitle, 44 MB_YESNO) == IDYES) ? 45 FTP_TRANSFER_TYPE_ASCII : FTP_TRANSFER_TYPE_BINARY; 46 dwTransferType |= INTERNET_FLAG_RELOAD; 47 48 // 直接下载文件并保存到本地 49 if (!FtpGetFile(m_hConnection, szFtpFileName, szLocalFileName, FALSE, 50 FILE_ATTRIBUTE_NORMAL, dwTransferType, 0)) 51 { 52 InternetErrorOut(hDlg, GetLastError(), TEXT("FtpGetFile")); 53 return(FALSE); 54 } 55 // 下载完成后的提示 56 MessageBox(hDlg, (dwTransferType == 57 (FTP_TRANSFER_TYPE_ASCII | INTERNET_FLAG_RELOAD)) ? 58 szAsciiDone : szBinaryDone, szBoxTitle, MB_OK); 59 return(TRUE); 60 }
Placing Files on an FTP Server
上传文件到FTP server 有两种方法:
① 使用FtpOpenFile 和InternetWriteFile
② 使用FtpPutFile
如果客户端想要往FTP server 中的文件追加内容则需要先使用FtpOpenFile 方法打开FTP server 中的文件,接着需要使用方法InternetWriteFile 追加信息到打开的文件中。
如果文件是直接从本地上传到FTP server 中的,那么可以使用FtpPutFile 方法。
下面的例子实现将本地的文件上传到FTP server 中,本地文件的名称从编辑框中获得,它的ID 是nLocalFileNameId;保存到FTP server 中的名字从编辑框中获得,它的ID 是nFtpFileNameId。hConnection 是方法InternetConnect 返回的handle。
1 /* 2 上传文件到FTP server 中 3 input: 4 hDlg —— 对话框的handle 5 nFtpFileNameId —— 保存上传到FTP server后的名字的编辑框的ID 6 nLocalFileNameId —— 保存要上传的文件的名字的编辑框ID 7 nListBoxId —— 显示FTP server 当前目录内容的List Box ID 8 output: 9 true —— 上传文件成功 10 false —— 上传文件失败 11 */ 12 BOOL WINAPI CFTPClientSeal::PutFtpFile(HWND hDlg, int nFtpFileNameId, 13 int nLocalFileNameId, int nListBoxId) 14 { 15 // 上传到FTP server 的文件的保存名称 16 TCHAR szFtpFileName[FTP_FUNCTIONS_BUFFER_SIZE]; 17 // 本地要上传到FTP server 的文件的名称 18 TCHAR szLocalFileName[FTP_FUNCTIONS_BUFFER_SIZE]; 19 DWORD dwTransferType; 20 TCHAR szBoxTitle[] = TEXT("Upload FTP File"); 21 TCHAR szASCIIQuery[] = 22 TEXT("Do you want to upload as ASCII text? (Default is binary)"); 23 TCHAR szAsciiDone[] = 24 TEXT("ASCII Transfer completed successfully..."); 25 TCHAR szBinaryDone[] = 26 TEXT("Binary Transfer completed successfully..."); 27 28 // 获得编辑框中上传到FTP server 的文件的保存名称 29 if (!GetDlgItemText(hDlg, nFtpFileNameId, szFtpFileName, 30 FTP_FUNCTIONS_BUFFER_SIZE) || // 获得编辑框中本地要上传到 31 // FTP server 的文件的名称 32 !GetDlgItemText(hDlg, nLocalFileNameId, szLocalFileName, 33 FTP_FUNCTIONS_BUFFER_SIZE)) 34 { 35 MessageBox(hDlg, 36 TEXT("Target File or Destination File Missing"), 37 szBoxTitle, 38 MB_OK | MB_ICONERROR); 39 return(FALSE); 40 } 41 42 dwTransferType = 43 (MessageBox(hDlg, 44 szASCIIQuery, 45 szBoxTitle, 46 MB_YESNO) == IDYES) ? 47 FTP_TRANSFER_TYPE_ASCII : FTP_TRANSFER_TYPE_BINARY; 48 49 // 文件上传到FTP server 50 if (!FtpPutFile(m_hConnection, 51 szLocalFileName, 52 szFtpFileName, 53 dwTransferType, 54 0)) 55 { 56 InternetErrorOut(hDlg, GetLastError(), TEXT("FtpGetFile")); 57 return(FALSE); 58 } 59 60 MessageBox(hDlg, 61 (dwTransferType == FTP_TRANSFER_TYPE_ASCII) ? 62 szAsciiDone : szBinaryDone, szBoxTitle, MB_OK); 63 // 需要将FTP server 当前目录下的内容重新加载显示出来 64 return(DisplayFtpDir(hDlg, 65 INTERNET_FLAG_RELOAD, nListBoxId)); 66 }
Deleting Files from an FTP Server
使用FtpDeleteFile 方法删除FTP server 中的文件,当然你登录FTP server 的账户必有拥有这样的特权。
下面的例子实现了删除一个FTP server 上的文件。删除文件的名称从编辑框中获取,它的ID 为nFtpFileNameId。hConnection 是方法InternetConnect 返回的handle。
1 /* 2 删除FTP server 中指定的文件或者目录 3 input: 4 hDlg —— 对话框的handle 5 nFtpFileNameId —— 保存要删除的FTP server上的文件 6 的名称的编辑框的ID 7 nListBoxId —— 显示FTP server 当前目录内容的List Box ID 8 output: 9 true —— 删除文件成功 10 false —— 删除文件失败 11 */ 12 BOOL WINAPI CFTPClientSeal::DeleteFtpFile(HWND hDlg, 13 int nFtpFileNameId, int nListBoxId) 14 { 15 TCHAR szFtpFileName[FTP_FUNCTIONS_BUFFER_SIZE]; 16 TCHAR szBoxTitle[] = TEXT("Delete FTP File"); 17 18 if (!GetDlgItemText(hDlg, nFtpFileNameId, szFtpFileName, 19 FTP_FUNCTIONS_BUFFER_SIZE)) 20 { 21 MessageBox(hDlg, TEXT("File Name Must Be Specified!"), 22 szBoxTitle, MB_OK | MB_ICONERROR); 23 return(FALSE); 24 } 25 26 if (!FtpDeleteFile(m_hConnection, szFtpFileName)) 27 { 28 InternetErrorOut(hDlg, 29 GetLastError(), 30 TEXT("FtpDeleteFile")); 31 return(FALSE); 32 } 33 34 MessageBox(hDlg, 35 TEXT("File has been deleted"), 36 szBoxTitle, 37 MB_OK); 38 return(DisplayFtpDir(hDlg, 39 INTERNET_FLAG_RELOAD, nListBoxId)); 40 }
Renaming Files and Directories on an FTP Server
可以通过FtpRenameFile 方法重命名FTP server 上的目录或者文件。FtpRenameFile 接受部分的路径(绝对路径)或者完整的路径(相对路径)。
以下的例子实现了重命名FTP server 上的一个文件或者目录。当前文件或者目录的名字从编辑框中获得,它的ID 为nOldFileNameId,并且新的名字从另一个编辑框中获得,它的ID 为nNewFileNameId。hConnection 是方法InternetConnect 返回的handle。
1 /* 2 重命名FTP server 中指定的文件或者目录 3 input: 4 hDlg —— 对话框的handle 5 nOldFileNameId —— 保存FTP server上原文件(目录)的名称的编辑框ID 6 nNewFileNameId —— 保存新的文件(目录)名的编辑框的ID 7 nListBoxId —— 显示FTP server 当前目录内容的List Box ID 8 output: 9 true —— 重命名文件成功 10 false —— 重命名文件失败 11 */ 12 BOOL WINAPI CFTPClientSeal::RenameFtpFile(HWND hDlg, int nOldFileNameId, 13 int nNewFileNameId, int nListBoxId) 14 { 15 // 原文件(目录)名,例如:1.txt 16 TCHAR szOldFileName[FTP_FUNCTIONS_BUFFER_SIZE]; 17 // 新文件(目录)名,例如 2.txt 18 TCHAR szNewFileName[FTP_FUNCTIONS_BUFFER_SIZE]; 19 TCHAR szBoxTitle[] = TEXT("Rename FTP File"); 20 21 // 获得编辑框中原文件(目录)名并保存到szOldFileName中 22 if (!GetDlgItemText(hDlg, nOldFileNameId, szOldFileName, 23 FTP_FUNCTIONS_BUFFER_SIZE) || // 获得编辑框中新文件(目录)名并 24 // 保存到szNewFileName中 25 !GetDlgItemText(hDlg, nNewFileNameId, szNewFileName, 26 FTP_FUNCTIONS_BUFFER_SIZE)) 27 { 28 MessageBox(hDlg, 29 TEXT("Both the current and new file names must be supplied"), 30 szBoxTitle, 31 MB_OK | MB_ICONERROR); 32 return(FALSE); 33 } 34 35 // 修改指定的文件或者目录名 36 if (!FtpRenameFile(m_hConnection, szOldFileName, szNewFileName)) 37 { 38 MessageBox(hDlg, 39 TEXT("FtpRenameFile failed"), 40 szBoxTitle, 41 MB_OK | MB_ICONERROR); 42 return(FALSE); 43 } 44 // 需要将FTP server 当前目录下的内容重新加载显示出来 45 return(DisplayFtpDir(hDlg, 46 INTERNET_FLAG_RELOAD, nListBoxId)); // Remember to refresh directory listing 47 }
注意:WinINet 并不支持server 应用的开发,即它只能被用于开发客户端。关于server 应用的开发请使用Microsoft Windows HTTP Services (WinHTTP)。
以上主要是我对MSDN 中FTP Sessions 文章的翻译加上自己的一些小修改。为了方便读者能够更简单的理解,我自己将上述的方法集成了一个类,并且在VS 2013 上运行了,界面以及用户的体验方面我并没有优化,因为我还要抓紧时间去做其他的事情。
我想大家应该希望能够正常的运行这个程序吧,那么你需要在自己的操作系统上建立一个FTP server,详细请看博客Win7下的内置FTP组件的设置(图文教程)
另外就是程序中还是有一点内容需要进行更改的,就是 CFTPClientSeal::InitInternet() 这个方法中的访问的IP 地址和端口号。
附上界面的图片,其中有再处要注意的地方我已经标注出来了。

2015-01-26 22:15:21
浙公网安备 33010602011771号