HarmonyOS:应用文件访问(ArkTS)

一、概述

应用需要对应用文件目录下的应用文件进行查看、创建、读写、删除、移动、复制、获取属性等访问操作,下文介绍具体方法。

二、接口说明

开发者通过基础文件操作接口(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%')
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值