为 URLOpenBlockingStreamW() 输出动态分配 buffer[]

Dynamically allocate buffer[] for URLOpenBlockingStreamW() output

我是 C++ 的超级新手,我正在尝试从 URL 下载可执行文件并将其写入磁盘。

我有以下代码可以成功下载文件并将其存储在内存中。我遇到的问题是将其写入磁盘。

我很确定这是我创建缓冲区的地方,下载的数据在转到新文件之前将写入缓冲区。

char buffer[4096];

4096 是我从别处得到的模板代码中的任意数字。我想不通或不知道的是如何根据 &pStream.

处的数据大小动态分配缓冲区大小

我曾尝试使用诸如 sizeof() 之类的函数,但这些函数只是让我获取内存地址大小,而不是值本身。

或者,是否有更好的方法来尝试完成此下载和写入?

#include <Windows.h>
#include <Urlmon.h>   // URLOpenBlockingStreamW()
#include <atlbase.h>  // CComPtr
#include <iostream>
#include "download.h"
#include <fstream>
#include <assert.h>
#include <chrono>
#include <thread>
#pragma comment( lib, "Urlmon.lib" )

struct ComInit
{
    HRESULT hr;
    ComInit() : hr(::CoInitialize(nullptr)) {}
    ~ComInit() { if (SUCCEEDED(hr)) ::CoUninitialize(); }
};

int download_file()
{
    ComInit init;
    HRESULT hr;

    // use CComPtr so you don't have to manually call Release()
    CComPtr<IStream> pStream;
    bool success = false;

    while (success == false)
    {
        try {
            // Open the HTTP request.
            hr = URLOpenBlockingStreamW(nullptr, L"https://www.foo.bar/download/somefile.exe", &pStream, 0, nullptr);
            if (FAILED(hr))
            {
                std::cout << "ERROR: Could not connect. HRESULT: 0x" << std::hex << hr << std::dec << "\n";
            }
            else
            {
                success = true;
            }
        }
        catch (const std::exception& ex) {
            std::cout << ex.what();
        }
    }


    // Download the response and write it to stdout.
    char buffer[4096]; // Issue is here I think
    do
    {
        DWORD bytesRead = 0;
        hr = pStream->Read(buffer, sizeof(buffer), &bytesRead);

        if (bytesRead > 0)
        {
            //std::cout.write(buffer, bytesRead);
            std::ofstream file;
            file.open("some_path_dot_exe", std::ios_base::binary);
            assert(file.is_open());
            for (int i = 0; i < sizeof(buffer) / sizeof(buffer[0]); ++i)
                file.write((char*)(buffer + i * sizeof(buffer[0])), sizeof(buffer[0]));
            file.close();
        }
    } while (SUCCEEDED(hr) && hr != S_FALSE);

    if (FAILED(hr))
    {
        std::cout << "ERROR: Download failed. HRESULT: 0x" << std::hex << hr << std::dec << "\n";
        return 2;
    }

    std::cout << "\n";

    return 0;
}

URLOpenBlockingStreamW() 给你的 IStream 不能保证能够预先给你完整的文件大小,所以如果你想在内存中保存整个文件,你会必须使用 std::vector 或其他 dynamically-growing 缓冲区。

不过,您实际上不需要将整个文件保存在内存中只是为了将其保存到磁盘,您可以使用固定数组并在下载时将其写入磁盘,就像您已经在做的那样。

真正的问题是,您在每个 Read() 打开和关闭文件,清除所有以前写入的数据。而您忽略了 Read() 给您的 bytesRead 值。

您需要打开文件一次,在下载完成之前一直保持打开状态,并且每次 write().

写入的内容不要超过缓冲区中的实际内容

试试这个:

#include <Windows.h>
#include <Urlmon.h>   // URLOpenBlockingStreamW()
#include <atlbase.h>  // CComPtr
#include <iostream>
#include "download.h"
#include <fstream>
#include <assert.h>
#include <chrono>
#include <thread>
#pragma comment( lib, "Urlmon.lib" )

struct ComInit
{
    HRESULT hr;
    ComInit() : hr(::CoInitialize(nullptr)) {}
    ~ComInit() { if (SUCCEEDED(hr)) ::CoUninitialize(); }
};

int download_file()
{
    ComInit init;
    HRESULT hr;

    // use CComPtr so you don't have to manually call Release()
    CComPtr<IStream> pStream;

    do
    {
        try {
            // Open the HTTP request.
            hr = URLOpenBlockingStreamW(nullptr, L"https://www.foo.bar/download/somefile.exe", &pStream, 0, nullptr);
            if (SUCCEEDED(hr)) break;

            std::cout << "ERROR: Could not connect. HRESULT: 0x" << std::hex << hr << std::dec << "\n";
        }
        catch (const std::exception& ex) {
            std::cout << ex.what();
        }
    }
    while (true);

    std::ofstream file("some_path_dot_exe", std::ios_base::binary);
    if (!file.is_open()) {
        std::cout << "ERROR: Download failed. Unable to create output file.\n";
        return 1;
    }

    // Download the response and write it to file.
    char buffer[4096];
    DWORD bytesRead;

    do
    {
        hr = pStream->Read(buffer, sizeof(buffer), &bytesRead);
        if (bytesRead > 0)
            file.write(buffer, bytesRead);

    } while (SUCCEEDED(hr) && hr != S_FALSE);

    file.close();

    if (FAILED(hr))
    {
        std::cout << "ERROR: Download failed. HRESULT: 0x" << std::hex << hr << std::dec << "\n";
        return 2;
    }

    std::cout << "\n";

    return 0;
}