HarmonyOS原生应用占用空间管理

南风春和
发布于 2024-8-1 16:39
浏览
0收藏

使用接口

接口

接口能力

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);

HarmonyOS原生应用占用空间管理 -鸿蒙开发者社区

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。

分类
已于2024-8-1 16:39:29修改
收藏
回复
举报
回复
    相关推荐