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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:52:03