HarmonyOS:组件布局保存至相册

一,需求背景

有这样一个需求,将页面上的某个自定义组件以图片的形式保存至相册。

二,需求拆解

根据需求分析,可将需求拆解成两步:

1,将组件转换成图片资源;

2,将图片保存到相册

其中,第2步又有两种情况:

1,App具有申请受限权限:ohos.permission.WRITE_IMAGEVIDEO 的能力;

2,App不具有申请受限权限的能力

三,方案实现

1,将组件转换成图片资源

通过组件的截图能力获取图片资源PixelMap

复制代码
componentSnapshot.get(viewId).then((pixelMap: PixelMap) => {

})

viewId:指组件id,在使用自定义组件时为组件添加的id

如:

复制代码
Image($r('app.media.image'))
        .width('38vp')
        .height('38vp')
        .id('image')

详细说明请查看官方文档

2,将图片保存到相册

1> 有受限权限:ohos.permission.WRITE_IMAGEVIDEO 时:
复制代码
    let helper = photoAccessHelper.getPhotoAccessHelper(context);
    let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpeg');
    let file = await fileIo.open(uri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
    let imagePackerApi = image.createImagePacker();
    let packOpts: image.PackingOption = { format: 'image/jpeg', quality: quality };
    imagePackerApi.packToFile(snapImage, file.fd, packOpts, (err: BusinessError) => {
      if (err) {
        console.error(`Failed to pack the image to file.code ${err.code},message is ${err.message}`);
      } else {
        console.info('Succeeded in packing the image to file.');
        imagePackerApi.release((err: BusinessError) => {
          if (err) {
            console.error(`Failed to release the image source instance.code ${err.code},message is ${err.message}`);
          } else {
            console.info('Succeeded in releasing the image source instance.');
            fileIo.close(file.fd);
          
          }
        })
      }
    })
2> 不具备申请受限权限能力时

首先要将第一步生成的PixelMap对象保存到应用的沙箱目录下

复制代码
//将PixelMap转成ArrayBuffer 对象
const imagePacker: image.ImagePacker = image.createImagePacker()
const buffer: ArrayBuffer = await imagePacker.packToData(pixelMap, {
     format: 'image/png',
     quality: 100
})


  /**
   * 将文件保存在应用的沙箱目录下,默认是txt文件
   */
  static saveToPrivate(
    context: common.UIAbilityContext,
    buffer: string | ArrayBuffer,
    fileName?: string
  ): Promise<string> {
    const filesDir: string = context.filesDir
    let name: string | undefined = fileName
    if (!name || name.length === 0) {
      name = new Date().toTimeString() + ".txt"
    }
    console.info('fileName is ' + name)
    const filePath: string = filesDir + "/" + name
    const file: fileIo.File = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE)

    return new Promise((resolve, reject) => {
      fileIo.write(file.fd, buffer).then((length: number) => {
        console.log("write file success, length: " + length)
        resolve(fileUri.getUriFromPath(filePath))
      }).catch((error: BusinessError) => {
        console.log("write file fail, message: " + error.message);
        reject('')
      }).finally(() => {
        fileIo.closeSync(file)
      })
    })
  }

将图片资源保存到沙箱目录下,并获取到对应的fileUri,注意这里是fileUri因为后面保存到相册要用到。

方案一:使用安全控件SaveButton 保存到相册
复制代码
 // 设置安全控件按钮属性
 saveButtonOptions: SaveButtonOptions = {
    icon: SaveIconStyle.FULL_FILLED,
    text: SaveDescription.SAVE_IMAGE,
    buttonType: ButtonType.Capsule
  } 


SaveButton(this.saveButtonOptions) // 创建安全控件按钮
    .onClick(async (event, result: SaveButtonOnClickResult) => {
       if (result == SaveButtonOnClickResult.SUCCESS) {
          try {
            let context = getContext();
            let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
            // 上一步报错到沙箱目录下的fileUri
            let fileUri = "file://adfad"
            let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest = photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(context,fileUri);
            await phAccessHelper.applyChanges(assetChangeRequest);
            console.info('createAsset successfully, uri: ' + assetChangeRequest.getAsset().uri);
            } catch (err) {
            console.error(`create asset failed with error: ${err.code}, ${err.message}`);
            }
        } else {
             console.error('SaveButtonOnClickResult create asset failed');
             }
      })
方案二:使用弹窗授权保存到相册
复制代码
 /**
   * 将指定uris路径下的图片或视频拷贝到相册中
   * @param uris 需保存到媒体库中的图片/视频文件对应的媒体库uri。
   *             仅支持处理图片、视频uri。不支持手动拼接的uri,需调用接口获取
   * @returns true:保存成功,false:保存失败
   */
  static async copyMediaToGallery(
    context: common.UIAbilityContext,
    uris: Array<string> | string,
    photoType: photoAccessHelper.PhotoType = photoAccessHelper.PhotoType.IMAGE
  ): Promise<boolean> {
    try {
      const photoHelper = photoAccessHelper.getPhotoAccessHelper(context)
      if (typeof uris === 'string') {
        uris = [uris]
      }
      let photoConfigs: Array<photoAccessHelper.PhotoCreationConfig> = []
      const fileNameExt: string = photoType === photoAccessHelper.PhotoType.IMAGE ? 'jpg' : 'mp4'
      uris.forEach(() => {
        photoConfigs.push({
          fileNameExtension: fileNameExt,
          photoType: photoType,
        })
      })
      const desFileUris: Array<string> = await photoHelper.showAssetsCreationDialog(uris, photoConfigs)
      if (uris.length !== desFileUris.length) {
        return false
      }
      for (let i = 0; i < uris.length; i++) {
        const srcFile: fileIo.File = fileIo.openSync(uris[i], fileIo.OpenMode.READ_ONLY)
        const desFile: fileIo.File = fileIo.openSync(desFileUris[i], fileIo.OpenMode.WRITE_ONLY)
        fileIo.copyFileSync(srcFile.fd, desFile.fd)
        fileIo.close(srcFile)
        fileIo.close(desFile)
      }
      console.info('copyPhotoToGallery success')
      return true
    } catch (err) {
      console.info('copyPhotoToGallery error: ' + err.code + ',' + err.message)
      return false
    }
  }

参考文档:

componentSnapshot(组件截图)

保存媒体库资源

相关推荐
崔庆才丨静觅8 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60619 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了9 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅9 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅10 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment10 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅10 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊10 小时前
jwt介绍
前端
爱敲代码的小鱼10 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax