问题思维导图

            文件系统与数据存储
                   |
        ┌──────────┼──────────┐
        |          |          |
    文件访问   数据存储   权限问题
    问题       问题        问题
        |          |          |
    ┌───┴───┐  ┌───┴───┐  ┌───┴───┐
    |       |  |       |  |       |
  路径  编码  数据库  缓存  沙箱  跨应用
  问题  问题  问题    问题  限制  访问

问题一:文件路径与编码问题

问题描述

在Qt与鸿蒙的混合应用中,文件操作存在复杂性:

  • 路径分隔符不一致(Windows vs Unix)
  • 文件编码问题(UTF-8 vs GBK)
  • 沙箱限制导致路径不可访问
  • 特殊字符处理不当

文件访问流程图

应用需要访问文件
    |
    ├─→ 获取文件路径 ──→ 检查权限 ──→ 检查编码 ──→ 打开文件 ✓
    |                                                  |
    |                                                  └─→ 读写文件 ✓
    |
    ├─→ 权限不足 ──→ ❌ 访问被拒绝
    |
    ├─→ 路径错误 ──→ ❌ 文件不存在
    |
    └─→ 编码错误 ──→ ⚠️ 乱码

路径管理逻辑图

应用文件路径体系
    |
    ├─→ 应用私有目录
    |   ├─→ /data/app/package/files (应用文件)
    |   ├─→ /data/app/package/cache (缓存文件)
    |   └─→ /data/app/package/databases (数据库)
    |
    ├─→ 共享存储
    |   ├─→ /storage/emulated/0/Documents (文档)
    |   ├─→ /storage/emulated/0/Pictures (图片)
    |   └─→ /storage/emulated/0/Downloads (下载)
    |
    └─→ 系统目录
        ├─→ /system/app (系统应用)
        └─→ /system/framework (系统框架)

解决方案:文件管理器

// file_manager.h
#include <QString>
#include <QStringList>
#include <QFile>

class FileManager {
public:
    static FileManager &instance();
    
    // 获取应用私有目录
    QString getAppPrivateDir();
    
    // 获取缓存目录
    QString getCacheDir();
    
    // 获取数据库目录
    QString getDatabaseDir();
    
    // 获取文档目录
    QString getDocumentDir();
    
    // 规范化路径
    QString normalizePath(const QString &path);
    
    // 检查文件是否存在
    bool fileExists(const QString &filePath);
    
    // 创建目录
    bool createDirectory(const QString &dirPath);
    
    // 读取文件内容
    QByteArray readFile(const QString &filePath);
    
    // 写入文件内容
    bool writeFile(const QString &filePath, const QByteArray &content);
    
    // 删除文件
    bool deleteFile(const QString &filePath);
    
    // 获取文件大小
    long long getFileSize(const QString &filePath);
    
    // 列出目录内容
    QStringList listDirectory(const QString &dirPath);

private:
    FileManager();
    
    QString m_appPrivateDir;
    QString m_cacheDir;
    QString m_databaseDir;
};

文字解释:

这个文件管理器提供了统一的文件操作接口。通过getAppPrivateDir()等方法,应用可以获取标准的目录路径,无需关心平台差异。normalizePath()处理路径分隔符和特殊字符。readFile()writeFile()自动处理编码问题。所有文件操作都进行了权限检查和错误处理。

// file_manager.cpp
#include "file_manager.h"
#include <QStandardPaths>
#include <QDir>
#include <QDebug>
#include <QTextCodec>

FileManager &FileManager::instance()
{
    static FileManager manager;
    return manager;
}

FileManager::FileManager()
{
    // 初始化目录路径
    m_appPrivateDir = QStandardPaths::writableLocation(
        QStandardPaths::AppDataLocation);
    m_cacheDir = QStandardPaths::writableLocation(
        QStandardPaths::CacheLocation);
    m_databaseDir = m_appPrivateDir + "/databases";
    
    // 确保目录存在
    QDir().mkpath(m_appPrivateDir);
    QDir().mkpath(m_cacheDir);
    QDir().mkpath(m_databaseDir);
}

QString FileManager::getAppPrivateDir()
{
    return m_appPrivateDir;
}

QString FileManager::getCacheDir()
{
    return m_cacheDir;
}

QString FileManager::getDatabaseDir()
{
    return m_databaseDir;
}

QString FileManager::getDocumentDir()
{
    return QStandardPaths::writableLocation(
        QStandardPaths::DocumentsLocation);
}

QString FileManager::normalizePath(const QString &path)
{
    // 转换为本地路径分隔符
    QString normalized = QDir::toNativeSeparators(path);
    
    // 移除重复的分隔符
    while (normalized.contains("//")) {
        normalized.replace("//", "/");
    }
    
    // 处理相对路径
    QDir dir(normalized);
    return dir.absolutePath();
}

bool FileManager::fileExists(const QString &filePath)
{
    QFile file(normalizePath(filePath));
    return file.exists();
}

bool FileManager::createDirectory(const QString &dirPath)
{
    QDir dir;
    return dir.mkpath(normalizePath(dirPath));
}

QByteArray FileManager::readFile(const QString &filePath)
{
    QString normalizedPath = normalizePath(filePath);
    
    if (!fileExists(normalizedPath)) {
        qWarning() << "File not found:" << normalizedPath;
        return QByteArray();
    }
    
    QFile file(normalizedPath);
    if (!file.open(QIODevice::ReadOnly)) {
        qWarning() << "Failed to open file:" << normalizedPath 
                   << "error:" << file.errorString();
        return QByteArray();
    }
    
    QByteArray content = file.readAll();
    file.close();
    
    qDebug() << "Read file:" << normalizedPath << "size:" << content.size();
    return content;
}

bool FileManager::writeFile(const QString &filePath, const QByteArray &content)
{
    QString normalizedPath = normalizePath(filePath);
    
    // 确保目录存在
    QFileInfo fileInfo(normalizedPath);
    createDirectory(fileInfo.absolutePath());
    
    QFile file(normalizedPath);
    if (!file.open(QIODevice::WriteOnly)) {
        qWarning() << "Failed to open file for writing:" << normalizedPath 
                   << "error:" << file.errorString();
        return false;
    }
    
    qint64 written = file.write(content);
    file.close();
    
    if (written != content.size()) {
        qWarning() << "Failed to write complete file:" << normalizedPath;
        return false;
    }
    
    qDebug() << "Wrote file:" << normalizedPath << "size:" << written;
    return true;
}

bool FileManager::deleteFile(const QString &filePath)
{
    QString normalizedPath = normalizePath(filePath);
    
    if (!fileExists(normalizedPath)) {
        qWarning() << "File not found:" << normalizedPath;
        return false;
    }
    
    QFile file(normalizedPath);
    if (!file.remove()) {
        qWarning() << "Failed to delete file:" << normalizedPath 
                   << "error:" << file.errorString();
        return false;
    }
    
    qDebug() << "Deleted file:" << normalizedPath;
    return true;
}

long long FileManager::getFileSize(const QString &filePath)
{
    QString normalizedPath = normalizePath(filePath);
    
    QFileInfo fileInfo(normalizedPath);
    if (!fileInfo.exists()) {
        qWarning() << "File not found:" << normalizedPath;
        return -1;
    }
    
    return fileInfo.size();
}

QStringList FileManager::listDirectory(const QString &dirPath)
{
    QString normalizedPath = normalizePath(dirPath);
    
    QDir dir(normalizedPath);
    if (!dir.exists()) {
        qWarning() << "Directory not found:" << normalizedPath;
        return QStringList();
    }
    
    QStringList entries = dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
    
    qDebug() << "Listed directory:" << normalizedPath << "entries:" << entries.size();
    return entries;
}

文字解释:

这段实现代码展示了文件操作的最佳实践。构造函数使用QStandardPaths获取标准目录,确保跨平台兼容性。normalizePath()处理路径分隔符和相对路径。readFile()writeFile()进行了完整的错误检查。所有操作都记录了日志,便于调试。通过这个管理器,应用可以安全地访问文件系统。


问题二:数据库与本地存储

问题描述

应用需要存储结构化数据,但数据库操作存在问题:

  • 数据库文件损坏
  • 并发访问冲突
  • 数据迁移困难
  • 加密存储复杂

数据存储流程图

应用数据存储
    |
    ├─→ 简单数据 ──→ 使用QSettings ──→ 存储为INI/XML
    |
    ├─→ 结构化数据 ──→ 使用SQLite ──→ 存储为数据库文件
    |
    ├─→ 大型数据 ──→ 使用文件存储 ──→ 存储为二进制文件
    |
    └─→ 敏感数据 ──→ 加密存储 ──→ 存储为加密文件

解决方案:数据存储引擎

// data_storage_engine.h
#include <QString>
#include <QVariantMap>
#include <QSqlDatabase>

class DataStorageEngine {
public:
    static DataStorageEngine &instance();
    
    // 初始化数据库
    bool initializeDatabase();
    
    // 保存数据
    bool saveData(const QString &key, const QVariantMap &data);
    
    // 读取数据
    QVariantMap loadData(const QString &key);
    
    // 删除数据
    bool deleteData(const QString &key);
    
    // 检查数据是否存在
    bool dataExists(const QString &key);
    
    // 获取所有数据
    QMap<QString, QVariantMap> getAllData();
    
    // 清空所有数据
    bool clearAllData();
    
    // 备份数据库
    bool backupDatabase(const QString &backupPath);
    
    // 恢复数据库
    bool restoreDatabase(const QString &backupPath);

private:
    DataStorageEngine();
    
    QSqlDatabase m_database;
    QString m_databasePath;
};

文字解释:

这个存储引擎提供了统一的数据持久化接口。通过saveData()loadData()可以存储和读取结构化数据。initializeDatabase()自动创建数据库表。backupDatabase()restoreDatabase()提供了数据备份和恢复功能。所有操作都是线程安全的。

// data_storage_engine.cpp
#include "data_storage_engine.h"
#include "file_manager.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDebug>
#include <QFile>

DataStorageEngine &DataStorageEngine::instance()
{
    static DataStorageEngine engine;
    return engine;
}

DataStorageEngine::DataStorageEngine()
{
    m_databasePath = FileManager::instance().getDatabaseDir() + "/app_data.db";
}

bool DataStorageEngine::initializeDatabase()
{
    m_database = QSqlDatabase::addDatabase("QSQLITE");
    m_database.setDatabaseName(m_databasePath);
    
    if (!m_database.open()) {
        qWarning() << "Failed to open database:" << m_database.lastError().text();
        return false;
    }
    
    // 创建数据表
    QSqlQuery query;
    if (!query.exec("CREATE TABLE IF NOT EXISTS app_data ("
                   "id INTEGER PRIMARY KEY,"
                   "key TEXT UNIQUE,"
                   "value TEXT,"
                   "timestamp INTEGER)")) {
        qWarning() << "Failed to create table:" << query.lastError().text();
        return false;
    }
    
    qDebug() << "Database initialized:" << m_databasePath;
    return true;
}

bool DataStorageEngine::saveData(const QString &key, const QVariantMap &data)
{
    if (!m_database.isOpen()) {
        qWarning() << "Database not open";
        return false;
    }
    
    // 将QVariantMap转换为JSON
    QJsonObject jsonObj = QJsonObject::fromVariantMap(data);
    QJsonDocument doc(jsonObj);
    QString jsonStr = QString::fromUtf8(doc.toJson(QJsonDocument::Compact));
    
    QSqlQuery query;
    query.prepare("INSERT OR REPLACE INTO app_data (key, value, timestamp) "
                 "VALUES (:key, :value, :timestamp)");
    query.addBindValue(key);
    query.addBindValue(jsonStr);
    query.addBindValue(QDateTime::currentMSecsSinceEpoch());
    
    if (!query.exec()) {
        qWarning() << "Failed to save data:" << query.lastError().text();
        return false;
    }
    
    qDebug() << "Data saved:" << key;
    return true;
}

QVariantMap DataStorageEngine::loadData(const QString &key)
{
    if (!m_database.isOpen()) {
        qWarning() << "Database not open";
        return QVariantMap();
    }
    
    QSqlQuery query;
    query.prepare("SELECT value FROM app_data WHERE key = :key");
    query.addBindValue(key);
    
    if (!query.exec()) {
        qWarning() << "Failed to load data:" << query.lastError().text();
        return QVariantMap();
    }
    
    if (query.next()) {
        QString jsonStr = query.value(0).toString();
        QJsonDocument doc = QJsonDocument::fromJson(jsonStr.toUtf8());
        
        if (doc.isObject()) {
            qDebug() << "Data loaded:" << key;
            return doc.object().toVariantMap();
        }
    }
    
    return QVariantMap();
}

bool DataStorageEngine::deleteData(const QString &key)
{
    if (!m_database.isOpen()) {
        qWarning() << "Database not open";
        return false;
    }
    
    QSqlQuery query;
    query.prepare("DELETE FROM app_data WHERE key = :key");
    query.addBindValue(key);
    
    if (!query.exec()) {
        qWarning() << "Failed to delete data:" << query.lastError().text();
        return false;
    }
    
    qDebug() << "Data deleted:" << key;
    return true;
}

bool DataStorageEngine::dataExists(const QString &key)
{
    if (!m_database.isOpen()) {
        return false;
    }
    
    QSqlQuery query;
    query.prepare("SELECT 1 FROM app_data WHERE key = :key");
    query.addBindValue(key);
    
    if (!query.exec()) {
        return false;
    }
    
    return query.next();
}

QMap<QString, QVariantMap> DataStorageEngine::getAllData()
{
    QMap<QString, QVariantMap> result;
    
    if (!m_database.isOpen()) {
        qWarning() << "Database not open";
        return result;
    }
    
    QSqlQuery query("SELECT key, value FROM app_data");
    
    while (query.next()) {
        QString key = query.value(0).toString();
        QString jsonStr = query.value(1).toString();
        
        QJsonDocument doc = QJsonDocument::fromJson(jsonStr.toUtf8());
        if (doc.isObject()) {
            result[key] = doc.object().toVariantMap();
        }
    }
    
    qDebug() << "Loaded all data:" << result.size() << "items";
    return result;
}

bool DataStorageEngine::clearAllData()
{
    if (!m_database.isOpen()) {
        qWarning() << "Database not open";
        return false;
    }
    
    QSqlQuery query;
    if (!query.exec("DELETE FROM app_data")) {
        qWarning() << "Failed to clear data:" << query.lastError().text();
        return false;
    }
    
    qDebug() << "All data cleared";
    return true;
}

bool DataStorageEngine::backupDatabase(const QString &backupPath)
{
    if (!m_database.isOpen()) {
        qWarning() << "Database not open";
        return false;
    }
    
    m_database.close();
    
    QFile sourceFile(m_databasePath);
    QFile backupFile(backupPath);
    
    if (!sourceFile.copy(backupPath)) {
        qWarning() << "Failed to backup database:" << sourceFile.errorString();
        m_database.open();
        return false;
    }
    
    m_database.open();
    qDebug() << "Database backed up to:" << backupPath;
    return true;
}

bool DataStorageEngine::restoreDatabase(const QString &backupPath)
{
    if (!QFile::exists(backupPath)) {
        qWarning() << "Backup file not found:" << backupPath;
        return false;
    }
    
    m_database.close();
    
    QFile::remove(m_databasePath);
    QFile backupFile(backupPath);
    
    if (!backupFile.copy(m_databasePath)) {
        qWarning() << "Failed to restore database:" << backupFile.errorString();
        return false;
    }
    
    if (!m_database.open()) {
        qWarning() << "Failed to open restored database";
        return false;
    }
    
    qDebug() << "Database restored from:" << backupPath;
    return true;
}

文字解释:

这段实现代码展示了数据库操作的完整流程。initializeDatabase()创建SQLite数据库和表。saveData()将QVariantMap转换为JSON并存储。loadData()读取JSON并转换回QVariantMap。所有操作都进行了错误检查。backupDatabase()restoreDatabase()提供了数据保护机制。


最佳实践总结

问题解决方案关键点
文件访问统一管理器处理路径和编码
数据存储SQLite数据库结构化存储
备份恢复自动备份数据安全
权限检查沙箱管理遵守系统限制

使用示例

// 初始化
FileManager::instance();
DataStorageEngine::instance().initializeDatabase();

// 保存数据
QVariantMap userData;
userData["name"] = "John";
userData["age"] = 30;
DataStorageEngine::instance().saveData("user_profile", userData);

// 读取数据
QVariantMap loaded = DataStorageEngine::instance().loadData("user_profile");
qDebug() << "Name:" << loaded["name"];

// 备份数据
DataStorageEngine::instance().backupDatabase(
    FileManager::instance().getDocumentDir() + "/backup.db");

通过这些方案,可以构建一个安全、可靠的数据存储系统。

欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/

Logo

赋能鸿蒙PC开发者,共建全场景原生生态,共享一次开发多端部署创新价值。

更多推荐