HarmonyOS 6 云开发-用户头像上传云存储
背景
在之前的文章中提到,我们通过Account Kit拿到用户的头像和名称,在软件开发中,我们需要拿到用户信息,需要把用户ID、名字、头像信息存储到云数据库和云存储中。这篇文章主要讲解怎样子获取用户的头像后,并上传用户头像到云存储的过程。
操作流程
- 用户登录软件,使用AGC的认证服务和华为账号登录。
- 使用Account Kit 获取用户的头像和名称。
- 将头像文件从临时链接下载到本地沙箱,再从沙箱中将文件上传到云存储。
- 获取云存储文件链接,修改AGC账户管理的名称和图片链接。
- 测试再次打开自动获取图片地址,并下载头像文件到沙箱中,并展示出来。
开发准备
- 开通AGC云存储服务
- 认证服务开启华为账号认证(现在手机号码认证需要自己购买第三方服务的形式),这里需要填写Client ID和Client Secret
- 检查是否已经添加SHA256证书/公钥指纹,没有的时候需要先添加上。
- 在配置完成后,回到项目设置页面,下载配置文件 “agconnect-services.json” ,放置到项目的AppScope/resources/rawfile目录下,如果没有rawfile文件夹,可以手动创建。需要注意,每次AGC项目设置有变动时,都需要重新下载配置文件。
- 配置Client ID,在entry模块的“module.json5”文件中,新增metadata,配置name为client_id,value为AGC中获取的Client ID的值。
配置用户认证SDK
- 在项目的Terminal中,把地址切换到entry文件夹
cd .\entry\
- 运行安装SDK命令
ohpm install @hw-agconnect/auth
- 在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);}
}
使用华为账号登录
登录账号,并完成云服务的初始化。登录方式有很多种,这里选择华为账号登录登录创建用户为了方便演示作用。
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');}
}
结果演示
用户信息和云存储均已开通
操作视频
总结
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)]# 总结希望这篇文章可以让你更了解云存储的使用,上传图片的操作方式。