HarmonyOS 鸿蒙Next中是否支持otg连接背夹设备(带外部存储功能)直接实现文件读写功能

HarmonyOS 鸿蒙Next中是否支持otg连接背夹设备(带外部存储功能)直接实现文件读写功能 【问题描述】:目前鸿蒙是否支持otg连接背夹设备(带外部存储功能)直接实现文件读写功能

【问题现象】:不知道鸿蒙这边是否支持otg连接带外部存储功能的设备通过api直接实现外部设备文件读写的功能

【版本信息】:未涉及

【复现代码】:未涉及

【尝试解决方案】:未涉及

5 回复

【解决方案】

一、如果需要通过接口来删除文件的话,可以使用fileManagerService.deleteToTrash来删除对应的文件。

二、如果需要使用公共目录下的数据库文件,主要有以下两种解决方案:

主要介绍如何直接使用公共目录下的数据库文件。

  1. 使用documentViewPicker.select()选择公共目录下的数据库文件。

注意:

  • 关系型数据库至少需要.db、.db-shm、.db-wal三个文件,因此此处需要引导用户选择对应的这三个文件。
  • 该接口获取的URI权限是临时只读权限,待退出应用后台后,获取的临时权限就会失效。
selectFile() {
  try {
    let documentSelectOptions = new picker.DocumentSelectOptions();
    documentSelectOptions.maxSelectNumber = 3;
    documentSelectOptions.fileSuffixFilters = ['.db', '.db-shm', '.db-wal'];
    documentSelectOptions.selectMode = picker.DocumentSelectMode.MIXED;
    let documentPicker = new picker.DocumentViewPicker(this.context);
    documentPicker.select(documentSelectOptions).then((result: Array<string>) => {
      console.info(`DocumentViewPicker.select successfully, result uri: ${JSON.stringify(result)}`);
      if (!result || result.length < 1) {
        console.warn(`未选择文件`);
        this.promptAction.showToast({ message: '请选择对应db、db-shm、db-wal文件' });
        return;
      }
      if (result.length < 3) {
        console.warn(`请选择必须的db、db-shm、db-wal文件`);
        this.promptAction.showToast({ message: '请选择对应db、db-shm、db-wal文件' });
        return;
      }
      // 持久化授权:如果是 /storage/Users/currentUser/Download/应用包名 目录下的文件无需持久化授权即可使用
      this.persistPermission(result);
      // 获取其中的db文件名、rootDir、customDir
      this.resolveDbMessage(result);
      this.promptAction.showToast({ message: `选择成功!` });
    }).catch((err: BusinessError) => {
      console.error(`documentPicker select failed, code is ${err.code},message is ${err.message}`);
    });
  } catch (e) {
    let err = e as BusinessError;
    console.error(`selectFile failed, code is ${err.code},message is ${err.message}`);
  }
}
  1. 使用fileShare.persistPermission()对上述选择的文件URI进行持久化授权,需在module.json5文件中配置ohos.permission.FILE_ACCESS_PERSIST权限。数据库只读场景至少需要有.db、.db-shm、.db-wal这三个文件的可读权限。

注意:

  • 持久化授权需要ohos.permission.FILE_ACCESS_PERSIST权限,该权限属于ACL受限权限,需要参考申请受限权限进行申请。
  • 持久化授权的数据存储在系统的数据库中,应用或者设备重启后需要使用fileShare.activatePermission()激活已持久化的授权才可以正常使用激活持久化授权。
  • 对于DOWNLOAD/应用包名对应的公共目录路径,应用无需使用持久化授权即可获得读取权限。
{
  "name": "ohos.permission.FILE_ACCESS_PERSIST"
},
persistPermission(uris: Array<string>) {
  try {
    // 获取已授权uri,对未授权uri重新授权
    let policyUris = this.dataPreferences.getSync('policyUris', []) as Array<string>;
    if (policyUris && policyUris.length > 0) {
      uris.filter((uri) => {
        // 过滤已授权uri
        return !policyUris.includes(uri);
      });
    }
    let policies: Array<fileShare.PolicyInfo> = [];
    uris.forEach((uri: string) => {
      let policyInfo: fileShare.PolicyInfo = {
        uri: uri,
        operationMode: fileShare.OperationMode.READ_MODE,
      };
      policies.push(policyInfo);
    });
    // 持久化授权:需在module.json5文件中配置ohos.permission.FILE_ACCESS_PERSIST权限
    fileShare.persistPermission(policies).then(() => {
      console.info(`persistPermission successfully. policies: ${JSON.stringify(policies)}`);
      // 授权成功,保存已授权uri
      let policyUris = this.dataPreferences.getSync('policyUris', []) as Array<string>;
      if (policyUris) {
        policyUris.push(...uris);
      }
      this.dataPreferences.putSync('policyUris', policyUris);
      this.dataPreferences.flushSync();
    }).catch((err: BusinessError<Array<fileShare.PolicyErrorResult>>) => {
      console.error(`persistPermission failed with error message: ${err.message}, error code: ${err.code}`);
      if (err.code == 13900001 && err.data) {
        for (let i = 0; i < err.data.length; i++) {
          console.error(`error code : ${JSON.stringify(err.data[i].code)}`);
          console.error(`error uri : ${JSON.stringify(err.data[i].uri)}`);
          console.error(`error reason : ${JSON.stringify(err.data[i].message)}`);
        }
      }
    });
  } catch (error) {
    let err: BusinessError = error as BusinessError;
    console.error(`persistPermission failed with err, Error code: ${err.code}, message: ${err.message}`);
  }
}
  1. 提取公共目录下对应的db文件名、rootDir、customDir,使用relationalStore.getRdbStore()以只读模式打开对应数据库,即可读取公共目录的数据库文件内容。
initRdb() {
  try {
    /**
     * 直接使用公共目录下数据库文件至少需要.db、.db-shm、.db-wal,所以在实际开库使用前需要提前校验是否有这对应三个文件的可读权限
     */
    if (!this.checkUirPermission()) {
      console.warn(`校验未通过,请重新选择必要的db、db-shm、db-wal文件.`);
      return;
    }
    let dbConfig: relationalStore.StoreConfig = {
      name: this.dbName,
      securityLevel: relationalStore.SecurityLevel.S3,
      rootDir: this.rootDir,
      customDir: this.customDir
    };
    console.info(`dbName: ${this.dbName}, rootDir: ${this.rootDir}, customDir: ${this.customDir}`);
    // 1.获取公共目录下的数据库文件,使用只读模式打开
    relationalStore.getRdbStore(this.context, dbConfig).then((rdbStore: relationalStore.RdbStore) => {
      this.store = rdbStore;
      try {
        if (rdbStore != undefined) {
          // 监听sql错误信息
          rdbStore.on('sqliteErrorOccurred', exceptionMessage => {
            let sqliteCode = exceptionMessage.code;
            let sqliteMessage = exceptionMessage.message;
            let errSQL = exceptionMessage.sql;
            console.error(`error log is ${sqliteCode}, errMessage is ${sqliteMessage}, errSQL is ${errSQL}`);
          });
        }
      } catch (err) {
        let code = (err as BusinessError).code;
        let message = (err as BusinessError).message;
        console.error(`Register observer failed, code is ${code},message is ${message}`);
      }
      this.promptAction.showToast({ message: `数据库初始化成功.` });
      console.info('initRdb successfully.');
    }).catch((err: BusinessError) => {
      console.error(`initRdb failed, code is ${err.code},message is ${err.message}`);
    });
  } catch (err) {
    console.error(`initRdb failed, code is ${err.code},message is ${err.message}`);
  }
}

完整示例代码如下,需在module.json5文件中配置ohos.permission.FILE_ACCESS_PERSIST权限(实际运行需提前将数据库对应的db文件、db-shm文件、db-wal文件预置在设备的公共目录下):

initRdb() {
  try {
    /**
     * 直接使用公共目录下数据库文件至少需要.db、.db-shm、.db-wal,所以在实际开库使用前需要提前校验是否有这对应三个文件的可读权限
     */
    if (!this.checkUirPermission()) {
      console.warn(`校验未通过,请重新选择必要的db、db-shm、db-wal文件.`);
      return;
    }
    let dbConfig: relationalStore.StoreConfig = {
      name: this.dbName,
      securityLevel: relationalStore.SecurityLevel.S3,
      rootDir: this.rootDir,
      customDir: this.customDir
    };
    console.info(`dbName: ${this.dbName}, rootDir: ${this.rootDir}, customDir: ${this.customDir}`);
    // 1.获取公共目录下的数据库文件,使用只读模式打开
    relationalStore.getRdbStore(this.context, dbConfig).then((rdbStore: relationalStore.RdbStore) => {
      this.store = rdbStore;
      try {
        if (rdbStore != undefined) {
          // 监听sql错误信息
          rdbStore.on('sqliteErrorOccurred', exceptionMessage => {
            let sqliteCode = exceptionMessage.code;
            let sqliteMessage = exceptionMessage.message;
            let errSQL = exceptionMessage.sql;
            console.error(`error log is ${sqliteCode}, errMessage is ${sqliteMessage}, errSQL is ${errSQL}`);
          });
        }
      } catch (err) {
        let code = (err as BusinessError).code;
        let message = (err as BusinessError).message;
        console.error(`Register observer failed, code is ${code},message is ${message}`);
      }
      this.promptAction.showToast({ message: `数据库初始化成功.` });
      console.info('initRdb successfully.');
    }).catch((err: BusinessError) => {
      console.error(`initRdb failed, code is ${err.code},message is ${err.message}`);
    });
  } catch (err) {
    console.error(`initRdb failed, code is ${err.code},message is ${err.message}`);
  }
}

更多关于HarmonyOS 鸿蒙Next中是否支持otg连接背夹设备(带外部存储功能)直接实现文件读写功能的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


您好,可以识别到带外部存储功能的设备,可以使用FilePicker进行文件的选择、读写、保存。

【背景知识】

[@ohos.file.picker (选择器)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-file-picker)选择器(Picker)是一个封装DocumentViewPicker、AudioViewPicker、PhotoViewPicker的API模块,具有选择与保存的能力。应用可以选择使用以下API来实现文件的选择和保存的功能。该类接口,需要应用在界面UIAbility中调用,否则无法拉起FilePicker应用、AudioPicker应用或PhotoPicker应用。

我试了下读写是可以的,但删除是必须要用户介入的吗?还有是否可以直接操作外置存储器中的sqlite数据库文件,

HarmonyOS Next支持OTG连接背夹设备进行文件读写。系统通过USB Host模式识别外部存储设备,用户可通过FileManager API访问设备文件系统,实现读写操作。需在module.json5中声明ohos.permission.USB_EXTENSION权限,并在设备管理器中配置USB外设访问。

是的,HarmonyOS Next支持通过OTG连接外部存储设备(如您提到的背夹设备)并进行文件读写。

在HarmonyOS Next中,这主要通过外部存储管理框架来实现。系统将识别到的USB大容量存储设备(UMS)作为外部卷(External Volume)进行挂载和管理。开发者可以使用标准的文件管理API(如@ohos.file.fs)来访问其中的文件,而无需直接处理底层的USB协议。

核心实现要点如下:

  1. 权限声明:首先需要在应用的module.json5配置文件中声明必要的权限,例如:

    "requestPermissions": [
      {
        "name": "ohos.permission.STORAGE_MANAGER"
      },
      {
        "name": "ohos.permission.WRITE_USER_STORAGE"
      }
    ]
    

    更精细的访问控制可能需要使用ohos.permission.FILE_ACCESS_MANAGER权限和用户授权流程。

  2. 设备发现与卷管理:系统服务(如USB服务存储服务)会自动管理设备的连接与断开。开发者可以通过@ohos.file.volumeManager模块来获取已挂载卷(包括外部卷)的列表和信息。

    import volumeManager from '@ohos.file.volumeManager';
    // 获取所有卷信息
    let volumeList = volumeManager.getAllVolumes();
    // 从中筛选出类型为EXTERNAL的卷
    
  3. 文件访问:获得外部卷的根路径(volume.path)后,即可使用@ohos.file.fs模块的标准文件接口(如fs.open, fs.read, fs.write, fs.listFile等)进行目录遍历和文件读写操作,与操作应用沙箱内文件的方法基本一致。

    import fs from '@ohos.file.fs';
    // 假设externalVolPath是外部卷的路径
    let dir = fs.listFileSync(externalVolPath);
    

总结:HarmonyOS Next提供了完整的系统框架来支持OTG外部存储设备的文件访问。对开发者而言,关键步骤是正确声明权限、通过存储管理API获取外部卷的访问入口,然后使用标准的文件系统API进行读写。这为扩展设备存储能力或进行数据迁移等功能提供了支持。

回到顶部