鸿蒙直传实践

2026-01-29   访问量:1004

本文档介绍如何不依赖 SDK,用简单的代码,在鸿蒙系统上直传文件到对象存储(Cloud Object Storage,COS)的存储桶。

注意:

本文档内容基于 XML 版本的 API

前提条件

登录 COS 控制台 并创建存储桶,得到 Bucket(存储桶名称) 和 Region(地域名称),详情请参见 创建存储桶

登录 访问管理控制台, 获取您的项目 SecretId 和 SecretKey。

实践步骤

整体步骤为:

1. 客户端调用服务端接口传入文件后缀,服务端根据后缀和时间戳等生成 cos key 以及直传的 url。

2. 服务端通过 STS SDK 获取临时密钥。

3. 服务端使用获取到的临时密钥对直传 url 进行签名并返回 url、签名、token 等信息。

4. 客户端获取到步骤 3 中的信息后,直接发起 put 请求并携带签名、token 等 header 进行上传。

具体代码可参见 鸿蒙示例

配置服务端实现签名

注意:

正式部署时服务端请加一层您的网站本身的权限检验。

出于安全考虑,后端获取临时密钥后生成直传 url 并直接对其进行签名,可参见 服务端签名实践

具体步骤为:

1. 通过 STS SDK 获取临时密钥。

2. 根据后缀名生成 cos key 以及直传 url 相关。

3. 使用临时密钥对直传 url 进行签名并返回直传 url、签名、token 等信息。

服务端配置步骤:

1. 配置好密钥、bucket 以及 region。

var config = {  // 获取腾讯云密钥,建议使用限定权限的子用户的密钥 https://console.cloud.tencent.com/cam/capi  secretId: process.env.COS_SECRET_ID,  secretKey: process.env.COS_SECRET_KEY,  // 密钥有效期  durationSeconds: 1800,  // 这里填写存储桶、地域,例如:test-1250000000、ap-guangzhou  bucket: process.env.PERSIST_BUCKET,  region: process.env.PERSIST_BUCKET_REGION,  // 限制的上传后缀  extWhiteList: ['jpg', 'jpeg', 'png', 'gif', 'bmp'],};

2. 终端执行

npm install

3. 启动服务

node app.js

到这里服务端就启动成功了,可以开始客户端的流程。

如有其他语言或自行实现可以参考以下流程:

1. 向服务端获取临时密钥,服务端首先使用固定密钥 SecretId、SecretKey 向 STS 服务获取临时密钥,得到临时密钥 tmpSecretId、tmpSecretKey 和 sessionToken,详情请参见 临时密钥生成及使用指引cos-sts-sdk

2. 对直传 url 进行签名,生成 authorization。

3. 返回直传 url、authorization、sessionToken 等信息,客户端上传文件时将得到的签名和 sessionToken,分别放到发请求时 header 的 authorization 和 x-cos-security-token 字段里。

鸿蒙端上传示例

1. 从服务端请求直传和签名信息。

import http from '@ohos.net.http';



/**

* 获取直传的url和签名等

*

* @param ext 文件后缀 直传后端会根据后缀生成cos key

* @returns 直传url和签名等

*/

public static async getStsDirectSign(ext: string): Promise<Object> {

 // 每一个httpRequest对应一个HTTP请求任务,不可复用

 let httpRequest = http.createHttp();

 //直传签名业务服务端url(正式环境 请替换成正式的直传签名业务url)

 //直传签名业务服务端代码示例可以参考:https://github.com/tencentyun/cos-demo/blob/main/server/direct-sign/nodejs/app.js

 let url = "http://127.0.0.1:3000/sts-direct-sign?ext=" + ext;

 try {

   let httpResponse = await httpRequest.request(url, { method: http.RequestMethod.GET });

   if (httpResponse.responseCode == 200) {

     let result = JSON.parse(httpResponse.result.toString())

     if (result.code == 0) {

       return result.data;

     } else {

       console.info(`getStsDirectSign error code: ${result.code}, error message: ${result.message}`);

     }

   } else {

     console.info("getStsDirectSign HTTP error code: " + httpResponse.responseCode);

   }

 } catch (err) {

   console.info("getStsDirectSign Error sending GET request: " + JSON.stringify(err));

 } finally {

   // 当该请求使用完毕时,调用destroy方法主动销毁。

   httpRequest.destroy();

 }

 return null;

}

2. 使用获取到的直传和签名信息开始上传文件。

import http from '@ohos.net.http';

import promptAction from '@ohos.promptAction'

import fs from '@ohos.file.fs';

import request from '@ohos.request';

import common from '@ohos.app.ability.common';

import { MediaBean } from '../bean/MediaBean';

import { MediaHelper } from './MediaHelper';



/**

* 上传文件(通过uploadTask实现)

* @param context context

* @param media 媒体文件

*/

public static async uploadFileByTask(context: common.Context, media: MediaBean, progressCallback: (uploadedSize: number, totalSize: number) => void) {

 // 获取直传签名等数据

 let ext = MediaHelper.getFileExtension(media.fileName);

 let directTransferData: any = await UploadHelper.getStsDirectSign(ext);

 if (directTransferData == null) {

   promptAction.showToast({ message: 'getStsDirectSign fail' });

   return;

 }



 // 业务服务端返回的上传信息

 let cosHost: String = directTransferData.cosHost;

 let cosKey: String = directTransferData.cosKey;

 let authorization: String = directTransferData.authorization;

 let securityToken: String = directTransferData.securityToken;



 // 生成上传的url

 let url = `https://${cosHost}/${cosKey}`;

 try {

   // 复制uri文件到cacheDir(因为request.uploadFile只接受internal:类型的路径)

   let file = await fs.open(media.fileUri, fs.OpenMode.READ_ONLY);

   let destPath = context.cacheDir + "/" + media.fileName;

   await fs.copyFile(file.fd, destPath);

   let realuri = "internal://cache/" + destPath.split("cache/")[1];



   let uploadConfig = {

     url: url,

     header: {

       "Content-Type": "application/octet-stream",

       "Authorization": authorization,

       "x-cos-security-token": securityToken,

       "Host": cosHost

     },

     method: "PUT",

     files: [{ filename: media.fileName, name: "file", uri: realuri, type: ext }],

     data: []

   };

   // 开始上传

   let uploadTask = await request.uploadFile(context, uploadConfig)



   uploadTask.on('progress', progressCallback);

   let upCompleteCallback = (taskStates) => {

     for (let i = 0; i < taskStates.length; i++) {

       promptAction.showToast({ message: '上传成功' });

       console.info("upOnComplete taskState:" + JSON.stringify(taskStates[i]));

     }

   };

   uploadTask.on('complete', upCompleteCallback);



   let upFailCallback = (taskStates) => {

     for (let i = 0; i < taskStates.length; i++) {

       promptAction.showToast({ message: '上传失败' });

       console.info("upOnFail taskState:" + JSON.stringify(taskStates[i]));

     }

   };

   uploadTask.on('fail', upFailCallback);

 } catch (err) {

   console.info("uploadFile Error sending PUT request: " + JSON.stringify(err));

   promptAction.showToast({ message: "uploadFile Error sending PUT request: " + JSON.stringify(err) });

 }

}


热门文章
更多>