HarmonyOS原生应用占用空间管理

使用接口

接口

接口能力

storageStatistics.getCurrentBundleStats

获取当前应用的存储空间大小 (包含缓存文件,安装文件等)

statvfs.getFreeSize

查询设备剩余可用空间

statvfs.getTotalSize

获取设备总空间 (排除系统占用空间)

场景一:计算应用缓存大小,并进行清理

1.可以通过storageStatistics.getCurrentBundleStats接口获取缓存大小。

应用的缓存文件因为级别和加密类型不同,会保存在以下目录中。例如:app级别,加密类型为el1的缓存会保存到/data/storage/el1/base/cache目录下。

/data/storage/el1/base/cache

/data/storage/el1/base/haps/entry/cache

/data/storage/el2/base/cache

/data/storage/el2/base/haps/entry/cache

这四个目录可以通过以下接口获取,其中el1与el2因为分区不同,需要切换加密等级才能获取到:

let moduleContext: common.Context;
moduleContext = this.context.createModuleContext('entry');
console.log('moduleContext + el2: '+moduleContext.cacheDir);
console.log('UIAbilityContext + el2: '+this.context.cacheDir);
moduleContext.area = contextConstant.AreaMode.EL1;
console.log('moduleContext + el1: '+moduleContext.cacheDir);
this.context.area = contextConstant.AreaMode.EL1;
console.log('UIAbilityContext + el1: '+this.context.cacheDir);

storageStatistics.getCurrentBundleStats((err: BusinessError, bundleStats: storageStatistics.BundleStats) => {
  if (err) {
    console.error(`Invoke getCurrentBundleStats failed, code is ${err.code}, message is ${err.message}`);
  } else {
    console.info(`Invoke getCurrentBundleStats succeeded, cacheSize is ${bundleStats.cacheSize}`);
  }
});

2.清除缓存,缓存会保存在上述四个文件夹中,可以递归删除

webview缓存(/data/storage/el2/base/cache/web/Cache)包含于在上述文件夹中,也可以调用WebviewController.removeCache单独清理web缓存。

//通过Context.cacheDir获取所有缓存路径
let cacheDir : string[] =[];
let moduleContext: common.Context;
moduleContext = getContext().createModuleContext('entry');
cacheDir.push(moduleContext.cacheDir);
cacheDir.push(getContext().cacheDir);
moduleContext.area = contextConstant.AreaMode.EL1;
getContext().area = contextConstant.AreaMode.EL1;
cacheDir.push(moduleContext.cacheDir);
cacheDir.push(getContext().cacheDir);
 
for (let i = 0; i < cacheDir.length; i++) {
  let cache = cacheDir[i];
  let exist = fs.accessSync(cache);
  if (exist) {
    try {
      fs.rmdirSync(cache);
    } catch (err) {
      console.log(err);
    }
  }
}

场景二:对文件夹中图片进行展示,并选择性清理(这里只对图片做了筛选,同理,还可以加上视频等)

1.制定图片过滤规则,过滤出对应格式(例如:jpg,png)的图片。

let listFileOption: ListFileOptions = {
  recursion: true,//列出子目录
  listNum: 0,// 列出文件名数量。可选,当设置0时,列出所有文件,默认为0。
  filter: {
    suffix: ['.jpg','.png'],//文件后缀
  }
}

2.将文件夹路径与listFile获取到的路径拼接起来,得到图片完整沙箱路径。

let path: string[] = [];
let fileNames = fs.listFileSync(dirPath, listFileOption)
for (let i = 0; i < fileNames.length; i++) {
  let filePath = dirPath + fileNames[i]
  console.log(filePath);
  path[i] = filePath
}

3.根据沙箱路径将图片转换为pixelmap。

function Picture(mediaPath: string): image.PixelMap {
  let options: image.SourceOptions = {
    sourceSize: {
      width: 100,
      height: 100
    },
    sourceDensity: 0
  }
  let decodingOptions: image.DecodingOptions = {}
 
  //获取文件
  let file = fs.openSync(mediaPath, fs.OpenMode.READ_ONLY);
  fs.copyFileSync(file.fd, getContext().filesDir + '/' + file.name + '1');
  //通过传入文件描述符来创建图片源实例
  let imageSource: image.ImageSource = image.createImageSource(file.fd, options);
  let imageInfo: image.ImageInfo = imageSource.getImageInfoSync();
  //这里只取图片中间一部分
  if (imageInfo.size.height > imageInfo.size.width) {
    let a = (imageInfo.size.height - imageInfo.size.width) / 2
    decodingOptions.desiredRegion = {
      size: {
        width: imageInfo.size.width,
        height: imageInfo.size.width
      },
      x: 0,
      y: a
    }
  } else {
    let a = (imageInfo.size.width - imageInfo.size.height) / 2
    decodingOptions.desiredRegion = {
      size: {
        width: imageInfo.size.height,
        height: imageInfo.size.height
      },
      x: a,
      y: 0
    }
  }
  let pixelMap: image.PixelMap = imageSource.createPixelMapSync(decodingOptions);
  return pixelMap
}

4.将图片展示出来,并删除指定图片,释放空间。

(1)单张图片删除。

定义长按手势,可以通过长按图片拉起菜单,选择删除指定图片。

@Builder
MenuBuilder(path: string) {
  Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
    Text('删除')
      .fontSize(20)
      .width(100)
      .height(50)
      .textAlign(TextAlign.Center)
      .onClick(() => {
        try {
          fs.unlinkSync(path);
          let i = this.mediaPath.indexOf(path);
          this.mediaPath.splice(i, 1);
        } catch (err) {
          console.log(err);
        }
      })
  }.width(100)
}

使用Grid将图片排列展示,并通过bindContextMenu给image组件绑定菜单,长按拉起删除选择框,并实时展示删除后剩余图片。

Grid() {
  ForEach(this.mediaPath, (item: string) => {
    GridItem() {
      Image(picture(item))
        .bindContextMenu(this.MenuBuilder(item), ResponseType.LongPress)
        .objectFit(ImageFit.Fill)
        .autoResize(false)
    }
  })
}

(2)一键删除所有图片。

点击一键删除后,会拉起一个弹窗确认,这里弹窗使用的promptAction.openCustomDialog。

Button('一键删除')
  .onClick(() => {
    let uiContext = this.getUIContext();
    let promptAction = uiContext.getPromptAction();
    let contentNode = new ComponentContent(uiContext, wrapBuilder(BuildText), this.mediaPath);
    this.content = contentNode;
    try {
      promptAction.openCustomDialog(contentNode
        , {
          showInSubWindow: false,
          offset: { dx: 5, dy: 5 },
          onWillDisappear: () => {
            this.mediaPath = FindAll(getContext().filesDir)
          }
        }
      )
    } catch (error) {
      let message = (error as BusinessError).message;
      let code = (error as BusinessError).code;
      console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
    }
  })

当前弹窗组件返回的reason中,暂不支持按钮关闭,故需自行实现。

自定义一个变量,使用@Watch去监测,一旦变量发生改变则触发回调关闭弹窗。

//自定义回调,当dialogState发生变化,关闭弹窗
@LocalStorageLink('CustomDialogInfo') @Watch('onChange') dialogState: number = 0;
......
// 自定义回调,关闭弹窗
onChange() {
  try {
    this.uiContext.getPromptAction().closeCustomDialog(this.content);
  } catch (error) {
    let message = (error as BusinessError).message;
    let code = (error as BusinessError).code;
    console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
  }
}
......
@Builder
function BuildText(params: string[]) {
  Column({ space: 10 }) {
    Text('请确认是否删除')
      .fontSize(30)
      .textAlign(TextAlign.Center)
      .fontWeight(FontWeight.Bold)
      .padding(8)
    Text('注意:一旦确认删除后,文件将不可恢复')
      .fontSize(20)
      .textAlign(TextAlign.Center)
      .padding({ left: 14, right: 14 })
    Flex({ justifyContent: FlexAlign.SpaceAround }) {
      Button('取消').onClick(() => {
        //改变dialogState,触发onChange回调,关闭弹窗
        let dialogInfo: SubscribedAbstractProperty<number> = storage.link('CustomDialogInfo');
        let dialogState = dialogInfo.get();
        console.log('取消', dialogInfo.get());
        dialogInfo.set(++dialogState);
      }).backgroundColor(0xffffff).fontColor(Color.Black)
      Button('确认')
        .onClick(() => {
          if (params!) {
            for (let i = 0; i < params.length; i++) {
              const filePath = params[i];
              try {
                fs.unlinkSync(filePath);
              } catch (err) {
                console.log(err);
              }
            }
          }
          //改变dialogState,触发onChange回调,关闭弹窗
          let dialogInfo: SubscribedAbstractProperty<number> = storage.link('CustomDialogInfo');
          let dialogState = dialogInfo.get();
          dialogInfo.set(++dialogState);
        }).backgroundColor(0xffffff).fontColor(Color.Black)
    }.margin({ bottom: 10 })
  }.backgroundColor('#FFF0F0F0')
}

常见问题

Q:为什么通过查询到设备总空间和设置中显示的不同?

A:statvfs.getTotalSizeSync获取到的是data分区大小,三方应用仅能查询到自己能够使用的空间大小,暂无查询手机总内存大小的接口。

Q:能够查询外卡空间大小吗?

A:暂不支持。

Q:为什么有些中文文件名的文件搜不到?

A:当前HarmonyOS文件系统仅支持utf-8格式,gbk格式的中文名的文件通过其它方式存入文件系统中,名称会乱码。

Q:为什么删除文件夹会报错:Directory not empty。

A:fs.rmdir是递归删除,会删除该文件夹以及子文件夹中的所有文件,但是当其子目录中有高权限的文件时,调用的接口无法删除此文件,导致无法删除此文件夹,报错:Directory not empty。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值