react-native-cos-sdk
v1.3.0
Published
Tencent COS XML SDK for React Native
Maintainers
Readme
相关资源
准备工作
- 您需要一个纯 React Native 项目或 React Native 原生混合项目,这个应用可以是您现有的工程,也可以是您新建的一个空的工程。
- React Native 版本要求:0.69.1 及以上
- 如需支持 HarmonyOS NEXT,还需满足:RNOH 基座为 0.72 / 0.77 / 0.82 / 0.84 版本线之一,DevEco Studio 5.0 及以上。
第一步:SDK 介绍
react-native-cos-sdk 目前兼容支持 iOS、Android、HarmonyOS NEXT,是通过 React Native 桥接原生 Android、iOS 和 HarmonyOS 的 COS SDK 实现。
第二步:集成 SDK
- 运行此命令:
使用npm:
npm install --save react-native-cos-sdk或者使用yarn:
yarn add react-native-cos-sdk- 在您的代码中,您可以使用 import 进行导入,然后开始使用:
import Cos from 'react-native-cos-sdk';关闭腾讯灯塔上报功能
为了持续跟踪和优化 SDK 的质量,给您带来更好的使用体验,我们在 SDK 中引入了 腾讯灯塔 SDK,腾讯灯塔只对 COS 侧的请求性能进行监控,不会上报业务侧数据。 若是想关闭该功能,可以在依赖引入和 import 时将 react-native-cos-sdk 替换为 react-native-cos-sdk-nobeacon 即可。
HarmonyOS NEXT 平台的 COS SDK 未接入腾讯灯塔,无需该操作;
setCloseBeacon在该平台为空实现。
HarmonyOS NEXT 额外集成步骤
鸿蒙侧的桥接实现随 npm 包一起发布在 harmony/cos_react_native 目录,需要在宿主鸿蒙工程中手动接入:
- 在
entry/oh-package.json5中添加依赖(cos_react_native为源码模块,随包发布):
{
"dependencies": {
"@rnoh/react-native-openharmony": "0.72.143",
"cos_react_native": "file:../../node_modules/react-native-cos-sdk/harmony/cos_react_native"
}
}注意:ohpm 对
file:依赖使用符号链接安装,若 es2abc 编译报Field {...Logger.moduleRecordIdx} has different value模块合并冲突, 需把符号链接解引用为真实目录(参考本仓库scripts/prepare_harmony.sh的cp -RL做法); 更稳妥的方式是先对harmony/cos_react_native执行hvigorw assembleHar产出 har, 再以file:./libs/cos_react_native.har引用(本仓库各 example 即此方式)。
@rnoh/react-native-openharmony的版本由宿主工程决定,SDK 不锁定版本线,请与您的 RNOH 基座保持一致。
- 在
entry/src/main/ets/RNPackagesFactory.ets中注册 ArkTS Package:
import { CosPackage } from 'cos_react_native';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [new CosPackage(ctx)];
}- 在
entry/src/main/cpp/CMakeLists.txt中加入 C++ 子目录(RNOH 的 TurboModule 需要 C++ 侧声明方法表):
add_subdirectory("${OH_MODULE_DIR}/cos_react_native/src/main/cpp" ./cos_react_native)- 在
entry/src/main/cpp/PackageProvider.cpp中注册 C++ Package:
#include "CosPackage.h"
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return { std::make_shared<CosPackage>(ctx) };
}- 在
entry/src/main/module.json5中申请网络权限:
{
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" }
]
}HarmonyOS NEXT 平台差异说明
鸿蒙侧的 COS SDK 与 Android/iOS 的能力并非完全一致,以下差异在使用前请务必确认。所有降级的方法都能正常 resolve(不会抛错),并在 hilog 中输出 not supported on HarmonyOS 的告警。
1. 日志相关方法为空实现
鸿蒙侧 COS SDK 未提供独立的日志模块,以下方法在鸿蒙上为空实现(调用不报错,但无实际效果):
enableLogcat、enableLogFile、addLogListener、removeLogListener、setMinLevel、setLogcatMinLevel、setFileMinLevel、setClsMinLevel、setDeviceID、setDeviceModel、setAppVersion、setExtras、setLogFileEncryptionKey、getLogRootDir、setCLsChannelAnonymous、setCLsChannelStaticKey、setCLsChannelSessionCredential、updateCLsChannelSessionCredential、addSensitiveRule、removeSensitiveRule。
如需在鸿蒙上排查问题,请在 registerService 时传 isDebuggable: true,SDK 会通过 hilog 输出请求日志。
2. 自定义 DNS
initCustomerDNS(dnsMap):支持,底层通过rcp.DnsConfiguration的静态解析规则实现,需在registerService之前调用。initCustomerDNSFetch/setDNSFetchIps:不支持。鸿蒙的rcp只接受静态 DNS 规则,无法在每次请求时回调 JS 动态解析。请改用initCustomerDNS。
3. 临时密钥回调有 15 秒超时
鸿蒙侧的凭证握手通过事件通知 JS 并挂起 Promise 等待回填。如果 initWithSessionCredentialCallback 的回调在 15 秒内没有返回有效凭证,该次请求会以 INVALID_CREDENTIALS(10001)快速失败,而不是无限等待。
此外,若回填的临时密钥缺少 expiredTime,鸿蒙 SDK 会认为该凭证永不过期、不再触发刷新,请确保 STS 返回中带上该字段。
initWithScopeLimitCredentialCallback(scope 限权模式)整体已不推荐使用。在鸿蒙上该模式还有额外限制:鸿蒙 SDK 会把首个凭证写入进程级全局缓存,同进程内切换 bucket 时后续请求会复用首个 bucket 的 scope 凭证导致 403,因此该模式在鸿蒙仅限单 bucket 场景。请优先使用initWithSessionCredentialCallback。
4. cancelAll 的作用域大于 Android/iOS
鸿蒙 SDK 的 cancelAll() 内部使用全局任务管理器,实际效果是取消当前进程内所有 COS 请求,而 Android/iOS 只取消该 service 实例的请求。如需精细控制,请对具体的传输任务调用 cancel()。
5. 响应头 key 统一为小写
headBucket、headObject 以及传输成功回调返回的 header,其 key 在鸿蒙与 iOS 上统一转为小写(如 etag、content-length);Android 保留服务端原始大小写。建议统一按小写读取,或做大小写无关的匹配。
6. Bucket 多版本/加速状态被压缩为布尔值
getBucketVersioning / getBucketAccelerate 在三端都返回 boolean,因此 COS 的 Suspended(已暂停)状态与「从未开启」无法区分,两者都返回 false。若业务需要区分三态,请直接调用 REST API。
7. 对已结束的传输任务调用 pause
Android 与鸿蒙会 reject(任务句柄已释放),iOS 会 resolve。建议在调用前先判断任务状态,不要依赖该行为。
8. 未支持的服务配置字段
CosXmlServiceConfig 中的 signInUrl、dnsCache、domainSwitch 在鸿蒙上不生效;TransferConfig 中的 forceSimpleUpload、enableVerification 在鸿蒙上不生效(如需强制简单上传,可将 divisionForUpload 设为大于文件大小的值)。这些字段被传入时会在 hilog 输出告警。
9. preBuildConnection 的实现方式
- Android:调用原生
preBuildConnectionAsync预热连接。 - 鸿蒙:通过发起一次
headBucket请求达到预热效果,会产生一次真实的 HTTP 请求。 - iOS:空实现(原生 SDK 不支持预连接),直接 resolve。
10. 三方库与导航库的选型注意
鸿蒙生态的三方库需要单独的适配包(@react-native-oh-tpl/* 或 @react-native-ohos/*),其版本线与 RNOH 基座强绑定。集成时注意:
- 导航库选型注意
react-native-screens的适配包状态。native-stack依赖react-native-screens的原生组件,其鸿蒙适配包在部分 RNOH 版本线上存在渲染缺陷(导航头部正常但页面主体整片空白);在 RNOH 0.72 LTS 线上,screens 适配包与document-picker等其他适配包存在新旧 API 基类(RNOHPackage/RNPackage)混用导致的 es2abc 模块合并冲突。本仓库example因此未使用 react-navigation,而是用百余行的自研极简导航(example/src/App.tsx,提供 push/navigate/goBack/setOptions/setParams 等价 API),业务代码零改动。若你的应用依赖 react-navigation,建议在 0.72 线使用native-stack,在较新版本线改用@react-navigation/stack(纯 JS 实现,不走原生 screens)规避。 - 适配包与 JS 主包的版本必须配对。适配包的
peerDependencies会指定它匹配的 JS 主包版本(例如@react-native-ohos/[email protected]要求[email protected]),版本错配时通常不报错,只是组件不渲染。 - 适配包依赖的 RNOH 版本应与宿主一致。适配包 har 内的
oh-package.json5里写明了它依赖的 RNOH 版本,宿主装了更高版本可能导致基座重复。 toLocaleString()等依赖 ICU 的 API 不可用。RNOH 的 Hermes 未内置 ICU,new Date().toLocaleString()会返回字符串"dateFormat not implemented"而不是抛错,很容易被忽略。请手动格式化日期数字。
11. 传输任务 pause / resume 的平台差异
- pause 后是否产生
PAUSED状态:iOS 只有在成功拿到断点数据(resumeData)时才回调PAUSED;小文件走简单上传、或分片合并请求已发出时拿不到断点数据,此时不会有PAUSED,任务会继续跑完。Android / 鸿蒙则会稳定回调PAUSED。请不要假设pause()之后一定能收到PAUSED。 - 简单上传(小文件)的 resume 语义是「重新开始」:三端一致,会重新上传整个文件而不是续传。
- pause / cancel 不会触发失败回调:三端都已保证用户主动中断不上报
failCallBack,取消与暂停的语义只通过stateCallback的CANCELED/PAUSED表达。 - 状态机可能出现
RESUMED_WAITING:Android / iOS 在 resume 后会先进入该中间态再转IN_PROGRESS;鸿蒙无此状态。做状态判断时请把它当作「进行中」处理。
12. doesBucketExist / doesObjectExist 的实现方式
三端均为异步实现(内部走 HeadBucket / HeadObject):仅当服务端返回 404 时视为「不存在」返回 false,网络失败、权限不足等错误会如实抛出,不会被吞成 false。
自定义请求 Header
支持在服务级(作用于该 service / transferManager 下所有请求)与请求级(仅本次上传/下载)设置自定义 HTTP header,以及声明哪些 header 不参与签名计算。
服务级——在 CosXmlServiceConfig 中配置:
await Cos.registerDefaultService({
region: 'ap-guangzhou',
// 作用于该 service 下所有请求
customHeaders: { 'x-cos-meta-app': 'my-app' },
// 声明不参与签名计算的 header
noSignHeaders: ['X-Injected-By-Proxy'],
});请求级——在 upload / download 的参数中配置,优先级高于服务级同名 header:
await transferManger.upload(bucket, cosPath, fileUri, {
region: 'ap-guangzhou',
customHeaders: {
'Content-Type': 'image/jpeg',
'x-cos-meta-author': 'alice', // 自定义对象元数据
'Pic-Operations': JSON.stringify({ /* 图片处理参数 */ }),
},
noSignHeaders: ['X-Injected-By-Proxy'],
});关于 noSignHeaders:COS 签名默认会覆盖请求携带的 header,若某个 header 会被中间代理注入或改写(本地签名时其值与服务端收到的不一致),可将其加入该列表以跳过签名。
注意:COS 服务端对签名内容有强制校验。请不要把请求实际携带的
Content-Type、Host、x-cos-*等 header 放入noSignHeaders,否则会收到Strict signature missing header that must be signed错误。该列表只应包含「签名时尚不存在、由链路后续环节注入」的 header。
第三步:开始使用
!
1. 初始化密钥
实现获取临时密钥
调用 Cos 的 initWithSessionCredentialCallback 方法,实现请求临时密钥并返回结果的过程。
import Cos from 'react-native-cos-sdk';
Cos.initWithSessionCredentialCallback(async () => {
// 首先从您的临时密钥服务器获取包含了密钥信息的响应,例如:
// 临时密钥服务器 url,临时密钥生成服务请参考 https://cloud.tencent.com/document/product/436/14048
let stsUrl = "http://stsservice.com/sts";
let response = null;
try{
response = await fetch(stsUrl);
} catch(e){
console.error(e);
return null;
}
// 然后解析响应,获取临时密钥信息
const responseJson = await response.json();
const credentials = responseJson.credentials;
const startTime = responseJson.startTime;
const expiredTime = responseJson.expiredTime;
const sessionCredentials = {
tmpSecretId: credentials.tmpSecretId,
tmpSecretKey: credentials.tmpSecretKey,
startTime: startTime,
expiredTime: expiredTime,
sessionToken: credentials.sessionToken
};
console.log(sessionCredentials);
// 最后返回临时密钥信息对象
return sessionCredentials;
})实现获取限制范围的临时密钥
该方式可以更精细的控制临时密钥的使用范围,STSCredentialScope 中包含了本次请求的action(操作)、region(地域)、bucket(桶名)、prefix(资源路径), 使用 STSCredentialScope 可以生成一个限定范围的临时密钥,例如根据 prefix 生成固定路径文件名的上传临时密钥,实现每个上传文件都有单独的临时密钥。
调用 Cos 的 initWithScopeLimitCredentialCallback 方法,实现请求临时密钥并返回结果的过程。
import Cos from 'react-native-cos-sdk';
// 使用范围限制的临时密钥初始化
Cos.initWithScopeLimitCredentialCallback(async (stsScopesArray:Array<STSCredentialScope>) => {
// 首先从您的临时密钥服务器获取包含了密钥信息的响应,例如:
// 临时密钥服务器 url,临时密钥生成服务请参考 https://cloud.tencent.com/document/product/436/14048
// 范围限制的临时密钥服务请参考:https://cloud.tencent.com/document/product/436/31923
let stsUrl = "http://stsservice.com/sts/scope";
console.log(JSON.stringify(stsScopesArray));
let response = null;
try{
response = await fetch(stsUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
// 将范围实体列表转换为post body中的json
body: JSON.stringify(stsScopesArray),
});
} catch(e){
console.error(e);
return null;
}
// 然后解析响应,获取临时密钥信息
const responseJson = await response.json();
const credentials = responseJson.credentials;
const startTime = responseJson.startTime;
const expiredTime = responseJson.expiredTime;
const sessionCredentials = {
tmpSecretId: credentials.tmpSecretId,
tmpSecretKey: credentials.tmpSecretKey,
startTime: startTime,
expiredTime: expiredTime,
sessionToken: credentials.sessionToken
};
console.log(sessionCredentials);
// 最后返回临时密钥信息对象
return sessionCredentials;
})强制使本地保存的临时密钥失效
该功能可以强制使 COS SDK 已经缓存的临时密钥失效,包括无限制使用范围和限制使用范围的临时密钥,失效后再使用 COS 接口功能时 SDK 会重新向业务临时密钥服务端获取新的临时密钥。 调用方法:
await Cos.forceInvalidationCredential();使用永久密钥进行本地调试
您可以使用腾讯云的永久密钥来进行开发阶段的本地调试。由于该方式存在泄漏密钥的风险,请务必在上线前替换为临时密钥的方式。
import Cos from 'react-native-cos-sdk';
let SECRET_ID = "SECRETID"; //永久密钥 secretId
let SECRET_KEY = "SECRETKEY"; //永久密钥 secretKey
Cos.initWithPlainSecret(
SECRET_ID,
SECRET_KEY
)2. 注册 COS 服务
// 存储桶所在地域简称,例如广州地区是 ap-guangzhou
let region = "COS_REGION";
// 创建 CosXmlServiceConfig 对象,根据需要修改默认的配置参数
let serviceConfig = {
region: region,
isDebuggable: true,
isHttps: true,
};
// 注册默认 COS Service
let cosService = await Cos.registerDefaultService(serviceConfig);
// 获取默认 COS Service
let cosService1 = Cos.getDefaultService();
// 创建 TransferConfig 对象,根据需要修改默认的配置参数
// TransferConfig 可以设置智能分块阈值 默认对大于或等于2M的文件自动进行分块上传,可以通过如下代码修改分块阈值
let transferConfig = {
forceSimpleUpload: false,
enableVerification: true,
divisionForUpload: 2097152, // 设置大于等于 2M 的文件进行分块上传
sliceSizeForUpload: 1048576, //设置默认分块大小为 1M
};
// 注册默认 COS TransferManger
let cosTransferManger = await Cos.registerDefaultTransferManger(serviceConfig, transferConfig);
// 获取默认 COS TransferManger
let cosTransferManger1 = Cos.getDefaultTransferManger();
// 也可以通过 registerService 和 registerTransferManger 注册其他实例, 用于后续调用
// 一般用 region 作为注册的key
let newRegion = "NEW_COS_REGION";
serviceConfig.region = newRegion;
let cosServiceNew = await Cos.registerService(newRegion, serviceConfig);
let cosTransferMangerNew = await Cos.registerTransferManger(newRegion, serviceConfig, transferConfig);
// 通过 key 获取 COS Service 和 COS TransferManger
let cosServiceNew1 = Cos.getService(newRegion);
let cosTransferMangerNew1 = Cos.getTransferManger(newRegion);注意:获取 COS Service 和 COS TransferManger 之前必须先要进行注册,否则会报错。 例如可以通过封装类似下面的方法控制获取前必须注册的流程
const SERVICE_CONFIG = {
region: "COS_REGION",
isDebuggable: true,
}
export async function getDefaultService(): Promise<CosService> {
if(Cos.hasDefaultService()){
return Cos.getDefaultService()
} else {
//注册默认service
return await Cos.registerDefaultService(SERVICE_CONFIG)
}
}参数说明
CosXmlServiceConfig 用于配置 COS 服务,其主要成员说明如下:
| 参数名称 | 描述 | 类型 | 默认值 | 支持平台 |
| ---------- | ------------------------------------------------------------ | ------ | ------ |------ |
| region | 存储桶地域 地域和访问域名 | String | null | Android和iOS |
| connectionTimeout | 连接超时时间(单位是毫秒) | Int | Android(15000) iOS(30000) | Android和iOS |
| socketTimeout | 读写超时时间(单位是毫秒) | Int | 30000 | Android |
| isHttps | 是否使用https协议 | Bool | true | Android和iOS |
| host | 设置除了 GetService 请求外的 host | String | null | Android和iOS |
| hostFormat | 设置 host 的格式化字符串,sdk 会将 ${bucket} 替换为真正的 bucket,${region} 替换为真正的 region例如将 hostFormat 设置为 ${bucket}.${region}.tencent.com,并且您的存储桶和地域分别为 bucket-1250000000 和 ap-shanghai,那么最终的请求地址为 bucket-1250000000.ap-shanghai.tencent.com注意,这个设置不会影响 GetService 请求 | String | null | Android |
| port | 设置请求的端口 | Int | null | Android |
| isDebuggable | 是否是 debug 模式(debug 模式会打印 debug 日志) | Bool | false | Android |
| signInUrl | 是否将签名放在 URL 中,默认放在 Header 中 | Bool | false | Android |
| userAgent | ua 拓展参数 | String | null | Android和iOS |
| dnsCache | 是否开启 DNS 解析缓存,开启后,将 DNS 解析的结果缓存在本地,当系统 DNS 解析失败后,会使用本地缓存的 DNS 结果 | Bool | true | Android |
| accelerate | 是否使用全球加速域名 | Bool | false | Android和iOS |
TransferConfig 用于配置 COS 上传服务,其主要成员说明如下:
| 参数名称 | 描述 | 类型 | 默认值 | 支持平台 | | ---------- | ------------------------------------------------------------ | ------ | ------ |------ | | divisionForUpload | 设置启用分块上传的最小对象大小 | Int | 2097152 | Android和iOS | | sliceSizeForUpload | 设置分块上传时的分块大小 | Int | 1048576 | Android和iOS | | enableVerification | 分片上传时是否整体校验 | Bool | true | Android和iOS | | forceSimpleUpload | 是否强制使用简单上传 | Bool | false | Android |
第四步:访问 COS 服务
上传对象
// 获取 CosTransferManger
let cosTransferManger: CosTransferManger = Cos.getDefaultTransferManger();
//let cosTransferManger: CosTransferManger = Cos.getTransferManger(newRegion);
// 存储桶名称,由 bucketname-appid 组成,appid 必须填入,可以在 COS 控制台查看存储桶名称。 https://console.cloud.tencent.com/cos5/bucket
let bucket = "examplebucket-1250000000";
let cosPath = "exampleobject"; //对象在存储桶中的位置标识符,即称对象键
let srcPath = "本地文件的路径"; //本地文件的路径
//若存在初始化分块上传的 UploadId,则赋值对应的 uploadId 值用于续传;否则,赋值 undefined
let _uploadId = undefined;
// 上传成功回调
let successCallBack = (header?: object) => {
// todo 上传成功后的逻辑
};
//上传失败回调
let failCallBack = (clientError?: CosXmlClientError, serviceError?: CosXmlServiceError) => {
// todo 上传失败后的逻辑
if (clientError) {
console.log(clientError);
}
if (serviceError) {
console.log(serviceError);
}
};
//上传状态回调, 可以查看任务过程
let stateCallBack = (state: TransferState) => {
// todo notify transfer state
};
//上传进度回调
let progressCallBack = (complete: number, target: number) => {
// todo Do something to update progress...
};
//初始化分块完成回调
let initMultipleUploadCallBack = (bucket: string, cosKey: string, uploadId: string) => {
//用于下次续传上传的 uploadId
_uploadId = uploadId;
};
//开始上传
let transferTask:TransferTask = await cosTransferManger.upload(
bucket,
cosPath,
srcPath,
{
uploadId: _uploadId,
resultListener: {
successCallBack: successCallBack,
failCallBack: failCallBack
},
stateCallback: stateCallBack,
progressCallback: progressCallBack,
initMultipleUploadCallback: initMultipleUploadCallBack,
}
);
//暂停任务
transferTask.pause();
//恢复任务
transferTask.resume();
//取消任务
transferTask.cancel();下载对象
// 高级下载接口支持断点续传,所以会在下载前先发起 HEAD 请求获取文件信息。
// 如果您使用的是临时密钥或者使用子账号访问,请确保权限列表中包含 HeadObject 的权限。
// CosTransferManger 支持断点下载,您只需要保证 bucket、cosPath、savePath
// 参数一致,SDK 便会从上次已经下载的位置继续下载。
// 获取 CosTransferManger
let cosTransferManger: CosTransferManger = Cos.getDefaultTransferManger();
//let cosTransferManger: CosTransferManger = Cos.getTransferManger(newRegion);
// 存储桶名称,由 bucketname-appid 组成,appid 必须填入,可以在 COS 控制台查看存储桶名称。 https://console.cloud.tencent.com/cos5/bucket
let bucket = "examplebucket-1250000000";
let cosPath = "exampleobject"; //对象在存储桶中的位置标识符,即称对象键
let downliadPath = "本地文件的路径"; //保存到本地文件的路径
// 下载成功回调
let successCallBack = (header?: object) => {
// todo 下载成功后的逻辑
};
//下载失败回调
let failCallBack = (clientError?: CosXmlClientError, serviceError?: CosXmlServiceError) => {
// todo 下载失败后的逻辑
if (clientError) {
console.log(clientError);
}
if (serviceError) {
console.log(serviceError);
}
};
//下载状态回调, 可以查看任务过程
let stateCallBack = (state: TransferState) => {
// todo notify transfer state
};
//下载进度回调
let progressCallBack = (complete: number, target: number) => {
// todo Do something to download progress...
};
//开始下载
let transferTask:TransferTask = = await cosTransferManger.download(
bucket,
cosPath,
downliadPath,
{
resultListener: {
successCallBack: successCallBack,
failCallBack: failCallBack
},
stateCallback: stateCallBack,
progressCallback: progressCallBack
}
);
//暂停任务
transferTask.pause();
//恢复任务
transferTask.resume();
//取消任务
transferTask.cancel();