1# 应用文件访问(ArkTS) 2 3应用需要对应用文件目录下的应用文件进行查看、创建、读写、删除、移动、复制、获取属性等访问操作,下文介绍具体方法。 4 5## 接口说明 6 7开发者通过基础文件操作接口([ohos.file.fs](../reference/apis-core-file-kit/js-apis-file-fs.md))实现应用文件访问能力,主要功能如下表所示。 8 9**表1** 基础文件操作接口功能 10 11| 接口名 | 功能 | 接口类型 | 支持同步 | 支持异步 | 12| -------- | -------- | -------- | -------- | -------- | 13| access | 检查文件是否存在 | 方法 | √ | √ | 14| close | 关闭文件 | 方法 | √ | √ | 15| copyFile | 复制文件 | 方法 | √ | √ | 16| createStream | 基于文件路径打开文件流 | 方法 | √ | √ | 17| listFile | 列出文件夹下所有文件名 | 方法 | √ | √ | 18| mkdir | 创建目录 | 方法 | √ | √ | 19| moveFile | 移动文件 | 方法 | √ | √ | 20| open | 打开文件 | 方法 | √ | √ | 21| read | 从文件读取数据 | 方法 | √ | √ | 22| rename | 重命名文件或文件夹 | 方法 | √ | √ | 23| rmdir | 删除整个目录 | 方法 | √ | √ | 24| stat | 获取文件详细属性信息 | 方法 | √ | √ | 25| unlink | 删除单个文件 | 方法 | √ | √ | 26| write | 将数据写入文件 | 方法 | √ | √ | 27| Stream.close | 关闭文件流 | 方法 | √ | √ | 28| Stream.flush | 刷新文件流 | 方法 | √ | √ | 29| Stream.write | 将数据写入流文件 | 方法 | √ | √ | 30| Stream.read | 从流文件读取数据 | 方法 | √ | √ | 31| File.fd | 获取文件描述符 | 属性 | - | - | 32| OpenMode | 设置文件打开标签 | 属性 | - | - | 33| Filter | 设置文件过滤配置项 | 类型 | - | - | 34 35> **注意:** 36> 37> 使用基础文件操作接口时,耗时较长的操作,例如:read、write等,建议使用异步接口,避免应用崩溃。 38 39## 开发示例 40 41在对应用文件开始访问前,开发者需要[获取应用文件路径](../application-models/application-context-stage.md#获取应用文件路径)。以从UIAbilityContext获取HAP级别的文件路径为例进行说明,UIAbilityContext的获取方式请参见[获取UIAbility的上下文信息](../application-models/uiability-usage.md#获取uiability的上下文信息)。 42 43下面介绍几种常用操作示例。 44 45### 新建并读写一个文件 46 47以下示例代码演示了如何新建一个文件并对其读写。 48 49```ts 50// pages/xxx.ets 51import { fileIo as fs, ReadOptions } from '@kit.CoreFileKit'; 52import { common } from '@kit.AbilityKit'; 53import { buffer } from '@kit.ArkTS'; 54 55// 获取应用文件路径 56let context = getContext(this) as common.UIAbilityContext; 57let filesDir = context.filesDir; 58 59function createFile(): void { 60 // 文件不存在时创建并打开文件,文件存在时打开文件 61 let file = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); 62 // 写入一段内容至文件 63 let writeLen = fs.writeSync(file.fd, "Try to write str."); 64 console.info("The length of str is: " + writeLen); 65 // 创建一个大小为1024字节的ArrayBuffer对象,用于存储从文件中读取的数据 66 let arrayBuffer = new ArrayBuffer(1024); 67 // 设置读取的偏移量和长度 68 let readOptions: ReadOptions = { 69 offset: 0, 70 length: arrayBuffer.byteLength 71 }; 72 // 读取文件内容到ArrayBuffer对象中,并返回实际读取的字节数 73 let readLen = fs.readSync(file.fd, arrayBuffer, readOptions); 74 // 将ArrayBuffer对象转换为Buffer对象,并转换为字符串输出 75 let buf = buffer.from(arrayBuffer, 0, readLen); 76 console.info("the content of file: " + buf.toString()); 77 // 关闭文件 78 fs.closeSync(file); 79} 80``` 81 82### 读取文件内容并写入到另一个文件 83 84以下示例代码演示了如何从一个文件读写内容到另一个文件。 85 86```ts 87// pages/xxx.ets 88import { fileIo as fs, ReadOptions, WriteOptions } from '@kit.CoreFileKit'; 89import { common } from '@kit.AbilityKit'; 90 91// 获取应用文件路径 92let context = getContext(this) as common.UIAbilityContext; 93let filesDir = context.filesDir; 94 95function readWriteFile(): void { 96 // 打开文件 97 let srcFile = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); 98 let destFile = fs.openSync(filesDir + '/destFile.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); 99 // 读取源文件内容并写入至目的文件 100 let bufSize = 4096; 101 let readSize = 0; 102 let buf = new ArrayBuffer(bufSize); 103 let readOptions: ReadOptions = { 104 offset: readSize, 105 length: bufSize 106 }; 107 let readLen = fs.readSync(srcFile.fd, buf, readOptions); 108 while (readLen > 0) { 109 readSize += readLen; 110 let writeOptions: WriteOptions = { 111 length: readLen 112 }; 113 fs.writeSync(destFile.fd, buf, writeOptions); 114 readOptions.offset = readSize; 115 readLen = fs.readSync(srcFile.fd, buf, readOptions); 116 } 117 // 关闭文件 118 fs.closeSync(srcFile); 119 fs.closeSync(destFile); 120} 121``` 122 123> **说明:** 124> 125> 使用读写接口时,需注意可选项参数offset的设置。对于已存在且读写过的文件,文件偏移指针默认在上次读写操作的终止位置。 126 127### 以流的形式读写文件 128 129以下示例代码演示了如何使用流接口读取test.txt的文件内容并写入到destFile.txt文件中。 130 131```ts 132// pages/xxx.ets 133import { fileIo as fs, ReadOptions } from '@kit.CoreFileKit'; 134import { common } from '@kit.AbilityKit'; 135 136// 获取应用文件路径 137let context = getContext(this) as common.UIAbilityContext; 138let filesDir = context.filesDir; 139 140async function readWriteFileWithStream(): Promise<void> { 141 // 创建并打开输入文件流 142 let inputStream = fs.createStreamSync(filesDir + '/test.txt', 'r+'); 143 // 创建并打开输出文件流 144 let outputStream = fs.createStreamSync(filesDir + '/destFile.txt', "w+"); 145 146 let bufSize = 4096; 147 let readSize = 0; 148 let buf = new ArrayBuffer(bufSize); 149 let readOptions: ReadOptions = { 150 offset: readSize, 151 length: bufSize 152 }; 153 // 以流的形式读取源文件内容并写入到目标文件 154 let readLen = await inputStream.read(buf, readOptions); 155 readSize += readLen; 156 while (readLen > 0) { 157 const writeBuf = readLen < bufSize ? buf.slice(0, readLen) : buf; 158 await outputStream.write(writeBuf); 159 readOptions.offset = readSize; 160 readLen = await inputStream.read(buf, readOptions); 161 readSize += readLen; 162 } 163 // 关闭文件流 164 inputStream.closeSync(); 165 outputStream.closeSync(); 166} 167``` 168 169> **说明:** 170> 171> 使用流接口时,需注意流的及时关闭。同时流的异步接口应严格遵循异步接口使用规范,避免同步、异步接口混用。流接口不支持并发读写。 172 173### 查看文件列表 174 175以下示例代码演示了如何查看文件列表。 176 177```ts 178import { fileIo as fs, Filter, ListFileOptions } from '@kit.CoreFileKit'; 179import { common } from '@kit.AbilityKit'; 180 181// 获取应用文件路径 182let context = getContext(this) as common.UIAbilityContext; 183let filesDir = context.filesDir; 184 185// 查看文件列表 186function getListFile(): void { 187 let listFileOption: ListFileOptions = { 188 recursion: false, 189 listNum: 0, 190 filter: { 191 suffix: [".png", ".jpg", ".txt"], 192 displayName: ["test*"], 193 fileSizeOver: 0, 194 lastModifiedAfter: new Date(0).getTime() 195 } 196 }; 197 let files = fs.listFileSync(filesDir, listFileOption); 198 for (let i = 0; i < files.length; i++) { 199 console.info(`The name of file: ${files[i]}`); 200 } 201} 202``` 203 204### 使用文件流 205 206以下示例代码演示了如何使用文件可读流,文件可写流。 207 208```ts 209// pages/xxx.ets 210import { fileIo as fs } from '@kit.CoreFileKit'; 211import { common } from '@kit.AbilityKit'; 212 213// 获取应用文件路径 214let context = getContext(this) as common.UIAbilityContext; 215let filesDir = context.filesDir; 216 217function copyFileWithReadable(): void { 218 // 创建文件可读流 219 const rs = fs.createReadStream(`${filesDir}/read.txt`); 220 // 创建文件可写流 221 const ws = fs.createWriteStream(`${filesDir}/write.txt`); 222 // 暂停模式拷贝文件。在拷贝数据时,将原始数据暂停,然后将数据复制到另一个位置,适用于对数据完整性和一致性要求较高的场景 223 rs.on('readable', () => { 224 const data = rs.read(); 225 if (!data) { 226 return; 227 } 228 ws.write(data); 229 }); 230} 231 232function copyFileWithData(): void { 233 // 创建文件可读流 234 const rs = fs.createReadStream(`${filesDir}/read.txt`); 235 // 创建文件可写流 236 const ws = fs.createWriteStream(`${filesDir}/write.txt`); 237 // 流动模式拷贝文件。数据的读取和写入是同时进行的,不需要暂停原始数据的访问,适用于对数据实时性要求较高的场景 238 rs.on('data', (emitData) => { 239 const data = emitData?.data; 240 if (!data) { 241 return; 242 } 243 ws.write(data as Uint8Array); 244 }); 245} 246``` 247 248### 使用文件哈希流 249 250哈希流是一种数据传输和存储技术,可以将任意长度的数据转换为固定长度的哈希值来验证数据的完整性和一致性。以下代码演示了如何使用文件哈希处理接口([ohos.file.hash](../reference/apis-core-file-kit/js-apis-file-hash.md))来处理文件哈希流。 251 252```ts 253// pages/xxx.ets 254import { fileIo as fs } from '@kit.CoreFileKit'; 255import { hash } from '@kit.CoreFileKit'; 256import { common } from '@kit.AbilityKit'; 257 258// 获取应用文件路径 259let context = getContext(this) as common.UIAbilityContext; 260let filesDir = context.filesDir; 261 262function hashFileWithStream() { 263 const filePath = `${filesDir}/test.txt`; 264 // 创建文件可读流 265 const rs = fs.createReadStream(filePath); 266 // 创建哈希流 267 const hs = hash.createHash('sha256'); 268 rs.on('data', (emitData) => { 269 const data = emitData?.data; 270 hs.update(new Uint8Array(data?.split('').map((x: string) => x.charCodeAt(0))).buffer); 271 }); 272 rs.on('close', async () => { 273 const hashResult = hs.digest(); 274 const fileHash = await hash.hash(filePath, 'sha256'); 275 console.info(`hashResult: ${hashResult}, fileHash: ${fileHash}`); 276 }); 277} 278 279```