如何使用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
相关产品推荐
相关产品推荐

