开发中使用——鸿蒙特有的图片保存到相册的方式
文章目录
- 保存图片到相册是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实战:一招搞定保存图片到相册