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(组件截图)

保存媒体库资源

相关推荐
pink大呲花2 分钟前
Vue.js 中 v-if 的使用及其原理
前端·javascript·vue.js
叶小秋3 分钟前
Next.js + Tailwind CSS 移动端适配方案
前端·next.js
北京_宏哥4 分钟前
🔥PC端自动化测试实战教程-4-pywinauto 操作PC端应用程序窗口 - 上篇(详细教程)
前端·python·测试
北京_宏哥6 分钟前
🔥PC端自动化测试实战教程-3-pywinauto 启动PC端应用程序 - 下篇(详细教程)
前端·windows·python
天官赐福_29 分钟前
vue2的scale方式适配大屏
前端·vue.js
江城开朗的豌豆29 分钟前
CSS篇:前端经典布局方案:左侧固定右侧自适应的6种实现方式
前端·css·面试
我儿长柏必定高中31 分钟前
Promise及使用场景
前端
无名友31 分钟前
HTML — 浮动
前端·css·html
0xJohnsoy33 分钟前
React中的this详解
前端
the_one33 分钟前
🚀「v-slide-in」+ 瀑布流实战指南:Vue 高级滑入动画一键实现,页面质感瞬间拉满!
前端·javascript·css