DEV Community

HarmonyOS
HarmonyOS

Posted on

HarmonyOS Practical Guide: Two Implementation Methods for Saving Online Images to the Gallery

Read the original article:HarmonyOS Practical Guide: Two Implementation Methods for Saving Online Images to the Gallery

Contents

  • Preface

  • Core Feature Implementation

    • Method 1: Permission Request Solution
      • 1.1 Permission Configuration
      • 1.2 Module Import
      • 1.3 Core Feature Implementation
      • 1.4 UI Component Usage
      • 1.5 Data Type Definition
    • Method 2: Permission-Free Solution
      • UI Customization Solution
      • 2.1 Module Import
      • 2.2 UI Component Implementation
      • 2.3 Core Feature Encapsulation
      • 2.4 Implementation Description
  • Solution Comparison and Analysis

  • Best Practice Recommendations

    • Selection Recommendations
  • Summary

Preface

Hello everyone, I am Ruocheng. This series is dedicated to helping developers quickly implement commonly used features in HarmonyOS apps, providing ready-to-use code examples.

In this article, we will go through two different methods for saving online images to the gallery:

  • Method 1: Requires gallery access permission, but ACL approval is relatively strict during app submission.
  • Method 2: Doesn't require permission requests; users can manually authorize access.

Let us dive into both solutions and see how they work in practice.

Core Feature Implementation

Method 1: Permission Request Solution

Important note: This method requires ACL permission during app release, which has a low approval rate. In real testing, 3 consecutive submissions were rejected.

1.1 Permission Configuration

First, configure the required permissions in the module.json5 file:

 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"
  }
]
Enter fullscreen mode Exit fullscreen mode

1.2 Module Import

Import the required feature modules:

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';
Enter fullscreen mode Exit fullscreen mode

1.3 Core Feature Implementation

A complete function encapsulation for downloading online images and saving them to the gallery::

// Save image to the gallery
async saveFile(url: string) {
  this.downLoadImg = true;

  try {
    // Request the permission for the gallery management module: 'ohos.permission.WRITE_IMAGEVIDEO'
    this.atManager.requestPermissionsFromUser(this.appContext, ['ohos.permission.WRITE_IMAGEVIDEO'])
      .then(async () => {
        //Permission granted, proceed to save the image
        let context = getContext();

        // Get an instance of the gallery management module for accessing and modifying media files in the album
        let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);

        // Display download toast (Note: After onClick is triggered, the createAsset API can be used within 10 seconds to create an image file.)
        this.promptAction.showToast({
          message: 'Downloading image... .',
          duration: 2000
        });

        // Create image resources
        let uri = await phAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg');
        let file = fs.openSync(uri, fs.OpenMode.READ_WRITE || fs.OpenMode.CREATE);
        let totalSize = 0;

        // Create HTTP request
        let httpRequest = http.createHttp();

        // Set up data reception listener
        httpRequest.on("dataReceive", (data: ArrayBuffer) => {
          let writeLen = fs.writeSync(file.fd, data);
          totalSize = totalSize + writeLen;
        });

        // Initiate streaming request
        httpRequest.requestInStream(url, {
          method: http.RequestMethod.GET,
          connectTimeout: 3000,
        }, httpCode => {
          console.info('requestInStream HTTP CODE is', httpCode)
        });

        // Set up download completion listener
        httpRequest.on("dataEnd", () => {
          fs.close(file);
          this.promptAction.showToast({
            message: "Image download completed and saved to the gallery",
            duration: 2000,
            alignment: Alignment.Center,
          });
          this.downLoadImg = false;
        });
      });
  } catch (err) {
    console.error(`requestPermissionsFromUser call Failed! error: ${err.code}`);
    this.downLoadImg = false;
  }
}
Enter fullscreen mode Exit fullscreen mode

1.4 UI Component Usage

// Download button component
Column() {
  Image($r("app.media.downImg"))
    .width(24)
    .height(24)
  Text('Download')
    .fontSize(12)
    .fontColor('#FFFFFF')
    .margin({ top: 4 })
}
.onClick(() => {
  if (this.downLoadImg) {
    promptAction.showToast({
      message: 'Downloading, please wait',
      duration: 1000,
    });
  } else {
    this.saveFile(this.dataList.pic_url)
  }
})
.margin({ right: 32 })
Enter fullscreen mode Exit fullscreen mode

1.5 Data Type Definition

@State dataList: Poem = {
  "_id": "68962176fb8dca9bad565162",
  "date": "20250810",
  "author": "Lorca",
  "content": "I will go far, beyond these hills and seas, until I am close to the stars.",
  "from": "Poet",
  "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"
}
Enter fullscreen mode Exit fullscreen mode

Core points: The code above demonstrates the complete flow of saving an online image to the gallery, with the image URL passed as a function parameter.


Method 2: Permission-Free Solution

This method does not require any explicit permission requests and is officially recommended. Although the default UI customization is limited, you can improve it with the following workaround.

UI Customization Solution

  1. Use a Stack layout to build a custom download button.
  2. Make the default SaveButton UI completely transparent.

2.1 Module Import

Import the required feature packages:

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

2.2 UI Component Implementation

Use the system-provided SaveButton component:

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: 'Permission setting failed.',
        duration: 2000
      });
    }
  })
  .fontColor('#FFFFFF')
  .iconSize(12)
  .width(24)
  .height(24)
  .backgroundColor('#4CD964')
  .padding(5)
Enter fullscreen mode Exit fullscreen mode

2.3 Core Feature Encapsulation

// Save image to the gallery
async saveFile(url: string) {
  this.downLoadImg = true;
  promptAction.showToast({
    message: 'Downloading image',
    duration: 1000
  });

  try {
    const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;

    // Skip explicit permission request by getting temporary authorization to save the image
    let helper = photoAccessHelper.getPhotoAccessHelper(context);

    try {
      // After the onClick is triggered, the createAsset API can be used within 5 seconds to create an image file. The temporary authorization will expire after 5 seconds.
      let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg');

      // Use the URI to open the file and write data continuously without time limits
      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: 'Image saved to the gallery',
        duration: 2000
      });
      this.downLoadImg = false
    } catch (error) {
      this.downLoadImg = false
      promptAction.showToast({
        message: 'Failed to save image',
        duration: 2000
      });
    }
  } catch (err) {
    this.downLoadImg = false
    console.error(`create asset failed with error: ${err.code}, ${err.message}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

2.4 Implementation Description

Important note: In this example, the second method does not use online images directly.
Instead, it demonstrates the process using built-in images from the app. Images must be stored in the rawfile folder, and the parameter passed in is actually the image name.

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


Solution Comparison and Analysis

Feature Method 1 (Permission Request) Method 2 (Permission-Free)
Permission Requirement Requires ACL permission No permission required
App Review Difficulty Strict, low approval rate Easy to pass review
UI Customization Fully customizable Limited, requires UI tricks
User Experience One-click download Requires manual authorization
Online Image Support Fully supported Requires additional handling
Development Complexity Moderate Simple

Best Practice Recommendations

Selection Recommendations

  • Recommended Method 2:For most use cases, it is recommended to use a permission-free solution to avoid issues during app review
  • Use Method 1 for special use cases:If your app places a high emphasis on user experience and you are confident about passing the ACL permission review.

Summary

This article detailed two solutions to save online images to the gallery in HarmonyOS:

  1. Permission request solution:Provides full features but faces stricter app review. Suitable for apps that require an excellent user experience.
  2. Permission-free solution:Easier to develop and review-friendly, making it the preferred choice for most apps.

By choosing the right solution, developers can ensure both functionality and a smooth app approval process. It is recommended to select an appropriate solution based on specific needs and project characteristics in actual development.

We hope this article can be helpful to those who are developing HarmonyOS apps. Feel free to leave comments or questions below for further discussion.

Written by Full-stack Developer Ruocheng

Top comments (0)