C++使用libcurl輕松實現(xiàn)文件下載
最近很多同學(xué)在后臺問我:"康哥,想用C++實現(xiàn)文件下載功能,但不知道從哪里入手,網(wǎng)上的教程要么太簡單,要么太復(fù)雜,有沒有適合新手的實戰(zhàn)教程?"
今天就來滿足大家的需求!用最簡單的方式,帶你掌握C++ + libcurl實現(xiàn)文件下載的核心技術(shù)。
不僅讓你學(xué)會基礎(chǔ)下載,更重要的是為后續(xù)的多線程高性能下載器打下堅實基礎(chǔ)!
為什么選擇libcurl?
在C++中實現(xiàn)HTTP下載,我們有很多選擇:
- socket編程:太底層,需要手動處理HTTP協(xié)議
- 第三方網(wǎng)絡(luò)庫:學(xué)習(xí)成本高,依賴復(fù)雜
- libcurl:工業(yè)級標(biāo)準(zhǔn),簡單易用,幾乎所有Linux系統(tǒng)都預(yù)裝
libcurl的優(yōu)勢:
- 久經(jīng)考驗: 被Git用于HTTP操作,被PHP內(nèi)置cURL擴(kuò)展采用
- 功能強(qiáng)大:支持HTTP/HTTPS/FTP等20+協(xié)議
- 文檔完善:官方文檔詳細(xì),社區(qū)活躍
- 性能優(yōu)秀:C語言實現(xiàn),效率極高
- 跨平臺:Windows/Linux/macOS全支持
環(huán)境準(zhǔn)備
Ubuntu/Debian系統(tǒng)
sudo apt-get update sudo apt-get install libcurl4-openssl-dev
CentOS/RHEL系統(tǒng)
sudo yum install libcurl-devel # 或者新版本使用 sudo dnf install libcurl-devel
驗證安裝
curl-config --version
如果顯示版本號,說明安裝成功!
第一個下載程序:HelloDownloader
我們從最簡單的例子開始。創(chuàng)建文件 hello_downloader.cpp:
#include <iostream>
#include <fstream>
#include <curl/curl.h>
// 數(shù)據(jù)寫入回調(diào)函數(shù)
size_t writeData(void* ptr, size_t size, size_t nmemb, FILE* stream) {
size_t written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main() {
CURL* curl;
FILE* fp;
CURLcode res;
// 下載鏈接(這是一個測試文件)
const char* url = "https://httpbin.org/json";
const char* outfilename = "test.json";
// 全局初始化curl
curl_global_init(CURL_GLOBAL_DEFAULT);
// 創(chuàng)建curl句柄
curl = curl_easy_init();
if(curl) {
// 打開本地文件準(zhǔn)備寫入
fp = fopen(outfilename, "wb");
if(!fp) {
std::cerr << "無法創(chuàng)建文件!" << std::endl;
curl_easy_cleanup(curl);
curl_global_cleanup();
return 1;
}
// 設(shè)置URL
curl_easy_setopt(curl, CURLOPT_URL, url);
// 設(shè)置寫入回調(diào)函數(shù)
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData);
// 設(shè)置寫入文件指針
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
// 跟隨重定向
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
// 執(zhí)行下載
res = curl_easy_perform(curl);
// 檢查結(jié)果
if(res != CURLE_OK) {
std::cerr << "下載失敗: " << curl_easy_strerror(res) << std::endl;
} else {
std::cout << "下載成功!文件保存為: " << outfilename << std::endl;
}
// 清理
fclose(fp);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
編譯運行:
g++ -o hello_downloader hello_downloader.cpp -lcurl ./hello_downloader
如果一切正常,你會看到:
下載成功!文件保存為: test.json
恭喜!你的第一個C++下載器誕生了!
進(jìn)階版本:帶進(jìn)度顯示的下載器
基礎(chǔ)版本太樸素?來個炫酷的進(jìn)度條版本!
#include <iostream>
#include <fstream>
#include <iomanip>
#include <curl/curl.h>
// 進(jìn)度回調(diào)函數(shù)
int progressCallback(void* ptr, double totalToDownload, double nowDownloaded,
double totalToUpload, double nowUploaded) {
if (totalToDownload <= 0.0) return 0;
double percentage = (nowDownloaded / totalToDownload) * 100.0;
int barWidth = 50;
int pos = static_cast<int>(barWidth * percentage / 100.0);
std::cout << "\r[";
for (int i = 0; i < barWidth; ++i) {
if (i < pos) std::cout << "=";
else if (i == pos) std::cout << ">";
else std::cout << " ";
}
std::cout << "] " << std::fixed << std::setprecision(1) << percentage << "%";
std::cout << " (" << static_cast<long>(nowDownloaded) << "/"
<< static_cast<long>(totalToDownload) << " bytes)";
std::cout.flush();
return 0;
}
// 寫入數(shù)據(jù)回調(diào)
size_t writeData(void* ptr, size_t size, size_t nmemb, FILE* stream) {
return fwrite(ptr, size, nmemb, stream);
}
class SimpleDownloader {
private:
CURL* curl;
public:
SimpleDownloader() {
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
}
~SimpleDownloader() {
if (curl) {
curl_easy_cleanup(curl);
}
curl_global_cleanup();
}
bool download(const std::string& url, const std::string& filename) {
if (!curl) return false;
FILE* fp = fopen(filename.c_str(), "wb");
if (!fp) {
std::cerr << "無法創(chuàng)建文件: " << filename << std::endl;
return false;
}
// 基本設(shè)置
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
// 進(jìn)度顯示設(shè)置
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progressCallback);
// 用戶代理(有些網(wǎng)站需要)
curl_easy_setopt(curl, CURLOPT_USERAGENT, "SimpleDownloader/1.0");
// 超時設(shè)置
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 300L); // 5分鐘超時
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L); // 連接30秒超時
std::cout << "開始下載: " << url << std::endl;
CURLcode res = curl_easy_perform(curl);
std::cout << std::endl; // 換行
fclose(fp);
if (res != CURLE_OK) {
std::cerr << "下載失敗: " << curl_easy_strerror(res) << std::endl;
return false;
}
std::cout << "下載完成!文件保存為: " << filename << std::endl;
return true;
}
};
int main() {
SimpleDownloader downloader;
// 你可以替換成任何你想下載的文件
std::string url = "https://httpbin.org/json";
std::string filename = "downloaded_file.json";
if (downloader.download(url, filename)) {
std::cout << "下載成功!" << std::endl;
} else {
std::cout << "下載失??!" << std::endl;
}
return 0;
}
編譯運行:
g++ -o progress_downloader progress_downloader.cpp -lcurl ./progress_downloader
你會看到類似這樣的效果:
開始下載: https://httpbin.org/json
[==================================================] 100.0% (429/429 bytes)
核心概念詳解
1. CURL句柄管理
// 全局初始化(程序啟動時調(diào)用一次) curl_global_init(CURL_GLOBAL_DEFAULT); // 創(chuàng)建會話句柄 CURL* curl = curl_easy_init(); // 使用完畢后清理 curl_easy_cleanup(curl); curl_global_cleanup();
2. 關(guān)鍵選項設(shè)置
// 基礎(chǔ)設(shè)置 curl_easy_setopt(curl, CURLOPT_URL, url); // 設(shè)置URL curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // 跟隨重定向 curl_easy_setopt(curl, CURLOPT_USERAGENT, "MyApp/1.0"); // 用戶代理 // 超時控制 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 300L); // 總超時時間 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L); // 連接超時 // SSL設(shè)置(HTTPS需要) curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); // 驗證證書 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); // 驗證主機(jī)名
3. 回調(diào)函數(shù)機(jī)制
libcurl通過回調(diào)函數(shù)處理數(shù)據(jù):
// 數(shù)據(jù)接收回調(diào)
size_t writeCallback(void* contents, size_t size, size_t nmemb, void* userp) {
size_t realsize = size * nmemb;
// 處理接收到的數(shù)據(jù)
return realsize; // 返回處理的字節(jié)數(shù)
}
// 進(jìn)度回調(diào)
int progressCallback(void* clientp, double dltotal, double dlnow,
double ultotal, double ulnow) {
// 顯示進(jìn)度信息
return 0; // 返回0繼續(xù),非0中止
}
常見問題與解決方案
問題1:編譯時找不到curl.h
解決方案:
# 檢查是否安裝開發(fā)包 dpkg -l | grep curl # 如果沒有,重新安裝 sudo apt-get install libcurl4-openssl-dev
問題2:下載HTTPS鏈接失敗
解決方案:
// 添加SSL設(shè)置 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); curl_easy_setopt(curl, CURLOPT_CAINFO, "/etc/ssl/certs/ca-certificates.crt");
問題3:某些網(wǎng)站返回403錯誤
解決方案:
// 設(shè)置更真實的用戶代理
curl_easy_setopt(curl, CURLOPT_USERAGENT,
"Mozilla/5.0 (Linux; x86_64) AppleWebKit/537.36");
// 添加請求頭
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Accept: */*");
headers = curl_slist_append(headers, "Accept-Encoding: gzip, deflate");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
性能優(yōu)化小貼士
1. 啟用壓縮
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); // 自動處理所有支持的編碼
2. 復(fù)用連接
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 120L); curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 60L);
3. 設(shè)置合適的緩沖區(qū)
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L); // 100KB緩沖區(qū)
總結(jié)
通過這篇教程,我們學(xué)會了:
- libcurl環(huán)境搭建:快速安裝和配置
- 基礎(chǔ)下載實現(xiàn):從最簡單的 demo 開始
- 進(jìn)階功能添加:進(jìn)度顯示、錯誤處理、超時控制
- 面向?qū)ο蠓庋b:用類封裝提高代碼復(fù)用性
- 常見問題解決:實際開發(fā)中的坑點和解決方案
現(xiàn)在你已經(jīng)掌握了C++單線程下載的核心技術(shù)!
但是,單線程下載在面對大文件時還是太慢了。試想一下:
- 下載幾GB的文件需要等很久
- 網(wǎng)絡(luò)中斷后又要重新開始
- ....
如果能實現(xiàn)多線程并發(fā)下載,速度提升10倍以上,那該多爽!
到此這篇關(guān)于C++使用libcurl輕松實現(xiàn)文件下載的文章就介紹到這了,更多相關(guān)C++ libcurl文件下載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于C++STL string類的介紹及模擬實現(xiàn)
這篇文章主要介紹了關(guān)于C++STL string類的介紹及模擬實現(xiàn)的相關(guān)資料,需要的朋友可以參考下面具體的文章內(nèi)容2021-09-09
用C語言舉例講解數(shù)據(jù)結(jié)構(gòu)中的算法復(fù)雜度結(jié)與順序表
這篇文章主要介紹了講解數(shù)據(jù)結(jié)構(gòu)中的算法復(fù)雜度結(jié)與順序表的C語言版示例,包括對時間復(fù)雜度和空間復(fù)雜度等概念的簡單講解,需要的朋友可以參考下2016-02-02
Qt使用QPainter實現(xiàn)自定義圓形進(jìn)度條
這篇文章主要介紹了Qt如何使用QPainter實現(xiàn)自定義圓形進(jìn)度條功能,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Qt有一定的幫助,需要的可以參考一下2022-06-06
Qt中QSettings配置文件的讀寫和應(yīng)用場景詳解
這篇文章主要給大家介紹了關(guān)于Qt中QSettings配置文件的讀寫和應(yīng)用場景的相關(guān)資料,QSettings能讀寫配置文件,當(dāng)配置文件不存在時,可生成配置文件,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10

