Skip to content

Instantly share code, notes, and snippets.

@kyle-go
Last active December 16, 2015 12:08
Show Gist options
  • Save kyle-go/5432193 to your computer and use it in GitHub Desktop.
Save kyle-go/5432193 to your computer and use it in GitHub Desktop.
#include "httpdownload.h"
#include <windows.h>
#include <wininet.h>
#include <shlwapi.h>
#include <tchar.h>
#include "scope.h"
#pragma comment(lib, "wininet")
#pragma comment(lib, "shlwapi")
int HttpDownLoad_real(
std::string& tmp_file,
const std::string& url,
const std::string& file,
std::function<void(double)> callback)
{
HINTERNET hSession = InternetOpen(_T("HTTPDOWNLOAD"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
scloud::ScopeGuard session([hSession]{InternetCloseHandle(hSession);});
if (!hSession) {
session.cancel();
return GetLastError();
}
HINTERNET hInternet = InternetOpenUrl(hSession, url.c_str(), NULL, 0 , INTERNET_FLAG_RAW_DATA, 0 );
scloud::ScopeGuard internet([hInternet]{InternetCloseHandle(hInternet);});
if (!hInternet) {
internet.cancel();
return GetLastError();
}
//get http return code.
char szContent[32] = {0};
DWORD dwInfoSize = 32;
HttpQueryInfoA(hInternet, HTTP_QUERY_STATUS_CODE, szContent, &dwInfoSize, NULL);
int ret = StrToIntA(szContent);
if (ret >= 400)
{
return ret;
}
//get file size
ZeroMemory(szContent, 32);
dwInfoSize = 32;
HttpQueryInfoA(hInternet, HTTP_QUERY_CONTENT_LENGTH, szContent, &dwInfoSize, NULL);
__int64 size = 0;
StrToInt64ExA(szContent, 10, &size);
if (size <= 0)
{
return ERROR_CONTENT_LENGTH_LESS_THAN_ZERO;
}
TCHAR szTmpFile[MAX_PATH] = {0};
if (!tmp_file.empty())
{
lstrcpy(szTmpFile, tmp_file.c_str());
}
else
{
TCHAR szTmpPath[MAX_PATH] = {0};
GetTempPath(MAX_PATH, szTmpPath);
if (0 == GetTempFileName(szTmpPath, _T("http"), 0, szTmpFile))
{
return GetLastError();
}
}
tmp_file = szTmpFile;
HANDLE hFile = CreateFile(szTmpFile,
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
scloud::ScopeGuard create_file([hFile]{CloseHandle(hFile);});
if (INVALID_HANDLE_VALUE == hFile)
{
create_file.cancel();
return GetLastError();
}
LARGE_INTEGER large_int = {0};
GetFileSizeEx(hFile, &large_int);
if (large_int.QuadPart > size)
{
return ERROR_TMP_FILE_TOO_BIG;
}
if (large_int.QuadPart > 0)
{
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_END))
{
return GetLastError();
}
if (INVALID_SET_FILE_POINTER == InternetSetFilePointer(
hInternet, large_int.LowPart, NULL, FILE_BEGIN, 0))
{
return GetLastError();
}
}
__int64 rsize = large_int.QuadPart;
for(;rsize < size;)
{
char buf[512] = {0};
DWORD dwRead = 0;
if (!InternetReadFile(hInternet, buf, 512, &dwRead))
{
return GetLastError();
}
if (dwRead == 0)
{
return ERROR_WTF_NO_DATA;
}
for(DWORD dwOnce=0; dwRead != dwOnce;)
{
DWORD dwWritten = 0;
if (0 == WriteFile(hFile, buf+dwOnce, dwRead-dwOnce, &dwWritten, NULL))
return GetLastError();
dwOnce += dwWritten;
}
rsize += dwRead;
if (rsize == size)
break;
if (callback)
callback((double)rsize/size);
}
if (rsize == size)
{
create_file.cancel();
CloseHandle(hFile);
SetFileAttributes(file.c_str(), FILE_ATTRIBUTE_NORMAL);
DeleteFile(file.c_str());
if (MoveFile(szTmpFile, file.c_str()))
{
//succeed.
if (callback)
callback(1.0);
return 0;
}
}
return GetLastError();
}
int HttpDownLoad( const std::string& url,
const std::string& file,
std::function<void(double)> callback,
int timeout /* = 30 */)
{
int ret = 0;
std::string tmp_file;
int my_timeout = timeout >= 30? timeout:30;
for (;;)
{
ret = HttpDownLoad_real(tmp_file, url, file, callback);
if (0 == ret)
{
return 0;
}
else
{
Sleep(3000);
my_timeout -= 3;
}
if(my_timeout <= 0)
break;
}
return ret;
}
#ifndef HTTP_DWONLOAD_H_
#define HTTP_DWONLOAD_H_
#include <string>
#include <functional>
enum {
ERROR_CONTENT_LENGTH_LESS_THAN_ZERO = 9527,
ERROR_WTF_NO_DATA,
ERROR_TMP_FILE_ERROR,
};
/*
@brief
download a http file.
@param
url http address.
file local path save file.
callback process call back.
timeout timeout in second
@return
0 if succeed, else return GetLastError() or HTTP ERROR CODE or...
*/
int HttpDownLoad( const std::string& url,
const std::string& file,
std::function<void(double)> callback,
int timeout = 30);
#endif //HTTP_DWONLOAD_H_
#ifndef SCOPE_H_
#define SCOPE_H_
#include <functional>
namespace scloud{
class ScopeGuard
{
public:
explicit ScopeGuard(std::function<void(void)> onExitScope)
: doExit_(onExitScope), cancel_(false){}
~ScopeGuard() {
if(!cancel_) {
doExit_();
}
}
void cancel() {
cancel_ = true;
}
private:
//disable copy
ScopeGuard(const ScopeGuard&);
void operator=(const ScopeGuard&);
std::function<void(void)> doExit_;
bool cancel_;
};
}//end namespace
#define SCLOUD_CAT_STRING(name, line) name##line
#define SCLOUD_XSOCPEGUARD_LOCK_CAT(name, line) SCLOUD_CAT_STRING(name,line)
#define SCLOUD_SCOPE_EXIT scloud::ScopeGuard SCLOUD_XSOCPEGUARD_LOCK_CAT(ScopeGuard, __LINE__)
#endif //SCOPE_H_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment