《HarmonyOS技术精讲-Core File Kit》第11篇:文件元数据读取——大小、时间与类型

📅 2026/7/5 8:07:04 👁️ 阅读次数 📝 编程学习
《HarmonyOS技术精讲-Core File Kit》第11篇:文件元数据读取——大小、时间与类型

《HarmonyOS技术精讲-Core File Kit》第11篇:文件元数据读取——大小、时间与类型

获取文件元数据,比你想的要复杂

HarmonyOS NEXT 开发里,fileManager.stat这个 API 经常被误用。很多人拿到文件路径后,第一反应是打开文件流去读文件头或者逐字节统计大小,或者直接用new Date()去猜文件时间——这些做法在性能上都不合适。

实际开发中,文件元数据的读取需求很常见:显示文件详情、排序文件列表、判断文件类型做分类展示。Core File Kit 提供的stat方法可以直接获取这些信息,不需要打开文件流,也不需要在应用层做额外计算。

这个功能本身不复杂,但真正容易出问题的点是返回的 FileStat 对象各个属性的含义类型判断的边界情况

解决什么问题

stat是一个系统级调用,直接从文件系统 inode 节点读取元数据。相比打开文件流再读取:

方式性能获取信息量适用场景
fileManager.stat高,不打开文件大小、时间、类型、权限等文件列表、详情展示
打开文件流读取低,需要 I/O 操作主要获取内容文件内容处理
fs.access仅判断存在性检查文件是否存在

推荐直接使用stat,这也是 Core File Kit 提供这个能力的初衷。

环境说明

DevEco Studio 版本:DevEco Studio 6.1.0 及以上 HarmonyOS SDK 版本:HarmonyOS 6.1.0(23) 及以上 目标设备:手机 / 平板

核心实现:读取文件大小、时间与类型

下面这段代码实现了一个完整的文件信息读取功能。它接收一个文件路径,返回文件大小、最后修改时间、创建时间,以及根据扩展名判断的文件类型。

import{fileManager}from'@kit.CoreFileKit';import{common}from'@kit.AbilityKit';interfaceFileMetaData{size:number;// 文件大小,单位字节sizeDesc:string;// 可读的大小描述,如 "1.2 MB"mtime:Date;// 最后修改时间ctime:Date;// 创建时间fileType:string;// 文件类型描述,如 "图片"、"文档"extension:string;// 文件扩展名mimeType:string;// MIME 类型}classFileMetaReader{privatecontext:common.Context;constructor(context:common.Context){this.context=context;}asyncgetFileMeta(filePath:string):Promise<FileMetaData>{// 1. 调用 stat 获取文件元数据conststat=awaitfileManager.stat(filePath);// 2. 计算可读的文件大小constsizeDesc=this.formatSize(stat.size);// 3. 获取扩展名并判断文件类型constextension=this.getExtension(filePath);constmimeType=this.getMimeType(extension);constfileType=this.getFileTypeDesc(extension);return{size:stat.size,sizeDesc,mtime:newDate(stat.mtime),ctime:newDate(stat.ctime),fileType,extension,mimeType};}privateformatSize(bytes:number):string{if(bytes===0)return'0 B';constunits=['B','KB','MB','GB','TB'];constk=1024;consti=Math.floor(Math.log(bytes)/Math.log(k));returnparseFloat((bytes/Math.pow(k,i)).toFixed(2))+' '+units[i];}privategetExtension(path:string):string{constdotIndex=path.lastIndexOf('.');if(dotIndex===-1)return'';returnpath.substring(dotIndex+1).toLowerCase();}privategetMimeType(ext:string):string{constmimeMap:Record<string,string>={'jpg':'image/jpeg','jpeg':'image/jpeg','png':'image/png','gif':'image/gif','webp':'image/webp','mp4':'video/mp4','mp3':'audio/mpeg','pdf':'application/pdf','txt':'text/plain','json':'application/json','html':'text/html','js':'application/javascript','css':'text/css'};returnmimeMap[ext]||'application/octet-stream';}privategetFileTypeDesc(ext:string):string{consttypeMap:Record<string,string>={'图片':['jpg','jpeg','png','gif','webp','bmp','svg'],'视频':['mp4','avi','mov','mkv','flv','wmv'],'音频':['mp3','wav','flac','aac','ogg'],'文档':['pdf','doc','docx','xls','xlsx','ppt','pptx','txt'],'压缩包':['zip','rar','7z','tar','gz'],'代码':['js','ts','html','css','json','xml','py','java']};for(const[type,exts]ofObject.entries(typeMap)){if(exts.includes(ext)){returntype;}}return'未知';}}

关键点说明:

  • fileManager.stat返回的FileStat对象中,size是字节数,mtimectime是时间戳(毫秒),需要自行转为Date对象。
  • 文件类型判断这里用了扩展名映射,实际项目中如果要求更高,可以读取文件头字节来判断,但性能会差一些。
  • formatSize方法处理了可读大小格式化,注意单位用 1024 而不是 1000。

使用示例

@Entry@Componentstruct FileInfoDemo{@StatefileInfo:string='';build(){Column(){Button('读取文件信息').onClick(async()=>{constreader=newFileMetaReader(getContext(this));constsandboxPath=getContext(this).getApplicationContext().cacheDir;consttestFilePath=sandboxPath+'/test.txt';// 先创建一个测试文件constfile=awaitfileManager.open(testFilePath,fileManager.OpenMode.CREATE);awaitfileManager.write(file.fd,'Hello HarmonyOS');awaitfileManager.close(file);constmeta=awaitreader.getFileMeta(testFilePath);this.fileInfo=`大小:${meta.sizeDesc}\n修改时间:${meta.mtime.toLocaleString()}\n类型:${meta.fileType}`;});Text(this.fileInfo).padding(16).fontSize(14);}.padding(24);}}

常见问题

问题 1:stat返回的时间是 UTC 还是本地时间?

现象:获取到的mtimectime打印出来发现和系统时间对不上。

原因FileStat.mtimeFileStat.ctime返回的是从 1970-01-01 00:00:00 UTC 到事件发生时的毫秒数,相当于 Unix 时间戳。这个值本身是 UTC 基准,不包含时区信息。

解决方案:使用new Date(stat.mtime)创建 Date 对象时,Date 会自动转为本地时区。如果要显示给用户,直接用toLocaleString()即可,不需要手动加减时区偏移。

问题 2:扩展名不存在的文件怎么处理类型判断?

现象:部分系统文件或临时文件没有扩展名,getExtension返回空字符串,导致 MIME 类型回退为application/octet-stream

原因:扩展名判断是轻量方案,但无法覆盖无扩展名或自定义扩展名的文件。

解决方案:对于无扩展名的文件,可以结合FileStat.mode判断文件类型(如目录、普通文件、符号链接等),或者读取文件的前 4-8 个字节做魔数判断。mode的值是文件权限和类型的组合,可以通过位掩码解析。

最佳实践

  1. 不要在主线程频繁调用stat:虽然stat是轻量级调用,但在文件列表场景下,如果一次性调用几百次,仍然会造成 UI 卡顿。建议在子线程或使用TaskPool批量处理。

  2. mtimectime不要混用mtime是最后修改时间,ctime是状态更改时间(包括权限、重命名等)。做文件排序时一般用mtime,做备份同步时建议用ctime

  3. MIME 类型判断推荐用扩展名+白名单:服务端上传文件时经常需要校验 MIME 类型。客户端判断可以用扩展名映射,但务必配合白名单机制,防止伪造扩展名绕过限制。如果安全性要求高,需要读取文件头验证。

FAQ

Q:stat返回的size是实际占用磁盘空间吗?
A:不是。size是文件逻辑大小,即文件内容的实际字节数。磁盘占用空间通常更大(因为文件系统块对齐),这个值需要通过stat.blocks计算。

Q:为什么同个文件多次调用stat返回的mtime不一样?
A:mtime的单位是毫秒,但文件系统的精度可能只有秒级或纳秒级。如果文件没有被修改,mtime应该是相同的。如果观察到差异,检查代码中是否有无意中修改文件属性的操作。

Q:应用沙箱外的文件能用stat吗?
A:受限于 HarmonyOS 的沙箱机制,stat只能访问应用自身沙箱内的文件,以及通过fileManager.open或文件选择器授权后的文件。跨应用访问需要对应权限声明。

示例代码地址:项目地址