当前位置: 首页 > 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.xdnf.cn/news/1201897.html

相关文章:

  • 打通视频到AI的第一公里:轻量RTSP服务如何重塑边缘感知入口?
  • UniappDay04
  • Java 排序
  • Kafka——请求是怎么被处理的?
  • flutter使用firebase集成谷歌,苹果登录
  • Claude Launcher:支持Kimi K2的Claude Code可视化启动工具
  • 力扣988. 从叶结点开始的最小字符串
  • 负载均衡集群HAproxy
  • keepalived
  • Redis做混沌测试都需要测哪些场景?预期如何?
  • 安宝特案例丨AR+AI赋能轨道交通制造:破解人工装配难题的创新实践
  • [免费]【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts)【论文+源码+SQL脚本】
  • 【代码解读】通义万相最新视频生成模型 Wan 2.2 实现解析
  • ESP32学习-按键中断
  • 【重学数据结构】二叉搜索树 Binary Search Tree
  • 源代码管理工具有哪些?有哪些管理场景?
  • [VLDB 2025]面向Flink集群巡检的交叉对比学习异常检测
  • mybatis-plus实体类主键生成策略
  • 设计模式(四)创建型:生成器模式详解
  • Java排序中(a).compareTo(b)与Integer.compare(a, b)区别
  • 推荐系统学习
  • 算法竞赛阶段二-数据结构(37)数据结构循环链表模拟实现
  • 【PCIe 总线及设备入门学习专栏 5.3.4 -- PCIe PHY Firmware 固件加载流程】
  • Android启动时间优化大全
  • 通信名词解释:I2C、USART、SPI、RS232、RS485、CAN、TCP/IP、SOCKET、modbus等
  • Window 部署 coze-stdio(coze 开发平台)
  • vue3.6更新哪些内容
  • 电子电路设计学习
  • MySQL - 索引(B+树)
  • Python Pandas.cut函数解析与实战教程