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```