一、概述
应用需要对应用文件目录下的应用文件进行查看、创建、读写、删除、移动、复制、获取属性等访问操作,下文介绍具体方法。
二、接口说明
开发者通过基础文件操作接口(ohos.file.fs)实现应用文件访问能力,主要功能如下表所示。
表1 基础文件操作接口功能,其中“√”表示支持,“-”表示不区分同步和异步。
接口名 | 功能 | 接口类型 | 支持同步 | 支持异步 |
---|---|---|---|---|
access | 检查文件是否存在 | 方法 | √ | √ |
close | 关闭文件 | 方法 | √ | √ |
copyFile | 复制文件 | 方法 | √ | √ |
createStream | 基于文件路径打开文件流 | 方法 | √ | √ |
listFile | 列出文件夹下所有文件名 | 方法 | √ | √ |
mkdir | 创建目录 | 方法 | √ | √ |
moveFile | 移动文件 | 方法 | √ | √ |
open | 打开文件 | 方法 | √ | √ |
read | 从文件读取数据 | 方法 | √ | √ |
rename | 重命名文件或文件夹 | 方法 | √ | √ |
rmdir | 删除整个目录 | 方法 | √ | √ |
stat | 获取文件详细属性信息 | 方法 | √ | √ |
unlink | 删除单个文件 | 方法 | √ | √ |
write | 将数据写入文件 | 方法 | √ | √ |
Stream.close | 关闭文件流 | 方法 | √ | √ |
Stream.flush | 刷新文件流 | 方法 | √ | √ |
Stream.write | 将数据写入流文件 | 方法 | √ | √ |
Stream.read | 从流文件读取数据 | 方法 | √ | √ |
File.fd | 获取文件描述符 | 属性 | - | - |
OpenMode | 设置文件打开标签 | 属性 | - | - |
Filter | 设置文件过滤配置项 | 类型 | - | - |
注意
使用基础文件操作接口时,耗时较长的操作,例如:read、write等,建议使用异步接口,避免应用崩溃。
三、开发示例
在对应用文件开始访问前,开发者需要获取应用文件路径。以从UIAbilityContext获取HAP级别的文件路径为例进行说明。
3.1 新建并读写一个文件
示例效果图
示例代码
import { fileIo as fs, ReadOptions } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { buffer } from '@kit.ArkTS';
// 获取应用文件路径
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;
function createFile(): void {
console.log("filesDir 路径:", filesDir)
// 文件不存在时创建并打开文件,文件存在时打开文件
let file = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
// 写入一段内容至文件
let writeLen = fs.writeSync(file.fd, "应用文件访问(ArkTS)");
console.info("内容长度: " + writeLen);
// 创建一个大小为1024字节的ArrayBuffer对象,用于存储从文件中读取的数据
let arrayBuffer = new ArrayBuffer(1024);
// 设置读取的偏移量和长度
let readOptions: ReadOptions = {
offset: 0,
length: arrayBuffer.byteLength
};
// 读取文件内容到ArrayBuffer对象中,并返回实际读取的字节数
let readLen = fs.readSync(file.fd, arrayBuffer, readOptions);
// 将ArrayBuffer对象转换为Buffer对象,并转换为字符串输出
let buf = buffer.from(arrayBuffer, 0, readLen);
console.info(`从 ${file.name} 文件读取的内容是:${buf.toString()}`);
// 关闭文件
fs.closeSync(file);
}
3.2 读取文件内容并写入到另一个文件
示例效果图
示例代码
import { fileIo as fs, ReadOptions, WriteOptions } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { buffer } from '@kit.ArkTS';
// 获取应用文件路径
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;
function readWriteFile(): void {
// 打开文件
let srcFile = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
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 readOptions: ReadOptions = {
offset: readSize,
length: bufSize
};
let readLen = fs.readSync(srcFile.fd, buf, readOptions);
while (readLen > 0) {
readSize += readLen;
let writeOptions: WriteOptions = {
length: readLen
};
fs.writeSync(destFile.fd, buf, writeOptions);
readOptions.offset = readSize;
readLen = fs.readSync(srcFile.fd, buf, readOptions);
console.log("读取文件内容并写入到另一个文件 结束")
}
// 关闭文件
fs.closeSync(srcFile);
fs.closeSync(destFile);
}
3.3 以流的形式读写文件
以下示例代码演示了如何使用流接口读取test.txt的文件内容并写入到destFile2.txt文件中。
效果图
示例代码
import { fileIo as fs, ReadOptions, WriteOptions } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { buffer } from '@kit.ArkTS';
// 获取应用文件路径
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;
async function readWriteFileWithStream(): Promise<void> {
// 创建并打开输入文件流
let inputStream = fs.createStreamSync(filesDir + '/test.txt', 'r+');
// 创建并打开输出文件流
let outputStream = fs.createStreamSync(filesDir + '/destFile2.txt', "w+");
let bufSize = 4096;
let readSize = 0;
let buf = new ArrayBuffer(bufSize);
let readOptions: ReadOptions = {
offset: readSize,
length: bufSize
};
// 以流的形式读取源文件内容并写入到目标文件
let readLen = await inputStream.read(buf, readOptions);
readSize += readLen;
while (readLen > 0) {
const writeBuf = readLen < bufSize ? buf.slice(0, readLen) : buf;
await outputStream.write(writeBuf);
readOptions.offset = readSize;
readLen = await inputStream.read(buf, readOptions);
readSize += readLen;
}
console.log("以流的形式读写文件 结束")
// 关闭文件流
inputStream.closeSync();
outputStream.closeSync();
}
说明
使用流接口时,需注意流的及时关闭。同时流的异步接口应严格遵循异步接口使用规范,避免同步、异步接口混用。流接口不支持并发读写。
3.4 查看文件列表
效果图
示例代码
import { fileIo as fs, ListFileOptions, ReadOptions, WriteOptions } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { buffer } from '@kit.ArkTS';
// 获取应用文件路径
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;
function getListFile(): void {
let listFileOption: ListFileOptions = {
recursion: false,
listNum: 0,
filter: {
suffix: [".png", ".jpg", ".txt"],
displayName: ["test*", "destFile*"],
fileSizeOver: 0,
lastModifiedAfter: new Date(0).getTime()
}
};
let files = fs.listFileSync(filesDir, listFileOption);
for (let i = 0; i < files.length; i++) {
console.info(`文件名称: ${files[i]}`);
}
}
3.5 使用文件流
效果图
示例代码
import { fileIo as fs, ListFileOptions, ReadOptions, WriteOptions } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { buffer } from '@kit.ArkTS';
// 获取应用文件路径
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;
/**
* 使用文件流
*/
function copyFileWithReadable(): void {
// 创建文件可读流
const rs = fs.createReadStream(`${filesDir}/test.txt`);
// 创建文件可写流
const ws = fs.createWriteStream(`${filesDir}/destFile.txt`);
// 暂停模式拷贝文件。在拷贝数据时,将原始数据暂停,然后将数据复制到另一个位置,适用于对数据完整性和一致性要求较高的场景
rs.on('readable', () => {
const data = rs.read();
if (!data) {
return;
}
ws.write(data);
});
console.log("使用文件流 copyFileWithReadable 结束")
}
function copyFileWithData(): void {
// 创建文件可读流
const rs = fs.createReadStream(`${filesDir}/test.txt`);
// 创建文件可写流
const ws = fs.createWriteStream(`${filesDir}/destFile.txt`);
// 流动模式拷贝文件。数据的读取和写入是同时进行的,不需要暂停原始数据的访问,适用于对数据实时性要求较高的场景
rs.on('data', (emitData) => {
const data = emitData?.data;
if (!data) {
return;
}
ws.write(data as Uint8Array);
});
console.log("使用文件流 copyFileWithData 结束")
}
3.6 使用文件哈希流
效果图
示例代码
import { fileIo as fs, ListFileOptions, ReadOptions, WriteOptions, hash} from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { buffer } from '@kit.ArkTS';
function hashFileWithStream() {
const filePath = `${filesDir}/test.txt`;
// 创建文件可读流
const rs = fs.createReadStream(filePath);
// 创建哈希流
const hs = hash.createHash('sha256');
rs.on('data', (emitData) => {
const data = emitData?.data;
hs.update(new Uint8Array(data?.split('').map((x: string) => x.charCodeAt(0))).buffer);
});
rs.on('close', async () => {
const hashResult = hs.digest();
const fileHash = await hash.hash(filePath, 'sha256');
console.info(`使用文件哈希流 test hashResult: ${hashResult}`);
console.info(`使用文件哈希流 test fileHash: ${fileHash}`);
});
}
四、开发示例完整代码
TestReadOrWriteFile.ets
import { fileIo as fs, ListFileOptions, ReadOptions, WriteOptions, hash} from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { buffer } from '@kit.ArkTS';
// 获取应用文件路径
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;
/**
* 新建并读写一个文件
*/
function createFile(): void {
console.log("filesDir 路径:", filesDir)
// /data/storage/el2/base/haps/entry/files
// 文件不存在时创建并打开文件,文件存在时打开文件
let file = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
// 写入一段内容至文件
let writeLen = fs.writeSync(file.fd, "应用文件访问(ArkTS)");
console.info("内容长度: " + writeLen);
// 内容长度: 25
// 创建一个大小为1024字节的ArrayBuffer对象,用于存储从文件中读取的数据
let arrayBuffer = new ArrayBuffer(1024);
// 设置读取的偏移量和长度
let readOptions: ReadOptions = {
offset: 0,
length: arrayBuffer.byteLength
};
// 读取文件内容到ArrayBuffer对象中,并返回实际读取的字节数
let readLen = fs.readSync(file.fd, arrayBuffer, readOptions);
// 将ArrayBuffer对象转换为Buffer对象,并转换为字符串输出
let buf = buffer.from(arrayBuffer, 0, readLen);
console.info(`从 ${file.name} 文件读取的内容是:${buf.toString()}`);
// 从 test.txt 文件读取的内容是:应用文件访问(ArkTS)
// 关闭文件
fs.closeSync(file);
}
/**
* 读取文件内容并写入到另一个文件
*/
function readWriteFile(): void {
// 打开文件
let srcFile = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
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 readOptions: ReadOptions = {
offset: readSize,
length: bufSize
};
let readLen = fs.readSync(srcFile.fd, buf, readOptions);
while (readLen > 0) {
readSize += readLen;
let writeOptions: WriteOptions = {
length: readLen
};
fs.writeSync(destFile.fd, buf, writeOptions);
readOptions.offset = readSize;
readLen = fs.readSync(srcFile.fd, buf, readOptions);
console.log("读取文件内容并写入到另一个文件 结束")
}
// 关闭文件
fs.closeSync(srcFile);
fs.closeSync(destFile);
}
/**
* 以流的形式读写文件
* @returns
*/
async function readWriteFileWithStream(): Promise<void> {
// 创建并打开输入文件流
let inputStream = fs.createStreamSync(filesDir + '/test.txt', 'r+');
// 创建并打开输出文件流
let outputStream = fs.createStreamSync(filesDir + '/destFile2.txt', "w+");
let bufSize = 4096;
let readSize = 0;
let buf = new ArrayBuffer(bufSize);
let readOptions: ReadOptions = {
offset: readSize,
length: bufSize
};
// 以流的形式读取源文件内容并写入到目标文件
let readLen = await inputStream.read(buf, readOptions);
readSize += readLen;
while (readLen > 0) {
const writeBuf = readLen < bufSize ? buf.slice(0, readLen) : buf;
await outputStream.write(writeBuf);
readOptions.offset = readSize;
readLen = await inputStream.read(buf, readOptions);
readSize += readLen;
}
console.log("以流的形式读写文件 结束")
// 关闭文件流
inputStream.closeSync();
outputStream.closeSync();
}
/**
* 查看文件列表
*/
function getListFile(): void {
let listFileOption: ListFileOptions = {
recursion: false,
listNum: 0,
filter: {
suffix: [".png", ".jpg", ".txt"],
displayName: ["test*", "destFile*"],
fileSizeOver: 0,
lastModifiedAfter: new Date(0).getTime()
}
};
let files = fs.listFileSync(filesDir, listFileOption);
for (let i = 0; i < files.length; i++) {
console.info(`文件名称: ${files[i]}`);
}
}
/**
* 使用文件流
*/
function copyFileWithReadable(): void {
// 创建文件可读流
const rs = fs.createReadStream(`${filesDir}/test.txt`);
// 创建文件可写流
const ws = fs.createWriteStream(`${filesDir}/destFile.txt`);
// 暂停模式拷贝文件。在拷贝数据时,将原始数据暂停,然后将数据复制到另一个位置,适用于对数据完整性和一致性要求较高的场景
rs.on('readable', () => {
const data = rs.read();
if (!data) {
return;
}
ws.write(data);
});
console.log("使用文件流 copyFileWithReadable 结束")
}
function copyFileWithData(): void {
// 创建文件可读流
const rs = fs.createReadStream(`${filesDir}/test.txt`);
// 创建文件可写流
const ws = fs.createWriteStream(`${filesDir}/destFile.txt`);
// 流动模式拷贝文件。数据的读取和写入是同时进行的,不需要暂停原始数据的访问,适用于对数据实时性要求较高的场景
rs.on('data', (emitData) => {
const data = emitData?.data;
if (!data) {
return;
}
ws.write(data as Uint8Array);
});
console.log("使用文件流 copyFileWithData 结束")
}
/**
* 使用文件哈希流
*/
function hashFileWithStream() {
const filePath = `${filesDir}/test.txt`;
// 创建文件可读流
const rs = fs.createReadStream(filePath);
// 创建哈希流
const hs = hash.createHash('sha256');
rs.on('data', (emitData) => {
const data = emitData?.data;
hs.update(new Uint8Array(data?.split('').map((x: string) => x.charCodeAt(0))).buffer);
});
rs.on('close', async () => {
const hashResult = hs.digest();
const fileHash = await hash.hash(filePath, 'sha256');
console.info(`使用文件哈希流 test hashResult: ${hashResult}`);
console.info(`使用文件哈希流 test fileHash: ${fileHash}`);
});
}
@Builder
function BaseButton(btnName: string, eventType: number) {
Column() {
Button(btnName)
.fontColor(Color.Black)
.fontSize(20)
.fontWeight(FontWeight.Medium)
.onClick(() => {
if (eventType == 1) {
createFile()
return
}
if (eventType == 2) {
readWriteFile()
return
}
if (eventType == 3) {
readWriteFileWithStream()
return
}
if (eventType == 4) {
getListFile()
return
}
if (eventType == 5) {
copyFileWithReadable()
copyFileWithData()
return
}
if (eventType == 6) {
hashFileWithStream()
return
}
})
}
}
@Entry
@Component
struct TestReadOrWriteFile {
@State message: string = '应用文件访问(ArkTS)';
build() {
Column({ space: 10 }) {
Text(this.message)
.id('TestReadOrWriteFileHelloWorld')
.fontSize($r('app.float.page_text_font_20fp'))
.fontWeight(FontWeight.Medium)
.margin({ top: 20 })
BaseButton('新建并读写一个文件', 1)
BaseButton('读取文件内容并写入到另一个文件', 2)
BaseButton('以流的形式读写文件', 3)
BaseButton('以流的形式读写文件', 4)
BaseButton('使用文件流', 5)
BaseButton('使用文件哈希流', 6)
}
.height('100%')
.width('100%')
}
}