HTTP 上传的报头格式

最近做的程序需要用到HTTP上传文件和表单提交的功能,在各大网站这方面的资料实在是少的可怜。找了好久,最后在国外的一家网站找到了相关的文档。做完工程后总结,开始的主要问题出在报头的格式上。现将报头的写法和相关的函数摘出来。程序的实现是用CHTTP类。

//报头格式,类似于这样--------------------------------------

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 ;
     } else
      // Converts into a Ansi string
      szPostedValue = _String2Ansi ((iter->second).szValue, bNeedToFree) ;

     nEntryValueTotalByte = CHttpToolA::StringLen (szPostedValue) ;
     _SetPostedValue (szPostedValue, bNeedToFree) ;
    } else {
     _SetPostedValue (NULL, FALSE) ;
     nEntryValueTotalByte = 0 ;
    }

    // Writes the boundary and headers
    _WritePost ("--") ;
    _WritePost (szBoundary) ;
    _WritePost ("/r/n") ;
    _WritePost ("Content-Disposition: form-data; name=/"") ;
    _WritePost (m_bUseUtf8, iter->first) ;  // Write form name
    _WritePost ("/"/r/n/r/n") ;
   }

   // Starts a new parameter
   m_objPostStat._StartNewEntry (iter->first, nEntryValueTotalByte.Value ()
    , static_cast<BOOL> ((iter->second).dwFlag & ParamFile), szEntryFile) ;

   if ( nEntryValueTotalByte == 0 )
    _WritePost ("/r/n") ;
   return NULL ;
  }

  // Writes the requested number of bytes
  DWORD  cbToWrite = nDesired ;
  if ( cbToWrite > m_objPostStat.CurrParamRemainByte () )
   cbToWrite = static_cast<DWORD> (m_objPostStat.CurrParamRemainByte ()) ;

  DWORD   cbWritten = 0 ;

  if ( m_objPostStat.CurrParamIsFile () ) {
   _InitPostCntxBuff () ;

   DWORD   cbBuff ;    // The number of bytes to read
   DWORD   cbRead ;    // The number of bytes read from the file
   DWORD   nFileIdx = m_objPostStat.PostedFileCount () - 1 ;

   while ( cbWritten < cbToWrite ) {
    cbBuff = cbToWrite - cbWritten >
     HTTPCLIENT_POSTCNTX_BUFF_SIZE ? HTTPCLIENT_POSTCNTX_BUFF_SIZE : cbToWrite - cbWritten ;

    // Read from file
    if ( !::ReadFile (m_ahPostedFiles[nFileIdx]
    , m_pbyCntxBuff
     , cbBuff
     , &cbRead
     , NULL)
     )
     HttpTool::ThrowException (HTTPCLIENT_ERR_READFILE_FAILED, ::GetLastError ()) ;

    // cbBuff and cbRead must be the same
    if ( cbBuff != cbRead )
     HttpTool::ThrowException (HTTPCLIENT_ERR_READ_UNEXPECTED_SIZE) ;

    _WritePost (m_pbyCntxBuff, cbBuff, TRUE) ;
    cbWritten += cbBuff ;
   }
  } else {
   _WritePost (
    reinterpret_cast<const BYTE *> (m_szPostedValue + m_objPostStat.CurrParamPostedByte ())
    , cbToWrite, TRUE) ;
   cbWritten = cbToWrite ;
  }

  // If all bytes are written, writes the last new line character
  if ( m_objPostStat.CurrParamRemainByte () == 0 )
   _WritePost ("/r/n") ;

 } catch (::SafeIntException & e) {
  _EndPostContext () ;   // Aborts the POST context if an error occurs
  HttpTool::ThrowException (e) ;
 } catch (Exception &) {
  _EndPostContext () ;   // Aborts the POST context if an error occurs
  throw ;
 }
 return NULL ;
}

 

这个类基本的实现就是这样,由于没时间,还没来的及去深入研究,等过几天试验一个简单的上传类再把代码发上来。

具体的代码大家可以去http://www.codeproject.com/去找。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值