我正在使用 Qt 开发 Android 应用程序。我需要从 HTTPS URL 下载文件。应用程序开发人员现在必须发布自己的 OpenSSL 副本。我发现最简单的方法是使用 android_openssl 项目。理论上,这为 Qt 提供了一个可用的 TLS 后端,用于从 HTTPS 服务器下载文件。
克隆最小的可重现示例:
git clone https://github.com/ftab/QuickDownloadTest --recursive
CMakeLists.txt(根据 Qt Creator 新项目向导生成的内容进行修改,并添加必要的行
android_openssl
):
cmake_minimum_required(VERSION 3.16)
project(QuickDownloadTest VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 6.5 REQUIRED COMPONENTS Quick Network)
qt_standard_project_setup(REQUIRES 6.5)
include(android_openssl/android_openssl.cmake)
qt_add_executable(appQuickDownloadTest
main.cpp
)
add_android_openssl_libraries(appQuickDownloadTest)
qt_add_qml_module(appQuickDownloadTest
URI QuickDownloadTest
VERSION 1.0
QML_FILES
Main.qml
)
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
set_target_properties(appQuickDownloadTest PROPERTIES
# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appQuickDownloadTest
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
target_link_libraries(appQuickDownloadTest
PRIVATE Qt6::Quick Qt6::Network
)
include(GNUInstallDirs)
install(TARGETS appQuickDownloadTest
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QObject>
#include <QNetworkReply>
class Downloader : public QObject
{
Q_OBJECT
public:
explicit Downloader(QObject* parent = nullptr) : QObject(parent)
{
};
Q_INVOKABLE void downloadFile(const QString &url)
{
QNetworkAccessManager manager;
QNetworkRequest request((QUrl(url)));
m_reply = manager.get(request);
QObject::connect(m_reply, &QNetworkReply::finished, this, &Downloader::on_fileDownloaded);
QObject::connect(m_reply, &QNetworkReply::errorOccurred, this, &Downloader::on_errorOccurred);
};
Q_INVOKABLE QByteArray data()
{
return m_data;
};
Q_INVOKABLE QString toString()
{
return QString(m_data);
};
public slots:
void on_fileDownloaded()
{
if (m_reply->error() == QNetworkReply::NoError)
{
m_data = m_reply->readAll();
qDebug() << "Download success";
emit success();
}
else
{
qDebug() << "Download error:" << m_reply->errorString();
emit error();
}
m_reply->deleteLater();
m_reply = nullptr;
};
void on_errorOccurred(QNetworkReply::NetworkError error)
{
qDebug() << "Download error occurred:" << error;
};
signals:
void error();
void success();
private:
QByteArray m_data;
QNetworkReply *m_reply{nullptr};
};
#include "main.moc"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
auto context = engine.rootContext();
auto downloader = new Downloader();
context->setContextProperty("Downloader", downloader);
engine.loadFromModule("QuickDownloadTest", "Main");
return app.exec();
}
主要.qml:
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Window
ApplicationWindow {
visible: true
flags: Qt.Window | Qt.FramelessWindowHint
ColumnLayout {
Button {
text: "Download File"
onClicked: Downloader.downloadFile("https://gist.githubusercontent.com/ftab/818fbe5080a18ecb7332c7fe4542eeb3/raw/82a9750622a5a4db4c5250068cf2f6bba117ff51/test.txt");
}
Label {
id: label
text: "Result"
}
}
Connections {
target: Downloader
function onSuccess()
{
label.text = Downloader.toString();
}
function onError()
{
label.text = "Error";
}
}
}
(配置 Qt 项目以在您所需的测试设备上构建 Android)
我希望下载能够正常工作,并且标签将使用我的
test.txt
文件(“v1.0.1”)的内容进行更新。
实际发生的事情是……什么也没有。显然。一旦我安装了
android_openssl
,我在尝试下载时不会遇到任何错误,但该请求似乎从未真正执行任何操作。我没有收到任何完成信号、错误信号或任何实际发生任何事情的指示。
m_reply->errorString()
),我会收到段错误。我该如何找出问题所在?是 android_openssl 损坏还是我做错了什么?
原因可能是你在回复完成之前就破坏了
QNetworkAccessManager
。它必须保持活力,直到答案到来。引用规格:
QNetworkAccessManager::~QNetworkAccessManager()
销毁 QNetworkAccessManager 对象并释放所有资源。请注意,从此类返回的 QNetworkReply 对象将此对象设置为其父对象,这意味着如果您不对它们调用 QObject::setParent() ,它们将随之被删除。
我使用 android_openssl,它对我来说工作正常。