【文件分片下载 libcurl】

简介: 【文件分片下载 libcurl】
// gcc -o multi_download multi_download.c -lcurl
#include <stdio.h>
#include <unistd.h>
#include <curl/curl.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
// fork();
struct fileInfo {
  const char *url;
  char *fileptr;
  int offset; //  start 
  int end;   // end
  pthread_t thid;
  double download; // 
  double totalDownload;
  FILE *recordfile;
};
#define THREAD_NUM    10
struct fileInfo **pInfoTable;
double downloadFileLength = 0;
// fwrite(fp, size, count, buf);
// libcurl
// 1.  fwrite/write
// mmap 
size_t writeFunc(void *ptr, size_t size, size_t memb, void *userdata) {
  // --> ptr
  struct fileInfo *info = (struct fileInfo *)userdata;
  //printf("writeFunc: %ld\n", size * memb);
  memcpy(info->fileptr + info->offset, ptr, size * memb);
  info->offset += size * memb;
  return size * memb;
}
// 
int progressFunc(void *userdata, double totalDownload, double nowDownload, double totalUpload, double nowUpload) {
  int percent = 0;
  static int print = 1;
  struct fileInfo *info = (struct fileInfo*)userdata;
  info->download = nowDownload;
  info->totalDownload = totalDownload;
  // save 
  if (totalDownload > 0) {
    int i = 0;
    double allDownload = 0;
    double total = 0;
    for (i = 0;i <= THREAD_NUM;i ++) {
      allDownload += pInfoTable[i]->download;
      total += pInfoTable[i]->totalDownload;
    }
    percent = (int)(allDownload / total * 100);
  }
  if (percent == print) {
    printf("threadid: %ld, percent: %d%%\n", info->thid, percent);
    print += 1;
  }
  return 0;
}
//
double getDownloadFileLength(const char *url) {
  CURL *curl = curl_easy_init();
  //printf("url: %s\n", url);
  curl_easy_setopt(curl, CURLOPT_URL, url);
  //curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36");
  curl_easy_setopt(curl, CURLOPT_HEADER, 1);
  curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
// 111
  CURLcode res = curl_easy_perform(curl);
  if (res == CURLE_OK) {
    printf("downloadFileLength success\n");
    curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &downloadFileLength);
  } else {
    printf("downloadFileLength error\n");
    downloadFileLength = -1;
  }
// 2222
  curl_easy_cleanup(curl);
  return downloadFileLength;
}
int recordnum = 0;
// curl
// 0 - 11
// multi
void *worker(void *arg) {
  struct fileInfo *info = (struct fileInfo*)arg;
  char range[64] = {0};
  // mutex_lock
  if (info->recordfile) {
    fscanf(info->recordfile, "%d-%d", &info->offset, &info->end);
  }
  // mutex_unlock
  if (info->offset > info->end) return NULL;
  snprintf(range, 64, "%d-%d", info->offset, info->end);
  //printf("threadid: %ld, download from: %d to: %d\n", info->thid, info->offset, info->end);
  // curl --> 
  CURL *curl = curl_easy_init();
  curl_easy_setopt(curl, CURLOPT_URL, info->url); // url
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunc); // save
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, info); 
  curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); // progress
  curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progressFunc);
  curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, info);
  curl_easy_setopt(curl, CURLOPT_RANGE, range);
  // http range 
// 111
// multicurl 
  CURLcode res = curl_easy_perform(curl);
  if (res != CURLE_OK) {
    printf("res %d\n", res);
  }
// 2222
  curl_easy_cleanup(curl);
  return NULL;
}
// https://releases.ubuntu.com/22.04/ubuntu-22.04.2-live-server-amd64.iso.zsync
// ubuntu.zsync.backup
int download(const char *url, const char *filename) {
  //
  long fileLength = getDownloadFileLength(url);
  printf("downloadFileLength: %ld\n", fileLength);
  // write
  int fd = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); // 
  if (fd == -1) {
    return -1;
  }
  if (-1 == lseek(fd, fileLength-1, SEEK_SET)) {
    perror("lseek");
    close(fd);
    return -1;
  }
  if (1 != write(fd, "", 1)) {
    perror("write");
    close(fd);
    return -1;
  }
  char *fileptr = (char *)mmap(NULL, fileLength, PROT_READ| PROT_WRITE, MAP_SHARED, fd, 0);
  if (fileptr == MAP_FAILED) {
    perror("mmap");
    close(fd);
    return -1;
  }
// fileLength 2014, 11
  FILE *fp = fopen("a.txt", "r");
// 0 99
// 100 199
// 200 299
// 300 399
// .....
// 1000 1023
  // thread arg
  int i = 0;
  long partSize = fileLength / THREAD_NUM;
  struct fileInfo *info[THREAD_NUM+1] = {NULL};
  for (i = 0;i <= THREAD_NUM;i ++) {
    info[i] = (struct fileInfo*)malloc(sizeof(struct fileInfo));
    memset(info[i], 0, sizeof(struct fileInfo));
    info[i]->offset = i * partSize;
    if (i < THREAD_NUM) {
      info[i]->end = (i+1) * partSize - 1;
    } else {
      info[i]->end = fileLength - 1;
    }
    info[i]->fileptr = fileptr;
    info[i]->url = url;
    info[i]->download = 0;
    info[i]->recordfile = fp;
  }
  pInfoTable = info;
  //pthread_t thid[THREAD_NUM+1] = {0};
  for (i = 0;i <= THREAD_NUM;i ++) {
    pthread_create(&(info[i]->thid), NULL, worker, info[i]);
    usleep(1);
  }
  for (i = 0;i <= THREAD_NUM;i ++) {
    pthread_join(info[i]->thid, NULL);
  }
  for (i = 0;i <= THREAD_NUM;i ++) {    
    free(info[i]);
  }
  if (fp)
    fclose(fp);
  munmap(fileptr, fileLength);
  close(fd);
  return 0;
}
//
void signal_handler(int signum) {
  printf("signum: %d\n", signum);
  //unlink("a.txt");
// save --> 
  int fd = open("a.txt",  O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  if (fd == -1) {
    exit(1);
  }
  int i = 0;
  for (i = 0;i <= THREAD_NUM;i ++) {
    char range[64] = {0};
    snprintf(range, 64, "%d-%d\r\n", pInfoTable[i]->offset, pInfoTable[i]->end);
    write(fd, range, strlen(range));
  }
  close(fd);
  exit(1);
}
// multi_download  https://releases.ubuntu.com/22.04/ubuntu-22.04.2-live-server-amd64.iso.zsync  ubuntu.zsync
int main(int argc, const char *argv[]) {
  if (argc != 3) {
    printf("arg error\n");
    return -1;
  }
  if (SIG_ERR == signal(SIGINT, signal_handler)) {
    perror("signal");
    return -1;
  }
  return download(argv[1], argv[2]);
}
相关文章
|
6月前
|
存储 缓存 Linux
如何在Linux环境下对pip的缓存地址进行修改
如何在Linux环境下对pip的缓存地址进行修改
1212 0
|
3月前
|
关系型数据库 MySQL 数据库
MySQL数据库安装——zip压缩包形式
MySQL数据库安装——zip压缩包形式
156 2
|
6月前
|
缓存 Linux 应用服务中间件
linux yum下载离线包缓存 安装到服务器 实测!!!
linux yum下载离线包缓存 安装到服务器 实测!!!
122 0
|
11月前
|
测试技术 Linux
收集yum install安装的软件的全量依赖 rpm 包
收集yum install安装的软件的全量依赖 rpm 包
261 0
|
Linux Android开发
Linux下tar命令结合split实现的分包和并包
Linux下tar命令结合split实现的分包和并包
682 1
|
PHP
PHP多文件分片压缩下载实现的详细介绍(二)
在上一篇博客中写了一个先压缩后分片下载的demo,但是存在一定的缺点,本篇介绍的是先分片然后放入压缩包最后再分片下载
99 0
|
XML JSON 搜索推荐
PHP ZipArchive 大文件分片下载压缩 支持断点续传
PHP ZipArchive 大文件分片下载压缩 支持断点续传
192 0
|
Java 程序员
【memcached】下载、安装、、出错一分钟全搞定
【memcached】下载、安装、、出错一分钟全搞定
214 0
|
JavaScript Windows
nodejs版本大文件之断点下载
由于只是做个测试,这里使用了expresss简单搭建了个后台服务,提供文件断点下载。
nodejs版本大文件之断点下载