将进度条添加到 wininet http 上传 C++

Adding progressbar to wininet http upload C++

我有使用 POST 方法在 HTTP 服务器上上传二进制文件的代码:

http_upload_file(PCHAR szServer, PCHAR szScript, PCHAR szParam, PCHAR szValue, PCHAR szFile)
{
    PCHAR szHeaders = "Content-Type: multipart/form-data; boundary=----qwerty";
    PCHAR szData    = "------qwerty\r\n"
                      "Content-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n"
                      "------qwerty\r\n"
                      "Content-Disposition: form-data; name=\"files[]\"; filename=\"%s\"\r\n"
                      "Content-Type: application/octet-stream\r\n"
                      "Content-Transfer-Encoding: binary\r\n\r\n";
    PCHAR szDataEnd = "\r\n------qwerty--\r\n";
    char  szHeader[512];

    HINTERNET hSession, hConnect, hRequest;
    DWORD     dwFileSize, dwBytesRead, dwContentLength,dwBytesWritten;

    hSession = InternetOpen(NULL, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);

    if (hSession)
    {
        hConnect = InternetConnect(hSession, szServer, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP,0, 0);

        if (hConnect)
        {
            hRequest = HttpOpenRequest(hConnect, "POST", szScript, NULL, NULL, 0, 0, 0);

            if (hRequest)
            {
                HANDLE hFile = CreateFile(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);

                if (hFile != INVALID_HANDLE_VALUE)
                {
                    dwFileSize      = GetFileSize(hFile, NULL);
                    wsprintf(szHeader, szData, szParam, szValue, szFile);
                    dwContentLength = lstrlen(szHeader) + dwFileSize + lstrlen(szDataEnd);
                    LPBYTE pBuf     = (LPBYTE)malloc(dwContentLength);
                    CopyMemory(&pBuf[0], szHeader, lstrlen(szHeader));
                    ReadFile(hFile, &pBuf[lstrlen(szHeader)], dwFileSize, &dwBytesRead, NULL);
                    CopyMemory(&pBuf[lstrlen(szHeader) + dwFileSize], szDataEnd, lstrlen(szDataEnd));
                    HttpSendRequest(hRequest, szHeaders, lstrlen(szHeaders), pBuf, dwContentLength);
                    CloseHandle(hFile);
                    free(pBuf);
                }
            }

            InternetCloseHandle(hRequest);
        }

        InternetCloseHandle(hConnect);
    }

    InternetCloseHandle(hSession);
}

它工作正常,但我想在文件上传时添加一些进度信息。

我可以在 HttpSendRequest(hRequest, szHeaders, lstrlen(szHeaders), pBuf, dwContentLength); 执行时获取传输内容的大小吗?问题是在上传大文件时我的 Form 卡住了,用户看不到已经上传了多少数据。因此我想添加 ProggessBar 用于显示数据传输的大小,但不知道如何获取此传输数据...

我很乐意提供任何建议。

您可以将上传分成小块,并在每个小块发送后更新进度条。

像往常一样调用 InternetConnect()HttpOpenRequest()

而不是 HttpSendRequest() 调用 HttpSendRequestEx(). Define the total file size via the lpBuffersIn parameter. For parameter dwFlags pass the value HSR_INITIATE to tell the API that you want iterative data transfer. There is a doc bug in the reference page, the dwFlags parameter is not reserved. Everyone is using it, for example MFC

INTERNET_BUFFERS buffer{ sizeof(buffer) };
buffer.dwBufferTotal = totalFileSizeInBytes;
BOOL success = HttpSendRequestEx( hRequest, &buffer, nullptr, HSR_INITIATE, 0 );

循环调用InternetWriteFile()来分段上传文件。使用不太小的缓冲区大小以减少 API(比如 16k)的开销。每次调用此 API 后,您可以测量当前时间和上次进度条更新时间之间的差异,并且仅在差异足够大(比如 1/10 秒)以减少 API 开销时才更新进度条更新 GUI。

上传完文件后,通过调用 HttpEndRequest() 告诉它 API。当然关闭句柄,检查错误等等。pp.

所有这些都应该在单独的线程中完成,以保持 GUI 线程的响应。通过使用消息 ID WM_APP + x 调用 PostMessage() 向 GUI 线程报告进度。您可以使用 wParamlParam 传递进度信息。然后只有在 GUI 线程中你才会实际更新进度条以保持业务逻辑和 GUI 清楚地分开。