Android平台QML FileDialog选文件路径异常无法打开的技术咨询
Android平台QML FileDialog选择下载目录JPG文件并在C++中正常打开的解决方案
问题描述
使用QML的FileDialog组件选择文件,代码在Linux和Windows平台运行正常,但在Android平台会获取到无法打开的异常文件路径,需要实现选择下载目录中的JPG文件并在C++程序中正常打开。
提供的代码
QML-GUI
import QtQuick import QtQuick.Controls import QtQuick.Dialogs import WM 1.0 import assets 1.0 import components 1.0 Item { property Sp selectedSp Rectangle { id: windowBox anchors.fill: parent Image { width: parent.width height: parent.height source: Style.imageBackground } ScrollView { id: scrollView anchors { left: parent.left right: parent.right top: parent.top bottom: commandBar.top margins: Style.sizeScreenMargin } clip: true Column { spacing: Style.sizeScreenMargin width: scrollView.width FileOpen { id: fileBox fileName: selectedSp.uiImageNeuDatei headerText: "Dateiauswahl" onButtonClicked: selectedSp.onSearchButtonClicked() } FileDialog { id: fileDialog title: "Auswahl Bild" onAccepted: selectedSp.onSetFile(fileDialog.selectedFile) visible: selectedSp.uiViewFileDlg.uiValue nameFilters: ["JPG Dateien (*.jpg)"] fileMode: FileDialog.OpenFile } } } } }
FileOpen组件
import QtQuick import QtQuick.Controls import QtQuick.Layouts import WM 1.0 import assets 1.0 import components 1.0 Panel { property StringDecorator fileName signal buttonClicked() width: parent.width*0.9 x: 15 contentComponent: Column { objectName: "FileOpen" width: parent.width spacing: Style.sizeControlSpacing Row { id: row1 spacing: Style.sizeControlSpacing width: parent.width StringEditorSingleLine { stringDecorator: fileName widthValue: 1 setWidthValue: false } Rectangle { id: button width: Style.widthDataControls height: Style.heightDataControls border { color: "#000000" width: 1 } Text { id: name text: "Suchen" verticalAlignment: Qt.AlignVCenter horizontalAlignment: Qt.AlignHCenter anchors { fill: parent margins: Style.heightDataControls / 4 } font.pixelSize: Style.pixelSizeDataControls } MouseArea { anchors.fill: parent onClicked: buttonClicked() } } } } }
C++代码
头文件
class WMLIBSHARED_EXPORT Sp : public QObject { Q_OBJECT Q_PROPERTY(datad::StringDecorator *uiImageNeuBeschreibung READ getImageNeuBeschreibung WRITE setImageNeuBeschreibung NOTIFY valueChanged) Q_PROPERTY(datad::StringDecorator *uiImageNeuDatei READ getImageNeuDatei WRITE setImageNeuDatei NOTIFY valueChanged) Q_PROPERTY(datad::BoolDecorator *uiViewFileDlg READ getViewFileDlg WRITE setViewFileDlg NOTIFY valueChanged) public: explicit Sp(QObject *parent = nullptr, QString urlArg = ""); ~Sp(); datad::StringDecorator *getImageNeuBeschreibung(); datad::StringDecorator *getImageNeuDatei(); datad::BoolDecorator *getViewFileDlg(void); void setImageNeuBeschreibung(datad::StringDecorator *); void setImageNeuBeschreibung(QString); void setImageNeuDatei(datad::StringDecorator *); void setImageNeuDatei(QString); void setViewFileDlg(datad::BoolDecorator *); void requestPermissions(); Q_SIGNALS: void valueChanged(); void imageOk(); void errorSp(int code, QString msg); public Q_SLOTS: void saveImage(); void onSetFile(QString fileNameArg); void onSearchButtonClicked(); private: QString url {}; datad::StringDecorator *imageNeuBeschreibung {nullptr}; datad::StringDecorator *imageNeuDatei {nullptr}; datad::BoolDecorator *viewFileDlg {nullptr}; QObject* rpcClient {nullptr}; datad::StringDecorator* spId {nullptr}; };
实现文件
Sp::Sp(QObject *parent, QString urlArg) : QObject {parent} , url(urlArg) { imageNeuDatei = new datad::StringDecorator(parent, "imageNeuDatei", "Datei", "<empty>"); imageNeuBeschreibung = new datad::StringDecorator(parent, "imageNeuBeschreibung", "Beschreibung", "<empty>"); viewFileDlg = new datad::BoolDecorator(parent, "viewfiledlg", "Zeige Datei Dialog", false); QObject::connect(imageNeuDatei, &datad::StringDecorator::valueChanged, this, &Sp::valueChanged); QObject::connect(imageNeuBeschreibung, &datad::StringDecorator::valueChanged, this, &Sp::valueChanged); QObject::connect( rpcClient, SIGNAL(messageReceived(const QJsonRpcMessage &)), this, SLOT(msgRecieved(const QJsonRpcMessage &))); } Sp::~Sp() { } void Sp::requestPermissions() { QStoragePermission storagePerm; if (storagePerm.status() != Qt::PermissionStatus::Granted) { storagePerm.request([this](const QPermission &perm) { if (perm.status() == Qt::PermissionStatus::Granted) { viewFileDlg->setValue(true); } else { Q_EMIT errorSp(1, "需要存储权限以访问图片文件"); } }); } else { viewFileDlg->setValue(true); } } void Sp::onSearchButtonClicked() { requestPermissions(); } void Sp::saveImage() { qDebug() << ">> Sp > saveImage (" << __LINE__ << ")"; viewFileDlg->setValue(false); QString filePath = imageNeuDatei->getValue(); QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { QString error = QString("无法打开文件: %1").arg(file.errorString()); qDebug() << ">> Sp > saveImage (" << __LINE__ << ") Fehler=" << error; Q_EMIT errorSp(2, error); return; } QImage tempImage; if (!tempImage.load(&file)) { QString error {"Bild konnte nicht geladen werden."}; qDebug() << ">> Sp > saveImage (" << __LINE__ << ") Fehler=" << error; Q_EMIT errorSp(2, error); file.close(); return; } file.close(); tempImage = tempImage.scaled(800, 600, Qt::KeepAspectRatio); if (!tempImage.save("temp.jpg", "JPG", 100)) { QString error {"Temporäre Datei konnte nicht geschrieben werden.."}; qDebug() << ">> Sp > saveImage (" << __LINE__ << ") Fehler=" << error; Q_EMIT errorSp(2, error); } else { QFile tempDatei {"temp.jpg"}; if (!tempDatei.open(QIODeviceBase::ReadOnly)) { QString error {"Bild konnte nicht geladen werden."}; qDebug() << ">> Sp > saveImage (" << __LINE__ << ") Fehler=" << error; Q_EMIT errorSp(2, error); } else { QByteArray blob {tempDatei.readAll()}; qDebug() << ">> Sp > saveImage (" << __LINE__ << ") Bytes = " << blob.size() << " gelesen"; QJsonArray params {}; params.append(QJsonValue(spId->getValue())); params.append(QJsonValue(QUrl(filePath).fileName())); params.append(QJsonValue(imageNeuBeschreibung->getValue())); params.append(QJsonValue(QString(blob.toBase64()))); QJsonRpcMessage msg; msg = msg.createRequest(QString("saveImage"), params); msgSend(msg); tempDatei.close(); tempDatei.remove(); } } } void Sp::setImageNeuBeschreibung(datad::StringDecorator *valArg) { imageNeuBeschreibung = valArg; } void Sp::setImageNeuBeschreibung(QString valArg) { imageNeuBeschreibung->setValue(valArg); } void Sp::setImageNeuDatei(datad::StringDecorator *valArg) { imageNeuDatei = valArg; } void Sp::setImageNeuDatei(QString valArg) { imageNeuDatei->setValue(valArg); } void Sp::setViewFileDlg(datad::BoolDecorator *valArg) { viewFileDlg = valArg; } void Sp::onSetFile(QString fileNameArg) { qDebug() << ">> Sp > onSetFile (" << __LINE__ << ") \t Datei = " << fileNameArg; imageNeuDatei->setValue(fileNameArg); }
解决方案步骤
1. 配置Android权限
在AndroidManifest.xml中添加必要的存储权限:
<!-- Android 12及以下 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- Android 13及以上 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
2. 运行时请求权限
在Sp类中新增requestPermissions()方法,替换原有的onSearchButtonClicked()逻辑,确保在打开文件对话框前获取权限,避免因权限缺失导致的文件访问失败。
3. 适配Android的Content URI读取
Android平台上FileDialog返回的是content://格式的URI,而非本地文件路径,因此不能使用QUrl::toLocalFile()解析。直接使用QFile打开该URI(Qt 5.15+支持此特性),修改saveImage()方法中的文件读取逻辑,绕过本地路径解析步骤。
4. 指定FileDialog打开下载目录(可选)
修改QML中的FileDialog,设置folder属性为系统下载目录,需要导入Qt.labs.platform模块(针对Qt Quick Controls 2):
import Qt.labs.platform 1.1 FileDialog { // ... 其他属性 folder: StandardPaths.writableLocation(StandardPaths.DownloadLocation) }
内容的提问来源于stack exchange,提问作者msauer75
相关产品推荐
相关产品推荐

