公司动态
【HarmonyOS NEXT】 鸿蒙图片或视频保存相册
在 HarmonyOS NEXT 中保存图片或视频到相册主要有三种方式它们的权限模型和适用场景各不相同。下面我将为你详细拆解并配合代码示例帮助你理解并选择最合适的方案。核心方法对比特性SaveButton保存控件PhotoViewPickerphotoAccessHelper 权限权限模型零权限用户点击控件时临时授权授权窗口仅弹一次。零权限拉起系统“保存到”界面由用户选择保存位置。需申请权限需在module.json5中配置ohos.permission.WRITE_IMAGEVIDEO等权限并动态请求。交互方式应用内控件点击即存无需用户选择保存路径。拉起系统FilePicker界面用户需二次确认保存路径。静默保存无额外交互权限授予后。授权时效授权有时效如10秒需在回调中完成核心操作。无时效限制用户确认后获得目标URI的读写权限。权限长期有效除非用户手动在设置中关闭。适用场景首选。UI中可放置按钮用户点击保存图片/小文件。体验最佳是官方推荐做法。需要用户明确知道文件保存位置或进行“另存为”操作时。需要后台自动保存、或处理大文件如2GB视频时用此方案配合分段写入更稳定。方案一使用 SaveButton 安全控件推荐这是HarmonyOS NEXT主推的方式能提供最流畅的用户体验因为它无需应用提前申请任何存储权限完全由用户点击控件的动作触发临时授权。1. 核心逻辑与代码首先在UI中放置一个SaveButton。当用户点击它且授权成功后在onClick回调里获取临时授权并执行保存操作。typescriptimport { photoAccessHelper } from kit.MediaLibraryKit; import { fileIo } from kit.CoreFileKit; import { common } from kit.AbilityKit; import { promptAction } from kit.ArkUI; import { BusinessError } from kit.BasicServicesKit; // 保存图片的函数在SaveButton的onClick中调用 async function savePhotoToGallery(context: common.UIAbilityContext, imageBuffer: ArrayBuffer) { let helper photoAccessHelper.getPhotoAccessHelper(context); try { // 1. 在相册中创建一个图片文件指定类型和格式 // 注意此操作需在用户点击SaveButton后的10秒内完成[citation:7] let uri await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, jpg); // 2. 打开文件并写入数据 let file await fileIo.open(uri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); await fileIo.write(file.fd, imageBuffer); await fileIo.close(file.fd); promptAction.showToast({ message: 已保存至相册 }); } catch (error) { const err: BusinessError error as BusinessError; console.error(Failed to save photo. Code is ${err.code}, message is ${err.message}); } } Entry Component struct SaveButtonDemo { build() { Row() { Column() { // 假设这是你要保存的图片 Image($r(app.media.demoImage)).width(300).height(300) // 保存控件用户点击即触发保存逻辑无需任何权限申请 SaveButton() .padding({ top: 12, bottom: 12, left: 24, right: 24 }) .onClick(async (event, result) { if (result SaveButtonOnClickResult.SUCCESS) { const context getContext(this) as common.UIAbilityContext; // 这里需要将你的图片转换为ArrayBuffer // 例如从网络下载、从沙箱读取或从PixelMap编码 // const imageBuffer: ArrayBuffer await getImageBuffer(); // await savePhotoToGallery(context, imageBuffer); } else { promptAction.showToast({ message: 授权失败 }); } }) } } } }方案二使用 PhotoViewPicker这种方案不涉及权限申请但会拉起系统选择器让用户自己决定保存位置适合“另存为”的场景。1. 核心逻辑与代码typescriptimport picker from ohos.file.picker; import fs from ohos.file.fs; async function saveWithPicker() { const photoSaveOptions new picker.PhotoSaveOptions(); // 可指定一个建议的文件名 photoSaveOptions.newFileNames [MyImage.jpg]; const photoViewPicker new picker.PhotoViewPicker(); try { // 拉起系统保存界面用户选择文件夹后返回保存的URI const photoSaveResult await photoViewPicker.save(photoSaveOptions); const uri photoSaveResult[0]; console.info(Save URI is: uri); // 拿到URI后可以像操作普通文件一样写入数据 let file fs.openSync(uri, fs.OpenMode.READ_WRITE); // 这里写入你的图片数据 // let writeLen fs.writeSync(file.fd, your image data); fs.closeSync(file); } catch (err) { console.error(Save failed, code is ${err.code}, message is ${err.message}); } }方案三申请权限 photoAccessHelper当需要应用自动、静默地保存文件时例如自动保存聊天图片就需要申请ohos.permission.WRITE_IMAGEVIDEO权限。这是一个系统级权限需要用户在弹窗中授权。1. 配置权限在module.json5中添加权限声明json{ module: { requestPermissions: [ { name: ohos.permission.WRITE_IMAGEVIDEO, reason: $string:reason_for_saving_photos, usedScene: { abilities: [EntryAbility], when: inuse } } ] } }2. 动态申请权限并保存typescriptimport { abilityAccessCtrl, common } from kit.AbilityKit; import { photoAccessHelper } from kit.MediaLibraryKit; import fs from ohos.file.fs; async function requestPermissionAndSave(context: common.UIAbilityContext, imageBuffer: ArrayBuffer) { const atManager abilityAccessCtrl.createAtManager(); const permissions: Arraystring [ohos.permission.WRITE_IMAGEVIDEO]; try { // 1. 动态申请权限 const result await atManager.requestPermissionsFromUser(context, permissions); if (result.authResults[0] abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { // 2. 权限已授权执行保存 let helper photoAccessHelper.getPhotoAccessHelper(context); let uri await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, jpg); let file fs.openSync(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); fs.writeSync(file.fd, imageBuffer); fs.closeSync(file); promptAction.showToast({ message: 保存成功 }); } } catch (err) { console.error(Request permission failed, error: ${err.code}); } }视频保存与“大文件”注意事项方法通用上述三种方法均适用于视频保存。只需将PhotoType.IMAGE改为PhotoType.VIDEO文件后缀改为.mp4即可。大文件处理对于体积较大的视频如超过2GB使用SaveButton可能会因授权窗口期10秒不够用而导致保存失败。解决方案对于此类场景推荐使用方案三申请权限模式。在获得长期权限后可以通过fs.openSync等API进行分段写入稳定地将大文件保存到相册。总结与建议首选SaveButton对于绝大多数保存图片和小视频的场景这是最佳实践提供了最友好的用户体验。需要用户确认位置时选PhotoViewPicker当你需要用户明确知道文件存到了哪里或者提供“另存为”功能时使用Picker。后台自动保存或处理超大文件选photoAccessHelper 权限这种方式虽然需要申请权限但提供了最灵活和持久的访问能力适合复杂或后台任务。