HarmonyOS6 - 图片保存到图库中的两种方式

1. 前言

在鸿蒙HarmonyOS日常开发过程中,经常会遇到需要将图片保存到图库的场景,今天我们就来梳理一下,这样的场景,我们有哪些选择?具体代码该如何写?

开发环境为:

开发工具:DevEco Studio 6.0.1 Release

API版本是:API21

本文所有代码都已使用模拟器测试成功!

本文包含内容如下:

下面我们分别对以上两种方式进行实战代码编写

2. 实战案例

1. 需要申请相册权限

这种方式需要先在工程main目录下的【module.json5】文件中配置如下权限信息:

新增代码如下:

json 复制代码
//权限放行
"requestPermissions": [
    {
        //网络权限
        "name": "ohos.permission.INTERNET",
    },
    {
        "name": "ohos.permission.READ_IMAGEVIDEO",
        "usedScene": {
            "abilities": [
                "EntryAbility"
            ],
            "when": "inuse"
        },
        "reason": "$string:file_reason_desc"
    },
    {
        "name": "ohos.permission.WRITE_IMAGEVIDEO",
        "usedScene": {
            "abilities": [
                "EntryAbility"
            ],
            "when": "inuse"
        },
        "reason": "$string:file_reason_desc"
    }
]

1. 网络图片

如果你有一张网络中的图片需要保存到图库中,那么代码如下:

js 复制代码
import { abilityAccessCtrl } from '@kit.AbilityKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import fs from '@ohos.file.fs';
import { http } from '@kit.NetworkKit';
import { promptAction } from '@kit.ArkUI';

/**
 * Desc: 案例:保存网络图片到图库(需要申请相册权限)
 * Author: 波波老师(vx:javabobo0513)
 */
@Entry
@Component
struct Page01 {
  atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  //网络图片地址
  @State imageUrl: string = 'https://img0.baidu.com/it/u=3361482875,3939563024&fm=253&fmt=auto&app=138&f=JPEG';

  build() {
    Column({ space: 15 }) {

      //图片
      Image(this.imageUrl)
        .width('100%')
      
      //下载按钮
      Button('保存图片到图库')
        .onClick(() => {
          try {
            let context = getContext(this);
            // 申请相册管理模块权限 'ohos.permission.WRITE_IMAGEVIDEO'
            this.atManager.requestPermissionsFromUser(context, ['ohos.permission.WRITE_IMAGEVIDEO'])
              .then(async () => {
                // 权限申请成功,开始保存到图库
                // 获取相册管理模块的实例,用于访问和修改相册中的媒体文件
                let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);

                // 创建图片资源
                let uri = await phAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg');
                let file = fs.openSync(uri, fs.OpenMode.READ_WRITE || fs.OpenMode.CREATE);
                let totalSize = 0;

                // 创建HTTP请求
                let httpRequest = http.createHttp();

                // 监听数据接收
                httpRequest.on("dataReceive", (data: ArrayBuffer) => {
                  let writeLen = fs.writeSync(file.fd, data);
                  totalSize = totalSize + writeLen;
                });

                // 发起流式请求
                httpRequest.requestInStream(this.imageUrl, {
                  method: http.RequestMethod.GET,
                  connectTimeout: 3000,
                }, httpCode => {
                  console.info('requestInStream HTTP CODE is', httpCode)
                });

                // 监听下载完成
                httpRequest.on("dataEnd", () => {
                  fs.close(file);
                  promptAction.showToast({
                    message: "下载图片结束,并成功保存至相册!",
                    duration: 5000,
                    alignment: Alignment.Center,
                  });
                });
              });
          } catch (err) {
            console.error(`requestPermissionsFromUser call Failed! error: ${err.code}`);
            promptAction.openToast({
              message: `requestPermissionsFromUser call Failed! error: ${err.code}`,
              duration: 5000,
              alignment: Alignment.Center,
            }).catch(() => {
              // TODO: Implement error handling.
            });
          }
        })
    }
    .width('100%')
    .height('100%')
  }
}

页面效果:

2. 本地图片

如果你想将工程中【media】目录里的图片保存到图库中,那么代码如下:

js 复制代码
import { abilityAccessCtrl } from '@kit.AbilityKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import fs from '@ohos.file.fs';
import { promptAction } from '@kit.ArkUI';

/**
 * Desc: 案例:保存本地图片到图库(需要申请相册权限)
 * Author: 波波老师(vx:javabobo0513)
 */
@Entry
@Component
struct Page03 {
  atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  @State image: Resource = $r('app.media.cat')

  build() {
    Column({ space: 15 }) {

      //图片
      Image(this.image)
        .width('100%')
      //下载按钮
      Button('保存图片到图库')
        .onClick(() => {
          try {
            let context = getContext(this);
            // 申请相册管理模块权限 'ohos.permission.WRITE_IMAGEVIDEO'
            this.atManager.requestPermissionsFromUser(context, ['ohos.permission.WRITE_IMAGEVIDEO'])
              .then(async () => {
                //获取指定资源ID对应的媒体文件内容,使用同步方式返回
                let img = context.resourceManager.getMediaContentSync(this.image.id)
                // 获取相册管理模块的实例,用于访问和修改相册中的媒体文件
                let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);

                // 指定文件类型、后缀和创建选项,创建图片或视频资源
                let uri = await phAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg');
                // openSync:以同步方法打开文件
                let file = fs.openSync(uri, fs.OpenMode.READ_WRITE || fs.OpenMode.CREATE);
                // 写入文件,file.fd:文件的文件描述符
                fs.writeSync(file.fd, img.buffer);
                // 关闭文件
                await fs.close(file);
                promptAction.showToast({
                  message: "图片成功保存至相册!",
                  duration: 5000,
                  alignment: Alignment.Center,
                });
              });
          } catch (err) {
            console.error(`requestPermissionsFromUser call Failed! error: ${err.code}`);
            promptAction.openToast({
              message: `requestPermissionsFromUser call Failed! error: ${err.code}`,
              duration: 5000,
              alignment: Alignment.Center,
            }).catch(() => {
              // TODO: Implement error handling.
            });
          }
        })
    }
    .width('100%')
    .height('100%')
  }
}

如果你想将工程中【rawfile】目录里的图片保存到图库中,那么代码如下:

js 复制代码
import { abilityAccessCtrl } from '@kit.AbilityKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import fs from '@ohos.file.fs';
import { promptAction } from '@kit.ArkUI';

/**
 * Desc: 案例:保存本地图片到图库(需要申请相册权限)
 * Author: 波波老师(vx:javabobo0513)
 */
@Entry
@Component
struct Page03 {
  atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  @State image: Resource = $rawfile('cat.jpg')

  // @State image: Resource = $r('app.media.cat')

  build() {
    Column({ space: 15 }) {

      //图片
      Image(this.image)
        .width('100%')
      //下载按钮
      Button('保存图片到图库')
        .onClick(() => {
          try {
            let context = getContext(this);
            // 申请相册管理模块权限 'ohos.permission.WRITE_IMAGEVIDEO'
            this.atManager.requestPermissionsFromUser(context, ['ohos.permission.WRITE_IMAGEVIDEO'])
              .then(async () => {
                //获取resources/rawfile目录下对应的rawfile文件内容,使用同步形式返回
                let img = context.resourceManager.getRawFileContentSync('cat.jpg')
                // 权限申请成功,开始保存到图库
                // 获取相册管理模块的实例,用于访问和修改相册中的媒体文件
                let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);

                // 指定文件类型、后缀和创建选项,创建图片或视频资源
                let uri = await phAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg');
                // openSync:以同步方法打开文件
                let file = fs.openSync(uri, fs.OpenMode.READ_WRITE || fs.OpenMode.CREATE);
                // file.fd:文件的文件描述符
                fs.writeSync(file.fd, img.buffer);
                fs.close(file);
                promptAction.showToast({
                  message: "图片成功保存至相册!",
                  duration: 5000,
                  alignment: Alignment.Center,
                });
              });
          } catch (err) {
            console.error(`requestPermissionsFromUser call Failed! error: ${err.code}`);
            promptAction.openToast({
              message: `requestPermissionsFromUser call Failed! error: ${err.code}`,
              duration: 5000,
              alignment: Alignment.Center,
            }).catch(() => {
              // TODO: Implement error handling.
            });
          }
        })
    }
    .width('100%')
    .height('100%')
  }
}

页面效果:

3. 注意点

该方式缺点是:需要申请相册权限,应用上架时 ACL 权限审核较为严格,审核通过率较低,所以这里不推荐这种方式

2. 无需申请相册权限-推荐

这种方式不需要申请相关权限,所以没有第一种方式的缺点,也是官方推荐的方式。唯一的缺点就是默认的UI样式比较丑,下面我们用案例代码来感受一下

注意:此种方式只需要在【module.json5】文件中配置【ohos.permission.INTERNET】权限即可,不需要配置【ohos.permission.READ_IMAGEVIDEO】和【ohos.permission.WRITE_IMAGEVIDEO】权限

1. 网络图片

如果你有一张网络中的图片需要保存到图库中,那么代码如下:

js 复制代码
import { common } from '@kit.AbilityKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { promptAction } from '@kit.ArkUI';
import { http } from '@kit.NetworkKit';
import fileIo from '@ohos.file.fs';

/**
 * Desc: 案例:保存网络图片到图库(不需要申请相册权限)
 * Author: 波波老师(vx:javabobo0513)
 */
@Entry
@Component
struct Page04 {
  //网络图片地址
  @State imageUrl: string = 'https://img0.baidu.com/it/u=3361482875,3939563024&fm=253&fmt=auto&app=138&f=JPEG';

  build() {
    Column({ space: 15 }) {

      //图片
      Image(this.imageUrl)
        .width('100%')

      //下载按钮(免去权限申请和权限请求等环节,获得临时授权,保存对应图片)
      SaveButton({ icon: SaveIconStyle.FULL_FILLED, text: SaveDescription.SAVE_IMAGE })
        .onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => {
          if (result === SaveButtonOnClickResult.SUCCESS) {
            try {
              const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
              // 使用 request 下载图片并在回调函数中保存图片到相册
              http.createHttp().request(this.imageUrl,
                {
                  method: http.RequestMethod.GET,
                  connectTimeout: 60000,
                  readTimeout: 60000
                },
                async (error: BusinessError, data: http.HttpResponse) => {
                  if (error) {
                    console.error(`http reqeust failed with. Code: ${error.code}, message: ${error.message}`);
                  } else {
                    if (http.ResponseCode.OK === data.responseCode) {
                      let imageBuffer: ArrayBuffer = data.result as ArrayBuffer;
                      try {
                        // 获取相册管理模块的实例,用于访问和修改相册中的媒体文件
                        let helper = photoAccessHelper.getPhotoAccessHelper(context);
                        // 指定文件类型、后缀和创建选项,创建图片或视频资源
                        let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg')
                        // openSync:以同步方法打开文件
                        let file = fileIo.openSync(uri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE)
                        // 写入文件,file.fd:文件的文件描述符
                        await fileIo.write(file.fd, imageBuffer);
                        // 关闭文件
                        await fileIo.close(file.fd);
                        promptAction.openToast({
                          message: '图片已成功保存至相册!',
                          duration: 4000
                        });
                      } catch (error) {
                        console.error("error is " + JSON.stringify(error))
                        promptAction.openToast({
                          message: "error is " + JSON.stringify(error),
                          duration: 4000
                        });
                      }
                    } else {
                      console.error("error occurred when image downloaded!")
                      promptAction.showToast({ message: 'error occurred when image downloaded!' });
                    }
                  }
                })
            } catch (err) {
              console.error(`create asset failed with error: ${err.code}, ${err.message}`);
            }
          } else {
            promptAction.openToast({
              message: '设置权限失败!',
              duration: 5000
            });
          }
        })
        .width(130)
        .height(35)
    }
    .width('100%')
    .height('100%')
  }
}

2. 本地图片

如果你想将工程【rawfile】目录中的图片保存到图库中,那么代码如下:

js 复制代码
import { common } from '@kit.AbilityKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import fs from '@ohos.file.fs';
import { promptAction } from '@kit.ArkUI';

/**
 * Desc: 案例:保存本地图片到图库(不需要申请相册权限)
 * Author: 波波老师(vx:javabobo0513)
 */
@Entry
@Component
struct Page02 {
  //图片名称
  @State imageName: string = 'cat.jpg'

  build() {
    Column({ space: 15 }) {

      //图片
      Image($rawfile(this.imageName))
        .width('100%')
      //下载按钮(免去权限申请和权限请求等环节,获得临时授权,保存对应图片)
      SaveButton({ icon: SaveIconStyle.FULL_FILLED, text: SaveDescription.SAVE_IMAGE })
        .onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => {
          if (result === SaveButtonOnClickResult.SUCCESS) {
            try {
              const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;

              // 免去权限申请和权限请求等环节,获得临时授权,保存对应图片
              let helper = photoAccessHelper.getPhotoAccessHelper(context);

              try {
                // onClick触发后5秒内通过createAsset接口创建图片文件,5秒后createAsset权限收回
                let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg');

                // 使用uri打开文件,可以持续写入内容,写入过程不受时间限制
                let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
                let content: Uint8Array | undefined =
                  context?.resourceManager.getRawFileContentSync('rawfile/' + this.imageName)
                await fs.write(file.fd, content?.buffer);
                await fs.close(file.fd);

                promptAction.openToast({
                  message: '图片已成功保存至相册!',
                  duration: 4000
                });
              } catch (error) {
                promptAction.openToast({
                  message: '图片保存至相册失败:' + JSON.stringify(error),
                  duration: 4000
                });
              }
            } catch (err) {
              console.error(`create asset failed with error: ${err.code}, ${err.message}`);
            }
          } else {
            promptAction.openToast({
              message: '设置权限失败!',
              duration: 5000
            });
          }
        })
        .width(130)
        .height(35)
    }
    .width('100%')
    .height('100%')
  }
}

图片存放路径如下:

如果你想将工程【media】目录中的图片保存到图库中,那么代码如下:

js 复制代码
import { common } from '@kit.AbilityKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import fs from '@ohos.file.fs';
import { promptAction } from '@kit.ArkUI';

/**
 * Desc: 案例:保存本地图片到图库(不需要申请相册权限)
 * Author: 波波老师(vx:javabobo0513)
 */
@Entry
@Component
struct Page02 {
  //图片名称
  @State image: Resource = $r('app.media.cat')

  build() {
    Column({ space: 15 }) {

      //图片
      Image(this.image)
        .width('100%')
      //下载按钮
      SaveButton({ icon: SaveIconStyle.FULL_FILLED, text: SaveDescription.SAVE_IMAGE })
        .onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => {
          console.log('result======1=' + result)
          console.log('result======2=' + JSON.stringify(result))
          if (result === SaveButtonOnClickResult.SUCCESS) {
            try {
              const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;

              // 免去权限申请和权限请求等环节,获得临时授权,保存对应图片
              let helper = photoAccessHelper.getPhotoAccessHelper(context);

              try {
                // 指定文件类型、后缀和创建选项,创建图片或视频资源。5秒后createAsset权限收回
                let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg');

                // 使用uri打开文件,可以持续写入内容,写入过程不受时间限制
                let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
                let content: Uint8Array | undefined =
                  context?.resourceManager.getMediaContentSync(this.image.id)
                // 写入文件,file.fd:文件的文件描述符
                await fs.write(file.fd, content?.buffer);
                // 关闭文件
                await fs.close(file.fd);

                promptAction.openToast({
                  message: '图片已成功保存至相册!',
                  duration: 4000
                });
              } catch (error) {
                promptAction.openToast({
                  message: '图片保存至相册失败:' + JSON.stringify(error),
                  duration: 4000
                });
              }
            } catch (err) {
              console.error(`create asset failed with error: ${err.code}, ${err.message}`);
            }
          } else {
            promptAction.openToast({
              message: '设置权限失败!',
              duration: 5000
            });
          }
        })
        .width(130)
        .height(35)
    }
    .width('100%')
    .height('100%')
  }
}

图片存放路径如下:

页面效果如下:

3. 小结

本文通过实战小案例代码演示了两种保存图片到图库的方式,大家可以根据自己项目的实际情况选择一种方式实现图片保存操作。

最后,希望这篇文章能够帮助到各位朋友!如果有任何问题,欢迎在评论区交流讨论!

相关推荐
行者962 小时前
用Flutter打造适配OpenHarmony的打卡组件:实践与优化
flutter·harmonyos·鸿蒙
lili-felicity3 小时前
React Native 鸿蒙跨平台开发:useColorScheme 自定义鸿蒙端深色模式的主题配色
react native·react.js·harmonyos
小雨下雨的雨3 小时前
Flutter跨平台开发实战: 鸿蒙与循环交互艺术:虚拟列表与百万级数据性能巅峰
flutter·华为·交互·harmonyos·鸿蒙系统
小雨下雨的雨3 小时前
Flutter跨平台开发实战: 鸿蒙与循环交互艺术:Sliver 视差滚动与沉浸式布局
flutter·华为·交互·harmonyos·鸿蒙系统
kirk_wang3 小时前
Flutter audioplayers 库鸿蒙平台适配实战:从原理到优化
flutter·移动开发·跨平台·arkts·鸿蒙
前端世界4 小时前
鸿蒙系统中的分布式任务依赖是如何处理的?原理、方案与实践
分布式·华为·harmonyos
小雨下雨的雨4 小时前
Flutter跨平台开发实战: 鸿蒙与循环交互艺术:卡片堆叠与叠放切换动效
flutter·华为·交互·harmonyos·鸿蒙系统
小雨下雨的雨4 小时前
Flutter跨平台开发实战: 鸿蒙与循环交互艺术:分布式联动与多端状态同步
分布式·flutter·华为·交互·harmonyos·鸿蒙系统
哈__4 小时前
React Native 鸿蒙跨平台开发:ToastAndroid 提示消息
react native·react.js·harmonyos