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

news/2024/12/18 15:15:49/文章来源:https://www.cnblogs.com/xpzll/p/18615040

写入图片到相册介绍

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

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

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

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

安全控件保存 - 基本使用

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

image-20241215205345414

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

对应的参数有

  • icon:设置图标,

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

    名称 说明
    DOWNLOAD 0 保存按钮的文字描述为“下载”。
    DOWNLOAD_FILE 1 保存按钮的文字描述为“下载文件”。
    SAVE 2 保存按钮的文字描述为“保存”。
    SAVE_IMAGE 3 保存按钮的文字描述为“保存图片”。
    SAVE_FILE 4 保存按钮的文字描述为“保存文件”。
    DOWNLOAD_AND_SHARE 5 保存按钮的文字描述为“下载分享”。
    RECEIVE 6 保存按钮的文字描述为“接收”。
    CONTINUE_TO_RECEIVE 7 保存按钮的文字描述为“继续接收”。
    SAVE_TO_GALLERY12+ 8 保存按钮的文字描述为“保存至图库”。
    EXPORT_TO_GALLERY12+ 9 保存按钮的文字描述为“导出”。
    QUICK_SAVE_TO_GALLERY12+ 10 保存按钮的文字描述为“快速保存图片”。
    RESAVE_TO_GALLERY12+ 11 保存按钮的文字描述为“重新保存”。

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

  • 举个🌰,如下图

    image-20241215210743262

    • 对应代码为
    @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%')}
    }
    

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

SaveButton()
.onClick((event: ClickEvent, result: SaveButtonOnClickResult) => {})

参数1:事件对象

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

image-20241215211512537

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

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

  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方法,传入当前上下文对象得到的

对应代码如下

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

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

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文件流读取原图,写入目标图)

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

// 导入相册访问帮助类
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<string> = [srcFileUri];// 指定待保存照片的创建选项,包括文件后缀和照片类型,标题和照片子类型可选let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [{title: 'test', // 可选,文件名fileNameExtension: 'jpg', // 文件后缀photoType: photoAccessHelper.PhotoType.IMAGE, // 文件类型}];// 获得当前上下文let context = getContext(this);let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);// 基于弹窗授权的方式获取媒体库的目标uri// 这句代码会弹出一个窗提示框,并显示出待写入相册的图片,问用户是否允许保存let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);// 设置一个写入文件流,文件位置为上面设置的相册位置let desFile: fileIo.File = await fileIo.open(desFileUris[0], fileIo.OpenMode.WRITE_ONLY);// 设置一个读取文件流,文件位置为之前的来源图片URIlet 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

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

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

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

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

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 {// 仅仅替换成我们想要写入的原本图片URIlet srcFileUri = this.imgUri;// 因为接下来的方法需要传入的是来源URI数组,所以包装成数组let srcFileUris: Array<string> = [srcFileUri];// 指定待保存照片的创建选项,包括文件后缀和照片类型,标题和照片子类型可选let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [{title: 'test', // 可选,文件名fileNameExtension: 'jpg', // 文件后缀photoType: photoAccessHelper.PhotoType.IMAGE, // 文件类型}];// 获得当前上下文let context = getContext(this);let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);// 基于弹窗授权的方式获取媒体库的目标uri// 这句代码会弹出一个窗提示框,并显示出待写入相册的图片,问用户是否允许保存let desFileUris: Array<string> =await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);// 设置一个写入文件流,文件位置为上面设置的相册位置let desFile: fileIo.File = await fileIo.open(desFileUris[0], fileIo.OpenMode.WRITE_ONLY);// 设置一个读取文件流,文件位置为之前的来源图片URIlet 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即可
  • 本篇内容请一定要有印象。因为后续我们要是制作文档扫描功能会用到

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/854831.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

清明上河图密码 26 集 电视剧 All In One

清明上河图密码 26 集 电视剧 All In One 《清明上河图密码》2024年播出的中国大陆古装悬疑电视剧,改编自中国大陆作家冶文彪创作的同名小说,由电影《男欢女爱》的导演杨帆与《深渊宝藏》的导演熊琛联合执导,张颂文、白百何领衔主演,周一围、侯岩松特别主演,张耀、夏梦、林…

python执行subprocess.run报错

问题 执行 subprocess.run(command, check=True) 时报错 File "C:\Users\xxx\AppData\Local\Programs\Python\Python39\lib\subprocess.py", line 1420, in _execute_childhp, ht, pid, tid = _winapi.CreateProcess(executable, args,FileNotFoundError:[WinError 2…

二:使用正点原子的直流无刷驱动板自写FOC控制永磁同步(PMSM)电机(位置闭环)

在上一篇博客中配置了UVW三相PWM的定时器,在此基础上增加配置ABZ编码器定时器启用一个定时器中断,用于PID处理 代码如下// 常量定义 #define PI 3.14159265359f #define PWM_RESOLUTION 5250.0f // PWM分辨率 #define CIRCLE_RESOLUTION 1000 // 圆周分辨率 #define MOTOR_…

易基因:游离细胞DNA(cfDNA)检测整体研究方案

01.技术简述 细胞游离DNA(cell-free DNA,cfDNA)是指在生物体的体液中(如血浆、尿液、脑脊液等)自由存在的、非细胞内的DNA片段。这些DNA片段通常来源于细胞凋亡(程序性死亡)或坏死(细胞损伤或死亡后释放),可以被释放到循环系统中,并在体液中被检测到。cfDNA研究和应…

域名历史是什么?怎么进行域名历史查询?

域名是互联网上用来识别和访问网站的一个易于记忆的地址。它将IP地址转换成人类可读的格式,例如 www.example.com。域名是互联网的基础,每个网站都需要一个独特的域名以便用户能够访问。 什么是域名历史? 域名历史记录了一个域名自注册以来的所有变更,包括所有权、DNS设置、…

四款电脑桌面好用的备忘录app推荐,可同步手机

1、sticky notes 这款是Windows系统自带的备忘记事工具,中文名叫“便笺”。点击桌面左下角“开始”,在程序列表中可找到。 它在电脑桌面上是以一张一张彩色便利贴展现的,可以记录简单的文字和图片,非常像纸质便利贴的电子版。 可以通过Office365和手机端同步数据。2、敬业签…

GO 学习笔记之零 (二)git 操作

一、克隆git上仓库指定分支到本地 1、使用 VSCODE 工具详见:GO 学习笔记之一 环境安装 - 夏之夜 - 博客园 文章中的 第四章节的 "导入git项目" 二、git项目本地更改提交到git仓库 1、使用 VSCODE 工具 1.1 工程只有一人开发,不存在冲突的场景点击 VSCODE工具上的“…

写了两个自动化脚本,从下载-构建-打包-提取-备份-更新-重启

##!/bin/bash# 定义颜色用于日志输出 RED=\033[0;31m GREEN=\033[0;32m YELLOW=\033[1;33m NC=\033[0m # No Color# 检查参数 if [ -z "$1" ]; thenecho -e "${RED}错误: 未传入分支号!${NC}"echo "用法: $0 <分支号>"exit 1 fi# 读取分支…

冰雪旅游大爆发:如何优化冰雪旅游管理与服务——看板软件篇

利用板栗看板进行冰雪旅游的旅游管理与运营可以带来诸多便利和效益。通过明确目标与需求、创建项目列表、信息整合与展示、游客管理与服务、数据分析与决策支持以及团队协作与沟通等方面的应用,可以显著提升冰雪旅游的管理效率和服务质量。Wind数据显示,自9月份以来,冰雪旅游…

CI/CD方案:推动智能汽车开发的高效利器

CI/CD方案:推动智能汽车开发的高效利器在智能化和自动驾驶技术飞速发展的背景下,汽车软件开发的复杂性正以前所未有的速度增长。面对市场对效率、质量和快速迭代的高需求,传统开发模式已然捉襟见肘。作为提升开发效率和质量的核心工具,CI/CD(持续集成与持续交付/部署)方案…

说一个使用okhttp的理由

当时公司为了让接入网关变的简单,于是就开发了一个client,然后给各个服务去使用。 有一次发送请求的时候,发现报timeout了,但实际上业务已经执行成功了。于是就去追代码,发现使用的尽然是hutool包的工具,那个真是糊涂呀 。 看代码发现没有指定timeout的参数就使用了默认参…

大学生职业规划模板汇总(大学生职业规划大赛PPT模板)

前言 全国大学生职业规划大赛是由教育部举办的赛事,首届大赛于2023年9月至2024年5月举办,总决赛在上海市举行。生涯教育与就业指导工作贯穿高校招生、培养、就业全过程,是就业指导服务的核心内容、强化价值观引导的重要载体、促进毕业生高质量充分就业的基础工作。以全国大学…