用Wininet下载二进制文件

问题描述 投票:2回答:1

我目前正在编写一个简单的程序,我想分发给我的朋友们。 我要完成的工作是在启动程序时将一些外部二进制文件从Internet写入缓冲区。 为此,我正在使用Windows internet(wininet)。 当前,我正在使用InternetReadFile将文件写入缓冲区,该缓冲区稍后将在程序中使用。 但是,文件没有被完全读取,因为当文件大小相同时,结果大小将比服务器上文件的大小小得多。

我想这样做,而不使用任何外部库。

有什么想法可以解决我的问题吗?

谢谢,安德鲁

c++ file download binary wininet
1个回答
7
投票

文档作以下说明:

InternetReadFile的操作与基本的ReadFile函数非常相似,但有一些例外。 通常,InternetReadFile从HINTERNET句柄中检索数据作为字节的顺序流。 每次调用InternetReadFile读取的数据量由dwNumberOfBytesToRead参数指定,并在lpBuffer参数中返回数据。 普通读取为每次InternetReadFile的调用检索指定的dwNumberOfBytesToRead,直到到达文件末尾。 为了确保检索到所有数据,应用程序必须继续调用InternetReadFile函数,直到该函数返回TRUE并且lpdwNumberOfBytesRead参数等于零为止。

基本上,不能保证该函数完全读取dwNumberOfBytesToRead 。 使用lpdwNumberOfBytesRead参数检查实际读取了多少个字节。

此外,一旦文件总大小大于dwNumberOfBytesToRead ,您将需要多次调用该调用。 因为它一次读取的内容不能超过dwNumberOfBytesToRead

如果预先拥有文件的总大小,则循环采用以下形式:

::DWORD error = ERROR_SUCCESS;
::BYTE data[SIZE]; // total file size.
::DWORD size = 0;
::DWORD read = 0;
do {
    ::BOOL result = ::InternetReadFile(stream, data+size, SIZE-size, &read);
    if ( result == FALSE ) {
        error = ::GetLastError();
    }
}
while ((error == ERROR_SUCCESS) && (read > 0) && ((size+=read) < SIZE));
  // check that `SIZE` was correct.
if (size != SIZE) {
}

如果不是,那么您需要将缓冲区中的数据写入另一个文件,而不是累加它。

编辑(样本测试程序)

这是一个完整的程序,可获取StackOverflow的首页。 这将以1K块的形式下载大约200K的HTML代码,并检索整个页面。 你可以运行它,看看是否可行?

#include <Windows.h>
#include <Wininet.h>
#include <iostream>
#include <fstream>

namespace {

    ::HINTERNET netstart ()
    {
        const ::HINTERNET handle =
            ::InternetOpenW(0, INTERNET_OPEN_TYPE_DIRECT, 0, 0, 0);
        if ( handle == 0 )
        {
            const ::DWORD error = ::GetLastError();
            std::cerr
                << "InternetOpen(): " << error << "."
                << std::endl;
        }
        return (handle);
    }

    void netclose ( ::HINTERNET object )
    {
        const ::BOOL result = ::InternetCloseHandle(object);
        if ( result == FALSE )
        {
            const ::DWORD error = ::GetLastError();
            std::cerr
                << "InternetClose(): " << error << "."
                << std::endl;
        }
    }

    ::HINTERNET netopen ( ::HINTERNET session, ::LPCWSTR url )
    {
        const ::HINTERNET handle =
            ::InternetOpenUrlW(session, url, 0, 0, 0, 0);
        if ( handle == 0 )
        {
            const ::DWORD error = ::GetLastError();
            std::cerr
                << "InternetOpenUrl(): " << error << "."
                << std::endl;
        }
        return (handle);
    }

    void netfetch ( ::HINTERNET istream, std::ostream& ostream )
    {
        static const ::DWORD SIZE = 1024;
        ::DWORD error = ERROR_SUCCESS;
        ::BYTE data[SIZE];
        ::DWORD size = 0;
        do {
            ::BOOL result = ::InternetReadFile(istream, data, SIZE, &size);
            if ( result == FALSE )
            {
                error = ::GetLastError();
                std::cerr
                    << "InternetReadFile(): " << error << "."
                    << std::endl;
            }
            ostream.write((const char*)data, size);
        }
        while ((error == ERROR_SUCCESS) && (size > 0));
    }

}

int main ( int, char ** )
{
    const ::WCHAR URL[] = L"http://stackoverflow.com/";
    const ::HINTERNET session = ::netstart();
    if ( session != 0 )
    {
        const ::HINTERNET istream = ::netopen(session, URL);
        if ( istream != 0 )
        {
            std::ofstream ostream("output.txt", std::ios::binary);
            if ( ostream.is_open() ) {
                ::netfetch(istream, ostream);
            }
            else {
                std::cerr << "Could not open 'output.txt'." << std::endl;
            }
            ::netclose(istream);
        }
        ::netclose(session);
    }
}

#pragma comment ( lib, "Wininet.lib" )
© www.soinside.com 2019 - 2024. All rights reserved.