Загрузка бинарных файлов с помощью Wininet - PullRequest
2 голосов
/ 05 августа 2011

В настоящее время я программирую простую программу, которую я хочу раздать своим друзьям.То, что я пытаюсь сделать, - это записать некоторые внешние двоичные файлы в буфер из Интернета после запуска программы.Для этого я использую Windows Internet (wininet).В настоящее время я использую InternetReadFile для записи файла в буфер, который я использую позже в программе.Тем не менее, файл не читается полностью, так как результирующий размер намного меньше, чем размер файла на сервере, когда он должен быть одинаковым.

Я хотел бы сделать это, не используялюбые внешние библиотеки.

Есть идеи, что может решить мою проблему?

Спасибо, Андрей

1 Ответ

7 голосов
/ 05 августа 2011

В документации содержатся следующие замечания:

InternetReadFile работает во многом подобно базовой функции ReadFile, за некоторыми исключениями.Обычно InternetReadFile извлекает данные из дескриптора HINTERNET в виде последовательного потока байтов.Количество данных, которые нужно прочитать для каждого вызова InternetReadFile, задается параметром dwNumberOfBytesToRead, а данные возвращаются в параметре lpBuffer.Обычное чтение извлекает указанное значение dwNumberOfBytesToRead для каждого вызова InternetReadFile до тех пор, пока не будет достигнут конец файла. Чтобы обеспечить получение всех данных, приложение должно продолжать вызывать функцию 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.Это загружает около 200K HTML-кода в 1K-блоках, и получается полная страница.Можете ли вы запустить это и посмотреть, работает ли он?

#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" )
...