在Harmony系统中,为了保障用户数据安全与应用之间的隔离,引入了应用沙箱的概念。本文将深入探讨应用沙箱机制、应用文件目录管理以及与之相关的文件访问与管理实践,旨在帮助开发者更好地理解与利用这些特性。
应用沙箱:安全隔离的核心
应用沙箱是一种以安全防护为目的的隔离机制,它确保每个应用运行在自己的封闭环境中,限制应用访问其他应用或系统核心数据的能力。这种机制有效地防止了恶意路径穿越访问,增强了数据安全性。
沙箱目录组成
- 应用文件目录:应用专属的文件存储区域,包括安装文件、资源、缓存等。
- 系统文件目录:应用运行所需的系统文件,对应用而言只读。
目录范围
应用沙箱目录代表应用可见的所有目录范围,其中应用文件目录位于沙箱目录内。系统文件目录对应用是只读的,而应用文件目录则允许应用进行读写操作。
应用文件目录结构与管理
应用文件目录遵循严格的层次结构,不同层级的目录具有不同的用途:
- 一级目录data/:代表应用文件的根目录。
- 二级目录storage/:本应用持久化文件的存储区域。
- 三级目录el1/ 和 el2/:分别表示设备级和用户级加密区,用于存储不同安全等级的数据。
- 四级与五级目录:通过
ApplicationContext
等接口获取的应用特定目录,如files
、cache
、preferences
等。
文件路径获取
- 开发者应当通过
Context
属性获取应用文件路径,而非直接构造路径字符串,以避免兼容性问题。
生命周期与说明
每种类型的文件路径都有其特定的生命周期和使用场景。例如,cacheDir
中的文件可能在系统空间不足时被自动清理,而filesDir
则用于长期保存的数据。
文件访问与管理接口
开发者可以通过ohos.file.fs
模块实现对应用文件的访问能力,包括创建、读写、删除文件等基本操作。
示例代码
创建并读写文件
// pages/xxx.ets
import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';
function createFile() {
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;
let file = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
fs.writeSync(file.fd, "Try to write str.");
let buf = new ArrayBuffer(1024);
let readLen = fs.readSync(file.fd, buf);
console.info("the content of file: " + String.fromCharCode.apply(null, new Uint8Array(buf.slice(0, readLen))));
fs.closeSync(file);
}
读取文件并写入另一文件
// pages/xxx.ets
import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';
function readWriteFile() {
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;
let srcFile = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE);
let destFile = fs.openSync(filesDir + '/destFile.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
let bufSize = 4096;
let readSize = 0;
let buf = new ArrayBuffer(bufSize);
let readLen = fs.readSync(srcFile.fd, buf, { offset: readSize });
while (readLen > 0) {
readSize += readLen;
fs.writeSync(destFile.fd, buf);
readLen = fs.readSync(srcFile.fd, buf, { offset: readSize });
}
fs.closeSync(srcFile);
fs.closeSync(destFile);
}
使用流接口读写文件
// pages/xxx.ets
import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';
async function readWriteFileWithStream() {
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;
let inputStream = fs.createStreamSync(filesDir + '/test.txt', 'r+');
let outputStream = fs.createStreamSync(filesDir + '/destFile.txt', "w+");
let bufSize = 4096;
let readSize = 0;
let buf = new ArrayBuffer(bufSize);
let readLen = await inputStream.read(buf, { offset: readSize });
readSize += readLen;
while (readLen > 0) {
await outputStream.write(buf);
readLen = await inputStream.read(buf, { offset: readSize });
readSize += readLen;
}
inputStream.closeSync();
outputStream.closeSync();
}
文件分享与网络交互
应用还可以使用ohos.request
模块将本地文件上传至网络服务器,或从网络下载资源文件到应用文件目录。
上传文件
// pages/xxx.ets
import common from '@ohos.app.ability.common';
import fs from '@ohos.file.fs';
import request from '@ohos.request';
let context = getContext(this) as common.UIAbilityContext;
let cacheDir = context.cacheDir;
let file = fs.openSync(cacheDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
fs.writeSync(file.fd, 'upload file test');
fs.closeSync(file);
let uploadConfig = {
url: 'https://xxx',
header: { key1: 'value1', key2: 'value2' },
method: 'POST',
files: [{ filename: 'test.txt', name: 'test', uri: 'internal://cache/test.txt', type: 'txt' }],
data: [{ name: 'name', value: 'value' }]
};
request.uploadFile(context, uploadConfig)
.then((uploadTask) => {
uploadTask.on('complete', (taskStates) => {
for (let i = 0; i < taskStates.length; i++) {
console.info(`upload complete taskState: ${JSON.stringify(taskStates[i])}`);
}
});
})
.catch((err) => {
console.error(`Invoke uploadFile failed, code is ${err.code}, message is ${err.message}`);
});
下载文件
// pages/xxx.ets
import common from '@ohos.app.ability.common';
import fs from '@ohos.file.fs';
import request from '@ohos.request';
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;
request.downloadFile(context, {
url: 'https://xxxx/xxxx.txt',
filePath: filesDir + '/xxxx.txt'
}).then((downloadTask) => {
downloadTask.on('complete', () => {
console.info('download complete');
let file = fs.openSync(filesDir + '/xxxx.txt', fs.OpenMode.READ_WRITE);
let buf = new ArrayBuffer(1024);
let readLen = fs.readSync(file.fd, buf);
console.info(`The content of file: ${String.fromCharCode.apply(null, new Uint8Array(buf.slice(0, readLen)))}`);
fs.closeSync(file);
})
}).catch((err) => {
console.error(`Invoke downloadTask failed, code is ${err.code}, message is ${err.message}`);
});
应用及文件系统空间统计
在系统中,可能出现系统空间不够或者cacheDir等目录受系统配额限制等情况,需要应用开发者关注系统剩余空间,同时控制应用自身占用的空间大小。
获取文件系统数据分区剩余空间大小
import statvfs from '@ohos.file.statvfs';
let path = "/data";
statvfs.getFreeSize(path, (err, number) => {
if (err) {
console.error(`Invoke getFreeSize failed, code is ${err.code}, message is ${err.message}`);
} else {
console.info(`Invoke getFreeSize succeeded, size is ${number}`);
}
});
获取当前应用的存储空间大小
import storageStatistics from "@ohos.file.storageStatistics";
storageStatistics.getCurrentBundleStats((err, bundleStats) => {
if (err) {
console.error(`Invoke getCurrentBundleStats failed, code is ${err.code}, message is ${err.message}`);
} else {
console.info(`Invoke getCurrentBundleStats succeeded, appsize is ${bundleStats.appSize}`);
}
});
通过以上实践,开发者可以更好地理解和利用应用沙箱机制,有效管理和保护应用数据,同时提升应用在网络交互中的性能与安全性。