当前位置: 首页 > news >正文

HarmonyOS 6 云开发-用户头像上传云存储

背景

在之前的文章中提到,我们通过Account Kit拿到用户的头像和名称,在软件开发中,我们需要拿到用户信息,需要把用户ID、名字、头像信息存储到云数据库和云存储中。这篇文章主要讲解怎样子获取用户的头像后,并上传用户头像到云存储的过程。

操作流程

  • 用户登录软件,使用AGC的认证服务和华为账号登录。
  • 使用Account Kit 获取用户的头像和名称。
  • 将头像文件从临时链接下载到本地沙箱,再从沙箱中将文件上传到云存储。
  • 获取云存储文件链接,修改AGC账户管理的名称和图片链接。
  • 测试再次打开自动获取图片地址,并下载头像文件到沙箱中,并展示出来。

开发准备

  • 开通AGC云存储服务

image-20250728111201128

  • 认证服务开启华为账号认证(现在手机号码认证需要自己购买第三方服务的形式),这里需要填写Client ID和Client Secret

image-20250728112434902

0000000000011111111.20250627154852.61152385488059591863570031386314:50001231000000:2800:C98984DDE35B8E2F9EB2D1F7511E485FC620D7902B87BB1A87EC895ED7F20F74.png

  • 检查是否已经添加SHA256证书/公钥指纹,没有的时候需要先添加上。

image-20250728145119965

  • 在配置完成后,回到项目设置页面,下载配置文件 “agconnect-services.json” ,放置到项目的AppScope/resources/rawfile目录下,如果没有rawfile文件夹,可以手动创建。需要注意,每次AGC项目设置有变动时,都需要重新下载配置文件。

image-20250728112552591

image-20250728112857423

  • 配置Client ID,在entry模块的“module.json5”文件中,新增metadata,配置name为client_id,value为AGC中获取的Client ID的值。

image-20250728145509030

image-20250728145620976

配置用户认证SDK

  • 在项目的Terminal中,把地址切换到entry文件夹
cd .\entry\

image-20250728113837390

  • 运行安装SDK命令
ohpm install @hw-agconnect/auth

image-20250728113931081

  • 在Ability的onCreate方法中初始化,主要代码如下展示
import auth from '@hw-agconnect/auth';
export default class EntryAbility extends UIAbility {onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {let file = this.context.resourceManager.getRawFileContentSync('agconnect-services.json');let json: string = buffer.from(file.buffer).toString();auth.init(this.context, json);}
}

image-20250728114312853

使用华为账号登录

登录账号,并完成云服务的初始化。登录方式有很多种,这里选择华为账号登录登录创建用户为了方便演示作用。

      Button("登录").width("80%").onClick(async () => {//用户登录let user = await auth.getCurrentUser();if (!user) {try {let signInResult: SignInResult = await auth.signIn({autoCreateUser: true,"credentialInfo": {"kind": 'hwid'}});this.ShowMessage("用户登录成功")} catch (e) {console.info(e)}}cloudCommon.init({region: cloudCommon.CloudRegion.CHINA,authProvider: auth.getAuthProvider(),functionOptions: { timeout: 10 * 1000 },storageOptions: { mode: request.agent.Mode.BACKGROUND, network: request.agent.Network.ANY },databaseOptions: { schema: "schema.json", traceId: "traceId" }})})

获取用户头像和名字

详细的参数解析可以查看 【HarmonyOS6】获取华为用户信息

      Button("获取用户头像和名字").width("80%").onClick(async () => {this.AuthRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest();// 获取头像昵称需要传如下scopethis.AuthRequest.scopes = ['profile', 'openid'];// 若开发者需要进行服务端开发,则需传如下permission获取authorizationCodethis.AuthRequest.permissions = ['serviceauthcode'];// 用户是否需要登录授权,该值为true且用户未登录或未授权时,会拉起用户登录或授权页面this.AuthRequest.forceAuthorization = false;// 用于防跨站点请求伪造this.AuthRequest.state = util.generateRandomUUID();// 用户没有授权的时候,是否弹窗提示用户授权this.AuthRequest.forceAuthorization = true;const controller = new authentication.AuthenticationController(this.getUIContext().getHostContext());const data: authentication.AuthorizationWithHuaweiIDResponse =await controller.executeRequest(this.AuthRequest);const authorizationWithHuaweiIDResponse = data as authentication.AuthorizationWithHuaweiIDResponse;const state = authorizationWithHuaweiIDResponse.state;if (state && this.AuthRequest.state !== state) {console.error(`Failed to authorize. The state is different, response state: ${state}`);return;}if (authorizationWithHuaweiIDResponse && authorizationWithHuaweiIDResponse.data) {//用户头像链接,有效期较短,建议先将头像下载保存后再使用,这里只是用于演示哈if (authorizationWithHuaweiIDResponse.data.avatarUri) {this.UserIcon = authorizationWithHuaweiIDResponse.data.avatarUri;}//用户昵称if (authorizationWithHuaweiIDResponse.data.nickName) {this.UserName = authorizationWithHuaweiIDResponse.data.nickName;}//唯一IDconst userUnionID = authorizationWithHuaweiIDResponse?.data?.unionID;//当前应用IDconst userOpenID = authorizationWithHuaweiIDResponse?.data?.openID;}})

将头像上传到云存储并获取图片下载地址

将头像上传到云存储的固定文件夹目录中,然后通过StorageBucket.getDownloadURL方法,获取图片的下载路径,下面仅展示关键代码

      Button("根据用户头像文件上传云存储").width("80%").onClick(async () => {let photoName: string = `AccountDemo_${this.UserName}.png`;//拼接沙箱存储地址let cachePath: string = `${GlobalContext.getContext().cacheDir}/${photoName}`//检查沙箱中图片是否存在if (!(await fileIo.access(cachePath))) {await this.DownloadIconToCache(cachePath, () => {this.SendIconToCloud(cachePath, photoName, () => {this.UpdateUserIconAndName(photoName)});});} else {//上传图片this.SendIconToCloud(cachePath, photoName, () => {this.UpdateUserIconAndName(photoName)});}})

完整代码

Index.ets

import { ImageType } from '@kit.UIDesignKit'
import { authentication } from '@kit.AccountKit'
import { util } from '@kit.ArkTS'
import { GlobalContext } from '../Common/GlobalContext'
import { request, BusinessError } from '@kit.BasicServicesKit'
import { cloudCommon, cloudStorage } from '@kit.CloudFoundationKit'
import fileIo from '@ohos.file.fs'
import auth, { AuthUser, SignInResult } from '@hw-agconnect/auth'@Entry
@ComponentV2
struct Index {@Local UserIcon: ImageType = $r('app.media.user_dark')@Local UserName: string = "炸鸡仔"AuthRequest?: authentication.AuthorizationWithHuaweiIDRequestUIContext: UIContext = this.getUIContext()/*** 从云存储下载的头像文件*/@Local CloudIcon: ImageType = $r('app.media.user_dark')/*** 云存储实例*/StorageBucket: cloudStorage.StorageBucket = cloudStorage.bucket();build() {Column({ space: 10 }) {Button("登录").width("80%").onClick(async () => {//用户登录let user = await auth.getCurrentUser();if (!user) {try {let signInResult: SignInResult = await auth.signIn({autoCreateUser: true,"credentialInfo": {"kind": 'hwid'}});this.ShowMessage("用户登录成功")} catch (e) {console.info(e)}} else {this.ShowMessage("用户已经登录")}cloudCommon.init({region: cloudCommon.CloudRegion.CHINA,authProvider: auth.getAuthProvider(),functionOptions: { timeout: 10 * 1000 },storageOptions: { mode: request.agent.Mode.BACKGROUND, network: request.agent.Network.ANY },databaseOptions: { schema: "schema.json", traceId: "traceId" }})})Image(this.UserIcon).width(80).height(80).borderRadius(40)Text(this.UserName).fontWeight(FontWeight.Bold).margin({ top: 5 })Button("获取用户头像和名字").width("80%").onClick(async () => {this.AuthRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest();// 获取头像昵称需要传如下scopethis.AuthRequest.scopes = ['profile', 'openid'];// 若开发者需要进行服务端开发,则需传如下permission获取authorizationCodethis.AuthRequest.permissions = ['serviceauthcode'];// 用户是否需要登录授权,该值为true且用户未登录或未授权时,会拉起用户登录或授权页面this.AuthRequest.forceAuthorization = false;// 用于防跨站点请求伪造this.AuthRequest.state = util.generateRandomUUID();// 用户没有授权的时候,是否弹窗提示用户授权this.AuthRequest.forceAuthorization = true;const controller = new authentication.AuthenticationController(this.getUIContext().getHostContext());const data: authentication.AuthorizationWithHuaweiIDResponse =await controller.executeRequest(this.AuthRequest);const authorizationWithHuaweiIDResponse = data as authentication.AuthorizationWithHuaweiIDResponse;const state = authorizationWithHuaweiIDResponse.state;if (state && this.AuthRequest.state !== state) {console.error(`Failed to authorize. The state is different, response state: ${state}`);return;}if (authorizationWithHuaweiIDResponse && authorizationWithHuaweiIDResponse.data) {//用户头像链接,有效期较短,建议先将头像下载保存后再使用,这里只是用于演示哈if (authorizationWithHuaweiIDResponse.data.avatarUri) {this.UserIcon = authorizationWithHuaweiIDResponse.data.avatarUri;}//用户昵称if (authorizationWithHuaweiIDResponse.data.nickName) {this.UserName = authorizationWithHuaweiIDResponse.data.nickName;}//唯一IDconst userUnionID = authorizationWithHuaweiIDResponse?.data?.unionID;//当前应用IDconst userOpenID = authorizationWithHuaweiIDResponse?.data?.openID;}})Button("根据用户头像文件上传云存储").width("80%").onClick(async () => {let photoName: string = `AccountDemo_${this.UserName}.png`;//拼接沙箱存储地址let cachePath: string = `${GlobalContext.getContext().cacheDir}/${photoName}`//检查沙箱中图片是否存在if (!(await fileIo.access(cachePath))) {await this.DownloadIconToCache(cachePath, () => {this.SendIconToCloud(cachePath, photoName, () => {this.UpdateUserIconAndName(photoName)});});} else {//上传图片this.SendIconToCloud(cachePath, photoName, () => {this.UpdateUserIconAndName(photoName)});}})Image(this.CloudIcon).width(80).height(80).borderRadius(40)Button("获取用户头像").width("80%").onClick(async () => {let user = await auth.getCurrentUser();if (user) {this.CloudIcon = user.getPhotoUrl();}})}.height('100%').width('100%')}private ShowMessage(value: string) {console.info(value)this.UIContext.getPromptAction().showToast({message: value,alignment: Alignment.Center,duration: 1000})}/*** 上传图片到云存储* @param cachePath* @param photoName*/private async SendIconToCloud(cachePath: string, photoName: string, completedEvent: () => void) {try {//上传云存储this.StorageBucket.uploadFile(GlobalContext.getContext(), {localPath: cachePath,cloudPath: `AccountDemo/${photoName}`}).then((cloudTask: request.agent.Task) => {//订阅任务进度的事件cloudTask.on('progress', (progress) => {});//订阅任务完成事件cloudTask.on('completed', (progress) => {this.ShowMessage("完成图片上传");completedEvent();});//订阅任务失败事件cloudTask.on('failed', (progress: request.agent.Progress) => {this.ShowMessage("上传错误");});cloudTask.start((err: BusinessError) => {if (err) {this.ShowMessage("上传错误" + err.message);}})})} catch (e) {console.error(e)}}/*** 下载图片到沙箱* @param cachePath* @param completedEvent*/private async DownloadIconToCache(cachePath: string, completedEvent: () => void) {//下载图片保存到沙箱地址const task = await request.downloadFile(GlobalContext.getContext(), {url: this.UserIcon as string,filePath: cachePath})task.on('progress', (value) => {//可以做进度条展示console.info(`正在下载图片${value}%`)})task.on("complete", async () => {//真的下载完成了this.ShowMessage("网络图片下载完成了");completedEvent();})task.on("fail", () => {this.ShowMessage("图片下载失败")})}/*** 更改用户名字和头像* @param photoName*/private async UpdateUserIconAndName(photoName: string) {//获取图片下载地址let iconUrl: string = await this.StorageBucket.getDownloadURL(`AccountDemo/${photoName}`)//修改用户名字let user = await auth.getCurrentUser();if (user) {try {await user.updateProfile({displayName: this.UserName,photoUrl: iconUrl})this.ShowMessage("完成用户名字修改")} catch (e) {console.info(e);}}}
}

GlobalContext

import { common } from '@kit.AbilityKit';export class GlobalContext {private static context: common.UIAbilityContext;public static initContext(context: common.UIAbilityContext): void {GlobalContext.context = context;}public static getContext(): common.UIAbilityContext {return GlobalContext.context;}
}

EntryAbility

import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { GlobalContext } from '../Common/GlobalContext';
import { buffer } from '@kit.ArkTS';
import auth from '@hw-agconnect/auth';const DOMAIN = 0x0000;export default class EntryAbility extends UIAbility {onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');GlobalContext.initContext(this.context);let file = this.context.resourceManager.getRawFileContentSync('agconnect-services.json');let json: string = buffer.from(file.buffer).toString();auth.init(this.context, json);}onDestroy(): void {hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');}onWindowStageCreate(windowStage: window.WindowStage): void {// Main window is created, set main page for this abilityhilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');windowStage.loadContent('pages/Index', (err) => {if (err.code) {hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));return;}hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');});}onWindowStageDestroy(): void {// Main window is destroyed, release UI related resourceshilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');}onForeground(): void {// Ability has brought to foregroundhilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');}onBackground(): void {// Ability has back to backgroundhilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');}
}

结果演示

用户信息和云存储均已开通

image-20250728165056645

image-20250728165124540

操作视频

3333

总结

tageDestroy’);
}

onForeground(): void {
// Ability has brought to foreground
hilog.info(DOMAIN, ‘testTag’, ‘%{public}s’, ‘Ability onForeground’);
}

onBackground(): void {
// Ability has back to background
hilog.info(DOMAIN, ‘testTag’, ‘%{public}s’, ‘Ability onBackground’);
}
}

# 结果演示## 用户信息和云存储均已开通[外链图片转存中...(img-Qdb3dZDF-1753694903856)][外链图片转存中...(img-sptYJ2Mz-1753694903856)]## 操作视频[外链图片转存中...(img-9KHUdknI-1753694903856)]# 总结希望这篇文章可以让你更了解云存储的使用,上传图片的操作方式。
http://www.dtcms.com/a/301886.html

相关文章:

  • 前端工程化常见问题总结
  • Windows|CUDA和cuDNN下载和安装,默认安装在C盘和不安装在C盘的两种方法
  • AI技术革命:产业重塑与未来工作范式转型。
  • 深入解析MIPI C-PHY (四)C-PHY物理层对应的上层协议的深度解析
  • 齐护Ebook科技与艺术Steam教育套件 可图形化micropython Arduino编程ESP32纸电路手工
  • 湖南(源点咨询)市场调研 如何在行业研究中快速有效介入 起头篇
  • Triton编译
  • 【n8n教程笔记——工作流Workflow】文本课程(第一阶段)——5.5 计算预订订单数量和总金额 (Calculating booked orders)
  • Rouge:面向摘要自动评估的召回导向型指标——原理、演进与应用全景
  • 分表分库与分区表
  • Android启动时间优化大全
  • 蛋白质反向折叠模型-ProteinMPNN安装教程
  • 学习日志20 python
  • 【unitrix】 6.18 二进制小数特质(t_decimal.rs)
  • EPOLLET 边缘触发模式深度解析
  • 抗辐照芯片在低轨卫星星座CAN总线通讯及供电系统的应用探讨
  • vue3的一些浅显用法
  • Day06–哈希表–242. 有效的字母异位词,349. 两个数组的交集,202. 快乐数,1. 两数之和
  • 浙大公开课—基于深度学习的特征匹配与姿态估计
  • (补题)拼图游戏
  • EPOLLIN事件的详细解析
  • 【时时三省】(C语言基础)指针数组和多重指针
  • MySQL 8.4 Windows 版安装记录与步骤参考
  • 【C语言网络编程基础】DNS 协议与请求详解
  • Context Engineering Notes
  • 持续优化Cypress自动化测试
  • FunctionCall 如何使用以及如何训练
  • 从MySQL的information_schema系统数据库中获取表的元数据信息
  • Dify 1.7.0 新特性解析:工作流革新与多模态能力突破
  • 基于springboot的在线购票系统/在线售票系统