使用WinINet API通过HTTP PUT上传HLS音频块时出现部分上传问题
我仔细看了你的WinINet PUT上传代码,遇到这种“本地写入字节数显示正常,但服务器那边偶尔接收到不完整音频块”的随机问题,大概率是代码里的几个细节处理不到位导致的,下面给你拆解问题和修复方案:
1. 修复InternetWriteFile的循环逻辑错误
你的代码里这段写入循环的判断逻辑完全搞反了,这是最可能导致数据丢失的核心问题:
while(dwBytesRead < dwBytesWritten && bRet) { sprintf(logDump,"dwBytesRead %d dwBytesWritten %d %s",dwBytesRead,dwBytesWritten,szFile); WriteLog(mLogFile,logDump); bRet=InternetWriteFile( m_hRequest, pBuffer+dwBytesWritten, dwBytesRead - dwBytesWritten ,&dwBytesWritten); }
正常逻辑应该是:当实际写入的字节数dwBytesWritten小于要写入的dwBytesRead时,才需要继续写入剩余数据。你现在的条件会导致本该补写剩余数据的场景直接跳过,或者错误进入循环,最终部分数据没写完就终止传输。
修正后的循环应该是这样,用一个变量跟踪总写入字节数,确保把本次读取的所有数据都写完:
DWORD totalWritten = 0; while (totalWritten < dwBytesRead && bRet) { bRet = InternetWriteFile(m_hRequest, pBuffer + totalWritten, dwBytesRead - totalWritten, &dwBytesWritten); if (bRet) { totalWritten += dwBytesWritten; sprintf(logDump,"Written %d bytes, total %d/%d for %s", dwBytesWritten, totalWritten, dwBytesRead, szFile); WriteLog(mLogFile,logDump); } else { int error = GetLastError(); sprintf(logDump,"InternetWriteFile failed with %lu while writing %s", error, szFile); WriteLog(mLogFile,logDump); bErrorFlag = true; break; } }
2. 规范请求句柄的创建与销毁
你的m_hRequest是类成员变量,当前代码没有在每次请求结束(包括异常退出)时显式关闭它。如果上一次请求出现异常,残留的句柄可能会干扰下一次上传,引发随机问题。
建议在每个break或函数返回前,都主动清理请求句柄:
// 在每个退出分支添加: if (m_hRequest != NULL) { InternetCloseHandle(m_hRequest); m_hRequest = NULL; }
不要依赖CloseHTTPSession来间接清理,确保每次上传都使用全新的请求句柄。
3. 移除不必要的请求标志
你在dwFlags里添加了INTERNET_FLAG_RELOAD,这个标志的作用是强制从服务器重新获取资源,对于PUT上传请求来说完全没必要,甚至可能干扰正常的传输流程。建议去掉这个标志:
DWORD dwFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI | INTERNET_FLAG_SECURE;
4. 增加上传后的数据校验逻辑
既然本地写入无错误但服务器接收不完整,建议在上传成功后,主动发起HEAD请求校验服务器上的文件大小,和本地文件对比,如果不一致就触发重试:
// 在HttpEndRequest成功且状态码正常后添加: DWORD remoteSize = 0; DWORD remoteSizeSize = sizeof(DWORD); HINTERNET hCheckRequest = HttpOpenRequest(m_hHttpSession, "HEAD", szPostURL, "HTTP/1.1", NULL, (const char**)"*/*\0", dwFlags, 1); if (hCheckRequest != NULL) { if (HttpSendRequest(hCheckRequest, NULL, 0, NULL, 0)) { if (HttpQueryInfo(hCheckRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &remoteSize, &remoteSizeSize, NULL)) { if (remoteSize != iFileSize) { sprintf(logDump,"Remote size %d != local size %d for %s, retrying...", remoteSize, iFileSize, szFile); WriteLog(mLogFile,logDump); bErrorFlag = true; // 这里可以触发重试逻辑,或者标记该文件需要重新上传 } } } InternetCloseHandle(hCheckRequest); }
5. 完善文件大小获取的校验
虽然你的音频块很小,不会触发大文件问题,但还是建议对GetFileSize的返回值做校验,避免因意外情况导致缓冲区设置错误:
DWORD fileSizeLow = GetFileSize(hFile, NULL); if (fileSizeLow == INVALID_FILE_SIZE) { int error = GetLastError(); sprintf(logDump,"GetFileSize failed with %lu for %s", error, szFile); WriteLog(mLogFile,logDump); bErrorFlag = true; CloseHandle(hFile); break; } BufferIn.dwBufferTotal = fileSizeLow; iFileSize = fileSizeLow;
以上调整中,修复InternetWriteFile的循环逻辑是最关键的一步,应该能解决你遇到的随机数据不完整问题。
内容的提问来源于stack exchange,提问作者niti

