HarmonyOS NEXT 实战:Storage 存储空间分析的设计与实现
HarmonyOS NEXT 实战:Storage 存储空间分析的设计与实现
前言
存储空间分析是文件管理应用的高级能力,帮助用户直观了解设备存储占用情况并释放空间。HarmonyExplorer 基于 HarmonyOS NEXT 的 Storage 统计能力,实现了总容量统计、分类大小统计、大文件扫描与缓存清理的完整方案。存储分析的核心难点在于扫描性能与数据可视化的平衡,既要快速统计大量文件,又要以直观的图表呈现。本文将完整拆解 StorageUtil 封装、StorageChart 组件、扫描与清理的落地实践。
提示:本文代码基于 HarmonyOS NEXT(API 12+)ArkTS 严格模式编写,禁用 any 类型与隐式断言,所有存储数据均使用命名接口显式声明。
一、存储空间分析功能设计
1.1 需求分析
通过对用户使用场景的调研,HarmonyExplorer 的存储分析模块需要覆盖以下能力点:
- 获取设备总容量、已用空间与可用空间
- 按文件类型分类统计占用大小(图片、视频、音频、文档等)
- 以图表与进度条直观展示存储占比
- 扫描大文件并支持快速定位清理
- 一键清理应用缓存,释放存储空间
1.2 架构分层
存储分析功能遵循 UI → ViewModel → Repository → Service → KitManager → Kits 的分层架构,职责清晰。
- UI 层:StoragePage 展示图表与详情列表
- Repository 层:StorageRepository 聚合统计数据
- KitManager 层:封装 Storage Statistics Kit
- Utils 层:StorageUtil 提供纯函数式工具方法
提示:存储统计涉及大量文件遍历,建议在子线程或异步任务中执行,避免阻塞 UI 主线程导致卡顿。
二、Storage Kit 获取存储信息
2.1 API 概览
HarmonyOS NEXT 通过 storageStatistics 模块提供存储统计能力,核心 API 包括获取总容量、剩余空间与目录统计。
| API 名称 | 作用 | 返回数据 |
|---|---|---|
| getTotalSizeOfVolume | 获取总容量 | 字节数 |
| getFreeSizeOfVolume | 获取可用空间 | 字节数 |
| getCurrentBundleStats | 获取应用占用 | BundleStats |
| getUserStorageStats | 用户存储统计 | 分类大小 |
2.2 获取存储信息
通过 storageStatistics 获取设备存储基础信息,封装为统一的 StorageInfo 数据模型。
// model/StorageModel.ets
export interface StorageInfo {
totalSize: number
freeSize: number
usedSize: number
usedPercent: number
}
export interface CategorySize {
category: string
size: number
percent: number
}
// manager/StorageKitManager.ets
import { storageStatistics } from '@kit.CoreFileKit'
import { StorageInfo } from '../model/StorageModel'
export class StorageKitManager {
static async getStorageInfo(): Promise<StorageInfo> {
const total: number = await storageStatistics.getTotalSizeOfVolume()
const free: number = await storageStatistics.getFreeSizeOfVolume()
const used: number = total - free
const percent: number = total > 0 ? Math.floor((used / total) * 100) : 0
return { totalSize: total, freeSize: free, usedSize: used, usedPercent: percent }
}
}
三、存储空间统计
3.1 总量统计
总量统计是存储分析的基础,HarmonyExplorer 在 StoragePage 进入时即触发统计,结果绑定到 ViewModel。
3.2 统计实现
StorageViewModel 持有存储状态,通过 StorageUtil 获取数据并更新 UI 状态。
// viewmodel/StorageViewModel.ets
import { StorageUtil } from '../utils/StorageUtil'
import { StorageInfo, CategorySize } from '../model/StorageModel'
@Observed
export class StorageViewModel {
storageInfo: StorageInfo = { totalSize: 0, freeSize: 0, usedSize: 0, usedPercent: 0 }
categoryList: CategorySize[] = []
isLoading: boolean = false
async loadStorageData(): Promise<void> {
this.isLoading = true
this.storageInfo = await StorageUtil.getStorageInfo()
this.categoryList = await StorageUtil.getCategorySizes()
this.isLoading = false
}
}
四、文件分类大小统计
4.1 分类策略
文件按业务类型分为五大类,每类对应不同的扩展名集合,统计时遍历文件目录累计大小。
| 分类 | 包含类型 | 统计来源 |
|---|---|---|
| 图片 | png/jpg/gif/webp | 图片目录 |
| 视频 | mp4/mov/avi | 视频目录 |
| 音频 | mp3/aac/flac | 音频目录 |
| 文档 | pdf/doc/txt | 文档目录 |
| 其他 | 其余类型 | 沙箱目录 |
4.2 统计实现
StorageUtil 遍历各分类目录,累计文件大小,返回分类统计列表。
// utils/StorageUtil.ets
import { fileIo } from '@kit.CoreFileKit'
import { StorageKitManager } from '../manager/StorageKitManager'
import { StorageInfo, CategorySize } from '../model/StorageModel'
export class StorageUtil {
static readonly CATEGORY_MAP: Record<string, string[]> = {
'图片': ['png', 'jpg', 'gif', 'webp'],
'视频': ['mp4', 'mov', 'avi'],
'音频': ['mp3', 'aac', 'flac'],
'文档': ['pdf', 'doc', 'txt']
}
static async getCategorySizes(): Promise<CategorySize[]> {
const storageInfo: StorageInfo = await StorageKitManager.getStorageInfo()
const totalUsed: number = storageInfo.usedSize
const result: CategorySize[] = []
const categories: string[] = Object.keys(StorageUtil.CATEGORY_MAP)
for (const category of categories) {
const size: number = await StorageUtil.calcCategorySize(category)
const percent: number = totalUsed > 0 ? Math.floor((size / totalUsed) * 100) : 0
result.push({ category: category, size: size, percent: percent })
}
return result
}
static async calcCategorySize(category: string): Promise<number> {
const exts: string[] = StorageUtil.CATEGORY_MAP[category]
if (exts === undefined) {
return 0
}
let total: number = 0
for (const ext of exts) {
total = total + await StorageUtil.scanByExt(ext)
}
return total
}
}
五、StorageChart 图表组件
5.1 组件实现
StorageChart 以环形图展示各分类占用比例,直观的可视化是存储分析的核心价值,让用户一眼看清空间分布。
// components/StorageChart.ets
@Component
export struct StorageChart {
@Prop categories: CategorySize[]
private colors: string[] = ['#007DFF', '#FF6B6B', '#4ECDC4', '#FFE66D', '#95A5A6']
build() {
Column({ space: 12 }) {
Text('存储占用分布').fontSize(16).fontWeight(FontWeight.Medium)
Stack() {
ForEach(this.categories, (item: CategorySize, index: number) => {
Progress({ value: item.percent, total: 100, type: ProgressType.Ring })
.width(120).height(120)
.color(this.colors[index % this.colors.length])
}, (item: CategorySize) => item.category)
}
ForEach(this.categories, (item: CategorySize, index: number) => {
Row({ space: 8 }) {
Circle({ width: 10, height: 10 }).fill(this.colors[index % this.colors.length])
Text(item.category + ' ' + item.percent.toString() + '%').fontSize(12)
}
}, (item: CategorySize) => item.category)
}.width('100%').padding(16)
}
}
六、ProgressBar 进度条展示
6.1 进度展示
除环形图外,HarmonyExplorer 还使用线性 ProgressBar 展示总存储使用率,配合数字提示形成双重反馈。
// components/StorageOverview.ets
@Component
export struct StorageOverview {
@ObjectLink viewModel: StorageViewModel
build() {
Column({ space: 12 }) {
Row({ space: 8 }) {
Text('已用 ' + StorageUtil.formatSize(this.viewModel.storageInfo.usedSize))
.fontSize(14).layoutWeight(1)
Text('总共 ' + StorageUtil.formatSize(this.viewModel.storageInfo.totalSize))
.fontSize(14).fontColor('#999999')
}
Progress({ value: this.viewModel.storageInfo.usedPercent, total: 100, type: ProgressType.Linear })
.width('100%').color('#007DFF')
Text('使用率 ' + this.viewModel.storageInfo.usedPercent.toString() + '%')
.fontSize(12).fontColor('#666666')
}.width('100%').padding(16)
}
}
StorageUtil 的格式化方法将字节数转换为易读的单位:
// utils/StorageUtil.ets
export class StorageUtil {
static formatSize(bytes: number): string {
if (bytes < 1024) {
return bytes.toString() + ' B'
}
if (bytes < 1024 * 1024) {
return (bytes / 1024).toFixed(1) + ' KB'
}
if (bytes < 1024 * 1024 * 1024) {
return (bytes / (1024 * 1024)).toFixed(1) + ' MB'
}
return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB'
}
}
七、存储详情列表
7.1 列表实现
存储详情列表展示各分类的具体占用,每项包含分类名、大小与占比,点击可进入分类文件列表。
// components/StorageDetailList.ets
@Component
export struct StorageDetailList {
@Prop list: CategorySize[]
onItemClick: (category: string) => void = () => {}
build() {
List({ space: 8 }) {
ForEach(this.list, (item: CategorySize) => {
ListItem() {
Row({ space: 12 }) {
Text(item.category).fontSize(14).layoutWeight(1)
Text(StorageUtil.formatSize(item.size)).fontSize(14).fontColor('#666666')
Text(item.percent.toString() + '%').fontSize(12).fontColor('#999999')
}.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(12)
}.onClick(() => this.onItemClick(item.category))
}, (item: CategorySize) => item.category)
}.width('100%').layoutWeight(1)
}
}
八、大文件扫描
8.1 扫描实现
大文件扫描按文件大小阈值筛选,帮助用户快速定位占用空间最大的文件。大文件扫描是释放存储空间最直接有效的手段。
// utils/StorageUtil.ets
import { FileInfo } from '../model/FileInfo'
export class StorageUtil {
static async scanLargeFiles(dirPath: string, threshold: number): Promise<FileInfo[]> {
const result: FileInfo[] = []
if (!fileIo.accessSync(dirPath)) {
return result
}
const names: string[] = fileIo.listFileSync(dirPath)
for (const name of names) {
const fullPath: string = dirPath + '/' + name
const stat: fileIo.Stat = fileIo.statSync(fullPath)
if (stat.isDirectory()) {
const sub: FileInfo[] = await StorageUtil.scanLargeFiles(fullPath, threshold)
for (const f of sub) {
result.push(f)
}
} else if (stat.size >= threshold) {
result.push({
id: fullPath,
name: name,
path: fullPath,
size: stat.size,
type: StorageUtil.getExt(name),
modifyTime: stat.mtime,
createTime: stat.mtime,
favorite: false
})
}
}
return result
}
static getExt(name: string): string {
const dotIndex: number = name.lastIndexOf('.')
return dotIndex > 0 ? name.substring(dotIndex + 1) : ''
}
}
九、缓存清理功能
9.1 清理实现
缓存清理针对应用临时目录与缓存目录,一键清空非必要文件,释放存储空间。清理前先统计可清理大小,确认后执行。完整清理流程如下:
- 调用 calcCacheSize 遍历缓存目录并统计可清理的文件总大小
- 弹出 ConfirmDialog 展示可释放空间并等待用户确认清理操作
- 用户确认后调用 cleanCache 执行清理并刷新存储统计数据
// utils/StorageUtil.ets
export class StorageUtil {
static async cleanCache(cacheDir: string): Promise<number> {
let cleaned: number = 0
if (!fileIo.accessSync(cacheDir)) {
return 0
}
const names: string[] = fileIo.listFileSync(cacheDir)
for (const name of names) {
const fullPath: string = cacheDir + '/' + name
const stat: fileIo.Stat = fileIo.statSync(fullPath)
if (stat.isDirectory()) {
fileIo.rmdirSync(fullPath)
} else {
cleaned = cleaned + stat.size
fileIo.unlinkSync(fullPath)
}
}
return cleaned
}
static async calcCacheSize(cacheDir: string): Promise<number> {
let total: number = 0
if (!fileIo.accessSync(cacheDir)) {
return 0
}
const names: string[] = fileIo.listFileSync(cacheDir)
for (const name of names) {
const stat: fileIo.Stat = fileIo.statSync(cacheDir + '/' + name)
total = total + stat.size
}
return total
}
}
提示:缓存清理要避免误删用户数据,建议只清理明确的临时目录,并在清理前弹出 ConfirmDialog 二次确认。
十、StorageUtil 工具类封装
10.1 完整封装
StorageUtil 整合存储信息获取、分类统计、大文件扫描与缓存清理,对外提供统一入口。
// utils/StorageUtil.ets
import { fileIo } from '@kit.CoreFileKit'
import { StorageKitManager } from '../manager/StorageKitManager'
import { StorageInfo, CategorySize, FileInfo } from '../model/StorageModel'
export class StorageUtil {
static async getStorageInfo(): Promise<StorageInfo> {
return await StorageKitManager.getStorageInfo()
}
static async getLargeFiles(dirPath: string): Promise<FileInfo[]> {
const threshold: number = 100 * 1024 * 1024
return await StorageUtil.scanLargeFiles(dirPath, threshold)
}
}
各存储操作的能力与阈值如下表,便于运维与扩展时统一调整:
| 操作 | 阈值/范围 | 触发方式 |
|---|---|---|
| 总量统计 | 全设备 | 进入页面自动 |
| 分类统计 | 五大类 | 进入页面自动 |
| 大文件扫描 | ≥100MB | 用户手动触发 |
| 缓存清理 | 缓存目录 | 用户确认后执行 |

总结
本文完整实现了 HarmonyExplorer 的存储空间分析模块,涵盖 Storage Kit 调用、总量与分类统计、StorageChart 图表、大文件扫描与缓存清理。分层架构让存储逻辑清晰可测试,图表与进度条的双重可视化大幅提升了数据可读性。大文件扫描与缓存清理也为用户释放空间提供了实用工具。希望这套方案能帮助你在鸿蒙项目中落地存储分析能力。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源
更多推荐


所有评论(0)