curl使用小记(三)——获取远端数据到内存缓冲区

简介: curl使用小记(三)——获取远端数据到内存缓冲区

curl使用小记(三)——获取远端数据到内存缓冲区

目录

1. 概述

我在博文《curl使用小记(二)——远程下载一张图片》中介绍了如何通过Curl获取远端的文件。不过在那个例子中,将获取远端数据与写入数据的步骤混杂到一起了。在多线程的场景下,这样做可能会造成读写冲突的问题。理论上,远端访问数据是先保存到内存中,在写出到文件中。而远端访问数据到内存可以看作是读操作,是不会读冲突的。所以一个很好的策略是,一次性将数据读取到内存Buf中,再写出到文件。

2. 实现

《curl使用小记(二)——远程下载一张图片》中的代码改进一下,具体的代码实例如下:

#include <iostream>
#include <curl/curl.h>
using namespace std;
//内存块结构体
struct MemoryStruct
{
  char *memory;
  size_t size;
  MemoryStruct()
  {
    memory = (char *)malloc(1);
    size = 0;
  }
  ~MemoryStruct()
  {
    free(memory);
    memory = NULL;
  }
};
//回调函数实现:一次请求可能多次调回调函数
size_t HttpPostWriteBack(void *contents, size_t size, size_t nmemb, void *userp)
{
  size_t realsize = size * nmemb;//一次回调返回的数据量
  struct MemoryStruct *mem = (struct MemoryStruct *)userp;
  char *ptr = (char *)realloc(mem->memory, mem->size + realsize);
  if (ptr == NULL)
  {
    printf("not enough memory (realloc returned NULL)\n");
    return 0;
  }
  mem->memory = ptr;
  memcpy(&(mem->memory[mem->size]), contents, realsize);
  mem->size += realsize;
  return realsize;//必须返回真实的数据
}
int main()
{
  const char *netlink = "http://cn.bing.com/th?id=OHR.GrandsCausses_EN-CN3335882379_800x480.jpg";
  const char *output = "D:/dst1.jpg";
  curl_global_init(CURL_GLOBAL_ALL);    //初始化全局资源
  CURL *curl = curl_easy_init();    //初始化句柄
  //需要的话,可以设置代理
  //curl_easy_setopt(curl, CURLOPT_PROXY, "127.0.0.1:7890");
  
  //访问网址
  curl_easy_setopt(curl, CURLOPT_URL, netlink);
  //设置用户代理
  curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
     
  //获取数据
  MemoryStruct chunk; 
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk);
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HttpPostWriteBack);
  ////实现下载进度
  //curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false);
  //curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback);
  //curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, nullptr);
  //运行
  curl_easy_perform(curl);
  curl_easy_cleanup(curl);      //释放句柄
  curl_global_cleanup(); //释放全局资源
  //写出数据
  FILE *fp = nullptr;
  if (fopen_s(&fp, output, "wb") != 0)
  {
    curl_easy_cleanup(curl);
    return 0;
  }
  fwrite(chunk.memory, chunk.size, 1, fp);
  fclose(fp);
  return 1;
}

这段代码其中一个关键改进在于,通过自定义结构体MemoryStruct,实现了一个类似于动态数组的设计。由于远端访问文件的数据量在一开始并不能确定,所以需要先访问一部分,然后将容器扩容,再访问一部分,再扩容。这个申请内存的扩容操作是通过C的realloc()函数来实现的。这个结构体MemoryStruct还利用了C++的RAII机制做内存管理。

另外一个关键就是CURLOPT_WRITEDATA于CURLOPT_WRITEFUNCTION的配合使用了。CURLOPT_WRITEFUNCTION用来设置回调函数,CURLOPT_WRITEDATA用来设置回调函数的出参,这个其实是C的编程思维,万物皆指针,所有的操作都被抽象成同一个函数接口,其实不是同一个东西。

3. 参考

  1. curl CURLOPT_WRITEDATA CURLOPT_WRITEFUNCTION 回调函数
  2. libcurl中CURLOPT_WRITEFUNCTION设置回调函数

分类: Web开发技术

标签: 文件流 , 缓冲区 , C++


相关文章
|
2月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
260 0
|
1天前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。
|
3天前
|
监控 Java easyexcel
面试官:POI大量数据读取内存溢出?如何解决?
【10月更文挑战第14天】 在处理大量数据时,使用Apache POI库读取Excel文件可能会导致内存溢出的问题。这是因为POI在读取Excel文件时,会将整个文档加载到内存中,如果文件过大,就会消耗大量内存。以下是一些解决这一问题的策略:
11 1
|
6天前
|
缓存 安全 Java
使用 Java 内存模型解决多线程中的数据竞争问题
【10月更文挑战第11天】在 Java 多线程编程中,数据竞争是一个常见问题。通过使用 `synchronized` 关键字、`volatile` 关键字、原子类、显式锁、避免共享可变数据、合理设计数据结构、遵循线程安全原则和使用线程池等方法,可以有效解决数据竞争问题,确保程序的正确性和稳定性。
13 2
|
10天前
|
存储 编译器
数据在内存中的存储
数据在内存中的存储
29 4
|
10天前
|
存储 缓存 固态存储
|
16天前
|
存储 机器学习/深度学习 人工智能
数据在内存中的存储
数据在内存中的存储
|
11天前
|
存储 C语言
深入C语言内存:数据在内存中的存储
深入C语言内存:数据在内存中的存储
|
27天前
|
缓存 NoSQL 算法
14)Redis 在内存用完时会怎么办?如何处理已过期的数据?
14)Redis 在内存用完时会怎么办?如何处理已过期的数据?
41 0
|
28天前
|
存储 弹性计算 网络协议
阿里云ECS内存型实例规格族特点、适用场景、指标数据参考
阿里云ECS提供了多样化的内存型实例规格族,专为需要高性能内存资源的应用场景设计。从最新的r8a系列到经过优化的re6p系列,旨在提供稳定、高效且安全的计算环境。这些实例不仅具备强大的计算性能与内存配比,还通过支持ESSD云盘和高效网络协议,显著提升了存储I/O能力和网络带宽,适用于大数据分析、高性能数据库、内存密集型应用等多种场景,为用户带来卓越的计算体验。本文将详细解析阿里云ECS中的多个内存型实例规格族,包括它们的核心特点、适用场景、实例规格及具体指标数据,为用户在云计算资源选型时提供参考。