#include "Base.h"
#include <sys/stat.h>
#include "curl/curl.h"
#include "curl/easy.h"
#include "FileDownload.h"
#define CHECK_CURL_ERROR(ERROR_CODE) if (ERROR_CODE != CURLE_OK){logDebug("%s, code:%d", #ERROR_CODE, int(ERROR_CODE));return;}
static int handleProgressUpdate(void *userData, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded)
{
auto self = (FileDownload*)userData;
if (totalToDownload > 0)
{
auto percent = nowDownloaded / totalToDownload * 100;
if (percent != self->percent)
{
self->percent = percent;
if (self->onDownloadUpdate)
{
self->onDownloadUpdate(self, nowDownloaded, totalToDownload);
}
}
}
return 0;
}
static size_t handleWirte(void *buff, size_t size, size_t nmemb, void *userData)
{
auto self = (FileDownload*)userData;
if (self->fileObj == nullptr)
{
self->checkOpenFile();
}
if (self->fileObj)
{
return fwrite(buff, size, nmemb, self->fileObj);
}
else
{
return 0;
}
}
static size_t handleHeader(void *ptr, size_t size, size_t nmemb, void *userData)
{
auto self = (FileDownload*)userData;
self->checkOpenFile();
self->errorInfo.append((const char*)ptr, size * nmemb);
return size * nmemb;
}
static void onDownloadWin32(std::shared_ptr<FileDownload> download)
{
auto curl = curl_easy_init();
if (curl == nullptr)
{
LOG_DEBUG("%s", "curl_easy_init fail");
return;
}
bool result = false;
download->fileIndex = 0;
while (!download->fileNameList.empty())
{
download->fileIndex++;
download->curl = curl;
auto& fileName = download->fileNameList.front();
download->curFileName = fileName;
download->percent = 0;
auto fileUrl = download->url + fileName;
auto savePath = download->saveDir + fileName;
double downloadFileLenth = 0;
long responseCode = 0;
CURLcode ret = CURLE_OK;
bool isExist = cocos2d::FileUtils::getInstance()->isFileExist(savePath);
if (isExist)
{
auto tmpcurl = curl_easy_init();
CHECK_CURL_ERROR(curl_easy_setopt(tmpcurl, CURLOPT_URL, fileUrl.c_str()));
CHECK_CURL_ERROR(curl_easy_setopt(tmpcurl, CURLOPT_HTTPGET, 1)); //使用HTTPGET
CHECK_CURL_ERROR(curl_easy_setopt(tmpcurl, CURLOPT_NOBODY, 1)); //不需求body
ret = curl_easy_perform(tmpcurl);
CHECK_CURL_ERROR(curl_easy_getinfo(tmpcurl, CURLINFO_RESPONSE_CODE, &responseCode));
if (ret == CURLE_OK && responseCode == 200)
{
curl_easy_getinfo(tmpcurl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &downloadFileLenth);
}
curl_easy_cleanup(tmpcurl);
}
struct stat info;
curl_off_t fileSize = stat(savePath.c_str(), &info) == 0 ? info.st_size : 0;
if (!isExist || fileSize < (int)downloadFileLenth)
{
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_URL, fileUrl.c_str()));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, fileSize));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_WRITEDATA, download.get()));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handleWirte));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, download.get()));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, handleHeader));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_HEADERDATA, download.get()));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, handleProgressUpdate));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 5L));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0));
CHECK_CURL_ERROR(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0));
ret = curl_easy_perform(curl);
CHECK_CURL_ERROR(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode));
}
if (download->fileObj)
{
fclose(download->fileObj);
download->fileObj = nullptr;
}
if ((ret == CURLE_OK && responseCode >= 200 && responseCode <= 299) || (ret == CURLE_RANGE_ERROR && responseCode == 416 && fileSize > 0))
{
download->fileNameList.erase(download->fileNameList.begin());
result = true;
}
else
{
result = false;
download->errorInfo += curl_easy_strerror(ret);
if (download->onDownloadError)
{
download->onDownloadError(download.get(), download->errorInfo);
}
LOG_DEBUG("%s", download->errorInfo.c_str());
break;
}
}
curl_easy_cleanup(curl);
if (result)
{
if (download->onDownloadFinish)
{
download->onDownloadFinish(download.get());
}
}
}
void FileDownload::start(std::shared_ptr<FileDownload> download)
{
if (!download->url.empty())
{
if (download->url.back() != '/')
{
download->url.push_back('/');
}
}
cocos2d::FileUtils::createDirectory(download->saveDir);
auto t = std::thread(onDownloadWin32, download);
t.detach();
}
void FileDownload::checkOpenFile()
{
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
if ((responseCode >= 200 && responseCode <= 299) && fileObj == nullptr)
{
fileObj = fopen((saveDir + curFileName).c_str(), "ab+");
}
}