用 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 找不到受信任的根证书。
正确做法(调试完务必保留):

  1. curl-ca-bundle.crt(同在官方 zip 的 bin/ 目录)拷到 exe 旁;
  2. 代码里指一下路径:
    curl_easy_setopt(c, CURLOPT_CAINFO, "curl-ca-bundle.crt");
    
  3. 或者 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"));
}
  • 主线程零阻塞
  • 想加进度、取消,继续用 watcherprogressValue / 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 + 线程 + 安全默认值

posted @ 2025-11-25 11:09  Tlink  阅读(21)  评论(0)    收藏  举报