# 基于WinInet的FTP文件上传实现
# 背景
对于在网络之间的文件传输,我们通常使用FTP传输协议。因为,FTP就是专门为了文件传输而生的,传输效率高,稳定性好。所以,FTP文件传输协议,是我们网络传输中常用的协议。
为了学习这方面的开发知识,自己专门写了个使用Windows提供的WinInet库实现了FTP的文件传输的基本功能。现在,我就把基于WinInet库实现的FTP文件下载和FTP文件上传分成两个文档分别进行解析。本文介绍的是基于WinInet的FTP文件上传的实现。
# 函数介绍
介绍FTP上传文件使用到的主要的WinInet库中的API函数。
## 1. InternetOpen 介绍
> 初始化一个应用程序,以使用 WinINet 函数。
>
> 函数声明
>
> ```c++
> HINTERNET InternetOpen(
> In LPCTSTR lpszAgent,
> In DWORD dwAccessType,
> In LPCTSTR lpszProxyName,
> In LPCTSTR lpszProxyBypass,
> In DWORD dwFlags
> );
> ```
>
> 参数
> lpszAgent
> 指向一个空结束的字符串,该字符串指定调用WinInet函数的应用程序或实体的名称。使用此名称作为用户代理的HTTP协议。
> dwAccessType
> 指定访问类型,参数可以是下列值之一:
>
> | Value | Meaning |
> | ---------------------------------------- | ---------------------------------------- |
> | INTERNET_OPEN_TYPE_DIRECT | 使用直接连接网络 |
> | INTERNET_OPEN_TYPE_PRECONFIG | 获取代理或直接从注册表中的配置,使用代理连接网络 |
> | INTERNET_OPEN_TYPE_PRECONFIG_ WITH_NO_AUTOPROXY | 获取代理或直接从注册表中的配置,并防止启动Microsoft JScript或Internet设置(INS)文件的使用 |
> | INTERNET_OPEN_TYPE_PROXY | 通过代理的请求,除非代理旁路列表中提供的名称解析绕过代理,在这种情况下,该功能的使用 |
>
> lpszProxyName
> 指针指向一个空结束的字符串,该字符串指定的代理服务器的名称,不要使用空字符串;如果dwAccessType未设置为INTERNET_OPEN_TYPE_PROXY,则此参数应该设置为NULL。
>
> lpszProxyBypass
> 指向一个空结束的字符串,该字符串指定的可选列表的主机名或IP地址。如果dwAccessType未设置为INTERNET_OPEN_TYPE_PROXY的 ,参数省略则为NULL。
>
> dwFlags
> 参数可以是下列值的组合:
>
> | VALUE | MEANING |
> | ------------------------ | ---------------------------------------- |
> | INTERNET_FLAG_ASYNC | 使异步请求处理的后裔从这个函数返回的句柄 |
> | INTERNET_FLAG_FROM_CACHE | 不进行网络请求,从缓存返回的所有实体,如果请求的项目不在缓存中,则返回一个合适的错误,如ERROR_FILE_NOT_FOUND |
> | INTERNET_FLAG_OFFLINE | 不进行网络请求,从缓存返回的所有实体,如果请求的项目不在缓存中,则返回一个合适的错误,如ERROR_FILE_NOT_FOUND |
>
> 返回值
> 成功:返回一个有效的句柄,该句柄将由应用程序传递给接下来的WinInet函数。
> 失败:返回NULL。
## 2. InternetConnect 介绍
> 建立 Internet 的连接。
>
> 函数声明
>
> ```c++
> HINTERNET WINAPI InternetConnect(
> HINTERNET hInternet,
> LPCTSTR lpszServerName,
> INTERNET_PORT nServerPort,
> LPCTSTR lpszUserName,
> LPCTSTR lpszPassword,
> DWORD dwService,
> DWORD dwFlags,
> DWORD dwContext
> );
> ```
>
> 参数说明
> hInternet:由InternetOpen返回的句柄。
> lpszServerName:连接的ip或者主机名
> nServerPort:连接的端口。
> lpszUserName:用户名,如无置NULL。
> lpszPassword:密码,如无置NULL。
> dwService:使用的服务类型,可以使用以下
>
> - INTERNET_SERVICE_FTP = 1:连接到一个 FTP 服务器上
> - INTERNET_SERVICE_GOPHER = 2
> - INTERNET_SERVICE_HTTP = 3:连接到一个 HTTP 服务器上
>
> dwFlags:文档传输形式及缓存标记。一般置0。
> dwContext:当使用回叫信号时, 用来识别应用程序的前后关系。
> 返回值
> 成功返回非0。如果返回0。要InternetCloseHandle释放这个句柄。
## 3. FtpOpenFile 介绍
> 启动访问FTP服务器上的远程文件以进行读取或写入。
>
> 函数声明
>
> ```c++
> HINTERNET FtpOpenFile(
> _In_ HINTERNET hConnect,
> _In_ LPCTSTR lpszFileName,
> _In_ DWORD dwAccess,
> _In_ DWORD dwFlags,
> _In_ DWORD_PTR dwContext
> );
> ```
>
> 参数
>
> - hConnect [in]
> 处理FTP会话。
>
> - lpszFileName [in]
> 指向包含要访问的文件名称的以NULL结尾的字符串。
>
> - dwAccess [in]
> 文件访问。 该参数可以是GENERIC_READ或GENERIC_WRITE,但不能同时使用。
>
> - dwFlags [in]
> 转移发生的条件。 应用程序应选择一种传输类型,以及指示文件缓存如何被控制的任何标志。传输类型可以是以下值之一。
>
> | VALUE | MEANING |
> | ----------------------------- | ---------------------------------------- |
> | FTP_TRANSFER_TYPE_ASCII | 使用FTP的ASCII(类型A)传输方法传输文件。 控制和格式化信息被转换为本地等价物。 |
> | FTP_TRANSFER_TYPE_BINARY | 使用FTP的图像(类型I)传输方法传输文件。 文件完全按照存在的方式进行传输,没有任何变化。 这是默认的传输方式。 |
> | FTP_TRANSFER_TYPE_UNKNOWN | 默认为FTP_TRANSFER_TYPE_BINARY。 |
> | INTERNET_FLAG_TRANSFER_ASCII | 以ASCII格式传输文件。 |
> | INTERNET_FLAG_TRANSFER_BINARY | 将文件作为二进制文件传输。 |
>
> 以下值用于控制文件的缓存。 应用程序可以使用这些值中的一个或多个。
>
> | VALUE | MEANING |
> | --------------------------- | ---------------------------------------- |
> | INTERNET_FLAG_HYPERLINK | 在确定是否从网络重新加载项目时,如果没有到期时间并且没有LastModified时间从服务器返回,则强制重新加载。 |
> | INTERNET_FLAG_NEED_FILE | 如果无法缓存文件,则导致创建临时文件。 |
> | INTERNET_FLAG_RELOAD | 强制从源服务器下载所请求的文件,对象或目录列表,而不是从缓存中下载。 |
> | INTERNET_FLAG_RESYNCHRONIZE | 如果资源自上次下载以来已被修改,请重新加载HTTP资源。 所有FTP资源都被重新加载。 |
>
> - dwContext [in]
> 指向包含将此搜索与任何应用程序数据相关联的应用程序定义值的变量。 这仅在应用程序已经调用InternetSetStatusCallback来设置状态回调函数时才会使用。
>
> 返回值
>
> - 如果成功则返回一个句柄,否则返回NULL。 要检索特定的错误消息,请调用GetLastError。
## 4. InternetWriteFile 介绍
> 函数声明
>
> ```c++
> BOOL InternetWriteFile(
> __in HINTERNET hFile,
> __out LPVOID lpBuffer,
> __in DWORD dwNumberOfBytesToRead,
> __out LPDWORD lpdwNumberOfBytesRead
> );
> ```
>
> 参数
>
> - hFile[in]
>
> 由InternetOpenUrl,FtpOpenFile, 或HttpOpenRequest函数返回的句柄.
>
> - lpBuffer[out]
>
> 缓冲器指针
>
> - dwNumberOfBytesToRead[in]
>
> 欲写入数据的字节量。
>
> - lpdwNumberOfBytesRead[out]
>
> 接收写入字节量的变量。该函数在做任何工作或错误检查之前都设置该值为零
>
> 返回值
> 成功:返回TRUE,失败,返回FALSE
# 实现原理
首先,我们先介绍下FTP的URL格式:
```
FTP://账号:密码@主机/子目录或文件
```
例如:ftp://admin:[email protected]/mycode/520.zip
其中,“FTP”就表示使用FTP传输数据;“账号”即�