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
- Method 1: Permission Request Solution
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:
"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 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';
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;
}
}
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 })
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"
}
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
- Use a Stack layout to build a custom download button.
- 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';
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)
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}`);
}
}
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.
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:
- Permission request solution:Provides full features but faces stricter app review. Suitable for apps that require an excellent user experience.
- 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.



Top comments (0)