【HarmonyOS NEXT】实现网络图片保存到手机相册
【问题描述】
给定一个网络图片的地址,实现将图片保存到手机相册
【API】
phAccessHelper.showAssetsCreationDialog
【官方文档】
https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-photoaccesshelper-V5#showassetscreationdialog12
【完整代码】
import { http } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
import util from '@ohos.util';
import { fileUri } from '@kit.CoreFileKit';
import fs, { ReadOptions } from '@ohos.file.fs';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { dataSharePredicates } from '@kit.ArkData';
import { promptAction } from '@kit.ArkUI';@Entry
@Component
struct SaveAlbum {@State message: string = 'Hello World';url1: string = "https://img20.360buyimg.com/img/jfs/t1/241153/31/4968/64736/65e53e56Fd3868b6e/b595d41ca8447ea4.jpg";url2: string ="https://upfile-drcn.platform.hicloud.com/ptTJ5B1eJ6k-d45UmOTF0Q.FYlVoBLGO3P9jZfjPUtqh2Cry9mQBzJButWu-okMhg2Xsd4zaoBTVAAsA08DPk1Vn7VFa1Mpl1Dp112CNKhEBjd4a9kP2NCKrQUpgq0HP_E3uqofnQ.6099200.png";build() {Column({ space: 30 }) {Text('保存到相册').fontSize(30)Column() {Text(this.url1)Button("保存").onClick(() => {this.downloadAndSave(this.url1)})}.margin({ top: 15, bottom: 15 })Column() {Text(this.url2)Button("保存").onClick(() => {this.downloadAndSave(this.url2);})}.margin({ top: 15, bottom: 15 })}.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center).height('100%').width('100%')}downloadAndSave(url: string) {const arr = url.split('.')const type = arr[arr.length -1]httpDownload(url, type).then((result: DownloadResult) => {if (result.isSuccess) {promptAction.showToast({ message: "下载成功" })} else {console.error("失败:" + result.msg);promptAction.showToast({ message: "下载失败❌,请查看日志" })}})}
}interface DownloadResult {isSuccess: boolean,msg: string
}async function httpDownload(imgUrl: string, imgType: string): Promise<DownloadResult> {return new Promise((resolve, reject) => {http.createHttp().request(imgUrl, async (error: BusinessError, data: http.HttpResponse) => { // 下载失败if (error) {return resolve({ isSuccess: false, msg: "下载失败" });} // 数据格式不正确if ((data.result instanceof ArrayBuffer) == false) {return resolve({ isSuccess: false, msg: "图片保存失败:数据流不支持" });} // 保存到Cache目录下let imageBuffer: ArrayBuffer = data.result as ArrayBuffer;const newFileName = util.generateRandomUUID() + "." + imgType;const newFilePath = getContext().cacheDir + "/" + newFileName;const newFileUri = fileUri.getUriFromPath(newFilePath);let file: fs.File = await fs.open(newFileUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);await fs.write(file.fd, imageBuffer);await fs.close(file);console.info("文件路径:" + newFileUri); // 保存成功 // resultData.status = 3; // return resolve(resultData);saveImageToAsset(newFileUri, imgType).then(() => { // 保存成功return resolve({ isSuccess: true, msg: "保存成功" });}).catch((error: Error) => { // 保存失败return resolve({ isSuccess: false, msg: "保存失败:" + error.message });});});})
}async function saveImageToAsset(uri: string, nameExtension: string): Promise<void> {console.info('ShowAssetsCreationDialogDemo: ' + uri);try {let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(getContext()); // 获取需要保存到媒体库的位于应用沙箱的图片/视频urilet srcFileUris: Array<string> = [uri];let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [{title: 'test2', // 可选fileNameExtension: nameExtension,photoType: photoAccessHelper.PhotoType.IMAGE, // 可选,支持:普通图片、动态图片subtype: photoAccessHelper.PhotoSubtype.DEFAULT,}];let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);console.info('showAssetsCreationDialog success, data is ' + desFileUris);if (desFileUris.length == 0) { // 用户拒绝保存throw (new Error("用户拒绝保存"))}await createAssetByIo(uri, desFileUris[0]);return Promise.resolve();} catch (err) {console.error('showAssetsCreationDialog failed, errCode is ' + err.code + ', errMsg is ' + err.message);return Promise.reject(err);}
}let context = getContext(this);
const createAssetByIo = async (sourceFilePath: string, targetFilePath: string) => {try {console.log(`context.fileDir ===> ${context.filesDir}`)let srcFile: fs.File = fs.openSync(sourceFilePath, fs.OpenMode.READ_ONLY);let targetFile: fs.File = await fs.open(targetFilePath, fs.OpenMode.READ_WRITE);let bufSize = 14096;let readSize = 0;let buf = new ArrayBuffer(bufSize);let readOptions: ReadOptions = { offset: readSize, length: bufSize };let readLen = fs.readSync(srcFile.fd, buf, readOptions);while (readLen > 0) {readSize += readLen;fs.writeSync(targetFile.fd, buf, { length: readLen });readOptions.offset = readSize;readLen = fs.readSync(srcFile.fd, buf, readOptions);}fs.closeSync(srcFile);fs.closeSync(targetFile);} catch (error) {console.error(`createAssetByIo :: error , msg is ${error} `);}
}
【效果图】



【其它问题】
关于授权窗,没显示图片缩略图的问题,官方有答复是下载最新版本的IDE
相关文章:

【HarmonyOS NEXT】实现网络图片保存到手机相册
【问题描述】 给定一个网络图片的地址,实现将图片保存到手机相册 【API】 phAccessHelper.showAssetsCreationDialog【官方文档】 https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-photoaccesshelper-V5#showassetscreationdialog…...

Pytorch详解-数据模块
Pytorch详解-数据模块 torch.utils.data.Dataset数据交互模块—Dataset的功能示例系列APIsconcatSubsetrandom_splitsampler unsqueeze DataLoaderDataLoader功能支持两种形式数据集读取自定义采样策略自动组装成批数据多进程数据加载自动实现锁页内存(Pinning Memo…...
浅谈openresty
熟悉了nginx后再来看openresty,不得不说openresty是比较优秀的。 对nginx和openresty的历史等在这此就不介绍了。 首先对标nginx,自然有优劣 一、开发难度 nginx: 毫无疑问nginx的开发难度比较高,需要扎实的c/c基础ÿ…...
【学习笔记】2024最新版SpringCloud教程
2024最新版SpringCloud教程 0 前言闲聊开篇简介 1 SpringBoot和SpringCloud版本选型 2 SpringCloud是什么能干吗 3 SpringCloud各组件的停更升级替换说明 4 项目实战之需求说明 5 项目实战之Maven父工程聚合说明和mysql驱动选择 6 项目实战之Mapper4一键生成Dao层代码 …...

Proxyless Service Mesh:下一代微服务架构体系
一、项目背景及意义 在当今的微服务架构中,应用程序通常被拆分成多个独立的服务,这些服务通过网络进行通信。这种架构的优势在于可以提高系统的可扩展性和灵活性,但也带来了新的挑战,比如: 服务间通信的复杂性&#…...

大数据Flink(一百一十八):SQL水印操作(Watermark)
文章目录 SQL水印操作(Watermark) 一、为什么要有WaterMark 二、Watermark解决的问题 三、代码演示 SQL水印操作(Watermark) 一、为什么要…...

【QGC】把QGroundControl地面站添加到Ubuntu侧边菜单栏启动
把QGroundControl地面站添加到Ubuntu侧边菜单栏启动 简介准备工作步骤 1: 创建 Desktop Entry 文件步骤 2: 编辑 Desktop Entry 文件步骤 3: 刷新应用程序菜单步骤 4: 将 QGroundControl 固定到侧边栏 环境: Ubuntu :20.04 LTS 简介 QGroundControl 是…...
PostgreSQL配置主从同步
PostgreSQL配置主从同步 1 主、备库安装postgresql软件 su - pg12 cd /home/pg12/resource tar -zxvf postgresql-12.9.tar.gz cd postgresql-12.9/ ./configure --prefix/home/pg12/soft/ make -j 16 && make install2 主、备库配置环境变量 vi ~/.bash_profile…...

基于python+django+vue的鲜花商城系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于pythondjangovueMySQL的线…...

李飞飞任CEO,空间智能公司World Labs亮相,全明星阵容曝光
人工智能的下个大方向已经出现,标志性学者决定下场创业。 本周五,一个重磅消息引爆了 AI 圈:斯坦福大学计算机科学家李飞飞正式宣布创办 AI 初创公司 ——World Labs,旨在向人工智能系统传授有关物理现实的深入知识。 李飞飞说道&…...
PyTorch详解-可视化模块
PyTorch详解-可视化模块 Tensorboard 基础与使用启动 TensorBoard访问 TensorBoard使用 TensorBoardSummaryWriter类介绍参数说明常用方法 CNN卷积核与特征图可视化参数说明返回值 混淆矩阵与训练曲线可视化混淆矩阵可视化训练曲线绘制 模型参数打印参数说明输出解释 Tensorboa…...

Bootstrap 警告信息(Alerts)使用介绍
本章将讲解警告(Alerts)以及 Bootstrap 所提供的用于警告的 class。警告(Alerts)向用户提供了一种定义消息样式的方式。它们为典型的用户操作提供了上下文信息反馈。 您可以为警告框添加一个可选的关闭按钮。为了创建一个内联的可…...

uniapp(H5)设置反向代理,设置成功后页面报错
设置反向代理后,页面报错图: 反向代理代码:devServer下面就是配置对应的代理,一般这样就没问题了 "h5": {"router": {"mode": "hash"},"devServer": {"port": 517…...
define、typedef和using的使用
define、typedef 和 using 是 C(以及 C 语言中的 define)中用于定义别名或简化复杂类型的三个关键字,但它们各自有着不同的用途和行为。下面将分别对比这三个关键字: 1. #define 定义方式:#define 是预处理指令&…...

vue element时间选择不能超过今天 时间选中长度不能超过7天
背景: 使用elenmet plus 组件实现时间选择;且日期时间选择不能超过今天;连续选中时间的长度范围不能超过7天 效果展示: 实现思路: 一、使用element组件自带的属性和方法; :disabled-date"disabledDate…...
如何 吧一个 一维数组 切分成相同等分,一维数组作为lstm的输入(三维数据)的数据预处理 collate_fn的应用
要将一个一维数组切分成相同等分,你可以使用 Python 的内置功能或者 NumPy 库(如果你处理的是数值数据)。以下是几种不同的方法: 方法3 pad_sequence 结合dataloader 应该是最佳方案 ### 方法 1: 使用 Python 的内置切片功能 如果…...
Remix 学习 - @remix-run/react 中主要的 hooks
在 remix-run/react 中,有几个常用的 hooks,它们帮助你在 Remix 应用中处理路由、数据加载和其他功能。以下是一些主要的 hooks: useLoaderData: 用于获取从 loader 函数中返回的数据。 通常在组件中调用,以便访问路由加载的数据…...

STL之stack
stack容器 - 先进后出” - stack是堆栈容器,是一种的容器。 - 头文件:#include <stack> stack的push()与pop()方法 stack.push(elem);//往栈头添加元素 stack.pop();//从栈头移除第一个元素 stack<int> stkInt; stkInt.push(1);stkInt…...

如何用3个月零基础入门网络安全?_网络安全零基础怎么学习
前 言 写这篇教程的初衷是很多朋友都想了解如何入门/转行网络安全,实现自己的“黑客梦”。文章的宗旨是: 1.指出一些自学的误区 2.提供客观可行的学习表 3.推荐我认为适合小白学习的资源.大佬绕道哈! →点击获取网络安全资料攻略← 一、自学…...

适合学生党开学买的蓝牙耳机?分享开放式耳机排行榜前十名
学生党开学想买耳机的话,我觉得比较适合入手开放式耳机,因为这类耳机佩戴舒适度高,长时间使用也不会感到不适或疲劳,同时保持耳道干爽透气,更加健康卫生,还能提供自然、开阔的音场,音质表现优秀…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...