C++ 下载文件 WinInet - 0kb 写入文件
Posted
技术标签:
【中文标题】C++ 下载文件 WinInet - 0kb 写入文件【英文标题】:C++ Download File WinInet - 0kb written to file 【发布时间】:2015-09-10 18:56:13 【问题描述】:谁能告诉我我的代码有什么问题?
我正在尝试使用 WinInet 从 Internet 下载文件。该函数可以很好地连接到目标站点,我不明白为什么这段代码不起作用。谁能帮帮我?
这是我的代码:
HANDLE hFile = CreateFileW(FilePath, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, NULL, NULL);
if (hFile != INVALID_HANDLE_VALUE || GetLastError() == ERROR_ALREADY_EXISTS)
CHAR Buffer[2048];
DWORD BytesRead=0, BytesToRead=0;
DWORD BytesWritten=0, BytesToWrite=0;
SetFilePointer(hFile, 0, 0, FILE_BEGIN);
do
if (BytesRead)
WriteFile(hFile, Buffer, BytesWritten, &BytesToWrite, FALSE);
while
(InternetReadFile(hRequest, (LPVOID)Buffer, BytesToRead, &BytesRead) != FALSE);
CloseHandle(hFile);
hRequest 被传递给函数,它是来自 HttpOpenRequestA 的 HINTERNET 句柄。
【问题讨论】:
WinINet trouble downloading file to client? 的可能重复项 您永远不会将BytesWritten
设置为 0
以外的任何值(顺便说一句,您在 WriteFile
调用中交换了 BytesWritten
和 BytesToWrite
的名称)。
但除此之外,其余代码看起来不错?
如果你修复它应该至少写入数据:)
您的第一个 if
-clause 没有意义。如果最后一个错误代码设置为ERROR_ALREADY_EXISTS
,您可能会最终写入由句柄INVALID_HANDLE_VALUE
标识的文件,即您没有写入任何内容。
【参考方案1】:
你的代码有一些逻辑问题。
您在调用CreateFileW()
时误用了GetLastError()
。不管文件是否已经存在,CreateFileW()
如果成功创建/打开文件,都不会返回INVALID_HANDLE
。这就是您需要检查的全部内容(仅当CreateFileW()
失败并且您想找出原因时才调用GetLastError()
)。此外,根本不需要调用SetFilePointer()
,因为CREATE_ALWAYS
确保打开的文件是空的,如果文件已经存在并且其中有数据,则截断文件。
您的 do..while
循环应该是 while
循环,以便首先调用 InternetReadFile()
。在第一次循环迭代中跳过 WriteFile()
是没有意义的。如果您使用do..while
循环,则不应将InternetReadFile()
用作循环条件。
更重要的是,如果InternetReadFile()
因错误而失败,只有你才打破循环。您期望它在响应结束时失败,但它实际上返回 TRUE 并将 BytesRead
设置为 0。这是记录在案的行为,您根本没有处理:
InternetReadFile function
InternetReadFile 的操作与基本的 ReadFile 函数非常相似,但有一些例外。通常,InternetReadFile 从 HINTERNET 句柄中检索数据作为顺序字节流。每次调用 InternetReadFile 要读取的数据量由 dwNumberOfBytesToRead 参数指定,数据在 lpBuffer 参数中返回。正常读取为每次调用 InternetReadFile 检索指定的 dwNumberOfBytesToRead,直到到达文件末尾。 为确保检索到所有数据,应用程序必须继续调用 InternetReadFile 函数,直到该函数返回 TRUE 并且 lpdwNumberOfBytesRead 参数等于零。如果将请求的数据写入缓存,这一点尤其重要,因为否则缓存将无法正确更新,下载的文件也不会提交到缓存。请注意,除非打开数据流的原始请求设置了 INTERNET_FLAG_NO_CACHE_WRITE 标志,否则缓存会自动发生。
ReadFile function
当同步读取操作到达文件末尾时,ReadFile 返回 TRUE 并将 *lpNumberOfBytesRead 设置为零。
在调用WriteFile()
时,您将BytesWritten
传递给nNumberOfBytesToWrite
参数,但BytesWritten
永远不会设置为0 以外的任何值,因此不会将任何内容写入文件。您需要改为传递BytesRead
。
话虽如此,请使用更像这样的东西:
HANDLE hFile = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hFile == INVALID_HANDLE_VALUE)
// handle error as needed...
else
BYTE Buffer[2048];
DWORD BytesRead, BytesWritten;
do
if (!InternetReadFile(hRequest, Buffer, sizeof(Buffer), &BytesRead))
// handle error as needed...
break;
if (!BytesRead)
break;
if (!WriteFile(hFile, Buffer, BytesRead, &BytesWritten, FALSE))
// handle error as needed...
break;
while (true);
CloseHandle(hFile);
MSDN 甚至有一个如何使用InternetReadFile()
的完整示例:
HOWTO: Using InternetReadFile To Get File
BOOL GetFile (HINTERNET IN hOpen, // Handle from InternetOpen()
CHAR *szUrl, // Full URL
CHAR *szFileName) // Local file name
DWORD dwSize;
CHAR szHead[] = "Accept: */*\r\n\r\n";
VOID * szTemp[25];
HINTERNET hConnect;
FILE * pFile;
if ( !(hConnect = InternetOpenUrl ( hOpen, szUrl, szHead,
lstrlen (szHead), INTERNET_FLAG_DONT_CACHE, 0)))
cerr << "Error !" << endl;
return 0;
if ( !(pFile = fopen (szFileName, "wb" ) ) )
cerr << "Error !" << endl;
return FALSE;
do
// Keep coping in 25 bytes chunks, while file has any data left.
// Note: bigger buffer will greatly improve performance.
if (!InternetReadFile (hConnect, szTemp, 50, &dwSize) )
fclose (pFile);
cerr << "Error !" << endl;
return FALSE;
if (!dwSize)
break; // Condition of dwSize=0 indicate EOF. Stop.
else
fwrite(szTemp, sizeof (char), dwSize , pFile);
// do
while (TRUE);
fflush (pFile);
fclose (pFile);
return TRUE;
【讨论】:
你的空闲时间显然太多了 ;) 我没有得到 MSDN 的例子。void* szTemp[25]
是什么?指向字节数组的指针?它没有被分配然后它在这个调用中读取 50 个字节InternetReadFile (hConnect, szTemp, 50, &dwSize)
他们似乎是故意这样做的,他们甚至对此发表评论。
@BarmakShemirani:很好。在 MSDN 的第一个示例中,VOID* szTemp[25];
需要改为 BYTE szTemp]50];
,而在他们的第二个示例中,TCHAR sz[1024];
需要改为BYTE sz[1024];
。我已将这些更改提交给 Microsoft。以上是关于C++ 下载文件 WinInet - 0kb 写入文件的主要内容,如果未能解决你的问题,请参考以下文章