文档管理中心

Native侧实现文件访问

概述

在对文件处理性能要求高的场景中,Native侧访问文件处理数据比在ArkTS侧操作文件(详见应用文件访问(ArkTS))有更高的效率和更快的响应,例如处理大文件、复杂的文件操作以及实时通信等低时延场景。根据文件位置的不同,应用在Native侧访问文件可以分为以下两种类型:

  • 类型一:访问应用沙箱内的文件进行读写操作,主要是通过沙箱路径进行访问。
  • 类型二:访问系统公共目录中的文件进行读写操作,可以使用文件picker来获取文件描述符。

本文将针对这两种场景给出具体的实现方案。

访问应用沙箱文件

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

方案一:ArkTS侧获取沙箱路径传递给Native侧访问文件

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

实现方案

这里以访问沙箱文件并写入文本的场景为例,实现方案分为Native侧定义操作文件的方法和ArkTS侧调用该方法两部分。

第一部分:在Native侧定义一个方法,用于接收沙箱路径并将文本写入到文件中。

  1. 通过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);
  2. 通过指定的路径打开文件。

    收起
    自动换行
    深色代码主题
    复制
    1. FILE *fp;
    2. fp = fopen(pathBuf, "w");
  3. 使用C标准库的文件操作函数写入文件。

    收起
    自动换行
    深色代码主题
    复制
    1. // Write a file using the file operation function of the C standard library.
    2. fprintf(fp, "%s", contentsBuf);
  4. 完整代码如下所示:

    收起
    自动换行
    深色代码主题
    复制
    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. // Convert the sandbox path and the contents of the text to be written into C-side variables through the Node-API interface.
    7. size_t pathSize, contentsSize;
    8. char pathBuf[BUFFER_SIZE], contentsBuf[BUFFER_SIZE];
    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. // Open the file through the specified path.
    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. OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "Open file successfully!");
    20. // Write a file using the file operation function of the C standard library.
    21. fprintf(fp, "%s", contentsBuf);
    22. fclose(fp);
    23. return nullptr;
    24. }
  5. 将该C++接口与ArkTS接口进行绑定和映射(详见Native侧方法的实现),同时在index.d.ts文件中,提供该接口方法以便于ArkTS侧调用。

    收起
    自动换行
    深色代码主题
    复制
    1. export const transferSandboxPath: (path: string, contents: string) => void;

第二部分:在Native侧访问沙箱文件写数据的功能实现后,在ArkTS侧调用该方法。

  1. 引用Native侧相应的so库。

    收起
    自动换行
    深色代码主题
    复制
    1. import FileAccess from 'libfile_access.so';
  2. 在ArkTS侧获取沙箱路径。

    收起
    自动换行
    深色代码主题
    复制
    1. private sandboxFilesDir: string = this.getUIContext().getHostContext()!.filesDir;
  3. 获取到沙箱路径后,将该路径传递给Native侧,同时传递需要写入的内容。

    收起
    自动换行
    深色代码主题
    复制
    1. FileAccess.transferSandboxPath(this.sandboxFilesDir, content);

通过上述步骤,实现了在Native侧通过ArkTS侧传递的沙箱路径访问与操作应用沙箱文件的方案。

效果展示

图 2 ArkTS侧传递沙箱路径到Native侧方案效果展示

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

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

实现方案

这里同样以访问沙箱文件并写入文本的场景为例,实现方案分为Native侧定义操作文件的方法和ArkTS侧调用该方法两部分。

第一部分:在Native侧定义一个方法,用于拼接沙箱路径并将文本写入到文件中。

  1. 根据实际文件位置拼接沙箱路径(详见应用沙箱路径和真实物理路径的对应关系)。

    收起
    自动换行
    深色代码主题
    复制
    1. char pathBuf[READ_SIZE] = {0};
    2. strncpy(pathBuf,FILE_PATH,READ_SIZE);
  2. 将要写入文本的内容通过Node-API接口传递到Native侧。

    收起
    自动换行
    深色代码主题
    复制
    1. napi_get_value_string_utf8(env, argv[0], contentsBuf, sizeof(contentsBuf), &contentsSize);
  3. 通过指定的路径打开文件。

    收起
    自动换行
    深色代码主题
    复制
    1. // Open the file through the specified path.
    2. FILE *fp;
    3. fp = fopen(pathBuf, "w");
  4. 使用C标准库的文件操作函数写入文件。

    收起
    自动换行
    深色代码主题
    复制
    1. // Write a file using the file operation function of the C standard library.
    2. fprintf(fp, "%s", contentsBuf);
  5. 完整代码如下所示:

    收起
    自动换行
    深色代码主题
    复制
    1. static napi_value SplicePath(napi_env env, napi_callback_info info) {
    2. size_t argc = 1;
    3. napi_value argv[1] = {nullptr};
    4. napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
    5. // Splice the sandbox path according to the actual file location.
    6. size_t contentsSize;
    7. char pathBuf[READ_SIZE] = {0};
    8. strncpy(pathBuf,FILE_PATH,READ_SIZE);
    9. // Convert the contents of the text to be written into C-side variables through the Node-API interface.
    10. char contentsBuf[BUFFER_SIZE];
    11. napi_get_value_string_utf8(env, argv[0], contentsBuf, sizeof(contentsBuf), &contentsSize);
    12. // Open the file through the specified path.
    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. OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "Open file successfully!");
    20. // Write a file using the file operation function of the C standard library.
    21. fprintf(fp, "%s", contentsBuf);
    22. fclose(fp);
    23. return nullptr;
    24. }
  6. 将该C++接口与ArkTS接口进行绑定和映射(详见Native侧方法的实现),同时在index.d.ts文件中,提供该接口方法。

    收起
    自动换行
    深色代码主题
    复制
    1. export const splicePath: (contents: string) => void;

第二部分:Native侧访问沙箱文件写数据的功能实现后,在ArkTS侧调用该方法。

  1. 引用Native侧相应的so库。

    收起
    自动换行
    深色代码主题
    复制
    1. import FileAccess from 'libfile_access.so';
  2. 在ArkTS侧调用该接口实现文件写入的操作。

    收起
    自动换行
    深色代码主题
    复制
    1. FileAccess.splicePath(content);

通过上述步骤,实现了在Native侧通过拼接沙箱路径访问与操作应用沙箱文件的方案。

效果展示

图 4 Native侧拼接沙箱路径方案效果展示

访问公共目录文件

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

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

场景一:写数据到公共目录文件

场景描述

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

图 7 Native侧写入公共目录文件场景示意图

实现方案

实现方案分为Native侧定义操作文件的方法和ArkTS侧调用该方法两部分。

第一部分:在Native侧定义一个方法,用于接收文件描述符并将数据写入到文件中,注意使用文件描述符操作文件需要引用头文件unistd.h。

  1. 将传入的文件描述符和要写入文件的内容通过Node-API接口传递到Native侧。

    收起
    自动换行
    深色代码主题
    复制
    1. // Convert the incoming file descriptor and the contents to be written into the file into C-side variables.
    2. napi_get_value_uint32(env, argv[0], &fd);
    3. napi_get_value_string_utf8(env, argv[1], contentsBuf, sizeof(contentsBuf), &contentsSize);
  2. 使用C标准库的文件操作函数写入文件。

    收起
    自动换行
    深色代码主题
    复制
    1. // Write a file using the file operation function of the C standard library.
    2. size_t buffSize = write(fd, contentsBuf, contentsSize);
  3. 根据write函数的返回值判断操作是否成功。

    收起
    自动换行
    深色代码主题
    复制
    1. std::string res;
    2. // According to the return value of the write function, judge whether the operation returns the result successfully.
    3. napi_value contents;
    4. if (buffSize == -1) {
    5. res = "Write File Failed!";
    6. OH_LOG_Print(LOG_APP, LOG_ERROR, DOMAIN, TAG, "%s", res.c_str());
    7. } else {
    8. res = "Write File Successfully!!!";
    9. OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "%s", res.c_str());
    10. }
    11. napi_create_string_utf8(env, res.c_str(), sizeof(res), &contents);
    12. return contents;
  4. 完整代码如下所示:

    收起
    自动换行
    深色代码主题
    复制
    1. static napi_value WriteFileUsingPickerFd(napi_env env, napi_callback_info info) {
    2. size_t argc = 2;
    3. napi_value argv[2] = {nullptr};
    4. napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
    5. unsigned int fd = -1;
    6. size_t contentsSize;
    7. char contentsBuf[BUFFER_SIZE];
    8. // Convert the incoming file descriptor and the contents to be written into the file into C-side variables.
    9. napi_get_value_uint32(env, argv[0], &fd);
    10. napi_get_value_string_utf8(env, argv[1], contentsBuf, sizeof(contentsBuf), &contentsSize);
    11. ftruncate(fd, 0);
    12. // Write a file using the file operation function of the C standard library.
    13. size_t buffSize = write(fd, contentsBuf, contentsSize);
    14. std::string res;
    15. // According to the return value of the write function, judge whether the operation returns the result successfully.
    16. napi_value contents;
    17. if (buffSize == -1) {
    18. res = "Write File Failed!";
    19. OH_LOG_Print(LOG_APP, LOG_ERROR, DOMAIN, TAG, "%s", res.c_str());
    20. } else {
    21. res = "Write File Successfully!!!";
    22. OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "%s", res.c_str());
    23. }
    24. napi_create_string_utf8(env, res.c_str(), sizeof(res), &contents);
    25. return contents;
    26. }
  5. 将该C++接口与ArkTS接口进行绑定和映射(详见Native侧方法的实现),同时在index.d.ts文件中,提供该接口方法。

    收起
    自动换行
    深色代码主题
    复制
    1. export const writeFileUsingPickerFd: (fd: number, contents: string) => string;

第二部分:Native侧访问公共目录文件写数据的功能实现后,在ArkTS侧调用该方法。

  1. 引用Native侧相应的so库。

    收起
    自动换行
    深色代码主题
    复制
    1. import FileAccess from 'libfile_access.so';
  2. 在ArkTS侧拉起picker选择文件并将文件描述符传入Native接口中。

    收起
    自动换行
    深色代码主题
    复制
    1. async function WriteFileByPicker(contents: string): Promise<string> {
    2. // Configure picker Selection Information
    3. const documentSelectOptions = new picker.DocumentSelectOptions();
    4. documentSelectOptions.maxSelectNumber = 1;
    5. documentSelectOptions.fileSuffixFilters = ['.txt'];
    6. let uris: Array<string> = [];
    7. const documentViewPicker = new picker.DocumentViewPicker();
    8. // Pull up the picker selection file
    9. return await documentViewPicker.select(documentSelectOptions).then((documentSelectResult: Array<string>) => {
    10. uris = documentSelectResult;
    11. let uri: string = uris[0];
    12. let path: string = new fileUri.FileUri(uri).path;
    13. Logger.info(`Open The File path is [${uri}]`);
    14. let file = fs.openSync(path, fs.OpenMode.WRITE_ONLY);
    15. // Call the native method to write a file
    16. let res = FileAccess.writeFileUsingPickerFd(file.fd, contents);
    17. fs.closeSync(file.fd);
    18. return res;
    19. }).catch((error: BusinessError) => {
    20. Logger.error(`Open The file failed, error code is [${error.code}], error message is [${error.message}]`);
    21. return 'Write Failed by Picker';
    22. })
    23. }

通过上述步骤,实现了在Native侧通过ArkTS侧picker传递的文件资源描述符访问公共目录文件并写入内容的方案。

效果展示

图 8 Native侧写公共目录文件场景方案效果展示

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

场景描述

ArkTS侧通过文件picker选择文件,并传递文件描述符到Native侧,Native侧通过文件描述符打开文件并读取文件数据。

图 9 Native侧读取公共目录文件场景示意图

实现方案

实现方案分为Native侧定义操作文件的方法和ArkTS侧调用该方法两部分。

第一部分:在Native侧定义一个方法,用于接收文件描述符并从文件中读取数据,注意使用文件描述符操作文件需要引用头文件unistd.h。

  1. 将传入的文件描述符通过Node-API接口传递到Native侧。

    收起
    自动换行
    深色代码主题
    复制
    1. // Convert the incoming file descriptor into a C-side variable.
    2. napi_get_value_uint32(env, argv[0], &fd);
  2. 使用C标准库的文件操作函数读取文件。

    收起
    自动换行
    深色代码主题
    复制
    1. // Use the file operation function of the C standard library to read the file.
    2. char buff[READ_SIZE];
    3. size_t buffSize = read(fd, buff, sizeof(buff));
  3. 判断读取是否成功并返回文件内容。

    收起
    自动换行
    深色代码主题
    复制
    1. // Judge whether the reading is successful or not and return the file content.
    2. napi_value contents;
    3. if (buffSize == -1) {
    4. OH_LOG_Print(LOG_APP, LOG_ERROR, DOMAIN, TAG, "Read File Failed!!!");
    5. } else {
    6. OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "Read File Successfully!!!");
    7. napi_create_string_utf8(env, buff, buffSize, &contents);
    8. }
    9. return contents;
  4. 完整代码如下所示:

    收起
    自动换行
    深色代码主题
    复制
    1. // entry/src/main/cpp/FileAccessMethods.cpp
    2. static napi_value ReadFileUsingPickerFd(napi_env env, napi_callback_info info) {
    3. size_t argc = 1;
    4. napi_value argv[1] = {nullptr};
    5. napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
    6. unsigned int fd = -1;
    7. // Convert the incoming file descriptor into a C-side variable.
    8. napi_get_value_uint32(env, argv[0], &fd);
    9. // Use the file operation function of the C standard library to read the file.
    10. char buff[READ_SIZE];
    11. size_t buffSize = read(fd, buff, sizeof(buff));
    12. // Judge whether the reading is successful or not and return the file content.
    13. napi_value contents;
    14. if (buffSize == -1) {
    15. OH_LOG_Print(LOG_APP, LOG_ERROR, DOMAIN, TAG, "Read File Failed!!!");
    16. } else {
    17. OH_LOG_Print(LOG_APP, LOG_INFO, DOMAIN, TAG, "Read File Successfully!!!");
    18. napi_create_string_utf8(env, buff, buffSize, &contents);
    19. }
    20. return contents;
    21. }
  5. 将该C++接口与ArkTS接口进行绑定和映射(详见Native侧方法的实现),同时在index.d.ts文件中,提供该接口方法。

    收起
    自动换行
    深色代码主题
    复制
    1. export const readFileUsingPickerFd: (fd: number) => string;

第二部分:Native侧访问公共目录文件读数据的功能实现后,在ArkTS侧调用该方法。

  1. 引用Native侧相应的so库。

    收起
    自动换行
    深色代码主题
    复制
    1. import FileAccess from 'libfile_access.so';
  2. 在ArkTS侧拉起picker选择文件并将文件描述符传入Native接口中。

    收起
    自动换行
    深色代码主题
    复制
    1. async function ReadFileByPicker(): Promise<string> {
    2. // Configure picker Selection Information
    3. const documentSelectOptions = new picker.DocumentSelectOptions();
    4. documentSelectOptions.maxSelectNumber = 1;
    5. documentSelectOptions.fileSuffixFilters = ['.txt'];
    6. // Pull up the picker selection file
    7. let uris: Array<string> = [];
    8. const documentViewPicker = new picker.DocumentViewPicker();
    9. return await documentViewPicker.select(documentSelectOptions).then((documentSelectResult: Array<string>) => {
    10. uris = documentSelectResult;
    11. let uri: string = uris[0];
    12. let path: string = new fileUri.FileUri(uri).path;
    13. Logger.info(`The Opened File path is [${uri}]`);
    14. let file = fs.openSync(path, fs.OpenMode.READ_ONLY);
    15. // Call the native method to read the file.
    16. let res = FileAccess.readFileUsingPickerFd(file.fd);
    17. fs.closeSync(file.fd);
    18. return res;
    19. }).catch((error: BusinessError) => {
    20. Logger.error(`Open The file failed, error code is [${error.code}], error message is [${error.message}]`);
    21. return 'Read Failed by Picker!';
    22. })
    23. }

通过上述步骤,实现了在Native侧通过ArkTS侧picker传递的文件资源描述符访问公共目录文件并读取内容的方案。

效果展示

图 10 Native侧读公共目录文件场景方案效果展示

在 开发与测试 开放能力API 中进行搜索
请输入您想要搜索的关键词