从追加写入到 fd 底层操作:FileUtil 文件读写完整指南

从追加写入到 fd 底层操作:FileUtil 文件读写完整指南 文章目录前言writeEasy最简单的写文件方式readText读取文件内容async 还是 sync怎么选fd 方式底层文件操作写操作的完整错误处理写在最后前言近期发现一款很有意思的HarmonyOS 三方库, 地址 pura/harmony-utils(V1.4.0) , 作者是桃花镇童长老, 我这里也是直接通过该作者公布的源码进行案例编写进行,写了到目前写了一部分demo ,感觉确实很有帮助,这里呢也是开始写一个系列的演示demo 供大家参考。如有帮助可以在OpenHarmony中进行下载安装进行使用哦案例demo导航展示↓↓↓↓↓↓接下来言归正传 ↓↓↓↓路径搞定了下一步就是最核心的读写操作。FileUtil 提供了两套读写接口高层封装版writeEasy、readText和底层 fd 版write、readSync配合文件描述符。初学者用高层版就够了有特殊需求再研究 fd。writeEasy最简单的写文件方式writeEasy是封装最好的写文件方法支持追加和覆盖两种模式追加写入在文件末尾追加内容不会清除已有内容// 追加写入第三个参数 true 追加constpFileUtil.getFilesDirPath(undefined,demo_file.txt);constnawaitFileUtil.writeEasy(p,this.inputText\n,true);this.addLog(writeEasy 写入${n}字节);覆盖写入清空文件内容重新写入// 覆盖写入第三个参数 false 覆盖constpFileUtil.getFilesDirPath(undefined,demo_file.txt);constnawaitFileUtil.writeEasy(p,[覆盖] this.inputText,false);this.addLog(覆盖写入${n}字节);三个参数文件完整路径要写入的字符串内容是否追加true 追加false 覆盖返回值是实际写入的字节数注意是字节不是字符数中文字符占 3 字节。重要文件不存在时writeEasy会自动创建不需要提前touch或createFile。readText读取文件内容// 异步读取constpFileUtil.getFilesDirPath(undefined,demo_file.txt);constcontentawaitFileUtil.readText(p);this.addLog(readText: content.substring(0,80));readText把文件内容作为字符串返回编码默认是 UTF-8。.substring(0, 80)是演示里的截断处理避免日志输出太长。同步版本// 同步读取constcontentFileUtil.readTextSync(p);this.addLog(readTextSync: content.substring(0,80));readTextSync会阻塞当前线程适合文件很小几 KB 以内且在非 UI 线程调用的场景。大文件或在 UI 线程里一定用 async 版本。async 还是 sync怎么选这是初学者最容易困惑的地方。简单原则场景推荐UI 线程Component中的回调async 版本加awaitWorker 线程、TaskPoolsync 或 async 都可以文件很小10KB且确认在非 UI 线程sync 性能稍好文件较大或不确定async 版本演示代码里的Btn回调是async () {}形式所以可以在里面awaitthis.Btn(readText (async),#388E3C,async(){try{constpFileUtil.getFilesDirPath(undefined,demo_file.txt);constcontentawaitFileUtil.readText(p);this.addLog(readText: content.substring(0,80));}catch(e){this.addLog(readText 失败: e,false);}})注意每个文件操作都包了try-catch文件不存在、权限不足等情况都会抛出异常不 catch 会导致应用崩溃。fd 方式底层文件操作fdFile Descriptor文件描述符是操作系统层面的文件操作方式更灵活但也更复杂。完整流程是打开文件 → 拿到 fd → 读/写 → 关闭文件// fd 方式读写constpFileUtil.getFilesDirPath(undefined,fd_demo.txt);// 1. 打开文件READ_WRITE CREATE TRUNC 读写权限不存在就创建清空内容constfileFileUtil.openSync(p,fs.OpenMode.READ_WRITE|fs.OpenMode.CREATE|fs.OpenMode.TRUNC);// 2. 写入异步constwLenawaitFileUtil.write(file.fd,fd方式写入的内容);this.addLog(write(fd) 写入${wLen}字节);// 3. 读取同步从偏移 0 开始constbufnewArrayBuffer(128);constrLenFileUtil.readSync(file.fd,buf,{offset:0});consttextString.fromCharCode(...newUint8Array(buf,0,rLen));this.addLog(readSync(fd):${text});// 4. 关闭必须FileUtil.closeSync(file);fs.OpenMode是按位或的标志位READ_WRITE可读可写CREATE文件不存在时创建TRUNC打开时截断文件清空内容读取时用ArrayBufferUint8Array是因为底层返回的是二进制数据String.fromCharCode把字节转成字符串。这种方式适合处理二进制数据图片、音频等。最重要的一点closeSync必须调用不关闭文件描述符会导致 fd 泄漏系统资源耗尽。推荐用try-finally确保关闭constfileFileUtil.openSync(p,fs.OpenMode.READ_WRITE|fs.OpenMode.CREATE|fs.OpenMode.TRUNC);try{constwLenawaitFileUtil.write(file.fd,fd方式写入的内容);// ... 其他操作}finally{FileUtil.closeSync(file);// 不管成功失败都关闭}写操作的完整错误处理演示代码里每个写操作都有try-catch结构这是正确做法this.Btn(writeEasy (追加写入),#4CAF50,async(){try{constpFileUtil.getFilesDirPath(undefined,demo_file.txt);constnawaitFileUtil.writeEasy(p,this.inputText\n,true);this.addLog(writeEasy 写入${n}字节);}catch(e){this.addLog(writeEasy 失败: e,false);}})常见的失败原因路径不存在父目录没有提前创建存储空间不足权限问题写在最后文件读写核心记这几点writeEasy最简单支持追加/覆盖文件不存在自动创建readText读成字符串readTextSync同步版小文件用async 为主UI 线程里一律用 async 版本fd 操作打开用完关闭三步缺一不可用try-finally兜底读写有了下一篇讲文件的移动、复制、删除和属性查询。