一、獲取文件大小
int64_t CHttpClient::GetFileSize(const std::string &url)
{
auto curl = curl_easy_init();
if (!curl)
{
curl_easy_cleanup(curl);
return -1;
}
double filesize = -1; //文件大小
curl_easy_setopt(curl,CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HEADER, 1);
curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
CURLcode res_code = curl_easy_perform(curl); //請(qǐng)求
if (res_code != CURLE_OK)
{
curl_easy_cleanup(curl);
return -1;
}
curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &filesize);
curl_easy_cleanup(curl);
std::cout << "獲取文件大小完成:" << (int64_t)filesize << std::endl;
return filesize;
}
二、劃分線程
auto client = GetClient();
auto length = client->GetFileSize(url);
//根據(jù)線程數(shù)劃分塊大小
auto blockSize = length / thNum;
auto lastBlock = (length % thNum == 0 ? blockSize : blockSize + length % thNum );
//啟動(dòng)多個(gè)線程進(jìn)行下載
std::vector<std::future<bool>> vecFt;
std::vector<std::shared_ptr<INetProtocol>> vecDownload;
for (size_t i = 0; i < thNum; i++)
{
auto block = (i == thNum - 1 ? lastBlock : blockSize);
auto dlClient = GetClient();
vecDownload.push_back(dlClient);
vecFt.emplace_back(std::async(std::bind(&INetProtocol::Download, dlClient, url, blockSize * i, block)));
}
三、下載區(qū)間數(shù)據(jù)
bool CHttpClient::Download(const std::string &url, int64_t offset, int64_t blocksize)
{
auto curl = curl_easy_init();
if (!curl)
{
curl_easy_cleanup(curl);
return false;
}
auto rangeEnd = offset + blocksize - 1;
std::cout << "開(kāi)始下載數(shù)據(jù),區(qū)間:" << offset << "-" << rangeEnd << ",塊大小:" << blocksize << std::endl;
m_blockData = std::make_shared<BlockData>(offset, blocksize);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_FTP_RESPONSE_TIMEOUT, 10);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CHttpClient::WriteBlock);
std::stringstream ss;
ss << offset << "-" << rangeEnd;
curl_easy_setopt(curl, CURLOPT_RANGE, ss.str().c_str());
CURLcode res_code = curl_easy_perform(curl);
if (res_code != CURLE_OK)
{
curl_easy_cleanup(curl);
return false;
}
std::cout << "下載數(shù)據(jù):" << offset << "-" << rangeEnd << ", 獲取數(shù)據(jù):" << m_blockData->readSize << "完成" << std::endl;
curl_easy_cleanup(curl);
return true;
}
四、等待所有線程完成
//同步等待所有線程完成
bool isFaild = false;
for (auto i = 0; i < vecFt.size(); i++)
{
if (!vecFt[i].get())
{
isFaild = true;
std::cout << "線程:" << i << "下載失敗" << std::endl;
}
}
五、合并線程數(shù)據(jù)
void CDownload::MergerData(const std::string &fileName, std::vector<std::shared_ptr<INetProtocol>>& vecDownload)
{
std::ofstream fout(fileName, std::ios::out | std::ios::binary);
if (!fout)
{
throw std::runtime_error("create local file faild");
}
for (auto& item : vecDownload)
{
fout << item->GetStream().rdbuf();
}
}
6、獲取服務(wù)器文件MD5
std::string CHttpClient::GetContextMd5(const std::string &url)
{
auto curl = curl_easy_init();
if (!curl)
{
curl_easy_cleanup(curl);
return std::string();
}
curl_easy_setopt(curl,CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HEADER, 1);
curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
std::string strMd5;
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &strMd5);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, CHttpClient::ReadHeader);
CURLcode res_code = curl_easy_perform(curl); //請(qǐng)求
if (res_code != CURLE_OK)
{
curl_easy_cleanup(curl);
return std::string();
}
curl_easy_cleanup(curl);
std::cout << "獲取Md5完成:" << strMd5 << std::endl;
return strMd5;
}
7、檢驗(yàn)文件MD5是否一致
bool CDownload::CheckFile(const std::string &url, const std::string& strLocalFile)
{
auto client = GetClient();
auto contextMd5 = client->GetContextMd5(url);
auto localMd5 = GetContentMd5(strLocalFile);
std::cout << "context md5:" << contextMd5 << std::endl;
std::cout << "local file md5:" << localMd5 << std::endl;
if (contextMd5 != localMd5)
{
std::cout << "md5 校驗(yàn)失敗, 文件下載失敗" << std::endl;
return false;
}
std::cout << "md5 校驗(yàn)一致, 文件下載成功" << std::endl;
return true;
}
//獲取本地文件MD5,然后通過(guò)Base64編碼輸出
std::string CDownload::GetContentMd5(const std::string &fileName)
{
std::ifstream fin(fileName, std::ios::in | std::ios::binary);
if (!fin)
{
throw std::runtime_error("獲取文件MD5失敗,文件打開(kāi)失敗");
}
EVP_MD_CTX *mdctx;
unsigned char *md5_digest;
unsigned int md5_digest_len = EVP_MD_size(EVP_md5());
mdctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(mdctx, EVP_md5(), NULL);
const int bufSize = 4 * 1024 * 1024;
char *pszBuf = new char[bufSize];
while (!fin.eof())
{
fin.read(pszBuf, bufSize);
EVP_DigestUpdate(mdctx, pszBuf, fin.gcount());
}
md5_digest = (unsigned char *)OPENSSL_malloc(md5_digest_len);
EVP_DigestFinal_ex(mdctx, md5_digest, &md5_digest_len);
EVP_MD_CTX_free(mdctx);
delete[] pszBuf;
return CBase64::encode(std::string((char*)md5_digest, md5_digest_len));
}
如需完整代碼,可評(píng)論區(qū)留言文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-756313.html
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-756313.html
到了這里,關(guān)于基于curl 使用http多線程下載大文件的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!