You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用QNetworkAccessManager通过HTTPS实现无阻塞GUI文件下载

解决PyQt5 QtNetwork HTTPS下载失败(SSL函数解析错误)问题

问题重现

实现的异步下载类可正常处理HTTP链接,但下载HTTPS链接时生成空文件,运行时输出以下SSL相关错误:

qt.network.ssl: QSslSocket: cannot resolve EVP_PKEY_base_id
qt.network.ssl: QSslSocket: cannot resolve SSL_get_peer_certificate
qt.network.ssl: QSslSocket: cannot call unresolved function SSL_get_peer_certificate
errors [<PyQt5.QtNetwork.QSslError object at 0x7fad867112a0>]
qt.network.ssl: QSslSocket: cannot call unresolved function EVP_PKEY_base_id

用户实现代码:

import os
from typing import Optional
import urllib.parse

from PyQt5.QtCore import pyqtSignal, QByteArray, QFile, QObject, QUrl
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkRequest


class AsyncDownloader(QObject):
    def __init__(self, url: str, filename: str, parent=None):
        super().__init__(parent)
        self.net_mgr = QNetworkAccessManager()
        self.req = QNetworkRequest(QUrl(url))
        self.fetch_task: Optional[QNetworkReply] = None
        self.data: Optional[QByteArray] = None
        self.file = QFile(filename)

        self.net_mgr.sslErrors.connect(self._ignore_ssl_errors)

    def start_fetch(self):
        self.fetch_task = self.net_mgr.get(self.req)
        self.fetch_task.downloadProgress.connect(self.on_progress)
        self.fetch_task.finished.connect(self.on_finished)
    
    def _ignore_ssl_errors(self, reply: QNetworkReply, errors: List[QSslError]):
        print(f"errors {errors}")
        reply.ignoreSslErrors(errors)
    
    def on_progress(self, bytes_received: int, bytes_total: int):
        print(f"bytes received {bytes_received} (total {bytes_total})")

    def on_finished(self):
        print("finished")
        self.data = self.fetch_task.readAll()
        if not self.file.open(QFile.WriteOnly):
            raise IOError(f"Unable to write to {self.file.fileName}")
        self.file.write(self.data)
        self.file.close()
        print(f"file written to {self.file.fileName()}")


if __name__ == '__main__':
    from pathlib import Path
    from PyQt5.QtWidgets import QApplication

    dl_path = os.path.join(str(Path.home()), "test_dl")
    os.makedirs(dl_path, exist_ok=True)
    app = QApplication([])
    downloader = AsyncDownloader(
        "https://github.com/PiRK/Electrum-ABC-Build-Tools/releases/download/v1.0/tor-linux",
        os.path.join(dl_path, "tor")
    )
    downloader.start_fetch()

    app.exec_()

用户曾尝试手动调用connectToHostEncrypted,但无效果,而wget可正常下载目标文件。


解决方案

1. 核心原因:缺失OpenSSL运行库

QtNetwork的HTTPS功能依赖OpenSSL库,错误信息中的函数解析失败,说明当前环境中PyQt5无法找到正确的OpenSSL库文件。

2. 修复步骤

(1)安装/配置OpenSSL

  • Linux系统:通过包管理器安装OpenSSL开发包
    # Debian/Ubuntu 系列
    sudo apt update && sudo apt install libssl-dev
    # RHEL/CentOS/Fedora 系列
    sudo dnf install openssl-devel
    
  • Windows系统:
    下载对应版本的OpenSSL预编译包,将libssl-*.dll和libcrypto-*.dll复制到Python的DLLs目录,或者添加到系统环境变量PATH中。

(2)验证PyQt5的SSL支持

在Python交互环境中执行以下代码,确认SSL功能是否正常:

from PyQt5.QtNetwork import QSslSocket
print("SSL支持状态:", QSslSocket.supportsSsl())
print("SSL库版本:", QSslSocket.sslLibraryVersionString())

如果输出SSL支持状态: False,说明当前PyQt5未启用SSL支持,需重新安装PyQt5:

pip uninstall pyqt5
pip install pyqt5 --upgrade

(3)修复代码中的类型错误

_ignore_ssl_errors方法中使用了List[QSslError]但未导入对应类型,需添加以下导入语句:

from PyQt5.QtNetwork import QSslError
from typing import List

⚠️ 注意:不要盲目忽略所有SSL错误,这会引入安全风险,正式环境需正确处理证书验证,仅在测试场景临时使用该逻辑。

(4)优化下载逻辑(可选)

当前代码将全部数据读入内存后再写入文件,大文件场景会占用过多内存,建议改为实时写入:

def start_fetch(self):
    self.fetch_task = self.net_mgr.get(self.req)
    self.fetch_task.downloadProgress.connect(self.on_progress)
    self.fetch_task.readyRead.connect(self.on_ready_read)
    self.fetch_task.finished.connect(self.on_finished)
    # 提前打开文件
    if not self.file.open(QFile.WriteOnly):
        raise IOError(f"无法写入文件: {self.file.fileName()}")

def on_ready_read(self):
    if self.file.isOpen():
        self.file.write(self.fetch_task.readAll())

def on_finished(self):
    print("下载完成")
    self.file.close()
    print(f"文件已写入: {self.file.fileName()}")
    # 检查下载是否出错
    if self.fetch_task.error() != QNetworkReply.NoError:
        print(f"下载错误: {self.fetch_task.errorString()}")

内容的提问来源于stack exchange,提问作者PiRK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 02:20:38