当前位置: 首页 > news >正文

开发中使用——鸿蒙特有的图片保存到相册的方式

文章目录

  • 保存图片到相册是App常用的功能,HarmonyOS开发中也不例外,一般情况下都是请求保存网络图片到本地。因为图片显示出来就已经加载过一次了,HarmonyOS开发又提供了一种简便的方式,通过对获取已加载的组件的截图,进行保存,这种效率更高。现在对两种方式进行汇总。
  • 一、前言
    • 1.自定义下载按钮,通过申请权限
    • 2.使用安全控件实现权限的直接访问
  • 二、实现方式1:通过获取网络图片数据,进行保存
  • 三、实现方式2:通过获取已加载的组件的截图,进行保存
  • 四、使用实例
  • 五、核心代码补充
  • 六、感谢
    • 我主要是在原来大佬们博客和官方文档的基础,进行了汇总,以及新API的适配工作。
    • 感谢大佬们的分享。
    • 参考

保存图片到相册是App常用的功能,HarmonyOS开发中也不例外,一般情况下都是请求保存网络图片到本地。因为图片显示出来就已经加载过一次了,HarmonyOS开发又提供了一种简便的方式,通过对获取已加载的组件的截图,进行保存,这种效率更高。现在对两种方式进行汇总。

  • 请求保存网络图片数据,进行保存。
  • 对获取已加载的组件的截图,进行保存。

一、前言

从功能上来说,保存图片到相机涉及到的权限是ohos.permission.WRITE_IMAGEVIDEO,仅特殊场景与功能才可申请此权限,例如应用需要克隆、备份或同步图片/视频类文件。还要一种方式是使用安全控件来临时申请权限。

1.自定义下载按钮,通过申请权限

          // 保存至图库按钮Button() {Row() {Image($r('app.media.ic_download')).width(20).height(20).fillColor(Color.White).margin({ right: 5 })Text('保存至图库').fontSize(18).fontWeight(FontWeight.Bold).fontColor(Color.White)}}.width('42%').height('6%').borderRadius(20).backgroundColor(`#${this.movie.bgColor}`).onClick(() => {// TODO: 实现保存至图库逻辑console.log('保存至图库');}).margin({ left: 10, right: 10 })

需要申请权限,在模块的module.json5中进行配置

      {"name": "ohos.permission.READ_IMAGEVIDEO","usedScene": {"abilities": ["EntryAbility"],"when": "inuse"},"reason": "$string:WRITE_IMAGEVIDEO"},{"name": "ohos.permission.WRITE_IMAGEVIDEO","usedScene": {"abilities": ["EntryAbility"],"when": "inuse"},"reason": "$string:WRITE_IMAGEVIDEO"}

进行下载操作时,进行申请权限

/*** 创建申请权限明细(非安全控件使用时使用)* @returns*/async reqPermissionsFromUser(): Promise<number[]> {let atManager = abilityAccessCtrl.createAtManager();let grantStatus = await atManager.requestPermissionsFromUser(this.context, ['ohos.permission.WRITE_IMAGEVIDEO']);return grantStatus.authResults;}/*** 用户申请权限(非安全控件使用时使用)*/async requestPermission() {let grantStatus = await this.reqPermissionsFromUser();for (let i = 0; i < grantStatus.length; i++) {if (grantStatus[i] === 0) {// 用户授权,可以继续访问目标操作}}}

2.使用安全控件实现权限的直接访问

          SaveButton({icon:SaveIconStyle.FULL_FILLED, text: SaveDescription.SAVE_IMAGE, buttonType: ButtonType.ROUNDED_RECTANGLE }).fontColor(Color.White).fontWeight(FontWeight.Medium).onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => {// TODO: 实现保存至图库逻辑console.log('保存至图库');})

二、实现方式1:通过获取网络图片数据,进行保存

  /*** 获取网络图片的原始二进制数据* @param url* @param callback*/async getPicArrayBuffer(url:string,callback:(buf:ArrayBuffer|null)=>void) {http.createHttp().request(url,(error: Error, data: http.HttpResponse) => {let arrayBuf:ArrayBuffer|null = null;if (error) {// SilToast.showToast(this.uiContext,'图片保存失败'),这里不做处理,自行处理arrayBuf = null;}else{// 判断网络获取到的资源是否为ArrayBuffer类型if (data.result instanceof ArrayBuffer) {arrayBuf = data.result as ArrayBuffer;}}callback(arrayBuf);})}/*** 保存ArrayBuffer到图库* @param buffer:图片ArrayBuffer* @returns*/async saveImageToPhoto(buffer: ArrayBuffer | string): Promise<void> {const helper = photoAccessHelper.getPhotoAccessHelper(this.context); // 获取相册管理模块的实例const uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg'); // 指定待创建的文件类型、后缀和创建选项,创建图片或视频资源const file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);let r =  await fs.write(file.fd, buffer);await fs.close(file.fd);// SilToast.showToast(this.uiContext,"图片保存成功")}

三、实现方式2:通过获取已加载的组件的截图,进行保存

  /*** 获取已加载的组件的截图(传入组件的组件id标识,找到对应组件进行截图)* 推荐使用,保存速度不是一般的快* @param componentId* @param callback*/getComponentSnapshot(componentId:string,callback:(buf:ArrayBuffer|null)=>void){this.uiContext.getComponentSnapshot().get(componentId, async (error: Error, pixelMap: image.PixelMap) => {if (error) {/// 获取失败callback(null);console.log("error: " + JSON.stringify(error))return;}let packOpts: image.PackingOption = { format: "image/jpeg", quality: 98 }const imagePackerApi: image.ImagePacker = image.createImagePacker();imagePackerApi.packToData(pixelMap, packOpts).then((data: ArrayBuffer) => {callback(data)console.info('Succeeded in packing the image.');}).catch((error: BusinessError) => {callback(null)console.error(`Failed to pack the image.code ${error.code},message is ${error.message}`);})});}/*** 保存ArrayBuffer到图库* @param buffer:图片ArrayBuffer* @returns*/async saveImageToPhoto(buffer: ArrayBuffer | string): Promise<void> {const helper = photoAccessHelper.getPhotoAccessHelper(this.context); // 获取相册管理模块的实例const uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg'); // 指定待创建的文件类型、后缀和创建选项,创建图片或视频资源const file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);let r =  await fs.write(file.fd, buffer);await fs.close(file.fd);// SilToast.showToast(this.uiContext,"图片保存成功")}

四、使用实例

使用安全控件实现权限的直接访问为例。

import { PicSaveManager } from '../utils/PicSaveManager';
import { SilToast } from '../utils/SilToastUtils';
// 图片下载管理
private  picSaveManager:PicSaveManager = new PicSaveManager(this.getUIContext());/// 使用安全控件实现权限的直接访问
SaveButton({icon:SaveIconStyle.FULL_FILLED, text: SaveDescription.SAVE_IMAGE, buttonType: ButtonType.ROUNDED_RECTANGLE }).fontColor(Color.White).fontWeight(FontWeight.Medium).onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => {if (result == SaveButtonOnClickResult.SUCCESS) {// 方法1:从网络请求下载,然后保存到相册// this.picSaveManager.getPicArrayBuffer(this.movie.poster_url, async (bufArray)=>{//     if(bufArray == null){//       SilToast.showToast(this.getUIContext(),'图片保存失败')//     }else{//       // 保存图片到相册//       await this.picSaveManager.saveImageToPhoto(bufArray);//       SilToast.showToast(this.getUIContext(),'图片保存成功')//     }// });/// 方法2:获取已加载图片的截图,然后保存到相册(保存速度不是一般的快)this.picSaveManager.getComponentSnapshot('moviePic', async (bufArray)=>{if(bufArray == null){SilToast.showToast(this.getUIContext(),'图片保存失败')}else{// 保存图片到相册await this.picSaveManager.saveImageToPhoto(bufArray);SilToast.showToast(this.getUIContext(),'图片保存成功')}})}})

五、核心代码补充


import { abilityAccessCtrl, common } from '@kit.AbilityKit';
import { http } from '@kit.NetworkKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo as fs } from '@kit.CoreFileKit';
import {  UIContext } from '@kit.ArkUI';
import { image } from '@kit.ImageKit';
import { BusinessError } from '@kit.BasicServicesKit';export class PicSaveManager{private uiContext:UIContext;private context:common.UIAbilityContext;/// 初始化时,获取到contextconstructor(uiContext:UIContext) {this.uiContext = uiContext;this.context = uiContext.getHostContext() as common.UIAbilityContext;}/*** 获取网络图片的原始二进制数据* @param url* @param callback*/async getPicArrayBuffer(url:string,callback:(buf:ArrayBuffer|null)=>void) {http.createHttp().request(url,(error: Error, data: http.HttpResponse) => {let arrayBuf:ArrayBuffer|null = null;if (error) {// SilToast.showToast(this.uiContext,'图片保存失败'),这里不做处理,自行处理arrayBuf = null;}else{// 判断网络获取到的资源是否为ArrayBuffer类型if (data.result instanceof ArrayBuffer) {arrayBuf = data.result as ArrayBuffer;}}callback(arrayBuf);})}/*** 获取已加载的组件的截图(传入组件的组件id标识,找到对应组件进行截图)* 推荐使用,保存速度不是一般的快* @param componentId* @param callback*/getComponentSnapshot(componentId:string,callback:(buf:ArrayBuffer|null)=>void){this.uiContext.getComponentSnapshot().get(componentId, async (error: Error, pixelMap: image.PixelMap) => {if (error) {/// 获取失败callback(null);console.log("error: " + JSON.stringify(error))return;}let packOpts: image.PackingOption = { format: "image/jpeg", quality: 98 }const imagePackerApi: image.ImagePacker = image.createImagePacker();imagePackerApi.packToData(pixelMap, packOpts).then((data: ArrayBuffer) => {callback(data)console.info('Succeeded in packing the image.');}).catch((error: BusinessError) => {callback(null)console.error(`Failed to pack the image.code ${error.code},message is ${error.message}`);})});}/*** 保存ArrayBuffer到图库* @param buffer:图片ArrayBuffer* @returns*/async saveImageToPhoto(buffer: ArrayBuffer | string): Promise<void> {const helper = photoAccessHelper.getPhotoAccessHelper(this.context); // 获取相册管理模块的实例const uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg'); // 指定待创建的文件类型、后缀和创建选项,创建图片或视频资源const file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);let r =  await fs.write(file.fd, buffer);await fs.close(file.fd);// SilToast.showToast(this.uiContext,"图片保存成功")}/*** 创建申请权限明细(非安全控件使用时使用)* @returns*/async reqPermissionsFromUser(): Promise<number[]> {let atManager = abilityAccessCtrl.createAtManager();let grantStatus = await atManager.requestPermissionsFromUser(this.context, ['ohos.permission.WRITE_IMAGEVIDEO']);return grantStatus.authResults;}/*** 用户申请权限(非安全控件使用时使用)*/async requestPermission() {let grantStatus = await this.reqPermissionsFromUser();for (let i = 0; i < grantStatus.length; i++) {if (grantStatus[i] === 0) {// 用户授权,可以继续访问目标操作}}}
}

六、感谢

我主要是在原来大佬们博客和官方文档的基础,进行了汇总,以及新API的适配工作。

感谢大佬们的分享。

参考

【HarmonyOS 5】 鸿蒙图片或视频保存相册

HarmonyOS 将图片保存到相册

HarmonyOS实战:一招搞定保存图片到相册

http://www.dtcms.com/a/341237.html

相关文章:

  • 气泡水位计在水库大坝安全监测中的应用
  • 大语言模型研究进展
  • 【MAR】OSCNet+学习笔记
  • 机器学习总复习
  • Redis知识总结
  • IDEA:控制台中文乱码
  • 【JavaEE】多线程 -- 定时器
  • 无人机感知系统详解
  • Excel表格复制到word中格式错乱
  • 【Java】深入浅出Spring中的@Autowired:自动注入的奥秘
  • 机器翻译 (Machine Translation) 经典面试笔试50题(包括详细答案)
  • imx6ull-驱动开发篇29——Linux阻塞IO 实验
  • Java并发容器详解
  • 【LLIE专题】BEM:一对多映射,贝叶斯增强模型
  • Deepseek+python自动生成禅道测试用例
  • 将集合拆分成若干个batch,并将batch存于新的集合
  • WMS仓储管理系统如何解决仓库的呆滞库存
  • 鸿蒙安卓前端中加载丢帧:ArkWeb分析
  • 第5.7节:awk赋值运算
  • IPSEC安全基础
  • Qt 中最经典、最常用的多线程通信场景
  • TDengine IDMP 运维指南(数据导入导出)
  • WIN10/WIN11:无法下载所有必需的文件 0x80072EE2 0x20000(未解决)
  • C++ std::sort的应用总结
  • Unity 大量子弹和小怪碰撞检测优化
  • GSPO:Towards scalable reinforcement learning for language models
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型和EasyOCR实现汽车牌照动态检测和识别(C#代码,UI界面版)
  • 使用UUP dump制作windows preview镜像
  • 手机、汽车如何实现卫星直连
  • imx6ull-驱动开发篇31——Linux异步通知