Skip to content

鸿蒙(ArkTs实现保存图片到相册)

保存图片到相册常规的方式需要申请'ohos.permission.WRITE_IMAGEVIDEO'权限,这个权限是ACL权限,申请是比较麻烦的,官方给到了另外一种方式:使用安全控件创建媒体资源,使用安全控件创建媒体资源无需在应用中申请相册管理模块权限'ohos.permission.WRITE_IMAGEVIDEO',详情请参考安全控件的保存控件。

使用安全控件创建媒体资源

开发步骤: 1、设置安全控件按钮属性。

2、创建安全控件按钮。

3、调用MediaAssetChangeRequest.createImageAssetRequestPhotoAccessHelper.applyChanges接口创建图片资源。

示例

ts
import { photoAccessHelper } from '@kit.MediaLibraryKit';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'
  @State saveButtonOptions: SaveButtonOptions = {
    icon: SaveIconStyle.FULL_FILLED,
    text: SaveDescription.SAVE_IMAGE,
    buttonType: ButtonType.Capsule
  } // 设置安全控件按钮属性

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
        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://com.example.temptest/data/storage/el2/base/haps/entry/files/test.jpg';
                 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');
             }
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

扩展

如果保存的图片是pixmap或者ArrayBuffer可以用以下工具类函数进行转换

js
    /**
     * 保存pixmap到沙箱,并且返回沙箱uri
     * @param pixmap 图片map
     * @param filesDir 保存的目录
     */
    public static savePixmap2SystemFileManager(pixmap: image.PixelMap, filesDir: string): Promise<string> {
        return new Promise(async (resolve, reject) => {
            try {
                let url = filesDir + `/${DateUtil.getTimeStamp()}.png`
                if (!pixmap) {
                    return;
                }
                const imgBuffer = await CommonUtil.transferPixelMap2Buffer(pixmap);
                const file = fileIo.openSync(url,
                    fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
                await fileIo.write(file.fd, imgBuffer);
                await fileIo.close(file.fd);
                resolve(url)
            } catch (e) {
                reject(e)
            }

        })
    }

    /**
     * 将pixelMap转成图片格式,以流的形式输出
     * @param pixelMap
     * @returns 
     */
    public static transferPixelMap2Buffer(pixelMap: image.PixelMap): Promise<ArrayBuffer> {
        return new Promise((resolve, reject) => {
            /**
             * 设置打包参数
             * format:图片打包格式,只支持 jpg 和 webp
             * quality:JPEG 编码输出图片质量
             * bufferSize:图片大小,默认 10M
             */
            let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 98 };
            // 创建ImagePacker实例
            const imagePackerApi = image.createImagePacker();
            imagePackerApi.packing(pixelMap, packOpts).then((buffer: ArrayBuffer) => {
                resolve(buffer);
            }).catch((err: BusinessError) => {
                reject();
            })
        })
    }

需要注意的点

需要注意这里创建filePath的时候,fs.statSync(pathDir + BaseConstants.fileDir).isDirectory();这行判断目录是否存在的代码会报错,只能在这里以try-catch的方式判断目录文件夹是否存在

js
    /**
     * 保存图片资源到相册
     * @param context
     * @param buff
     * @returns
     */
    public static async saveAlbumAbility(context: common.UIAbilityContext, pixmap: image.PixelMap): Promise<void> {
        const permissions: Array<Permissions> =
            ["ohos.permission.INTERNET", "ohos.permission.WRITE_IMAGEVIDEO", "ohos.permission.READ_IMAGEVIDEO"];
        const result = await PermissionSetting.checkPermissions(context as common.UIAbilityContext,
            permissions)

        if (result.flag) {
            return new Promise(async (resolve, reject) => {
                try {

                    // 创建写入的路径
                    let pathDir = context.filesDir;
                    let filePath = pathDir + BaseConstants.fileDir
                    try {
                        let isDirectory = fs.statSync(pathDir + BaseConstants.fileDir).isDirectory();
                    } catch (err) {
                        // 代表该文件夹不存在需要创建
                        if (err.code === 13900002) {
                            fs.mkdirSync(pathDir + BaseConstants.fileDir);
                        }
                    }
                    const uri = await CommonUtil.savePixmap2SystemFileManager(pixmap, filePath)
                    const fileUrl: string = fileUri.getUriFromPath(uri);
                    // 这种方式不需要权限
                    let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
                    // 需要确保fileUri对应的资源存在
                    let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest =
                        photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(context, fileUrl);
                    await phAccessHelper.applyChanges(assetChangeRequest);

                    // 通过getPhotoAccessHelper写入路径(这种方式也可以进行保存)
                    // let helper = photoAccessHelper.getPhotoAccessHelper(context);
                    // let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg')
                    // let file = await fileIo.open(uri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE)
                    // // 写入文件
                    // await fileIo.write(file.fd, buff);
                    // // 关闭文件
                    // await fileIo.close(file.fd);

                    resolve()
                } catch (error) {
                    Logger.info("save image fail", error)
                    reject()
                }
            })

        } else {
            ServiceAccessFilter.showToast("申请权限失败")
        }

    }

上次更新于: