最近做的程序需要用到HTTP上传文件和表单提交的功能,在各大网站这方面的资料实在是少的可怜。找了好久,最后在国外的一家网站找到了相关的文档。做完工程后总结,开始的主要问题出在报头的格式上。现将报头的写法和相关的函数摘出来。程序的实现是用CHTTP类。
//报头格式,类似于这样--------------------------------------
cpp 代码
- LPCSTR szDefUsrAgent = "Ryeol HTTP Client Class" ;
- LPCSTR szGET = "GET" ;
- LPCSTR szPost = "POST" ;
- LPCSTR szHTTP = "HTTP://" ;
- LPCSTR szHTTPS = "HTTPS://" ;
- LPCSTR szSlash = "/" ;
- LPCSTR szCacheControl = "Cache-Control" ;
- LPCSTR szNoCache = "no-cache" ;
- LPCSTR szContentType = "Content-Type" ;
- LPCSTR szMultipartFormDataBoundary = "multipart/form-data; boundary=" ;
- LPCSTR szFormUrlEncoded = "application/x-www-form-urlencoded" ;
- LPCSTR szDefBoundary = "----FB3B405B7EAE495aB0C0295C54D4E096-" ; //这行分隔符很重要的,开始的程序就是没加这个,一直不行。
- LPCSTR szDefUploadContType = "multipart/form-data; boundary=" "----FB3B405B7EAE495aB0C0295C54D4E096-" ;
- LPCSTR szNULL = "NULL" ;
- LPCSTR szEmptyString = "" ;
- LPCSTR szColonSlashSlash = "://" ;
- 下面是提出来的一段函数代码:
- void CHttpToolA::AddHeader (HINTERNET hRequest, LPCSTR szName, LPCSTR szValue, UINT /* CodePage */)
- throw (Exception &)
- {
- HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::AddHeader: hRequest can not be NULL.") ;
- HTTPTOOL_ASSERT (szName != NULL, "CHttpToolA::AddHeader: szName can not be NULL.") ;
- ::SafeInt<size_t> cbHeader ;
- ::SafeInt<DWORD> cchHeader ;
- try {
- cbHeader = ::strlen (szName) ;
- cbHeader += szValue ? ::strlen (szValue) : 0 ;
- cbHeader += (4 + 1) ; // for ": ", "\r\n", '\0'
- cchHeader = cbHeader - 1 ;
- } catch (::SafeIntException & e) {
- ThrowException (e) ;
- }
- PSTR szHeader = (PSTR) ::malloc (sizeof (CHAR) * (cbHeader.Value ())) ;
- if ( szHeader == NULL )
- ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
- ::strcpy (szHeader, szName) ;
- ::strcat (szHeader, ": ") ;
- ::strcat (szHeader, szValue ? szValue : "") ;
- ::strcat (szHeader, "\r\n") ;
- // Adds a header
- if ( !::HttpAddRequestHeadersA (
- hRequest
- , szHeader // headers to append to the request.
- , cchHeader.Value () // header length
- , HTTP_ADDREQ_FLAG_ADD // flags
- )
- ) {
- SAFEFREE (szHeader) ;
- ThrowException (HTTPCLIENT_ERR_HTTPADDREQUESTHEADERS_FAILED, ::GetLastError ()) ;
- }
- SAFEFREE (szHeader) ;
- }
- void CHttpToolA::SendRequest (HINTERNET hRequest, LPCSTR szPosted, UINT /* CodePage */)
- throw (Exception &)
- {
- HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::SendRequest: hRequest can not be NULL.") ;
- ::SafeInt<DWORD> cchPosted ;
- try {
- cchPosted = szPosted ? ::strlen (szPosted) : 0 ;
- } catch (::SafeIntException & e) {
- ThrowException (e) ;
- }
- if ( !::HttpSendRequestA (
- hRequest
- , NULL // Additional header
- , 0 // The length of the additional header
- , (void *) szPosted // A posted data
- , cchPosted.Value () // The length of the posted data
- ) )
- ThrowException (HTTPCLIENT_ERR_HTTPSENDREQUEST_FAILED, ::GetLastError ()) ;
- }
- void CHttpToolA::SendRequestEx (HINTERNET hRequest, DWORD dwPostedSize)
- throw (Exception &)
- {
- HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::SendRequestEx: hRequest can not be NULL.") ;
- INTERNET_BUFFERSA BufferIn;
- BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS ); // Must be set or error will occur
- BufferIn.Next = NULL;
- BufferIn.lpcszHeader = NULL;
- BufferIn.dwHeadersLength = 0;
- BufferIn.dwHeadersTotal = 0;
- BufferIn.lpvBuffer = NULL;
- BufferIn.dwBufferLength = 0;
- BufferIn.dwBufferTotal = dwPostedSize; // This is the only member used other than dwStructSize
- BufferIn.dwOffsetLow = 0;
- BufferIn.dwOffsetHigh = 0;
- if ( !::HttpSendRequestExA (
- hRequest
- , &BufferIn
- , NULL
- , 0
- , 0
- ) )
- ThrowException (HTTPCLIENT_ERR_HTTPSENDREQUESTEX_FAILED, ::GetLastError ()) ;
- }
- // The returned string must be freed by using the ::free () function.
- LPSTR CHttpToolA::CreateUploadBoundary (void)
- throw ()
- {
- GUID guid ;
- if ( FAILED ( ::CoCreateGuid (&guid)) )
- return NULL ;
- PSTR szBoundary = (PSTR) ::malloc (sizeof (CHAR) * 44) ;
- if ( szBoundary == NULL )
- return NULL ;
- ::sprintf (szBoundary, "----LYOUL-%.08x%.04x%.04x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x-"
- , guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1]
- , guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
- return szBoundary ;
- }
- void CHttpClientT<HttpTool, HttpEncoder>::_StartUploadContext (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl
- , DWORD dwFlags, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd)
- throw (Exception &)
- {
- // Closes any existing Post Context
- _EndPostContext () ;
- BOOL bBorrowedInternet = TRUE ;
- BOOL bBorrowedConnection = TRUE ;
- HINTERNET hRequest = NULL ;
- HANDLE * ahFileHandles = NULL ;
- LPSTR * aszMimeTypes = NULL ;
- ::SafeInt<DWORD> nPostedFileCount = 0 ;
- try {
- // Calculates the nubmer of bytes to upload
- ::SafeInt<size_t> nTotalByte = 0 ;
- ::SafeInt<size_t> nValuesTotalByte = 0 ;
- size_t cchBoundary = HttpTool::StringLen (_GetUploadBoundary ()) ;
- PCSZ szFirstParamName = NULL ;
- PCSZ szFirstParamFileName = NULL ;
- size_t cbFirstParamValue = 0 ;
- BOOL bFirstParamIsFile = FALSE ;
- if ( !m_mapParam.Empty () ) {
- try {
- ConstMapIter iter ;
- // Get the number of files
- for (iter = m_mapParam.Begin (); iter != m_mapParam.End (); ++iter) {
- if ( (iter->second).dwFlag & ParamFile )
- nPostedFileCount++ ;
- }
- if ( nPostedFileCount.Value () ) {
- // Allocates memory for handles and MimeTypes of the uploaded files
- ahFileHandles = (HANDLE *) ::calloc (nPostedFileCount.Value (), sizeof (HANDLE)) ;
- if ( ahFileHandles == NULL )
- HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
- // Initializes the file handles
- for (DWORD i = 0; i < nPostedFileCount; i++)
- ahFileHandles[i] = INVALID_HANDLE_VALUE ;
- aszMimeTypes = (LPSTR *) ::calloc (nPostedFileCount.Value (), sizeof (LPSTR)) ;
- if ( aszMimeTypes == NULL )
- HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
- }
- /*
- Calculates the total upload size
- <1> The number of bytes to upload If the parameter is not a file
- = strlen ("--") + strlen (Boundary) + strlen ("\r\n")
- + strlen ("Content-Disposition: form-data; name=\"\"\r\n\r\n")
- + strlen (The name of the parameter) + strlen (The value of the parameter) + strlen ("\r\n") ;
- <2> The number of bytes to upload If the parameter is a file
- = strlen ("--") + strlen (Boundary) + strlen ("\r\n")
- + strlen ("Content-Disposition: form-data; name=\"\"; filename=\"\"\r\n")
- + strlen (The name of the parameter) + strlen (The value of the parameter)
- + strlen ("Content-Type: \r\n\r\n") + strlen (The Mime Type of the file)
- + The length of the file + strlen ("\r\n")
- The last boundary
- = strlen ("--") + strlen (Boundary) + strlen ("--\r\n")
- The total upload size
- = ( <1> * The number of normal parameters)
- + ( <2> * The number of file parameters)
- + The last boundary
- */
- ::SafeInt<size_t> cbValue ;
- DWORD nFileIdx = 0 ;
- nTotalByte = 0 ;
- nValuesTotalByte = 0 ;
- for (iter = m_mapParam.Begin (); iter != m_mapParam.End (); ++iter) {
- if ( !((iter->second).dwFlag & ParamFile) ) {
- // If the parameter is not a file
- // = strlen ("--") + strlen (Boundary) + strlen ("\r\n")
- // + strlen ("Content-Disposition: form-data; name=\"\"\r\n\r\n")
- // + strlen (The name of the parameter) + strlen (The value of the parameter) + strlen ("\r\n") ;
- nTotalByte += (cchBoundary + 4) ;
- nTotalByte += 43 ;
- if ( m_bUseUtf8 ) {
- nTotalByte += _Utf8EncodeLen (iter->first) ;
- cbValue = _Utf8EncodeLen ((iter->second).szValue) ;
- } else {
- nTotalByte += _String2AnsiLen (iter->first) ;
- cbValue = _String2AnsiLen ((iter->second).szValue) ;
- }
- nTotalByte += cbValue ;
- nValuesTotalByte += cbValue ;
- nTotalByte += 2 ;
- // Saves the state of the first parameter
- if ( iter == m_mapParam.Begin () ) {
- szFirstParamName = iter->first ;
- cbFirstParamValue = cbValue.Value () ;
- bFirstParamIsFile = FALSE ;
- szFirstParamFileName = NULL ;
- }
- } else {
- // If the parameter is a file
- // = strlen ("--") + strlen (Boundary) + strlen ("\r\n")
- // + strlen ("Content-Disposition: form-data; name=\"\"; filename=\"\"\r\n")
- // + strlen (The name of the parameter) + strlen (The value of the parameter)
- // + strlen ("Content-Type: \r\n\r\n") + strlen (The Mime Type of the file)
- // + The length of the file + strlen ("\r\n")
- nTotalByte += (cchBoundary + 4) ;
- nTotalByte += 54 ;
- if ( m_bUseUtf8 ) {
- nTotalByte += _Utf8EncodeLen (iter->first) ;
- nTotalByte += _Utf8EncodeLen ((iter->second).szValue) ;
- } else {
- nTotalByte += _String2AnsiLen (iter->first) ;
- nTotalByte += _String2AnsiLen ((iter->second).szValue) ;
- }
- nTotalByte += 18 ;
- // Get the file size and MimeType
- cbValue = 0 ;
- if ( (iter->second).szValue ) {
- // Open the file
- ahFileHandles[nFileIdx] = HttpTool::OpenFile ((iter->second).szValue) ;
- // Get the file size
- if ( ahFileHandles[nFileIdx] != INVALID_HANDLE_VALUE )
- cbValue = HttpTool::GetFileSize (ahFileHandles[nFileIdx], (iter->second).szValue) ;
- }
- // Throws an exception
- if ( m_bStrictFileCheck && (ahFileHandles[nFileIdx] == INVALID_HANDLE_VALUE) )
- HttpTool::ThrowException (HTTPCLIENT_ERR_OPENFILE_FAILED, ::GetLastError (), (iter->second).szValue) ;
- // Get the MimeType of the file
- aszMimeTypes[nFileIdx] = HttpTool::GetMimeType (ahFileHandles[nFileIdx], m_nAnsiCodePage) ;
- nTotalByte += CHttpToolA::StringLen (aszMimeTypes[nFileIdx]) ;
- nTotalByte += cbValue ;
- nValuesTotalByte += cbValue ;
- nTotalByte += 2 ;
- nFileIdx++ ;
- // Saves the state of the first parameter
- if ( iter == m_mapParam.Begin () ) {
- szFirstParamName = iter->first ;
- cbFirstParamValue = cbValue.Value () ;
- bFirstParamIsFile = TRUE ;
- szFirstParamFileName = (iter->second).szValue ;
- }
- }
- }
- // The last boundary
- // = strlen ("--") + strlen (Boundary) + strlen ("--\r\n")
- nTotalByte += (cchBoundary + 6) ;
- } catch (::SafeIntException & e) {
- HttpTool::ThrowException (e) ;
- }
- } else {
- // The total upload size
- // = (strlen ("\r\n") + strlen ("--") + strlen (Boundary) + strlen ("--\r\n")
- nTotalByte = cchBoundary + 8 ;
- nValuesTotalByte = 0 ;
- }
- ::SafeInt<DWORD> dwTotalByte ;
- try {
- dwTotalByte = nTotalByte ;
- } catch (::SafeIntException & e) {
- HttpTool::ThrowException (e) ;
- }
- // Get WinInet handles
- if ( hInternet == NULL ) {
- hInternet = OpenInternet () ;
- bBorrowedInternet = FALSE ;
- }
- if ( hConnection == NULL ) {
- hConnection = OpenConnection (hInternet, szUrl, szUsrName, szUsrPwd) ;
- bBorrowedConnection = FALSE ;
- }
- hRequest = OpenRequest (hConnection, HttpTool::szPost, szUrl, dwFlags, szReferer) ;
- AddRequestHeader (hRequest) ; // Adds user's custom header
- // Adds the Content-Type header
- HttpTool::AddHeader (hRequest, HttpTool::szContentType, _GetUploadContType (), m_nAnsiCodePage) ;
- // Make a connection to the server
- HttpTool::SendRequestEx (hRequest, dwTotalByte.Value ()) ;
- // Activates the Post Context
- m_objPostStat._MakeActive (nTotalByte.Value (), nValuesTotalByte.Value (), m_mapParam.Count (), nPostedFileCount.Value ()) ;
- m_bBorrowedInternet = bBorrowedInternet ;
- m_hInternet = hInternet ;
- m_bBorrowedConnection = bBorrowedConnection ;
- m_hConnection = hConnection ;
- m_hRequest = hRequest ;
- m_ahPostedFiles = ahFileHandles ;
- ahFileHandles = NULL ;
- m_aszMimeTypes = aszMimeTypes ;
- aszMimeTypes = NULL ;
- m_bIsPost = FALSE ;
- // Saves the initial Post context
- if ( !m_mapParam.Empty () ) {
- m_objInitialStat = m_objPostStat ;
- // It always does not throw an overflow exception.
- // So it's safe. (doesn't need to restore the internal states)
- m_objInitialStat._StartNewEntry (szFirstParamName, cbFirstParamValue
- , bFirstParamIsFile, szFirstParamFileName) ;
- }
- } catch (Exception &) {
- HttpTool::CloseRequest (hRequest) ;
- if ( !bBorrowedConnection ) HttpTool::CloseRequest (hConnection) ;
- if ( !bBorrowedInternet ) HttpTool::CloseRequest (hInternet) ;
- for (DWORD i = 0; i < nPostedFileCount; i++) {
- if ( ahFileHandles ) {
- if ( ahFileHandles[i] != INVALID_HANDLE_VALUE ) {
- ::CloseHandle (ahFileHandles[i]) ;
- ahFileHandles[i] = INVALID_HANDLE_VALUE ;
- }
- }
- if ( aszMimeTypes )
- SAFEFREE ( (aszMimeTypes[i]) ) ;
- }
- SAFEFREE (ahFileHandles) ;
- SAFEFREE (aszMimeTypes) ;
- throw ;
- }
- }
- CHttpClientT<HttpTool, HttpEncoder>::_ProceedUploadContext (DWORD nDesired)
- throw (Exception &)
- {
- // If the Post context is not started
- if ( !m_objPostStat.IsActive () )
- HttpTool::ThrowException (HTTPCLIENT_ERR_POST_NOT_STARTED) ;
- HTTPCLIENT_ASSERT (m_hInternet != NULL, "CHttpClientT::_ProceedUploadContext: m_hInternet can not be NULL.") ;
- HTTPCLIENT_ASSERT (m_hConnection != NULL, "CHttpClientT::_ProceedUploadContext: m_hConnection can not be NULL.") ;
- HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpClientT::_ProceedUploadContext: m_hRequest can not be NULL.") ;
- HTTPCLIENT_ASSERT (nDesired != 0, "CHttpClientT::_ProceedUploadContext: nDesired can not be zero.") ;
- try {
- // If all parameters are posted
- // releases the Post context
- if ( m_objPostStat._IsComplete () ) {
- HttpTool::EndRequest (m_hRequest) ;
- return _ReleasePostResponse () ;
- }
- LPCSTR szBoundary = _GetUploadBoundaryA () ;
- // If there is no parameter to upload
- if ( m_objPostStat.TotalCount () == 0 ) {
- // Writes the last boundary
- _WritePost ("\r\n--") ;
- _WritePost (szBoundary) ;
- _WritePost ("--\r\n") ;
- return NULL ;
- }
- // If the current parameter is completed
- if ( m_objPostStat.CurrParamIsComplete () ) {
- // If all parameters are sent
- if ( m_objPostStat.TotalCount () == m_objPostStat.PostedCount () ) {
- // Writes the last boundary
- _WritePost ("--") ;
- _WritePost (szBoundary) ;
- _WritePost ("--\r\n") ;
- return NULL ;
- }
- DWORD nNextIdx = m_objPostStat.PostedCount () ;
- DWORD nNextFileIdx = m_objPostStat.PostedFileCount () ;
- PCSZ szEntryFile = NULL ;
- ::SafeInt<size_t> nEntryValueTotalByte = 0 ;
- ConstMapIter iter = m_mapParam.Begin () ;
- for (size_t i = 0; i < nNextIdx; i++, ++iter) ;
- if ( (iter->second).dwFlag & ParamFile ) {
- // If the parameter is a file parameter
- szEntryFile = (iter->second).szValue ;
- if ( m_ahPostedFiles[nNextFileIdx] != INVALID_HANDLE_VALUE )
- nEntryValueTotalByte = HttpTool::GetFileSize (m_ahPostedFiles[nNextFileIdx], szEntryFile) ;
- // Writes the boundary and headers
- _WritePost ("--") ;
- _WritePost (szBoundary) ;
- _WritePost ("\r\n") ;
- _WritePost ("Content-Disposition: form-data; name=\"") ;
- _WritePost (m_bUseUtf8, iter->first) ; // Writes form name
- _WritePost ("\"; filename=\"") ;
- _WritePost (m_bUseUtf8, szEntryFile) ; // Writes file path
- _WritePost ("\"\r\nContent-Type: ") ;
- _WritePost (m_aszMimeTypes[nNextFileIdx]) ;
- _WritePost ("\r\n\r\n") ;
- } else {
- // If the parameter is not a file parameter
- if ( (iter->second).szValue && (iter->second).szValue[0] != '\0' ) {
- LPCSTR szPostedValue ;
- BOOL bNeedToFree ;
- if ( m_bUseUtf8 ) {
- szPostedValue = _Utf8Encode ((iter->second).szValue) ;
- bNeedToFree = TRUE ;
- }