OpenHarmony文件访问接口mod_fileio解析
概述
mod_fileio
模块是OpenHarmony文件管理API中的核心模块之一,提供了完整的文件I/O操作功能。该模块基于Node.js N-API构建,为JavaScript应用提供了丰富的文件系统操作接口,包括文件读写、目录操作、流处理、文件监控等功能。
模块架构
目录结构
bash
interfaces/kits/js/src/mod_fileio/
├── class_constants/ # 常量定义
├── class_dir/ # 目录操作类
├── class_dirent/ # 目录条目类
├── class_file/ # 文件操作类
├── class_stat/ # 文件状态类
├── class_stat_v9/ # 文件状态类V9版本
├── class_stream/ # 流操作类
├── class_watcher/ # 文件监控类
├── properties/ # 属性函数实现
├── common_func.cpp # 公共函数实现
├── common_func.h # 公共函数声明
├── module.cpp # 模块导出(标准版本)
└── module_v9.cpp # 模块导出(V9版本)
模块版本
mod_fileio模块提供了两个版本:
- 标准版本 (
module.cpp
) - 导出为fileio
模块 - V9版本 (
module_v9.cpp
) - 导出为fs
模块
两个版本在功能上有所差异,V9版本更加精简,主要包含核心的文件操作功能。
核心组件详解
1. 模块导出机制
标准版本 (module.cpp)
cpp
static napi_value Export(napi_env env, napi_value exports)
{
std::vector<unique_ptr<NExporter>> products;
products.emplace_back(make_unique<PropNExporter>(env, exports));
products.emplace_back(make_unique<DirentNExporter>(env, exports));
products.emplace_back(make_unique<DirNExporter>(env, exports));
products.emplace_back(make_unique<StatNExporter>(env, exports));
products.emplace_back(make_unique<StreamNExporter>(env, exports));
products.emplace_back(make_unique<WatcherNExporter>(env, exports));
products.emplace_back(make_unique<Constants>(env, exports));
for (auto &&product : products) {
if (!product->Export()) {
HILOGE("INNER BUG. Failed to export class %{public}s for module fileio",
product->GetClassName().c_str());
return nullptr;
}
}
return exports;
}
V9版本 (module_v9.cpp)
cpp
static napi_value Export(napi_env env, napi_value exports)
{
InitOpenMode(env, exports);
std::vector<unique_ptr<NExporter>> products;
products.emplace_back(make_unique<PropNExporterV9>(env, exports));
products.emplace_back(make_unique<FileNExporter>(env, exports));
products.emplace_back(make_unique<StatNExporterV9>(env, exports));
for (auto &&product : products) {
if (!product->Export()) {
HILOGE("INNER BUG. Failed to export class %{public}s for module fileio",
product->GetClassName().c_str());
return nullptr;
}
}
return exports;
}
2. 公共函数模块 (common_func)
文件打开模式常量
cpp
constexpr int RDONLY = 00; // 只读
constexpr int WRONLY = 01; // 只写
constexpr int RDWR = 02; // 读写
constexpr int CREATE = 0100; // 创建
constexpr int TRUNC = 01000; // 截断
constexpr int APPEND = 02000; // 追加
constexpr int NONBLOCK = 04000; // 非阻塞
constexpr int DIRECTORY = 0200000; // 目录
constexpr int NOFOLLOW = 0400000; // 不跟随符号链接
constexpr int SYNC = 04010000; // 同步
核心工具函数
CommonFunc类提供了以下关键功能:
- ConvertJsFlags - JavaScript标志位转换
- GetReadArg - 读取参数解析
- GetWriteArg - 写入参数解析
- GetCopyPathArg - 复制路径参数解析
- InitOpenMode - 初始化打开模式
3. 类组件详解
3.1 目录操作类 (class_dir)
DirNExporter类提供目录操作功能:
cpp
class DirNExporter final : public NExporter {
public:
static napi_value CloseSync(napi_env env, napi_callback_info info);
static napi_value ReadSync(napi_env env, napi_callback_info info);
static napi_value ListFileSync(napi_env env, napi_callback_info info);
static napi_value Read(napi_env env, napi_callback_info info);
static napi_value Close(napi_env env, napi_callback_info info);
static napi_value ListFile(napi_env env, napi_callback_info info);
};
主要功能:
- 同步/异步目录读取
- 目录关闭
- 文件列表获取
3.2 文件操作类 (class_file)
FileNExporter类提供基础文件操作:
cpp
class FileNExporter final : public NExporter {
public:
static napi_value Constructor(napi_env env, napi_callback_info cbinfo);
static napi_value GetFD(napi_env env, napi_callback_info cbinfo);
};
主要功能:
- 文件对象构造
- 文件描述符获取
3.3 流操作类 (class_stream)
StreamNExporter类提供流式文件操作:
cpp
class StreamNExporter final : public NExporter {
public:
static napi_value WriteSync(napi_env env, napi_callback_info cbinfo);
static napi_value FlushSync(napi_env env, napi_callback_info cbinfo);
static napi_value ReadSync(napi_env env, napi_callback_info cbinfo);
static napi_value CloseSync(napi_env env, napi_callback_info cbinfo);
static napi_value Write(napi_env env, napi_callback_info cbinfo);
static napi_value Read(napi_env env, napi_callback_info cbinfo);
static napi_value Close(napi_env env, napi_callback_info cbinfo);
};
主要功能:
- 同步/异步流读写
- 流刷新
- 流关闭
3.4 文件状态类 (class_stat)
提供文件状态信息获取功能,包括标准版本和V9版本。
3.5 文件监控类 (class_watcher)
提供文件系统监控功能,可以监听文件变化事件。
4. 属性函数模块 (properties)
properties目录包含了大量的文件操作函数实现,每个函数都有对应的.h和.cpp文件:
核心文件操作函数
函数名 | 功能描述 |
---|---|
open | 打开文件 |
close | 关闭文件 |
read_text | 读取文本文件 |
write | 写入文件 |
copy_file | 复制文件 |
rename | 重命名文件 |
unlink | 删除文件 |
mkdir | 创建目录 |
rmdir | 删除目录 |
stat | 获取文件状态 |
lstat | 获取链接状态 |
chmod | 修改文件权限 |
chown | 修改文件所有者 |
fsync | 同步文件数据 |
fdatasync | 同步文件数据(不含元数据) |
ftruncate | 截断文件 |
lseek | 文件定位 |
symlink | 创建符号链接 |
link | 创建硬链接 |
hash | 计算文件哈希 |
watcher | 文件监控 |
异步操作支持
每个核心函数都提供了同步和异步两个版本:
cpp
// 同步版本
static napi_value Sync(napi_env env, napi_callback_info info);
// 异步版本
static napi_value Async(napi_env env, napi_callback_info info);
调用关系图
mod_fileio模块 module.cpp/module_v9.cpp PropNExporter DirNExporter FileNExporter StreamNExporter StatNExporter WatcherNExporter Constants properties目录 open/close/read/write等函数 目录操作 文件操作 流操作 状态查询 文件监控 常量定义 common_func 公共工具函数 参数解析 标志位转换 编码处理
接口设计特点
1. 统一的N-API接口
所有类都继承自NExporter
基类,提供统一的导出机制:
cpp
class NExporter {
public:
virtual bool Export() = 0;
virtual std::string GetClassName() = 0;
};
2. 同步/异步双重支持
每个核心功能都提供同步和异步两个版本,满足不同场景需求:
- 同步版本:直接返回结果,适合简单操作
- 异步版本:通过回调返回结果,适合I/O密集型操作
3. 错误处理机制
使用UniError
类进行统一的错误处理:
cpp
UniError(EINVAL).ThrowErr(env, "Invalid parameter");
4. 内存管理
使用智能指针管理内存,避免内存泄漏:
cpp
std::unique_ptr<char[]> bufferGuard;
核心操作实现详解
1. 文件打开操作实现
同步打开 (Open::Sync)
cpp
napi_value Open::Sync(napi_env env, napi_callback_info info)
{
NFuncArg funcArg(env, info);
if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
return nullptr;
}
// 1. 解析路径参数
bool succ = false;
unique_ptr<char[]> path = nullptr;
tie(succ, path, ignore) = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
if (!succ) {
UniError(EINVAL).ThrowErr(env, "Invalid path");
return nullptr;
}
// 2. 解析标志位参数
unsigned int flags = O_RDONLY;
if (funcArg.GetArgc() >= NARG_CNT::TWO) {
auto [succGetFlags, authFlags] = NVal(env, funcArg[NARG_POS::SECOND]).ToInt32(O_RDONLY);
if (!succGetFlags || authFlags < 0) {
UniError(EINVAL).ThrowErr(env, "Invalid flags");
return nullptr;
}
flags = static_cast<unsigned int>(authFlags);
(void)CommonFunc::ConvertJsFlags(flags); // 转换JavaScript标志位到系统标志位
}
// 3. 检查远程URI
int fd = -1;
if (ModuleRemoteUri::RemoteUri::IsRemoteUri(path.get(), fd, flags)) {
return NVal::CreateInt64(env, fd).val_;
}
// 4. 解析模式参数
int32_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
if (funcArg.GetArgc() != NARG_CNT::THREE) {
size_t flagsFirst { flags };
if ((flagsFirst & O_CREAT) || (flagsFirst & O_TMPFILE)) {
UniError(EINVAL).ThrowErr(env, "called with O_CREAT/O_TMPFILE but no mode");
return nullptr;
}
} else {
tie(succ, mode) = NVal(env, funcArg.GetArg(NARG_POS::THIRD)).ToInt32(mode);
if (!succ) {
UniError(EINVAL).ThrowErr(env, "Invalid mode");
return nullptr;
}
}
// 5. 执行系统调用
fd = open(path.get(), flags, mode);
if (fd == -1) {
if (errno == ENAMETOOLONG) {
UniError(errno).ThrowErr(env, "Filename too long");
return nullptr;
}
UniError(errno).ThrowErr(env);
return nullptr;
}
return NVal::CreateInt64(env, fd).val_;
}
异步打开 (Open::Async)
cpp
napi_value Open::Async(napi_env env, napi_callback_info info)
{
// 1. 参数解析(与同步版本相同)
NFuncArg funcArg(env, info);
// ... 参数解析代码 ...
// 2. 创建异步工作参数
auto arg = make_shared<int32_t>();
// 3. 定义执行函数
auto cbExec = [path = string(path.get()), flags, mode, arg](napi_env env) -> UniError {
return DoOpenExec(path, flags, mode, arg);
};
// 4. 定义完成回调
auto cbComplCallback = [arg](napi_env env, UniError err) -> NVal {
if (err) {
if (err.GetErrno(ERR_CODE_SYSTEM_POSIX) == ENAMETOOLONG) {
return {env, err.GetNapiErr(env, "Filename too long")};
}
return { env, err.GetNapiErr(env) };
}
return { NVal::CreateInt64(env, *arg) };
};
// 5. 调度异步工作
NVal thisVar(env, funcArg.GetThisVar());
if (argc == NARG_CNT::ONE || (argc == NARG_CNT::TWO &&
!NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
return NAsyncWorkPromise(env, thisVar).Schedule("FileIOOpen", cbExec, cbComplCallback).val_;
} else {
NVal cb(env, funcArg[argc - 1]);
return NAsyncWorkCallback(env, thisVar, cb).Schedule("FileIOOpen", cbExec, cbComplCallback).val_;
}
}
2. 文件读取操作实现
文本读取 (ReadText::Sync)
cpp
napi_value ReadText::Sync(napi_env env, napi_callback_info info)
{
// 1. 参数解析
NFuncArg funcArg(env, info);
if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
return nullptr;
}
// 2. 获取文件路径
auto [resGetFirstArg, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
if (!resGetFirstArg) {
UniError(EINVAL).ThrowErr(env, "Invalid path");
return nullptr;
}
// 3. 解析读取选项(位置、长度、编码)
auto [resGetReadTextArg, position, hasLen, len, encoding] = GetReadTextArg(env, funcArg[NARG_POS::SECOND]);
if (!resGetReadTextArg) {
UniError(EINVAL).ThrowErr(env, "Invalid option");
return nullptr;
}
// 4. 打开文件并获取文件状态
struct stat statbf;
FDGuard sfd;
sfd.SetFD(open(path.get(), O_RDONLY));
if ((!sfd) || (fstat(sfd.GetFD(), &statbf) == -1)) {
UniError(errno).ThrowErr(env);
return nullptr;
}
// 5. 验证位置参数
if (position > statbf.st_size) {
UniError(EINVAL).ThrowErr(env, "Invalid position");
return nullptr;
}
// 6. 计算读取长度
len = (!hasLen || len > static_cast<size_t>(statbf.st_size)) ? statbf.st_size : len;
// 7. 分配缓冲区
std::unique_ptr<char[]> readbuf = std::make_unique<char[]>(len + 1);
if (readbuf == nullptr) {
UniError(EINVAL).ThrowErr(env, "file is too large");
return nullptr;
}
// 8. 初始化缓冲区
if (memset_s(readbuf.get(), len + 1, 0, len + 1) != EOK) {
UniError(errno).ThrowErr(env, "dfs mem error");
return nullptr;
}
// 9. 执行读取操作
ssize_t ret = 0;
if (position >= 0) {
ret = pread(sfd.GetFD(), readbuf.get(), len, position); // 指定位置读取
} else {
ret = read(sfd.GetFD(), readbuf.get(), len); // 当前位置读取
}
if (ret == -1) {
UniError(EINVAL).ThrowErr(env, "Invalid read file");
return nullptr;
}
// 10. 返回UTF-8字符串
return NVal::CreateUTF8String(env, readbuf.get(), ret).val_;
}
3. 流操作实现
流写入 (StreamNExporter::WriteSync)
cpp
napi_value StreamNExporter::WriteSync(napi_env env, napi_callback_info info)
{
// 1. 参数解析
NFuncArg funcArg(env, info);
if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
return nullptr;
}
// 2. 获取流实体
bool succ = false;
FILE *filp = nullptr;
auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
if (!streamEntity || !streamEntity->fp) {
UniError(EBADF).ThrowErr(env, "Stream may has been closed");
return nullptr;
} else {
filp = streamEntity->fp.get();
}
// 3. 解析写入参数
void *buf = nullptr;
size_t len = 0;
int64_t position = -1;
unique_ptr<char[]> bufGuard = nullptr;
tie(succ, bufGuard, buf, len, position) =
CommonFunc::GetWriteArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
if (!succ) {
return nullptr;
}
// 4. 定位文件位置(如果指定了位置)
if (position >= 0 && (fseek(filp, static_cast<long>(position), SEEK_SET) == -1)) {
UniError(errno).ThrowErr(env);
return nullptr;
}
// 5. 执行写入操作
size_t writeLen = fwrite(buf, 1, len, filp);
if ((writeLen == 0) && (writeLen != len)) {
UniError(errno).ThrowErr(env);
return nullptr;
}
// 6. 返回实际写入长度
return NVal::CreateInt64(env, writeLen).val_;
}
流读取 (StreamNExporter::ReadSync)
cpp
napi_value StreamNExporter::ReadSync(napi_env env, napi_callback_info info)
{
// 1. 参数解析和流实体获取
NFuncArg funcArg(env, info);
// ... 参数解析代码 ...
// 2. 解析读取参数
void *buf = nullptr;
size_t len = 0;
int64_t pos = -1;
tie(succ, buf, len, pos, ignore) =
CommonFunc::GetReadArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
if (!succ) {
return nullptr;
}
// 3. 定位文件位置
if (pos >= 0 && (fseek(filp, static_cast<long>(pos), SEEK_SET) == -1)) {
UniError(errno).ThrowErr(env);
return nullptr;
}
// 4. 执行读取操作
size_t actLen = fread(buf, 1, len, filp);
if ((actLen != len && !feof(filp)) || ferror(filp)) {
UniError(errno).ThrowErr(env);
}
// 5. 返回实际读取长度
return NVal::CreateInt64(env, actLen).val_;
}
4. 目录操作实现
目录读取 (DirNExporter::ReadSync)
cpp
napi_value DirNExporter::ReadSync(napi_env env, napi_callback_info info)
{
// 1. 参数解析
NFuncArg funcArg(env, info);
if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
return nullptr;
}
// 2. 获取目录实体
DirEntity *dirEntity = GetDirEntity(env, info);
if (!dirEntity || !dirEntity->dir_) {
UniError(EBADF).ThrowErr(env, "Dir has been closed yet");
return nullptr;
}
DIR *dir = dirEntity->dir_.get();
// 3. 读取目录条目
struct dirent tmpDirent;
{
lock_guard(dirEntity->lock_); // 线程安全保护
errno = 0;
dirent *res = nullptr;
do {
res = readdir(dir);
if (res == nullptr && errno) {
UniError(errno).ThrowErr(env);
return nullptr;
} else if (res == nullptr) {
return NVal::CreateUndefined(env).val_; // 目录结束
} else if (string(res->d_name) == "." || string(res->d_name) == "..") {
continue; // 跳过当前目录和父目录
} else {
if (EOK != memcpy_s(&tmpDirent, sizeof(dirent), res, res->d_reclen)) {
UniError(errno).ThrowErr(env);
return nullptr;
}
break;
}
} while (true);
}
// 4. 创建Dirent对象
napi_value objDirent = NClass::InstantiateClass(env, DirentNExporter::className_, {});
if (!objDirent) {
return nullptr;
}
// 5. 设置目录条目数据
auto direntEntity = NClass::GetEntityOf<DirentEntity>(env, objDirent);
if (!direntEntity) {
return nullptr;
}
direntEntity->dirent_ = tmpDirent;
return objDirent;
}
目录文件列表 (DirNExporter::ListFileSync)
cpp
napi_value DirNExporter::ListFileSync(napi_env env, napi_callback_info info)
{
// 1. 参数解析
NFuncArg funcArg(env, info);
if (!funcArg.InitArgs(NARG_CNT::ONE)) {
UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
return nullptr;
}
// 2. 获取目录实体和列表数量
auto dirEntity = CheckDirEntity(env, funcArg.GetThisVar());
if (!dirEntity) {
return nullptr;
}
auto [succ, listNum] = ParseJsListNum(env, funcArg[NARG_POS::FIRST]);
if (!succ) {
UniError(EINVAL).ThrowErr(env, "Invalid listNum");
return nullptr;
}
// 3. 读取目录条目列表
vector<dirent> dirents;
{
lock_guard(dirEntity->lock_);
dirent *res = nullptr;
int listCount = 0;
auto dir = dirEntity->dir_.get();
do {
errno = 0;
res = readdir(dir);
if (res == nullptr && errno) {
UniError(errno).ThrowErr(env);
return nullptr;
} else if (res == nullptr) {
break; // 目录结束
} else if (string(res->d_name) == "." || string(res->d_name) == "..") {
continue; // 跳过特殊目录
} else {
dirents.push_back(*res);
listCount++;
}
} while (listCount < listNum || listNum == 0); // 按数量限制或全部读取
}
// 4. 转换为JavaScript数组
return DoListFileVector2NV(env, dirents);
}
5. 异步操作实现机制
异步工作调度
cpp
// 异步操作的基本模式
napi_value SomeAsyncFunction(napi_env env, napi_callback_info info)
{
// 1. 参数解析
NFuncArg funcArg(env, info);
// ... 参数解析 ...
// 2. 定义执行函数(在工作线程中运行)
auto cbExec = [captured_vars](napi_env env) -> UniError {
// 执行实际的I/O操作
int result = some_system_call();
if (result == -1) {
return UniError(errno);
}
return UniError(ERRNO_NOERR);
};
// 3. 定义完成回调(在主线程中运行)
auto cbCompl = [captured_vars](napi_env env, UniError err) -> NVal {
if (err) {
return { env, err.GetNapiErr(env) };
} else {
// 返回结果
return NVal::CreateSomeValue(env, result);
}
};
// 4. 调度异步工作
NVal thisVar(env, funcArg.GetThisVar());
if (is_promise_mode) {
return NAsyncWorkPromise(env, thisVar).Schedule("ProcedureName", cbExec, cbCompl).val_;
} else {
NVal cb(env, funcArg[callback_index]);
return NAsyncWorkCallback(env, thisVar, cb).Schedule("ProcedureName", cbExec, cbCompl).val_;
}
}
6. 错误处理机制
统一错误处理
cpp
// UniError类的使用
if (some_operation_failed) {
if (errno == ENAMETOOLONG) {
UniError(errno).ThrowErr(env, "Filename too long");
} else {
UniError(errno).ThrowErr(env);
}
return nullptr;
}
参数验证
cpp
// 参数数量验证
if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
return nullptr;
}
// 参数类型验证
auto [succ, value] = NVal(env, funcArg[NARG_POS::FIRST]).ToInt32();
if (!succ || value < 0) {
UniError(EINVAL).ThrowErr(env, "Invalid parameter");
return nullptr;
}
使用示例
基本文件操作
javascript
import fileio from '@ohos.fileio';
// 打开文件
let file = fileio.openSync('/path/to/file', fileio.OpenMode.READ_ONLY);
// 读取文件
let buffer = new ArrayBuffer(1024);
fileio.readSync(file.fd, buffer);
// 关闭文件
fileio.closeSync(file.fd);
目录操作
javascript
import fileio from '@ohos.fileio';
// 打开目录
let dir = fileio.opendirSync('/path/to/directory');
// 读取目录条目
let dirent = fileio.readSync(dir.fd);
// 列出文件
let files = fileio.listFileSync(dir.fd);
// 关闭目录
fileio.closedirSync(dir.fd);
流操作
javascript
import fileio from '@ohos.fileio';
// 创建流
let stream = fileio.createStream('/path/to/file', 'r+');
// 写入数据
let data = new TextEncoder().encode('Hello World');
fileio.writeSync(stream.fd, data);
// 刷新流
fileio.flushSync(stream.fd);
// 关闭流
fileio.closeSync(stream.fd);
性能优化
1. 异步I/O
大量使用异步I/O操作,避免阻塞主线程:
cpp
static napi_value Async(napi_env env, napi_callback_info info) {
// 异步操作实现
napi_work work;
napi_create_async_work(env, nullptr, resource_name,
Execute, Complete, &work);
napi_queue_async_work(env, work);
}
2. 缓冲区管理
使用ArrayBuffer进行高效的数据传输:
cpp
tie(succ, buf, bufLen) = txt.ToArraybuffer();
3. 参数验证
在C++层面进行参数验证,减少JavaScript层的开销:
cpp
if (!succ || position < 0) {
UniError(EINVAL).ThrowErr(env, "Invalid parameter");
return { false, nullptr, 0, position, offset };
}
安全考虑
1. 路径验证
所有路径操作都进行安全验证:
cpp
tie(succ, src, ignore) = NVal(env, srcPath).ToUTF8StringPath();
2. 权限检查
文件操作前进行权限检查:
cpp
static napi_value AccessSync(napi_env env, napi_callback_info info);
3. 资源管理
使用RAII模式确保资源正确释放:
cpp
~AsyncIOWrtieArg() = default;
总结
mod_fileio
模块是OpenHarmony文件管理API的核心组件,提供了完整、高效、安全的文件I/O操作功能。其设计具有以下特点:
- 模块化设计:清晰的目录结构和职责分离
- 版本兼容:提供标准版本和V9版本
- 性能优化:异步I/O和高效的内存管理
- 安全可靠:完善的错误处理和权限控制
- 易于使用:统一的API设计和丰富的功能支持