背景
在之前的文章中提到,我们通过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方法中初始化,主要代码如下展示
typescript
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);
}
}

使用华为账号登录
登录账号,并完成云服务的初始化。登录方式有很多种,这里选择华为账号登录登录创建用户为了方便演示作用。
typescript
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】获取华为用户信息
typescript
Button("获取用户头像和名字")
.width("80%")
.onClick(async () => {
this.AuthRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest();
// 获取头像昵称需要传如下scope
this.AuthRequest.scopes = ['profile', 'openid'];
// 若开发者需要进行服务端开发,则需传如下permission获取authorizationCode
this.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;
}
//唯一ID
const userUnionID = authorizationWithHuaweiIDResponse?.data?.unionID;
//当前应用ID
const userOpenID = authorizationWithHuaweiIDResponse?.data?.openID;
}
})
将头像上传到云存储并获取图片下载地址
将头像上传到云存储的固定文件夹目录中,然后通过StorageBucket.getDownloadURL方法,获取图片的下载路径,下面仅展示关键代码
typescript
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
typescript
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.AuthorizationWithHuaweiIDRequest
UIContext: 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();
// 获取头像昵称需要传如下scope
this.AuthRequest.scopes = ['profile', 'openid'];
// 若开发者需要进行服务端开发,则需传如下permission获取authorizationCode
this.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;
}
//唯一ID
const userUnionID = authorizationWithHuaweiIDResponse?.data?.unionID;
//当前应用ID
const 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
typescript
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
typescript
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 ability
hilog.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 resources
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
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');
}
}
结果演示
用户信息和云存储均已开通


操作视频

总结
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)]
# 总结
希望这篇文章可以让你更了解云存储的使用,上传图片的操作方式。