首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

鸿蒙开发之无须申请权限写入图片到相册

  • 24-12-16 16:22
  • 2407
  • 364656
juejin.cn

鸿蒙开发之不申请权限写入图片到相册

写入图片到相册介绍

在上一篇中,猫林老师给大家分享了如何在不申请权限的情况下读取相册内容。这一篇教大家如何写入图片到相册。

这在应用开发中,也是一个很常见的场景,比如我们要做一个文件扫描的功能。那是不是得把扫描的结果保存到图库呢?再比如我们做一个美颜的功能,那把相册里的原始图片读取出来后,经过我们的美容处理,是不是还得把美容后的结果写入回相册呢?

所以这也是我们必须要学习和掌握的内容。其实写入相册也是HarmonyOS管控的比较严的一个权限,但好在HarmonyOS依然考虑到写入相册对于开发者而言也是一个非常常用的一个功能,因而提供了两种方案来实现图片保存到相册,分别是:

  1. 安全控件保存
  2. 弹窗授权保存

安全控件保存 - 基本使用

这其实就是HarmonyOS提供的一个类似按钮一样的组件,他叫SaveButton,在界面上放一个它即可轻松实现保存,我们先看看它长啥样。我们在界面上写一个SaveButton,如下图

image-20241215205345414.png

可以看到,从外形上看,就是一个带图标的按钮。当然,如果你要是觉得这个按钮的图标、文字不是你想要的,你还可以在使用时传递参数来修改。

对应的参数有

  • icon:设置图标,

    • 如果写SaveButton时写了{},并给了其他参数,唯独没给icon,则没有图标。
    • 如果要设置,仅能设置两个值,分别代表线条图标,填充图标。分别为:1
      1. SaveIconStyle.FULL_FILLED
      2. SaveIconStyle.LINES
    • 这两个值没太大变化可以理解为前一个线条粗一条,后一个线条细一点
  • text: 设置文字,但是仅提供固定的几个文字让你选择,无法自定义。可选择的文字见下表

    名称值说明
    DOWNLOAD0保存按钮的文字描述为“下载”。
    DOWNLOAD_FILE1保存按钮的文字描述为“下载文件”。
    SAVE2保存按钮的文字描述为“保存”。
    SAVE_IMAGE3保存按钮的文字描述为“保存图片”。
    SAVE_FILE4保存按钮的文字描述为“保存文件”。
    DOWNLOAD_AND_SHARE5保存按钮的文字描述为“下载分享”。
    RECEIVE6保存按钮的文字描述为“接收”。
    CONTINUE_TO_RECEIVE7保存按钮的文字描述为“继续接收”。
    SAVE_TO_GALLERY12+8保存按钮的文字描述为“保存至图库”。
    EXPORT_TO_GALLERY12+9保存按钮的文字描述为“导出”。
    QUICK_SAVE_TO_GALLERY12+10保存按钮的文字描述为“快速保存图片”。
    RESAVE_TO_GALLERY12+11保存按钮的文字描述为“重新保存”。

​ buttonType:设置按钮样式、胶囊、圆形、普通(跟按钮的三大样式一样)

  • 举个🌰,如下图

image-20241215210743262.png

scss
代码解读
复制代码
* 对应代码为 ```dart @Entry @Component struct Index { build() { Column({ space: 20 }) { // 什么都不传时,有图标有文字,文字默认为下载 SaveButton() // 仅传图标只有图标 SaveButton({ icon: SaveIconStyle.FULL_FILLED }) // 仅传文字只有文字,因为选择的是Save枚举,所以显示保存 SaveButton({ text: SaveDescription.SAVE }) // 有图标,且按钮样式为原型 SaveButton({ icon: SaveIconStyle.FULL_FILLED, buttonType: ButtonType.Circle }) } .width('100%') .height('100%') } } ```

回到正题,如何使用它保存图片到相册呢?我们需要给它加点击事件

dart
代码解读
复制代码
SaveButton() .onClick((event: ClickEvent, result: SaveButtonOnClickResult) => { })

参数1:事件对象

参数2:用户授权结果,因为这个按钮自带弹框,问用户是否允许保存到图库,如下图。所以我们需要拿到用户的点击结果,只有点击了允许,我们才往下执行代码。那如何判断用户点了允许呢?就是根据SaveButtonOnClickResult,它有两个值:如果返回的结果是SUCCESS即为用户授权,如为:TEMPORARY_AUTHORIZATION_FAILED即为授权失败

image-20241215211512537.png

安全控件保存 - 实现写入图片到图库(相册)

根据官方说明,需要两大步

  1. 调用MediaAssetChangeRequest类的的createImageAssetRequest方法创建一个资产变更请求
  2. 调用PhotoAccessHelper实例对象的 applyChanges方法,传入上述请求。用来提交本次媒体变更请求。

这里先解释下MediaAssetChangeRequest这个类,这个类是专门用来向系统做资产变更请求的。

那什么是手机资产呢?可以简单粗暴的理解为手机里的任意文件数据都是资产。比如图片是资产、视频也是资产。对资产做变更说人话就相当于是对这些文件做改变。所以,我们要写入一个图片到相册,就相当于是资产变更。就需要用到MediaAssetChangeRequest这个类。可是,根据我们刚刚说的,任意文件都叫资产,而我们这次要做的是给相册创建一张新图片,因此使用这个类的createImageAssetRequest方法,代表创建一个图片资产变更请求对象。

正如上面所说MediaAssetChangeRequest.createImageAssetRequest仅仅只是得到一个图片资产变更请求的对象,还得同意这个请求才行。怎么同意呢?即用PhotoAccessHelper实例对象的 applyChanges方法来向系统提交本次请求。

以上是对概念的解释,我们聊聊代码使用

  1. MediaAssetChangeRequest类的的createImageAssetRequest方法需要传入两个参数。1. 当前上下文。 2. 图片的uri。

  2. PhotoAccessHelper实例对象的 applyChanges方法,首先需要创建PhotoAccessHelper类的实例(这是一个专门用来管理相册的实例),再调用applyChanges。而PhotoAccessHelper的实例是通过photoAccessHelper.getPhotoAccessHelper方法,传入当前上下文对象得到的

对应代码如下

ts
代码解读
复制代码
// 获取当前上再问 let context = getContext(); // 使用createImageAssetRequest得到一个创建图片资产的请求 let assetChangeRequest = photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(context, 图片的URI); // 得到PhotoAccessHelper的实例对象 let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context); // 利用上面得到的实例对象,调用applyChanges方法并传入创建图片资产的请求。 // 注意:这是一个异步操作,最好用.then捕捉,或者用await等待也可 await phAccessHelper.applyChanges(assetChangeRequest);

好了,知道这些以后,我们结合上篇文章的读取相册图片,来实现一个读取相册图片,并根据它创建一张一模一样的新图片写入到相册功能,代码如下

dart
代码解读
复制代码
import { photoAccessHelper } from '@kit.MediaLibraryKit'; @Entry @Component struct Index { @State imgUri: string = '' build() { Column({ space: 20 }) { // 选择图片 Button('选择图片') .width('80%') .onClick(() => { // 实例化选择器 let photoPicker = new photoAccessHelper.PhotoViewPicker() // 开始选择图片,设置只允许选择图片,且最大选择1张 photoPicker.select({ MIMEType: photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE, maxSelectNumber: 1 }) .then((res: photoAccessHelper.PhotoSelectResult) => { // 读取成功则赋值给变量去展示到界面 this.imgUri = res.photoUris[0] }) .catch((err: Error) => { // 出错回调 console.log(err.message) }) }) // 安全控件 SaveButton() .onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => { // 判断用户允许授权 if (result == SaveButtonOnClickResult.SUCCESS) { // 得到当前上下文 let context = getContext(); // 创建图片资产请求对象 let assetChangeRequest = photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(context, this.imgUri); // 得到photoAccessHelper实例,也即得到图片管理实例对象 let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context); // 提交本次请求 await phAccessHelper.applyChanges(assetChangeRequest); } else { console.error('SaveButtonOnClickResult create asset failed'); } }) } .width('100%') .height('100%') } }

这一段代码不多,倒是概念需要理解。总而言之核心就是:准备一个创建图片的请求(通过URI指定创建什么图片),然后向系统提交本次请求

安全控件保存 - 优缺点总结

优点:代码简单,固定的两大步:创建请求、提交请求

缺点:必须再在界面上提供一个额外按钮,且此按钮无法深度自定义

弹窗授权保存 - 基本使用

这是一套不用在界面上额外添加按钮,或者也可以深度进行按钮定制的一套方式。

其开发步骤虽对比安全控件步骤略多,但依然属于比较简单实现的一种方式(我们后续如果做文档扫描功能,会用这种方式更恰当)。

我们来看看大概的实现步骤:

  1. 指定待保存到图库(相册)的图片URI
  2. 指定待保存照片的创建选项,包括文件后缀和照片类型,文件标题等
  3. 调用showAssetsCreationDialog,基于弹窗授权的方式获取媒体库的目标uri
  4. 将来源照片内容写入到媒体库的目标uri(需要用到fs文件流读取原图,写入目标图)

根据步骤,我们来看看大致实现代码,如下

dart
代码解读
复制代码
// 导入相册访问帮助类 import { photoAccessHelper } from '@kit.MediaLibraryKit'; // 导入文件流 import { fileIo } from '@kit.CoreFileKit'; async function example() { try { // 指定待保存到媒体库的位于应用沙箱的图片uri,注意:这个URI仅仅只是虚构的uri,实际替换成本身图片Uri即可 let srcFileUri = 'file://com.example.temptest/data/storage/el2/base/haps/entry/files/test.jpg'; // 因为接下来的方法需要传入的是来源URI数组,所以包装成数组 let srcFileUris: Array = [ srcFileUri ]; // 指定待保存照片的创建选项,包括文件后缀和照片类型,标题和照片子类型可选 let photoCreationConfigs: Array = [ { title: 'test', // 可选,文件名 fileNameExtension: 'jpg', // 文件后缀 photoType: photoAccessHelper.PhotoType.IMAGE, // 文件类型 } ]; // 获得当前上下文 let context = getContext(this); let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context); // 基于弹窗授权的方式获取媒体库的目标uri // 这句代码会弹出一个窗提示框,并显示出待写入相册的图片,问用户是否允许保存 let desFileUris: Array = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs); // 设置一个写入文件流,文件位置为上面设置的相册位置 let desFile: fileIo.File = await fileIo.open(desFileUris[0], fileIo.OpenMode.WRITE_ONLY); // 设置一个读取文件流,文件位置为之前的来源图片URI let srcFile: fileIo.File = await fileIo.open(srcFileUri, fileIo.OpenMode.READ_ONLY); // 开始将读出来的文件流复制给写文件流(即往相册写内容) await fileIo.copyFile(srcFile.fd, desFile.fd); // 关闭文件流 fileIo.closeSync(srcFile); fileIo.closeSync(desFile); } catch (err) { console.error(`failed to create asset by dialog successfully errCode is: ${err.code}, ${err.message}`); } }

这里因为篇幅关系,就不解释文件流了。跟Node、Java等语言中的文件流概念完全一致。无非就是读取、写入两套。这里略过不表。

这里需要解释的代码是:photoCreationConfigs用来设置写入到相册里的文件之文件名、文件类型、后缀等

showAssetsCreationDialog会出来一个弹窗让用户确认(如下图)。如果用户确认后,会将设置的写入路径、读取路径变的具有操作权限方便后续文件流操作

image-20241215235833073.png

当然,如果你实在看不懂代码,猫林老师给你个绝招:这段代码你会复制即可。然后需要改的部分仅仅只有两处

  1. 来源图片URI也即srcFileUri这个变量,把这个变量改成你要写入到相册的原始图片路径。
  2. photoCreationConfigs这个变量里,把要写入的新图片文件名改了

弹窗授权保存 - 实现写入图片到图库

我们继续实现一个读取相册图片,并根据它创建一张一模一样的新图片写入到相册功能,代码如下

dart
代码解读
复制代码
import { photoAccessHelper } from '@kit.MediaLibraryKit'; import { fileIo } from '@kit.CoreFileKit'; @Entry @Component struct Index { @State imgUri: string = '' build() { Column({ space: 20 }) { // 选择图片 Button('选择图片') .width('80%') .onClick(() => { // 实例化选择器 let photoPicker = new photoAccessHelper.PhotoViewPicker() // 开始选择图片,设置只允许选择图片,且最大选择1张 photoPicker.select({ MIMEType: photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE, maxSelectNumber: 1 }) .then((res: photoAccessHelper.PhotoSelectResult) => { // 读取成功则赋值给变量去展示到界面 this.imgUri = res.photoUris[0] }) .catch((err: Error) => { // 出错回调 console.log(err.message) }) }) Button('保存图片') .width('80%') .onClick(async () => { try { // 仅仅替换成我们想要写入的原本图片URI let srcFileUri = this.imgUri; // 因为接下来的方法需要传入的是来源URI数组,所以包装成数组 let srcFileUris: Array = [ srcFileUri ]; // 指定待保存照片的创建选项,包括文件后缀和照片类型,标题和照片子类型可选 let photoCreationConfigs: Array = [ { title: 'test', // 可选,文件名 fileNameExtension: 'jpg', // 文件后缀 photoType: photoAccessHelper.PhotoType.IMAGE, // 文件类型 } ]; // 获得当前上下文 let context = getContext(this); let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context); // 基于弹窗授权的方式获取媒体库的目标uri // 这句代码会弹出一个窗提示框,并显示出待写入相册的图片,问用户是否允许保存 let desFileUris: Array = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs); // 设置一个写入文件流,文件位置为上面设置的相册位置 let desFile: fileIo.File = await fileIo.open(desFileUris[0], fileIo.OpenMode.WRITE_ONLY); // 设置一个读取文件流,文件位置为之前的来源图片URI let srcFile: fileIo.File = await fileIo.open(srcFileUri, fileIo.OpenMode.READ_ONLY); // 开始将读出来的文件流复制给写文件流(即往相册写内容) await fileIo.copyFile(srcFile.fd, desFile.fd); // 关闭文件流 fileIo.closeSync(srcFile); fileIo.closeSync(desFile); } catch (err) { console.error(`failed to create asset by dialog successfully errCode is: ${err.code}, ${err.message}`); } }) } .width('100%') .height('100%') } }

细心的读者已经发现了,这个代码仅仅就是复制基本使用里的代码,只是替换了srcFileUri这个变量的值。这也能实现图片写入。

总结

  • 由于HarmonyOS对用户的隐私绝对保护,导致相册读写的权限难以申请。好在HarmonyOS提供了这种无须申请权限即可读取与写入相册的方法。
  • 虽看代码感觉繁琐略多,但实际上代码都是固定的,例如上述的弹窗授权。我们仅需改改来源图片URI即可
  • 本篇内容请一定要有印象。因为后续我们要是制作文档扫描功能会用到
注:本文转载自juejin.cn的猫林老师的文章"https://juejin.cn/post/7448636246863216694"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

104
前端
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top