2011-08-05 96 views
2

我目前正在编写一个简单的程序,我想分发给我的朋友。我试图完成的任务是在启动程序时将一些外部二进制文件写入到互联网的缓冲区中。为此,我使用Windows Internet(wininet)。目前,我正在使用InternetReadFile将文件写入稍后在程序中使用的缓冲区中。但是,文件并未完全读取,因为结果大小比服务器上的文件大小要小得多,因为它应该是相同的。使用Wininet下载二进制文件

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

任何想法可以解决我的问题?

感谢, 安德鲁

+0

编辑我的答案,包括一个完整的工作程序。你可以试试看看它是否有效? –

回答

7

documentation作如下评论:

工作的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) { 
} 

如果没有,那么你需要在缓冲区中的数据写入到另一个文件,而不是积累吧。

EDIT(样本测试程序)

这里有一个完整的程序,获取的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") 
+0

谢谢你的回复。我在while循环中调用了InternetReadFile,直到它返回true,但该文件仍然损坏并且大小不正确。 – Andrew

+0

@Andrew:再次阅读文档。对于任何“成功”条件都返回“TRUE”,包括“只读取1个字节的数据”。它应该第一次返回“TRUE”,所以不要循环,直到它返回“TRUE”。 –

+0

@Andrew:你有没有试过在任何地方复制数据,只计算函数返回的字节总数?确保这将返回适当的字节数以开始。 –