基于curl 使用http多线程下载大文件

这篇具有很好参考价值的文章主要介绍了基于curl 使用http多线程下载大文件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、获取文件大小

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); //请求
    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);
//根据线程数划分块大小
    auto blockSize = length / thNum;
    auto lastBlock = (length % thNum == 0 ? blockSize : blockSize + length % thNum );
    
    //启动多个线程进行下载
    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)));
    }

三、下载区间数据

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 << "开始下载数据,区间:" << 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 << "下载数据:" << offset << "-" << rangeEnd << ", 获取数据:" << 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;
        }       
    }

五、合并线程数据

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、获取服务器文件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); //请求
    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、检验文件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 校验失败, 文件下载失败" << std::endl;
        return false;
    }
    std::cout << "md5 校验一致, 文件下载成功" << std::endl;
    return true;
}


//获取本地文件MD5,然后通过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失败,文件打开失败");
    }

    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));
}

如需完整代码,可评论区留言文章来源地址https://www.toymoban.com/news/detail-756313.html

到了这里,关于基于curl 使用http多线程下载大文件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • http 协议大文件下载

            http 协议的响应报文格式包括起始行+头部字段+响应正文,其格式为:         响应的正文只能通过http响应消息进行传输,如果是小文件(比如小于1M)传输,则没有什么问题,直接把文件内容加载到正文进行回应即可,但如果是大文件(比如大于10M),不可能把

    2024年02月09日
    浏览(29)
  • 网络:如何使用curl命令测试HTTP代理的有效性

    在我们的日常工作中,代理服务器扮演着重要的角色,它帮助我们访问局部网络无法直接访问的资源。然而,代理的设置和验证有时候会比较复杂。幸运的是,作为一名开发者应该熟悉curl,一个强大的工具来测试和验证代理服务器的设置。本文将引导我们如何使用curl命令测

    2024年04月22日
    浏览(33)
  • Web服务器实现|基于阻塞队列线程池的Http服务器|线程控制|Http协议

    代码地址:WebServer_GitHub_Addr 摘要 本实验通过C++语言,实现了一个基于阻塞队列线程池的多线程Web服务器。该服务器支持通过http协议发送报文,跨主机抓取服务器上特定资源。与此同时,该Web服务器后台通过C++语言,通过原生系统线程调用 pthread.h ,实现了一个 基于阻塞队列

    2024年02月07日
    浏览(52)
  • linux下下载文件的常用命令wget,curl等使用方法及使用示例

    在 Linux 操作系统中,有许多下载文件的工具可供选择。这些工具包括命令行工具和图形界面工具,每个工具都有其自身的特点和用途。以下是一些常用的下载文件工具: wget :一个功能强大的命令行下载工具,支持 HTTP、HTTPS、FTP 等协议,能够断点续传、递归下载等。 curl :

    2024年04月17日
    浏览(65)
  • Golang中基于HTTP协议的网络服务

    HTTP协议是基于TCP/IP协议栈的,并且它也是一个面向普通文本的协议。 只要搞清楚了HTTP请求的报文(报文的头部(header)和主体(body))应该包含的内容,使用任何一个文本编译器,就饿可以编写一个完整的HTTP请求报文。 在这种情况下,直接使用 net.Dial 函数,就可以。 使

    2023年04月09日
    浏览(25)
  • java通过http网络url下载文件

    上面代码报错,修改 URL url = new URL(fileUrl); ,使用 URL url = new URL(new URI(fileUrl).toASCIIString()); 原因: URL url = new URL(fileUrl); 和 URL url = new URL(new URI(fileUrl).toASCIIString()); 之间有一些微小的区别。 URL url = new URL(fileUrl);:这种方式直接使用 URL 类的构造函数创建一个 URL 对象。它假设

    2024年02月12日
    浏览(35)
  • 基于 HTTP Range 实现文件分片并发下载!

    目录 前言 基础下载功能 进阶下载功能 单片下载 多片下载  浏览器发送预检(preflight)请求 express 不支持多段 range multipart/** 搭配 boundary=** 分片下载功能 “只读的” ArrayBuffer 对象 DataView 子类 Uint8Array 操作二进制数据 Blob + createObjectURL 创建 url 全部代码 通用的文件分片下载

    2024年02月10日
    浏览(41)
  • python基于http的网络通信和网站端口暴露;Python网络编程之HTTP协议的python应用

    HTTP(Hypertext Transfer Protocol)即超文本传输协议,是Web应用程序使用的协议,在Web浏览器和Web服务器之间传递HTML页面和数据。HTTP是基于TCP/IP协议来传输数据的,是一种无状态的协议。 关键特点: 支持客户/服务器模式:Web浏览器作为HTTP客户端通过URL向HTTP服务器发送HTTP请求,

    2024年02月04日
    浏览(38)
  • Java基于ftp协议实现文件的上传和下载

    相比其他协议,如 HTTP 协议,FTP 协议要复杂一些。与一般的 C/S 应用不同点在于一般的C/S 应用程序一般只会建立一个 Socket 连接,这个连接同时处理服务器端和客户端的连接命令和数据传输。而FTP协议中将命令与数据分开传送的方法提高了效率。 FTP 使用 2 个端口,一个数据

    2024年02月11日
    浏览(37)
  • Linux curl 命令下载文件

    ​ 在Linux中curl是一个利用URL规则在命令行下工作的文件传输工具,可以说是一款很强大的http命令行工具。它支持文件的上传和下载,是综合传输工具,但按传统,习惯称curl为下载工具。 语法: 常见参数: 1、基本用法 执行后,www.linux.com 的html就会显示在屏幕上了 Ps:由于

    2024年02月15日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包