用 Qt + CMake + libcurl 写出不卡界面的 HTTPS 下载器
环境:Qt 5/6 + CMake + MinGW + libcurl 8.17
关键词:QtConcurrent、CMake 找库、SSL 证书、QTextEdit 卡顿、HTTP 请求流程
1. 为什么写这篇
网上 Qt + curl 的例子不少,但大多:
- 直接关 SSL 验证
- 把
curl_easy_perform堵在主线程 - 一次性
appendHtml把几 MB 网页卡成 PPT
本文把调库 → 编译 → 线程 → 渲染 → SSL 证书整条链路串起来,可复制,可直接跑。
2. CMake 怎么找到 curl
curl 没提供 *Config.cmake 时,最简两步:
set(CURL_HOME_DIR "E:/MinGW/curl-8.17.0_4-win64-mingw")
find_library(CURL_LIBRARY
NAMES curl libcurl # 动态/静态都行
PATHS "${CURL_HOME_DIR}/lib"
NO_DEFAULT_PATH)
target_include_directories(yourTarget PRIVATE "${CURL_HOME_DIR}/include")
target_link_libraries(yourTarget PRIVATE ${CURL_LIBRARY})
静态库再加一行:
target_compile_definitions(yourTarget PRIVATE CURL_STATICLIB)
原理:
find_library只负责“把完整路径找回来”,剩下和用系统库一样。
3. 最小可运行 HTTP GET
static size_t writeFn(void *p, size_t sz, size_t n, QByteArray *buf)
{
buf->append(static_cast<const char*>(p), sz * n);
return sz * n;
}
QByteArray html;
CURL *c = curl_easy_init();
curl_easy_setopt(c, CURLOPT_URL, "https://qt.io");
curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, writeFn);
curl_easy_setopt(c, CURLOPT_WRITEDATA, &html);
curl_easy_perform(c);
curl_easy_cleanup(c);
4. SSL 证书异常处理
首次运行极大概率遇到:
SSL peer certificate or SSH remote key was not OK
原因:libcurl 找不到受信任的根证书。
正确做法(调试完务必保留):
- 把
curl-ca-bundle.crt(同在官方 zip 的bin/目录)拷到 exe 旁; - 代码里指一下路径:
curl_easy_setopt(c, CURLOPT_CAINFO, "curl-ca-bundle.crt"); - 或者 Windows 直接用系统证书:
curl_easy_setopt(c, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
关掉验证(
SSL_VERIFYPEER 0)只能临时调通,生产环境必须验证书,否则中间人攻击随手就来。
5. 界面卡顿?把请求扔后台
Qt 官方线程工具:QtConcurrent::run + QFutureWatcher
// 耗时的下载函数
static QByteArray download(const QString &url)
{
QByteArray buf;
CURL *c = curl_easy_init();
...
curl_easy_perform(c);
curl_easy_cleanup(c);
return buf;
}
// 按钮槽
void MainWindow::onBtnClicked()
{
ui->btn->setEnabled(false);
auto *watcher = new QFutureWatcher<QByteArray>(this);
connect(watcher, &QFutureWatcher<QByteArray>::finished,
this, [this, watcher]() {
ui->txtLog->setPlainText(watcher->result());
ui->btn->setEnabled(true);
watcher->deleteLater();
});
watcher->setFuture(QtConcurrent::run(download, "https://www.baidu.com"));
}
- 主线程零阻塞;
- 想加进度、取消,继续用
watcher的progressValue/cancel()即可。
6. 渲染别用 appendHtml
QTextEdit::append(QString) 会触发完整 HTML/CSS 排版,几 MB 源码秒卡。
秒开方案:
ui->txtLog->setPlainText(html); // 只看源码
真要高亮,用 QPlainTextEdit + QSyntaxHighlighter,或分段 insertHtml + processEvents(),再或者异步灌数据,总之别一次性塞。
7. 一张图总结流程
graph TD
A[CMake find_library] --> B[带证书编译链接]
B --> C[QtConcurrent 后台下载]
C --> D[返回 QByteArray]
D --> E[setPlainText 显示]
8. 结语
- 调通 HTTP ≠ 关掉 SSL;证书验证是 HTTPS 的底线。
- 任何阻塞 IO 都别放主线程,使用
QtConcurrent一把梭
QFuture<QByteArray> f = QtConcurrent::run([=]{
return 阻塞式下载(url); // 这里随便堵
});
- 大文本渲染避开 HTML 引擎,
QPlainTextEdit
把这几条记住,Qt 里再集成任何 C 库(OpenCV / SQLite / libusb...)都是同一套打法:
include + link + 线程 + 安全默认值。

浙公网安备 33010602011771号