【文件分片下载 libcurl】

本文介绍了一段C代码,使用CURL库实现HTTP范围请求,通过多线程下载大文件并记录下载进度,同时处理信号中断时的数据保存。
摘要由CSDN通过智能技术生成

// 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]);

}




libcurl 是一个非常强大的开源网络库,它提供了在各种操作系统上进行文件下载的功能。使用 libcurl 可以实现简单且有效的文件下载。下面是一个使用 libcurl 下载文件的示例: 1. 引入 libcurl文件: ```c #include <curl/curl.h> ``` 2. 定义回调函数: 这个回调函数会在下载数据时被调用,我们可以在这个函数中处理下载的数据。 ```c size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; FILE *file = (FILE *)userp; if (file != NULL) { fwrite(contents, size, nmemb, file); } return realsize; } ``` 3. 执行下载: 在主函数中,我们可以使用 libcurl 提供的函数进行下载。 ```c int main(void) { CURL *curl; FILE *file; curl = curl_easy_init(); if (curl) { // 设置下载的 URL curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/file.txt"); // 打开文件用于保存下载的数据 file = fopen("file.txt", "wb"); if (file != NULL) { // 设置回调函数 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, file); // 执行下载 curl_easy_perform(curl); // 关闭文件 fclose(file); } // 清理资源 curl_easy_cleanup(curl); } return 0; } ``` 以上是使用 libcurl 下载文件的简单示例,它能够通过指定的 URL 下载文件,并将文件保存在本地。使用 libcurl 还可以设置代理、设置下载进度回调等更多功能,使得文件下载功能更加强大和灵活。通过 libcurl,我们可以轻松地在我们的应用程序中实现文件下载功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值