【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()); // 获取需要保存到媒体库的位于应用沙箱的图片/视频uri
    let 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
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/874858.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ISO27001信息安全管理体系认证怎么做?

ISO27001认证是关于信息安全管理体系认证&#xff0c;ISO27001将有效保证企业在信息安全领域的可靠性&#xff0c;降低企业泄密风险&#xff0c;更好的保存核心数据。下面给大家详细讲讲ISO27001信息安全管理体系认证详细办理流程。 一、ISO27001信息安全管理体系认证的办理条…

【Centos】Centos系统换yum源

【Centos】Linux&#xff0c;Centos系统换yum源 1、备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak/etc/yum.repos.d/CentOS-Base.repo 是yum的配置文件 /etc/yum.repos.d/CentOS-Base.repo.bak 是我们备份的配置文件 2、下载yum源 这里…

【EI稳定,马来亚大学主办】2024年计算机与信息安全国际会议(WCCIS 2024,9月27-29)

2024年计算机与信息安全国际会议 (WCCIS 2024) 将于2024年9月27-29日召开。 会议旨在为从事计算机与信息安全的专家学者、工程技术人员、技术研发人员提供一个共享科研成果和前沿技术&#xff0c;了解学术发展趋势&#xff0c;拓宽研究思路&#xff0c;加强学术研究和探讨&…

【编译原理】编译器概述、编译器结构、编译器实例

编译器概述、编译器结构、编译器实例 编译器概述 1.编译器是一个程序 核心功能是把源代码翻译成目标代码 比如源代码&#xff1a;C/C&#xff0c;Java&#xff0c;C#&#xff0c;html 目标代码&#xff1a;X86&#xff0c;IA64,ARM,… 把一种源程序翻译成另外一种源程序&…

/bin/bash的作用

1、为啥使用不了很多命令&#xff1f; 我刚进入一个新系统&#xff1a; 我当时蒙蔽了&#xff0c;这是啥意思&#xff0c;为啥没命令? 原因是&#xff1a;当时进入的shell并没有初始化这些路径环境&#xff0c;所以正确的方法是&#xff1a; 2、/bin/bash运行的过程中执行…

Mac清理其他文件:释放存储空间的高效指南

每个Mac用户都可能遇到存储空间不足的问题&#xff0c;尤其是当“其他”文件积累到一定体积时。在Mac上&#xff0c;“其他”文件通常包括各种系统文件、缓存、文档以及不被归类为应用程序、照片、电影或音乐的其他类型的文件。这些文件往往不易被注意&#xff0c;但逐渐占用了…

C语言代码练习(第十八天)

今日练习&#xff1a; 48、猴子吃桃问题。猴子第1天摘下若干个桃子&#xff0c;当即吃了一半&#xff0c;还不过瘾&#xff0c;又多吃了一个。第2天早上又将剩下的桃子吃掉一半&#xff0c;又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时&…

TMGM:欧元区储蓄率处于结构性增高

法国的家庭储蓄率进一步上升&#xff0c;从2024年第一季度的家庭总可支配收入(GDI)的17.6%上升到2024年第二季度的17.9%&#xff0c;这信息来自于法国国家统计和经济研究所&#xff08;INSEE&#xff09;。这也是欧元区正在进行的上升趋势的早期迹象。虽然第二季度数字还没出来…

【C++】C++ STL探索:容器适配器 Stack 与 Queue 的使用及模拟实现

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现List使用及其模拟实现 本文将详细介绍如何使用容器适…

探索最佳 Shell 工具:全面测评 Bash、Zsh、Fish、Tcsh 和 Ksh

感谢浪浪云支持发布 浪浪云活动链接 &#xff1a;https://langlangy.cn/?i8afa52 文章目录 1. 简介2. 测评工具3. 测评标准4. Bash 测评4.1 易用性4.2 功能特性4.3 性能4.4 可定制性4.5 社区和支持 5. Zsh 测评5.1 易用性5.2 功能特性5.3 性能5.4 可定制性5.5 社区和支持 6. F…

3款数据恢复免费版软件评测:帮你轻松解决数据丢失问题

如今数字化时代&#xff0c;数据至关重要&#xff0c;珍贵照片、重要文档、成长记录的视频音频&#xff0c;承载回忆与努力。但数据丢失风险常伴&#xff0c;误删、格式化、病毒攻击等意外频发&#xff0c;令人陷入绝望&#xff0c;如坠数据黑洞。所幸科技发展&#xff0c;数据…

精益生产现场管理和改善的“蜕变”之旅:从理念到落地的全方位指南

精益生产现场管理和改善&#xff0c;正逐步成为众多企业转型升级的必经之路。今天&#xff0c;就让我们&#xff08;深圳天行健企业管理咨询公司&#xff09;带大家一起踏上这场从理念到落地的“蜕变”之旅&#xff0c;探索精益生产现场管理与改善的方方面面&#xff0c;为企业…

API安全 | 发现API的5个小tips

在安全测试目标时&#xff0c;最有趣的测试部分是它的 API。API 是动态的&#xff0c;它们比应用程序的其他部分更新得更频繁&#xff0c;并且负责许多后端繁重的工作。在现代应用程序中&#xff0c;我们通常会看到 REST API&#xff0c;但也会看到其他形式&#xff0c;例如 Gr…

游戏论坛网站|基于Springboot+vue的游戏论坛网站系统游戏分享网站(源码+数据库+文档)

游戏论坛|游戏论坛系统|游戏分享网站 目录 基于Springbootvue的游戏论坛网站系统游戏分享网站 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大…

JAVA同城服务系统大集结活动报名通道已开启构建你的同城圈子系统小程序源码

同城服务系统大集结&#xff0c;活动报名通道已开启&#xff01; &#x1f389; 【开篇号角】同城服务大狂欢&#xff0c;集结号已吹响&#xff01; 嘿&#xff0c;小伙伴们&#xff01;你们期待已久的同城服务系统大集结活动终于来啦&#xff01;&#x1f38a; 是的&#xff…

kubernetes微服务基础及类型

目录 1 什么是微服务 2 微服务的类型 3 ipvs模式 ipvs模式配置方式 4 微服务类型详解 4.1 ClusterIP 4.2 ClusterIP中的特殊模式headless 4.3 nodeport 4.4 metalLB配合loadbalance实现发布IP 1 什么是微服务 用控制器来完成集群的工作负载&#xff0c;那么应用如何暴漏出去&…

【卷起来】VUE3.0教程-08-路由管理

在Vue中&#xff0c;我们可以通过vue-router路由管理页面之间的关系。 Vue Router是Vue.js的官方路由&#xff0c;它与Vue.js核心深度集成&#xff0c;让用Vue.js构建单页应用变得轻而易举。 &#x1f332; 在Vue中引入路由 安装路由 npm install --save vue-router 建立三个…

详聊LLaMa技术细节:LLaMA大模型是如何炼成的?

本文介绍来自 Meta AI 的 LLaMa 模型&#xff0c;类似于 OPT&#xff0c;也是一种完全开源的大语言模型。LLaMa 的参数量级从 7B 到 65B 大小不等&#xff0c;是在数万亿个 token 上面训练得到。值得一提的是&#xff0c;LLaMa 虽然只使用公共的数据集&#xff0c;依然取得了强…

读论文-《基于计算机视觉的工业金属表面缺陷检测综述》

文章目录 1. 背景1.1 工业需求1.2 传统方法的局限1.3 计算机视觉技术的优势 2. 技术流程2.1 光学成像2.1.1照明方式2.1.2 缺陷和背景特性 2.2 图像预处理2.3 缺陷检测2.4 结果分析和决策 3. 关键算法3.1 光学成像技术相关算法3.2 图像预处理相关算法3.2.1 图像增强3.2.2特征提取…

【JS】将class转为构造函数需要注意的细节

前言 将 class 转为构造函数看似很简单&#xff0c;但作为封装者&#xff0c;有很多注意事项 class Person {constructor(name) {this.name name;}fn() { console.log(this.name); } }实现 初步转化如下&#xff1a; function Person() {this.name name } Person.prototy…