鸿蒙HarmonyOS (开发进阶)Native侧实现文件访问

打印 上一主题 下一主题

主题 998|帖子 998|积分 2994

鸿蒙NEXT开发实战往期必看文章:
一分钟了解”纯血版!鸿蒙HarmonyOS Next应用开发!
“非常具体的” 鸿蒙HarmonyOS Next应用开发学习门路!(从零基础入门到精通)
HarmonyOS NEXT应用开发案例实践总结合(连续更新......)
HarmonyOS NEXT应用开发性能优化实践总结(连续更新......)

概述

在对文件处置处罚性能要求高的场景中,Native侧访问文件处置处罚数据比在ArkTS侧操作文件有更高的效率和更快的响应,例如处置处罚大文件、复杂的文件操作以及实时通信等低时延场景。根据文件位置的差别,应用在Native侧访问文件可以分为以下三种范例:


  • 范例一:访问应用沙箱内的文件举行读写操作,重要是通过沙箱路径举行访问;
  • 范例二:访问应用资源文件举行读操作,可以通过传递资源管理器举行访问;
  • 范例三:访问系统公共目次中的文件举行读写操作,可以使用文件picker来获取文件描述符。
本文将针对这三种场景给出具体的实现方案。
访问应用沙箱文件

应用沙箱是一种以安全防护为目的的隔离机制,避免数据受到恶意路径穿越访问。在这种沙箱的掩护机制下,应用可见的目次范围即为“应用沙箱目次”,沙箱中的文件就需要通过沙箱路径去举行访问。Native侧获取沙箱路径的方案有两种:


  • 方案一:ArkTS侧获取沙箱路径传递给Native侧访问文件。
  • 方案二:Native侧直接拼接沙箱路径访问文件。
方案一:ArkTS侧获取沙箱路径传递给Native侧访问文件

图1 ArkTS侧获取沙箱路径传递给Native侧访问文件示意图

实现方案
这里以访问沙箱文件并写入文本的场景为例,实现分案分为Native侧定义操作文件的方法和ArkTS侧调用该方法两部门。
第一部门:在Native侧定义一个方法,用于吸收沙箱路径并将文本写入到文件中。

  • 将沙箱路径和要写入文本的内容通过Node-API接口传递到Native侧。
    1. napi_get_value_string_utf8(env, argv[0], pathBuf, sizeof(pathBuf), &pathSize);
    2. napi_get_value_string_utf8(env, argv[1], contentsBuf, sizeof(contentsBuf), &contentsSize);
    复制代码
  • 通过指定的路径打开文件。
    1. FILE *fp;
    2. fp = fopen(pathBuf, "w");
    复制代码
  • 使用C标准库的文件操作函数写入文件。
    1. fprintf(fp, "%s", contentsBuf);
    复制代码
  • 完整代码如下所示:
    1. // entry/src/main/cpp/FileAccessMethods.cpp
    2. static napi_value TransferSandboxPath(napi_env env, napi_callback_info info) {
    3.     size_t argc = 2;
    4.     napi_value argv[2] = {nullptr};
    5.     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
    6.     //将沙箱路径和要写入文本的内容通过Node-API接口转换成C侧变量
    7.     size_t pathSize, contentsSize;
    8.     char pathBuf[256], contentsBuf[256];
    9.     napi_get_value_string_utf8(env, argv[0], pathBuf, sizeof(pathBuf), &pathSize);
    10.     napi_get_value_string_utf8(env, argv[1], contentsBuf, sizeof(contentsBuf), &contentsSize);
    11.     //通过指定的路径打开文件
    12.     snprintf(pathBuf, sizeof(pathBuf), "%s/TransferSandboxPath.txt", pathBuf);
    13.     FILE *fp;
    14.     fp = fopen(pathBuf, "w");
    15.     if (fp == nullptr) {
    16.         OH_LOG_Print(LOG_APP, LOG_ERROR, DOMAIN, TAG, "open file error!");
    17.         return nullptr;
    18.     }
    19.     //使用C标准库的文件操作函数写入文件
    20.     fprintf(fp, "%s", contentsBuf);
    21.     fclose(fp);
    22.     return nullptr;
    23. }
    复制代码
  • 将该C++接口与ArkTS接口举行绑定和映射,同时在index.d.ts文件中,提供该接口方法以便于ArkTS侧调用。
    1. export const transferSandboxPath: (path: string, contents: string) => void;
    复制代码
第二部门:在Native侧访问沙箱文件写数据的功能实现后,在ArkTS侧调用该方法。

  • 引用Native侧相应的so库。
    1. import FileAccess from 'libfile_access.so';
    复制代码
  • 在ArkTS侧获取沙箱路径。
    1. private sandboxFilesDir: string = getContext(this).filesDir;
    复制代码
  • 获取到沙箱路径后,将该路径传递给Native侧,同时传递需要写入的内容。
    1. FileAccess.transferSandboxPath(this.sandboxFilesDir, content);
    复制代码
通过上述步骤,实现了在Native侧通过ArkTS侧传递的沙箱路径访问与操作应用沙箱文件的方案。
效果展示
图2 ArkTS侧传递沙箱路径到Native侧方案效果展示



方案二:Native侧直接拼接沙箱路径访问文件

图3 Native侧直接拼接沙箱路径访问文件示意图

实现方案
这里同样以访问沙箱文件并写入文本的场景为例,实现分案分为Native侧定义操作文件的方法和ArkTS侧调用该方法两部门。
第一部门:在Native侧定义一个方法,用于拼接沙箱路径并将文本写入到文件中。

  • 根据实际文件位置拼接沙箱路径。
    1. char pathBuf[256] = "/data/storage/el2/base/haps/entry/files/SplicePath.txt";
    复制代码
  • 将要写入文本的内容通过Node-API接口传递到Native侧。
    1. napi_get_value_string_utf8(env, argv[0], contentsBuf, sizeof(contentsBuf), &contentsSize);
    复制代码
  • 通过指定的路径打开文件。
    1. FILE *fp;
    2. fp = fopen(pathBuf, "w");
    复制代码
  • 使用C标准库的文件操作函数写入文件。
    1. fprintf(fp, "%s", contentsBuf);
    复制代码
  • 完整代码如下所示:
    1. // entry/src/main/cpp/FileAccessMethods.cppstatic napi_value SplicePath(napi_env env, napi_callback_info info) {    size_t argc = 1;    napi_value argv[1] = {nullptr};    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);    //根据实际文件位置拼接沙箱路径    size_t contentsSize;    char pathBuf[256] = "/data/storage/el2/base/haps/entry/files/SplicePath.txt";    //将要写入文本的内容通过Node-API接口转换成C侧变量    char contentsBuf[256];    napi_get_value_string_utf8(env, argv[0], contentsBuf, sizeof(contentsBuf), &contentsSize);    //通过指定的路径打开文件    FILE *fp;    fp = fopen(pathBuf, "w");    if (fp == nullptr) {        OH_LOG_Print(LOG_APP, LOG_ERROR, DOMAIN, TAG, "open file error!");        return nullptr;    }    //使用C标准库的文件操作函数写入文件    fprintf(fp, "%s", contentsBuf);    fclose(fp);    return nullptr;}
    复制代码
  • 将该C++接口与ArkTS接口举行绑定和映射,同时在index.d.ts文件中,提供该接口方法。
    1. export const splicePath: (contents: string) => void;
    复制代码
第二部门:Native侧访问沙箱文件写数据的功能实现后,在ArkTS侧调用该方法。

  • 引用Native侧相应的so库。
    1. import FileAccess from 'libfile_access.so';
    复制代码
  • 在ArkTS侧调用该接口实现文件写入的操作。
    1. FileAccess.splicePath(content);
    复制代码
通过上述步骤,实现了在Native侧通过拼接沙箱路径访问与操作应用沙箱文件的方案。
效果展示
图4 Native侧拼接沙箱路径方案效果展示



访问应用包内资源文件

Native侧可以通过Resource Manager操作应用资源文件中的Rawfile目次和文件,这里以Native侧读取Rawfile文件内容的场景为例先容该方案。
图5 Native侧访问应用资源文件方案示意图

实现方案
实现分案分为Native侧定义操作文件的方法和ArkTS侧调用该方法两部门。
第一部门:在Native侧定义一个读取文件的方法,注意使用Resource Manager需要引用头文件rawfile/raw_file_manager.h,并在工程的cmakelists.txt文件中链接动态库librawfile.z.so。

  • 将传入的Resource Manager对象转换为Native对象。
    1. NativeResourceManager *mNativeResMgr = OH_ResourceManager_InitNativeResourceManager(env, argv[0]);
    复制代码
  • 将传入的文件名通过Node-API接口传递到Native侧。
    1. napi_get_value_string_utf8(env, argv[0], contentsBuf, sizeof(contentsBuf), &contentsSize);
    复制代码
  • 通过资源对象打开文件。
    1. RawFile *rawFile = OH_ResourceManager_OpenRawFile(mNativeResMgr, fileNameBuf);
    复制代码
  • 通过资源对象读取文件内容。
    1. long len = OH_ResourceManager_GetRawFileSize(rawFile);
    2. std::unique_ptr<char[]> data = std::make_unique<char[]>(len);
    3. OH_ResourceManager_ReadRawFile(rawFile, data.get(), len);
    复制代码
  • 完整代码如下所示。
    1. // entry/src/main/cpp/FileAccessMethods.cpp
    2. static napi_value TransferResourceMgr(napi_env env, napi_callback_info info) {
    3.     size_t argc = 2;
    4.     napi_value argv[2] = {nullptr};
    5.     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
    6.     //将传入的resource manager对象转换为Native对象
    7.     NativeResourceManager *mNativeResMgr = OH_ResourceManager_InitNativeResourceManager(env, argv[0]);
    8.     size_t fileNameSize;
    9.     char fileNameBuf[256];
    10.     //将传入的文件名通过Node-API接口转换成C侧变量
    11.     napi_get_value_string_utf8(env, argv[1], fileNameBuf, sizeof(fileNameBuf), &fileNameSize);
    12.     //通过资源对象打开文件
    13.     RawFile *rawFile = OH_ResourceManager_OpenRawFile(mNativeResMgr, fileNameBuf);
    14.     if (rawFile != nullptr) {
    15.         OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "OH_ResourceManager_OpenRawFile success.");
    16.     }
    17.     //通过资源对象读取文件内容
    18.     long len = OH_ResourceManager_GetRawFileSize(rawFile);
    19.     std::unique_ptr<char[]> data = std::make_unique<char[]>(len);
    20.     OH_ResourceManager_ReadRawFile(rawFile, data.get(), len);
    21.     OH_ResourceManager_CloseRawFile(rawFile);
    22.     OH_ResourceManager_ReleaseNativeResourceManager(mNativeResMgr);
    23.     napi_value contents;
    24.     napi_create_string_utf8(env, data.get(), len, &contents);
    25.     return contents;
    26. }
    复制代码
  • 将该C++接口与ArkTS接口举行绑定和映射,同时在index.d.ts文件中,提供该接口方法。
    1. export const transferResourceMgr: (resMgr: resourceManager.ResourceManager, path: string) => string;
    复制代码
第二部门:Native侧访问Rawfile文件读数据的功能实现后,在ArkTS侧调用该方法。

  • 引用Native侧相应的so库。
    1. import FileAccess from 'libfile_access.so';
    复制代码
  • 在ArkTS侧获取Resource Manager。
    1. private resMgr: resourceManager.ResourceManager = getContext().resourceManager;
    复制代码
  • 在ArkTS侧调用该接口传递Resource Manager和文件名并读取返回的文件内容。
    1. let rawfileContext = FileAccess.transferResourceMgr(this.resMgr, FileNameList[2]);
    复制代码
通过上述步骤,实现了在Native侧通过ArkTS侧传递的Resource Manager访问与读取应用资源文件的方案。
效果展示
图6 ArkTS侧传递resource manager到Native侧方案效果展示



访问公共目次文件

系统公共目次下储存的是用户文件,应用对用户文件的操作需要提前获取用户授权,或由用户操作完成。我们可以通过系统预置的文件选择器(FilePicker)实现该能力,目前重要有创建文件、写入和读取三类操作,创建文件可以直接使用picker,针对Native侧,有如下两种场景:


  • 场景一:写数据到公共目次文件;
  • 场景二:从公共目次文件中读取数据。
场景一:写数据到公共目次文件

场景描述
ArkTS侧通过文件picker在公共目次下创建文件,并传递文件描述符到Native侧,Native侧通过文件描述符打开文件并将数据写入到文件中。
图7 Native侧写入公共目次文件场景示意图

实现方案
实现分案分为Native侧定义操作文件的方法和ArkTS侧调用该方法两部门。
第一部门:在Native侧定义一个方法,用于吸收文件描述符并将数据写入到文件中,注意使用文件描述符操作文件需要引用头文件unistd.h。

  • 将传入的文件描述符和要写入文件的内容通过Node-API接口传递到Native侧。
    1. napi_get_value_uint32(env, argv[0], &fd);
    2. napi_get_value_string_utf8(env, argv[1], contentsBuf, sizeof(contentsBuf), &contentsSize);
    复制代码
  • 使用C标准库的文件操作函数写入文件。
    1. size_t buffSize = write(fd, contentsBuf, contentsSize);
    复制代码
  • 根据write函数的返回值判定操作是否乐成。
    1. std::string res;
    2. napi_value contents;
    3. if (buffSize == -1) {
    4.     res = "Write File Failed!";
    5.     OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "%s", res.c_str());
    6. } else {
    7.     res = "Write File Successfully!!!";
    8.     OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "%s", res.c_str());
    9. }
    10. napi_create_string_utf8(env, res.c_str(), sizeof(res), &contents);
    11. return contents;
    复制代码
  • 完整代码如下所示:
    1. // entry/src/main/cpp/FileAccessMethods.cpp
    2. static napi_value WriteFileUsingPickerFd(napi_env env, napi_callback_info info) {
    3.     size_t argc = 2;
    4.     napi_value argv[2] = {nullptr};
    5.     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
    6.     unsigned int fd = -1;   
    7.     size_t contentsSize;
    8.     char contentsBuf[256];
    9.     //将传入的文件描述符和要写入文件的内容转换成C侧变量
    10.     napi_get_value_uint32(env, argv[0], &fd);
    11.     napi_get_value_string_utf8(env, argv[1], contentsBuf, sizeof(contentsBuf), &contentsSize);
    12.     ftruncate(fd, 0);
    13.     //使用C标准库的文件操作函数写入文件
    14.     size_t buffSize = write(fd, contentsBuf, contentsSize);
    15.     std::string res;
    16.     //根据write函数的返回值判断操作是否成功返回结果
    17.     napi_value contents;
    18.     if (buffSize == -1) {
    19.         res = "Write File Failed!";
    20.         OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "%s", res.c_str());
    21.     } else {
    22.         res = "Write File Successfully!!!";
    23.         OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "%s", res.c_str());
    24.     }
    25.     napi_create_string_utf8(env, res.c_str(), sizeof(res), &contents);
    26.     return contents;
    27. }
    复制代码
  • 将该C++接口与ArkTS接口举行绑定和映射,同时在index.d.ts文件中,提供该接口方法。
    1. export const writeFileUsingPickerFd: (fd: number, contents: string) => string;
    复制代码
第二部门:Native侧访问公共目次文件写数据的功能实现后,在ArkTS侧调用该方法。

  • 引用Native侧相应的so库。
    1. import FileAccess from 'libfile_access.so';
    复制代码
  • 在ArkTS侧拉起picker选择文件并将文件描述符传入Native接口中。
    1. // entry/src/main/ets/common/utils/FileOperate.ets
    2. async function WriteFileByPicker(contents: string): Promise<string> {
    3.   //配置picker选择信息
    4.   const documentSelectOptions = new picker.DocumentSelectOptions();
    5.   documentSelectOptions.maxSelectNumber = 1;
    6.   documentSelectOptions.fileSuffixFilters = ['.txt'];
    7.   let uris: Array<string> = [];
    8.   const documentViewPicker = new picker.DocumentViewPicker();
    9.   //拉起picker选择文件
    10.   return await documentViewPicker.select(documentSelectOptions).then((documentSelectResult: Array<string>) => {
    11.     uris = documentSelectResult;
    12.     let uri: string = uris[0];
    13.     let path: string = new fileUri.FileUri(uri).path;
    14.     console.info(`Open The File path is [${uri}]`);
    15.     let file = fs.openSync(path, fs.OpenMode.WRITE_ONLY);
    16.     //调用native方法写文件
    17.     let res = FileAccess.writeFileUsingPickerFd(file.fd, contents);
    18.     fs.closeSync(file.fd);
    19.     return res;
    20.   }).catch((error: BusinessError) => {
    21.     console.error(`Open The file failed, error code is [${error.code}], error message is [${error.message}]`);
    22.     return 'Write Failed by Picker';
    23.   })
    24. }
    复制代码
通过上述步骤,实现了在Native侧通过ArkTS侧picker传递的文件资源描述符访问公共目次文件并写入内容的方案。
效果展示
图8 Native侧写公共目次文件场景方案效果展示



场景二:从公共目次文件中读取数据

场景描述
ArkTS侧通过文件picker选择文件,并传递文件描述符到Native侧,Native侧通过文件描述符打开文件并读取文件数据。
图9 Native侧读取公共目次文件场景示意图

实现方案
实现分案分为Native侧定义操作文件的方法和ArkTS侧调用该方法两部门。
第一部门:在Native侧定义一个方法,用于吸收文件描述符并将数据写入到文件中,注意使用文件描述符操作文件需要引用头文件unistd.h。

  • 将传入的文件描述符通过Node-API接口传递到Native侧。
    1. napi_get_value_uint32(env, argv[0], &fd);
    复制代码
  • 使用C标准库的文件操作函数读取文件。
    1. char buff[1000];
    2. size_t buffSize = read(fd, buff, sizeof(buff));
    复制代码
  • 判定读取是否乐成并返回文件内容。
    1. napi_value contents;
    2. if (buffSize == -1) {
    3.     OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "Read File Failed!!!");
    4. } else {
    5.     OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "Read File Successfully!!!");
    6.     napi_create_string_utf8(env, buff, buffSize, &contents);
    7. }
    8. return contents;
    复制代码
  • 完整代码如下所示:
    1. // entry/src/main/cpp/FileAccessMethods.cppstatic napi_value ReadFileUsingPickerFd(napi_env env, napi_callback_info info) {    size_t argc = 1;    napi_value argv[1] = {nullptr};    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);    unsigned int fd = -1;    //将传入的文件描述符转换成C侧变量    napi_get_value_uint32(env, argv[0], &fd);    //使用C标准库的文件操作函数读取文件    char buff[1000];    size_t buffSize = read(fd, buff, sizeof(buff));    //判定读取是否乐成并返回文件内容    napi_value contents;    if (buffSize == -1) {        OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "Read File Failed!!!");    } else {        OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "Read File Successfully!!!");        napi_create_string_utf8(env, buff, buffSize, &contents);    }    return contents;}
    复制代码
  • 将该C++接口与ArkTS接口举行绑定和映射,同时在index.d.ts文件中,提供该接口方法。
    1. export const readFileUsingPickerFd: (fd: number) => string;
    复制代码
第二部门:Native侧访问公共目次文件读数据的功能实现后,在ArkTS侧调用该方法。

  • 引用Native侧相应的so库。
    1. import FileAccess from 'libfile_access.so';
    复制代码
  • 在ArkTS侧拉起picker选择文件并将文件描述符传入Native接口中。
    1. // entry/src/main/ets/common/utils/FileOperate.ets
    2. async function ReadFileByPicker(): Promise<string> {
    3.   //配置picker选择信息
    4.   const documentSelectOptions = new picker.DocumentSelectOptions();
    5.   documentSelectOptions.maxSelectNumber = 1;
    6.   documentSelectOptions.fileSuffixFilters = ['.txt'];
    7.   //拉起picker选择文件
    8.   let uris: Array<string> = [];
    9.   const documentViewPicker = new picker.DocumentViewPicker();
    10.   return await documentViewPicker.select(documentSelectOptions).then((documentSelectResult: Array<string>) => {
    11.     uris = documentSelectResult;
    12.     let uri: string = uris[0];
    13.     let path: string = new fileUri.FileUri(uri).path;
    14.     console.info(`The Opened File path is [${uri}]`);
    15.     let file = fs.openSync(path, fs.OpenMode.READ_ONLY);
    16.     //调用native方法读文件
    17.     let res = FileAccess.readFileUsingPickerFd(file.fd);
    18.     fs.closeSync(file.fd);
    19.     return res;
    20.   }).catch((error: BusinessError) => {
    21.     console.error(`Open The file failed, error code is [${error.code}], error message is [${error.message}]`);
    22.     return 'Read Failed by Picker!';
    23.   })
    24. }
    复制代码
通过上述步骤,实现了在Native侧通过ArkTS侧picker传递的文件资源描述符访问公共目次文件并读取内容的方案。
效果展示
图10 Native侧读公共目次文件场景方案效果展示





免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

三尺非寒

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表