HarmonyOS 文件管理服务:让你的应用支持"删除到回收站"

什么是文件管理服务

你有没有遇到过这种情况:不小心删了一个重要文件,然后就找不回来了?电脑上一般有回收站,删了文件还能从回收站里恢复。但手机上呢?很多手机 App 删除文件就是直接删了,没有后悔的机会。

文件管理服务(File Manager Service Kit)就是来解决这个问题的。它是鸿蒙生态下的文件管理服务,提供了"删除到回收站"的能力。简单说,就是让你的 App 删除文件时,不是直接删掉,而是放到回收站里,用户可以随时恢复。

你可能会问:回收站在哪?在鸿蒙系统里,回收站是系统级的功能,用户可以在文件管理器里找到。你只需要调用文件管理服务的接口,把文件"移到回收站"就行,剩下的事情系统会帮你处理。

核心功能

文件管理服务目前提供的核心功能就一个:

  1. 删除到回收站:把文件移到回收站,而不是直接删除

虽然功能看起来简单,但这个能力非常重要。想想看,你的 App 如果有文件管理功能,用户肯定会期望有回收站,就像电脑上的回收站一样。没有回收站的文件管理器,用户用起来总是提心吊胆的,生怕一不小心删错了东西。

环境搭建

硬件要求

  • 设备类型:华为手机、平板、PC/2in1
  • HarmonyOS 系统:HarmonyOS 5.0.5 Release 及以上

软件要求

  • DevEco Studio 版本:DevEco Studio 5.0.5 Release 及以上
  • HarmonyOS SDK 版本:HarmonyOS 5.0.5 Release SDK 及以上

搭建步骤

  1. 安装 DevEco Studio:去华为开发者官网下载安装,跟着提示走就行
  2. 配置开发环境:确保网络环境正常,DevEco Studio 需要联网才能用
  3. 设备调试:使用真机进行调试,模拟器也可以

项目结构

└── entry/src/main
   ├── ets
   │  ├── entryability
   │  │  └── EntryAbility.ets    // 程序入口类
   │  └── pages
   │     └── Index.ets           // 主界面
   └── resources                 // 资源文件目录

项目结构非常简单,核心逻辑都在 Index.ets 里。这个 Codelab 的重点就是教你调用文件管理服务的接口,所以代码量不大。

第一步:导入模块

import { fileManagerService } from '@kit.FileManagerServiceKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { picker } from '@kit.CoreFileKit';
import { PromptAction } from '@kit.ArkUI';

导入的模块有点多,一个个来说:

  • fileManagerService:文件管理服务的核心接口,我们用它来调用"删除到回收站"功能。这是整个文章最重要的模块
  • BusinessError:错误处理用的。华为的 Kit 接口基本都用这个来返回错误信息,包括错误码和错误描述
  • common:获取上下文用的。调用文件管理服务接口时需要传入应用的上下文
  • hilog:日志工具,开发调试的时候看输出用的。出了问题也能查日志,建议别删
  • picker:文件选择器,让用户选择要删除的文件。你想啊,用户得先选中一个文件,才能删除它对吧
  • PromptAction:弹出提示用的,比如"删除成功"、"删除失败"这种提示框

第二步:定义日志标签

const domain = 0x0000;
const tag = 'deleteTag';

定义日志的标签,方便在控制台里找到我们的日志输出。domain 是日志的域,tag 是标签名,你可以随便取,只要方便识别就行。

为什么要定义这个?因为鸿蒙系统里会有很多日志输出,如果你不加标签,根本找不到自己写的日志。就像在一堆信件里找自己的信,有个名字标记就好找多了。

第三步:创建文件选择器

let context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
let promptAction: PromptAction = this.getUIContext().getPromptAction();
let selectOptions: picker.DocumentSelectOptions = new picker.DocumentSelectOptions();
selectOptions.defaultFilePathUri = 'file://docs/storage/Users/currentUser';
let documentPicker = new picker.DocumentViewPicker(context);

创建文件选择器的过程,我一行一行解释:

  1. 获取上下文(context):这是应用的运行环境,很多接口都需要传入这个参数。你可以理解为"当前页面的环境信息"
  2. 获取提示操作(promptAction):用来弹出提示信息,比如 Toast 提示。不获取这个的话,你就没办法弹提示了
  3. 创建选择选项(selectOptions):可以设置默认打开的路径。这里设置为用户文件目录,这样用户打开选择器时直接看到的就是自己的文件
  4. 创建文件选择器(documentPicker):用来让用户选择文件。这个选择器是系统提供的,长得和系统文件管理器一样

defaultFilePathUri 设置了文件选择器打开时默认显示的路径。如果你不设置这个,选择器可能会打开一个默认目录,用户还得自己导航到想要的目录,体验不好。

第四步:选择文件并删除到回收站

documentPicker.select(selectOptions, async (err: BusinessError, documentSelectResult: Array<string>) => {
  if (err) {
    hilog.error(domain, tag, 'DocumentViewPicker.select failed with err: ' + JSON.stringify(err));
    return;
  }
  // 选择成功,处理文件
});

调用 documentPicker.select 打开文件选择器。用户会看到一个系统文件选择界面,可以选择一个或多个文件。

选择完成后,回调函数会被调用:

  • 如果 err 有值,说明出了问题(比如用户取消了选择),打印错误信息然后 return
  • 如果 err 没有值,说明选择成功,documentSelectResult 里就是用户选中的文件 URI 列表
try {
  for (let uri of documentSelectResult) {
    hilog.info(domain, tag, 'deleteToTrash start');
    const ret: string = await fileManagerService.deleteToTrash(uri);
    hilog.info(domain, tag, 'deleteToTrash end, ret:%{public}', ret);
  }
  promptAction.showToast({
    message: `delete success`,
    duration: 5
  });
} catch (error) {
  hilog.error(domain, tag, `error.code: ${error.code}, error.message: ${error.message}`);
  promptAction.showToast({
    message: `delete failed, error.code: ${error.code}, error.message: ${error.message}`,
    duration: 5
  });
}

这是核心逻辑,我拆开来说:

  1. 遍历用户选择的所有文件:用户可能选了多个文件,所以用 for...of 循环处理
  2. 调用 fileManagerService.deleteToTrash(uri):这是整个文章最重要的 API。它不会直接删除文件,而是把文件移到回收站里。用户可以在文件管理器的回收站里找到这些文件,随时恢复
  3. 全部成功后,弹出"删除成功"的提示:让用户知道操作完成了
  4. 如果出错了,弹出错误信息:可能是因为文件不存在、权限不足等原因

deleteToTrash 的返回值是一个字符串,表示操作结果。你可以根据这个返回值判断是否成功。

为什么要用 try...catch?因为删除文件可能会失败。比如文件已经被删了、文件路径不对、没有权限等等。如果不捕获异常,程序可能会崩溃。

完整代码

把上面的步骤合在一起,完整的代码是这样的:

import { fileManagerService } from '@kit.FileManagerServiceKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { picker } from '@kit.CoreFileKit';
import { PromptAction } from '@kit.ArkUI';

const domain = 0x0000;
const tag = 'deleteTag';

@Entry
@Component
struct Index {
  build() {
    Column() {
      Button($r('app.string.select_files_to_delete'))
        .onClick(async () => {
          try {
            let context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
            let promptAction: PromptAction = this.getUIContext().getPromptAction();
            let selectOptions: picker.DocumentSelectOptions = new picker.DocumentSelectOptions();
            selectOptions.defaultFilePathUri = 'file://docs/storage/Users/currentUser';
            let documentPicker = new picker.DocumentViewPicker(context);
            documentPicker.select(selectOptions, async (err: BusinessError, documentSelectResult: Array<string>) => {
              if (err) {
                hilog.error(domain, tag, 'DocumentViewPicker.select failed with err: ' + JSON.stringify(err));
                return;
              }
              try {
                for (let uri of documentSelectResult) {
                  hilog.info(domain, tag, 'deleteToTrash start');
                  const ret: string = await fileManagerService.deleteToTrash(uri);
                  hilog.info(domain, tag, 'deleteToTrash end, ret:%{public}', ret);
                }
                promptAction.showToast({
                  message: `delete success`,
                  duration: 5
                });
              } catch (error) {
                hilog.error(domain, tag, `error.code: ${error.code}, error.message: ${error.message}`);
                promptAction.showToast({
                  message: `delete failed, error.code: ${error.code}, error.message: ${error.message}`,
                  duration: 5
                });
              }
            });
          } catch (error) {
            let err: BusinessError = error as BusinessError;
            hilog.error(domain, tag, 'DocumentViewPicker failed with err: ' + JSON.stringify(err));
          }
        })
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

整个流程就是:点击按钮 → 打开文件选择器 → 用户选择文件 → 调用 deleteToTrash 删除到回收站 → 显示结果提示。

看起来代码挺长的,但其实逻辑很简单。大部分代码都是在做准备工作(创建选择器、获取上下文),真正干活的就一行:fileManagerService.deleteToTrash(uri)

实际应用场景

文件管理服务在实际开发中有很多用途:

文件管理器

// 文件管理器中的删除功能
async function deleteFileToTrash(uri: string) {
  try {
    const ret: string = await fileManagerService.deleteToTrash(uri);
    console.info('文件已移到回收站');
    // 可以在这里更新 UI,把文件从列表里移除
  } catch (error) {
    console.error(`删除失败: ${error.code}, ${error.message}`);
    // 提示用户删除失败
  }
}

在文件管理器里,用户删除文件时,调用 deleteToTrash 把文件移到回收站,而不是直接删除。这样用户可以随时从回收站恢复文件。

你可能会问:那回收站满了怎么办?鸿蒙系统的回收站有自动清理机制,会按照先进先出的原则清理旧文件。你不需要自己管理回收站的空间。

笔记应用

// 笔记应用中删除附件
async function deleteNoteAttachment(attachmentUri: string) {
  try {
    await fileManagerService.deleteToTrash(attachmentUri);
    // 更新笔记,移除附件引用
    console.info('附件已移到回收站');
  } catch (error) {
    console.error(`删除附件失败: ${error.code}`);
  }
}

在笔记应用里,用户删除笔记附件时,也可以用这个功能。附件被移到回收站,用户如果发现误删了,可以去回收站恢复。

图片编辑应用

// 图片编辑应用中删除原图
async function deleteOriginalImage(imageUri: string) {
  try {
    await fileManagerService.deleteToTrash(imageUri);
    console.info('原图已移到回收站');
  } catch (error) {
    console.error(`删除原图失败: ${error.code}`);
  }
}

在图片编辑应用里,编辑完图片后,如果用户想删除原图,可以用这个功能把原图移到回收站。这样即使用户后悔了,也能从回收站找回来。

适用场景

文件管理服务适合以下场景:

  • 文件管理器:文件删除和回收站管理
  • 笔记应用:笔记附件的删除
  • 图片应用:图片文件的删除
  • 文档应用:文档文件的删除
  • 下载管理器:下载文件的删除

注意事项

  1. 文件权限:删除文件前,需要确保对文件有读写权限。可以通过文件选择器让用户授权,这是最简单的方式
  2. 文件 URI:要确保传入的文件 URI 是有效的,否则会删除失败。URI 格式一般是 file://docs/...
  3. 回收站管理:删除到回收站的文件会占用存储空间,提醒用户定期清理回收站
  4. 错误处理:要做好错误处理,比如文件不存在、权限不足等情况。不要让程序因为删除失败就崩溃
  5. 用户体验:删除前最好给用户一个确认提示,避免误删。毕竟移到回收站虽然可以恢复,但多一步确认总是好的

核心流程图

删除文件到回收站的完整流程:

用户点击删除按钮

创建文件选择器

打开系统文件选择界面

用户是否选择了文件?

取消操作

获取文件 URI 列表

遍历每个文件 URI

调用 deleteToTrash 移到回收站

操作是否成功?

弹出删除成功提示

弹出删除失败提示

用户可在回收站恢复文件

文件管理服务的应用场景:

文件管理服务

文件管理器

笔记应用

图片编辑应用

下载管理器

文件删除与回收站管理

笔记附件删除

原图删除

下载文件清理

用户可随时从回收站恢复

总结

文件管理服务让你的应用支持"删除到回收站",核心流程:

  1. 导入文件管理服务模块
  2. 创建文件选择器,让用户选择要删除的文件
  3. 调用 deleteToTrash 把文件移到回收站
  4. 处理成功和失败的情况

虽然功能看起来简单,但"删除到回收站"是一个非常实用的功能。它让用户不再担心误删文件,提升了应用的用户体验。如果你的 App 有文件管理功能,一定要加上这个能力。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐