【HarmonyOS NEXT】实现网络图片保存到手机相册

news2024/11/15 3:27:58

【问题描述】
给定一个网络图片的地址,实现将图片保存到手机相册

【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
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【FastAPI】离线使用Swagger UI 或 国内网络如何快速加载Swagger UI

在FastAPI中&#xff0c;默认情况下&#xff0c;当应用启动时&#xff0c;Swagger UI 会通过在线加载 Swagger UI 的静态资源。这意味着如果应用运行在没有互联网连接的环境中&#xff0c;默认的 Swagger 文档页面将无法加载。 为了在离线环境中使用 Swagger UI&#xff0c;你…

二分思想与相关例题(上)

在上一篇浅谈二分思想中&#xff0c;我们谈到了提过二分的本质&#xff0c;其实就是不断折半&#xff0c;折到最后折无可折的那个结果就是最符合要求的结果。 现在我们从答案出发&#xff0c;对答案的整体可能范围不断二分&#xff0c;最后找到最合适的答案。我们称这种方法为…

Visual Studio安装教程

这次我给大家讲一下Visual Studio安装 一、官网下载 官网下载地址&#xff1a;https://visualstudio.microsoft.com/zh-hans/downloads/ 下载下来的是一个.exe文件 双击打开&#xff0c;出现下面的界面 二、安装visual studio &#xff08;一&#xff09;更改安装路径 首先&am…

Oracle数据恢复—Oracle数据库误删除表数据如何恢复数据?

删除Oracle数据库数据一般有以下2种方式&#xff1a;delete、drop或truncate。下面针对这2种删除oracle数据库数据的方式探讨一下oracle数据库数据恢复方法&#xff08;不考虑全库备份和利用归档日志&#xff09;。 1、delete误删除的数据恢复方法。 利用oracle提供的闪回方法…

小程序——生命周期

文章目录 运行机制更新机制生命周期介绍应用级别生命周期页面级别生命周期组件生命周期生命周期两个细节补充说明总结 运行机制 用一张图简要概述一下小程序的运行机制 冷启动与热启动&#xff1a; 小程序启动可以分为两种情况&#xff0c;一种是冷启动&#xff0c;一种是热…

202409011在飞凌的OK3588-C的核心板跑Rockchip原厂的Android12时挂载触摸屏ft5x06之后使用i2c-tools检测

1|console:/ # i2cdetect --version console:/ # i2cdetect -l console:/ # i2cdetect -F 7 console:/ # i2cdetect -a -y 7 1|console:/ # i2cdump --version console:/ # i2cdump -f -y 7 0x38 202409011在飞凌的OK3588-C的核心板跑Rockchip原厂的Android12时挂载触摸屏ft5x0…

基于SpringBoot+Vue+MySQL的瑜伽馆管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 本系统采用SpringBoot作为后端框架&#xff0c;Vue.js构建前端用户界面&#xff0c;MySQL作为数据库存储系统&#xff0c;实现了瑜伽馆的全面数字化管理。系统涵盖会员管理、课程预约、教练排班、收入统计等功能模块&#xff0c…

苍穹外卖Day01

文章目录 目录 文章目录 前端环境搭建 后端环境搭建 后端-数据库环境搭建 前后端联调 前端环境搭建 打开文件夹&#xff08;确保nginx在英文目录下&#xff09;双击ngnix.exe启动nginx服务&#xff0c;访问端口号80在地址栏输入localhost打开界面 后端环境搭建 熟悉项目…

Node.js运行环境搭建

【图书介绍】《Node.jsMongoDBVue.js全栈开发实战》-CSDN博客 《Node.jsMongoDBVue.js全栈开发实战&#xff08;Web前端技术丛书&#xff09;》(邹琼俊)【摘要 书评 试读】- 京东图书 (jd.com) 本节介绍如何搭建Node.js运行环境。 1.2.1 Node.js运行环境安装 进入Node.js官…

淘宝 npmmirror 镜像站的使用

1、访问网址 https://npmmirror.com/ 2、由于国内直接使用 npm 的官方镜像是非常慢的&#xff0c;推荐使用淘宝 NPM 镜像。 淘宝 NPM 镜像是一个完整 npmjs.org 镜像&#xff0c;可以用于代替官方版本。 3、操作方法也非常的简单&#xff0c;只需要在安装完npm后&#xff0…

数据结构:堆的算法

目录 一堆的向上调整算法二堆的向下调整算法三堆的应用:堆排序四TOPK问题 一堆的向上调整算法 我们在堆中插入一个数据一般实在堆的最后插入然后可以一步一步与上层结点&#xff08;父结点进行比较&#xff09;&#xff0c;继而进行交换&#xff0c;完成二叉树的结构&#xff0…

Spring Cloud之三 网关 Gateway

1:Intellij 新建项目 spring-cloud-gateway 2:pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLoca…

Vulnhub靶场 DC-2

靶机地址:https://www.vulnhub.com/entry/dc-2,311/ 导入到VMware里面去, 设置NAT模式 namp扫描一下c段获取ip地址, 然后再扫描ip地址获取详细的信息 得到ip 192.168.75.134 无法访问 按照下面这个方法可以访问了 在kali上的处理 flag1 网站上就存在 提示了一个cewl工具,…

【Unity踩坑】使用Input System后UI EventSystem的冲突

在项目中使用Input System&#xff0c;在UI中添加了元素后&#xff0c;再次运行出现下面的错误&#xff1a; InvalidOperationException: You are trying to read Input using the UnityEngine.Input class, but you have switched active Input handling to Input System pac…

基于SpringBoot+Vue+MySQL的考编论坛网站

系统展示 用户前台界面 管理员后台界面 系统背景 在当前信息化高速发展的时代&#xff0c;考编已成为众多求职者的重要选择。然而&#xff0c;备考过程中信息获取、经验交流及资源分享的需求日益凸显。基于SpringBoot、Vue.js与MySQL构建的考编论坛网站应运而生&#xff0c;旨在…

微软面向所有用户推出 Xbox Game Pass Standard

2024 年 8 月下旬&#xff0c;微软启动了 Xbox Game Pass Standard 的公开测试&#xff0c;这是其不断发展的 Game Pass 套餐中的一个新层级。几周后的今天&#xff0c;Xbox Game Pass 标准版已向支持地区的所有 Xbox 用户开放。 Xbox Game Pass 标准版每月收费 14.99 美元。以…

【可测试性实践】C++ 单元测试代码覆盖率统计入门

引言 最近在调研C工程怎么做单元测试和代码覆盖率统计&#xff0c;由于我们工程有使用Boost库&#xff0c;尝试使用Boost.Test来实现单元测试并通过Gcov和Lcov来生成代码覆盖率报告。本文记录完整的搭建测试Demo&#xff0c;希望能带来一定参考。 常用C单测框架对比 特性Goo…

实时系统资源监测:AutoPowerOptionsOK确保电脑性能与节能兼备

科技赋予生活翅膀&#xff0c;让我们在快节奏中翱翔&#xff0c;协调工作与梦想&#xff0c;让每一个梦想都有机会照进现实&#xff0c;绽放光彩——科技的进步不仅推动了社会的发展&#xff0c;也极大地改善了人们的日常生活。在众多科技成果中&#xff0c;电脑作为信息处理的…

Day8 | Java框架 | Maven

Day8 | Java框架 | Maven 分模块开发与设计分模块开发意义分模块开发步骤&#xff08;模块拆分&#xff09; 依赖管理概念依赖传递可选依赖与排除依赖 继承与聚合聚合继承 属性属性配置与使用资源文件引用属性版本管理 多环境配置与应用多环境开发跳过测试&#xff08;了解&…

场外期权是什么?个人可以参与场外期权交易吗?

今天期权懂带你了解场外期权是什么?个人可以参与场外期权交易吗?通过了解场外期权的特点、优点和缺点&#xff0c;投资者可以更好地决定是否使用这种工具进行风险管理或投资操作。 场外期权是什么? 场外期权是指通过私下协商而非在公开交易所交易的期权合约。以下是关于场…