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方法中初始化,主要代码如下展示
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)]

# 总结

希望这篇文章可以让你更了解云存储的使用,上传图片的操作方式。
相关推荐
万少3 小时前
AI编程神器!Trae+Claude4.0 简单配置 让HarmonyOS开发效率飙升 - 坚果派
前端·aigc·harmonyos
zhanshuo4 小时前
HarmonyOS UI 交互实战指南:教你用 ArkUI 打造高响应体验
harmonyos
zhanshuo5 小时前
新手也能搞定!鸿蒙开发者资源包获取与实战全流程指南
harmonyos
zzywxc7876 小时前
AI在编程、测试、数据分析等领域的前沿应用(技术报告)
人工智能·深度学习·机器学习·数据挖掘·数据分析·自动化·ai编程
优测云服务平台10 小时前
优测推出HarmonyOS全场景测试服务,解锁分布式场景应用卓越品质!
分布式·harmonyos
HarmonyOS小助手12 小时前
【宝藏贴】HarmonyOS官方模板优秀案例 · 第1期:便捷生活-购物中心
harmonyos·鸿蒙·鸿蒙生态
沫小北13 小时前
HarmonyOS Lottie动画库总结
前端·harmonyos
Blossom.11814 小时前
基于深度学习的图像分类:使用ShuffleNet实现高效分类
人工智能·python·深度学习·目标检测·机器学习·分类·数据挖掘