HarmonyOS Next 干货分享:网络图片保存到相册的两种实现方案 原创 精华

全栈若城
发布于 2025-9-10 10:32
浏览
1收藏

@toc

前言

大家好,我是若城。本系列专注于帮助开发者快速实现 HarmonyOS Next 应用中的常用功能,提供即拿即用的代码示例。

本篇文章将详细介绍两种将网络图片保存到相册的实现方法:

  • 方法一:需要申请相册权限,但在应用上架时 ACL 权限审核较为严格
  • 方法二:无需申请相册权限,通过用户手动授权实现

让我们开始深入了解这两种方案的具体实现!

核心功能实现

方法一:权限申请方案

重要提醒:此方法在应用上架过程中需要申请 ACL 权限,审核通过率较低。根据实际测试,连续提交 3 次申请均被拒绝。

1.1 权限配置

首先在 module.json5 文件中配置所需权限:

 HarmonyOS Next 干货分享:网络图片保存到相册的两种实现方案-鸿蒙开发者社区

 HarmonyOS Next 干货分享:网络图片保存到相册的两种实现方案-鸿蒙开发者社区

"requestPermissions": [
  {
    "name": "ohos.permission.INTERNET"
  },
  {
    "name": "ohos.permission.READ_IMAGEVIDEO",
    "usedScene": {
      "abilities": [
        "EntryAbility"
      ],
      "when": "inuse"
    },
    "reason": "$string:CAMERA"
  },
  {
    "name": "ohos.permission.WRITE_IMAGEVIDEO",
    "usedScene": {
      "abilities": [
        "EntryAbility"
      ],
      "when": "inuse"
    },
    "reason": "$string:CAMERA"
  }
]

1.2 模块导入

导入所需的功能模块:

import { abilityAccessCtrl, common } from '@kit.AbilityKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import fs from '@ohos.file.fs';
import { http } from '@kit.NetworkKit';
import { promptAction } from '@kit.ArkUI';

1.3 核心功能实现

下载网络图片并保存到相册的完整函数封装:

// 保存图片到相册
async saveFile(url: string) {
  this.downLoadImg = true;
  
  try {
    // 申请相册管理模块权限 'ohos.permission.WRITE_IMAGEVIDEO'
    this.atManager.requestPermissionsFromUser(this.appContext, ['ohos.permission.WRITE_IMAGEVIDEO'])
      .then(async () => {
        // 权限申请成功,开始保存到图库
        let context = getContext();
        
        // 获取相册管理模块的实例,用于访问和修改相册中的媒体文件
        let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
        
        // 显示下载提示(注意:onClick触发后10秒内通过createAsset接口创建图片文件,10秒后权限收回)
        this.promptAction.showToast({
          message: '图片下载中....',
          duration: 2000
        });

        // 创建图片资源
        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(url, {
          method: http.RequestMethod.GET,
          connectTimeout: 3000,
        }, httpCode => {
          console.info('requestInStream HTTP CODE is', httpCode)
        });
        
        // 监听下载完成
        httpRequest.on("dataEnd", () => {
          fs.close(file);
          this.promptAction.showToast({
            message: "下载图片结束,并保存至相册",
            duration: 2000,
            alignment: Alignment.Center,
          });
          this.downLoadImg = false;
        });
      });
  } catch (err) {
    console.error(`requestPermissionsFromUser call Failed! error: ${err.code}`);
    this.downLoadImg = false;
  }
}

1.4 UI 组件使用

// 下载按钮组件
Column() {
  Image($r("app.media.downImg"))
    .width(24)
    .height(24)
  Text('下载')
    .fontSize(12)
    .fontColor('#FFFFFF')
    .margin({ top: 4 })
}
.onClick(() => {
  if (this.downLoadImg) {
    promptAction.showToast({
      message: '正在下载中,请稍后',
      duration: 1000,
    });
  } else {
    this.saveFile(this.dataList.pic_url)
  }
})
.margin({ right: 32 })

1.5 数据类型定义

@State dataList: Poem = {
  "_id": "68962176fb8dca9bad565162",
  "date": "20250810",
  "author": "洛尔迦",
  "content": "我会走得很远,远过这些山丘,远过这些大海,直到靠近星星。",
  "from": "诗人",
  "like": 117,
  "pic_url": "https://pics.tide.moreless.io/dailypics/FnDB9yZb_8lAL9tvw2Ug3x1AO6Dh?imageView2/1/w/1366/h/768/format/webp",
  "share": 114,
  "thumb": "https://pics.tide.moreless.io/dailypics/FnDB9yZb_8lAL9tvw2Ug3x1AO6Dh?imageView2/1/w/1366/h/768/format/webp?imageView2/1/w/300/h/300/format/webp"
}

核心要点:上述代码详细展示了如何将网络图片保存到相册的完整流程,函数传参为网络图片的 URL 地址。


方法二:免权限申请方案

第二种方法无需申请相关权限,是官方推荐的实现方式。虽然默认 UI 样式定制性有限,但我们可以通过以下技巧来解决:

UI 定制解决方案

  1. 使用 Stack 布局,正常编写自定义下载按钮 UI
  2. SaveButton 默认 UI 进行完全透明化处理

2.1 模块导入

引入所需的功能包:

import { abilityAccessCtrl, common } from '@kit.AbilityKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import fs from '@ohos.file.fs';
import { promptAction } from '@kit.ArkUI';

2.2 UI 组件实现

使用系统提供的 SaveButton 组件:

SaveButton({ icon: SaveIconStyle.FULL_FILLED })
  .onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => {
    if (result === SaveButtonOnClickResult.SUCCESS) {
      this.saveFile(this.dataList.date)
    } else {
      this.downLoadImg = false
      promptAction.showToast({
        message: '设置权限失败!',
        duration: 2000
      });
    }
  })
  .fontColor('#FFFFFF')
  .iconSize(12)
  .width(24)
  .height(24)
  .backgroundColor('#4CD964')
  .padding(5)

2.3 核心功能封装

// 保存图片到相册
async saveFile(url: string) {
  this.downLoadImg = true;
  promptAction.showToast({
    message: '图片下载中',
    duration: 1000
  });
  
  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(`${url}.jpg`)
      await fs.write(file.fd, content?.buffer);
      await fs.close(file.fd);
      
      promptAction.showToast({
        message: '图片已保存至相册',
        duration: 2000
      });
      this.downLoadImg = false
    } catch (error) {
      this.downLoadImg = false
      promptAction.showToast({
        message: '图片保存失败',
        duration: 2000
      });
    }
  } catch (err) {
    this.downLoadImg = false
    console.error(`create asset failed with error: ${err.code}, ${err.message}`);
  }
}

2.4 实现说明

重要说明:第二种方法示例中并未直接使用网络图片,而是使用应用内置图片进行演示。图片需要存放在 rawfile 文件夹中,传递的参数实际上是图片的名称。

 HarmonyOS Next 干货分享:网络图片保存到相册的两种实现方案-鸿蒙开发者社区


方案对比分析

特性 方法一(权限申请) 方法二(免权限)
权限要求 需要申请 ACL 权限 无需申请权限
上架难度 审核严格,通过率低 审核容易通过
UI 定制性 完全自定义 受限,需要技巧处理
用户体验 一键下载 需要用户手动授权
网络图片支持 完整支持 需要额外处理
开发复杂度 中等 简单

最佳实践建议

选择建议

  • 推荐方法二:对于大多数应用场景,建议使用免权限方案,避免上架审核问题
  • 特殊场景使用方法一:如果应用对用户体验要求极高,且有把握通过 ACL 权限审核

总结

本文详细介绍了 HarmonyOS Next 中实现网络图片保存到相册的两种方案:

  1. 权限申请方案:功能完整但上架审核严格,适合对用户体验要求极高的场景
  2. 免权限方案:开发简单、审核友好,是大多数应用的最佳选择

通过合理选择实现方案,开发者可以在保证功能完整性的同时,确保应用能够顺利上架。建议在实际开发中根据具体需求和项目特点选择合适的方案。

希望这篇文章能够帮助到正在开发 HarmonyOS Next 应用的朋友们!如果有任何问题,欢迎在评论区交流讨论。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
2
收藏 1
回复
举报
回复
    相关推荐