tencent cloud

对象存储

动态与公告
产品动态
产品公告
产品简介
产品概述
功能概览
应用场景
产品优势
基本概念
地域和访问域名
规格与限制
产品计费
计费概述
计费方式
计费项
免费额度
计费示例
查看和下载账单
欠费说明
常见问题
快速入门
控制台快速入门
COSBrowser 快速入门
用户指南
创建请求
存储桶
对象
数据管理
批量处理
全球加速
监控与告警
运维中心
数据处理
内容审核
智能工具箱
数据工作流
应用集成
工具指南
工具概览
环境安装与配置
COSBrowser 工具
COSCLI 工具
COSCMD 工具
COS Migration 工具
FTP Server 工具
Hadoop 工具
COSDistCp 工具
HDFS TO COS 工具
GooseFS-Lite 工具
在线辅助工具
自助诊断工具
实践教程
概览
访问控制与权限管理
性能优化
使用 AWS S3 SDK 访问 COS
数据容灾备份
域名管理实践
图片处理实践
COS 音视频播放器实践
工作流实践
数据直传
内容审核实践
数据安全
数据校验
大数据实践
COS 成本优化解决方案
在第三方应用中使用 COS
迁移指南
本地数据迁移至 COS
第三方云存储数据迁移至 COS
以 URL 作为源地址的数据迁移至 COS
COS 之间数据迁移
Hadoop 文件系统与 COS 之间的数据迁移
数据湖存储
云原生数据湖
元数据加速
数据加速器 GooseFS
数据处理
数据处理概述
图片处理
媒体处理
内容审核
文件处理
文档处理
故障处理
获取 RequestId 操作指引
通过外网上传文件至 COS 缓慢
访问 COS 时返回403错误码
资源访问异常
POST Object 常见异常
API 文档
简介
公共请求头部
公共响应头部
错误码
请求签名
操作列表
Service 接口
Bucket 接口
Object 接口
批量处理接口
数据处理接口
任务与工作流
内容审核接口
云查毒接口
SDK 文档
SDK 概览
准备工作
Android SDK
C SDK
C++ SDK
.NET(C#) SDK
Flutter SDK
Go SDK
iOS SDK
Java SDK
JavaScript SDK
Node.js SDK
PHP SDK
Python SDK
React Native SDK
小程序 SDK
错误码
鸿蒙(Harmony) SDK
终端 SDK 质量优化
安全与合规
数据容灾
数据安全
访问管理
常见问题
热门问题
一般性问题
计费计量问题
域名合规问题
存储桶配置问题
域名和 CDN 问题
文件操作问题
日志监控问题
权限管理问题
数据处理问题
数据安全问题
预签名 URL 问题
SDK 类问题
工具类问题
API 类问题
服务协议
Service Level Agreement
隐私政策
数据处理和安全协议
联系我们
词汇表
文档对象存储SDK 文档iOS SDK上传对象实践教程

上传对象实践教程

PDF
聚焦模式
字号
最后更新时间: 2026-02-27 15:17:40
本文档介绍如何搭建安全的临时密钥服务以及如何使用 iOS SDK 初始化并上传。

方案优势

权限安全:可以有效限定安全的权限范围,只能用于上传指定的一个文件路径。
路径安全:由服务端决定随机的 COS 文件路径,可以有效避免已有文件被覆盖的问题和安全风险。

上传流程

1. 客户端选择文件,将原始文件名发送给服务端。
2. 服务端根据文件名后缀,生成带时间的随机 COS 文件路径,并申请对应权限的临时密钥和 cos key 返回给前端。
3. iOS 端使用高级上传接口上传文件到 COS。

临时密钥搭建

临时密钥(临时访问凭证) 是通过 CAM 云 API 提供的接口,获取到权限受限的密钥。当需要发起 COS API 请求时,需要用到获取临时密钥接口返回信息中 的 TmpSecretId、TmpSecretKey 和 Token 三个字段,用于计算签名。
getKeyAndCredentials 接口会返回临时密钥信息和上传 需要的 Bucket、 Region、 cosKey。
各语言示例代码:
NodeJS
Go
PHP
Python
Java
.Net
完整代码可参考 示例代码
// 临时密钥服务例子
const STS = require('qcloud-cos-sts');
const express = require('express');
const pathLib = require('path');
// 配置参数
const config = {
secretId: process.env.SecretId,
secretKey: process.env.SecretKey,
proxy: process.env.Proxy,
durationSeconds: 1800,
bucket: process.env.Bucket,
region: process.env.Region,
// 密钥的上传操作权限列表
allowActions: [
// 简单上传
'name/cos:PutObject',
// 分块上传
'name/cos:InitiateMultipartUpload',
'name/cos:ListMultipartUploads',
'name/cos:ListParts',
'name/cos:UploadPart',
'name/cos:CompleteMultipartUpload',
],
};

// 生成要上传的 COS 文件路径文件名
const generateCosKey = function (ext) {
const date = new Date();
const m = date.getMonth() + 1;
const ymd = `${date.getFullYear()}${m < 10 ? `0${m}` : m}${date.getDate()}`;
const r = ('000000' + Math.random() * 1000000).slice(-6);
const cosKey = `file/${ymd}/${ymd}_${r}${ext ? `${ext}` : ''}`;
return cosKey;
};
// 创建临时密钥服务
const app = express();
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
// 获取临时密钥
function getSts({ cosKey, condition }) {
return new Promise((resolve, reject) => {
// 获取临时密钥
const AppId = config.bucket.substr(config.bucket.lastIndexOf('-') + 1);
let resource =
'qcs::cos:' +
config.region +
':uid/' +
AppId +
':' +
config.bucket +
'/' +
cosKey;
console.log('检查resource是否正确', resource);
const policy = {
version: '2.0',
statement: [
{
action: config.allowActions,
effect: 'allow',
resource: [
// cos 相关授权路径
resource,
// ci 相关授权路径,按需使用
// 'qcs::ci:' + config.region + ':uid/' + AppId + ':bucket/' + config.bucket + '/' + 'job/*',
],
condition
},
],
};
const startTime = Math.round(Date.now() / 1000);
STS.getCredential(
{
secretId: config.secretId,
secretKey: config.secretKey,
proxy: config.proxy,
region: config.region,
durationSeconds: config.durationSeconds,
// endpoint: 'sts.internal.tencentcloudapi.com', // 支持设置 sts 内网域名
policy: policy,
},
function (err, tempKeys) {
if (tempKeys) tempKeys.startTime = startTime;
if (err) {
reject(err);
} else {
resolve(tempKeys);
}
}
);
});
}
// 返回临时密钥和上传信息,客户端自行计算签名
app.get('/getKeyAndCredentials', function (req, res, next) {
// 业务自行实现 用户登录态校验,比如对 token 校验
// const userToken = req.query.userToken;
// const canUpload = checkUserRole(userToken);
// if (!canUpload) {
// res.send({ error: '当前用户没有上传权限' });
// return;
// }

// 上传文件可控制类型、大小,按需开启
const permission = {
limitExt: false, // 限制上传文件后缀
extWhiteList: ['jpg', 'jpeg', 'png', 'gif', 'bmp'], // 限制的上传后缀
limitContentType: false, // 限制上传 contentType
limitContentLength: false, // 限制上传文件大小
};
// 客户端传进原始文件名,这里根据文件后缀生成随机 Key
const filename = req.query.filename;
if (!filename) {
res.send({ error: '请传入文件名' });
}
const ext = pathLib.extname(filename);
const cosKey = generateCosKey(ext);
const condition = {};
// 1. 限制上传文件后缀
if (permission.limitExt) {
const extInvalid = !ext || !extWhiteList.includes(ext);
if (extInvalid) {
res.send({ error: '非法文件,禁止上传' });
}
}
// 2. 限制上传文件 content-type
if (permission.limitContentType) {
Object.assign(condition, {
'string_like_if_exist': {
// 只允许上传 content-type 为图片类型
'cos:content-type': 'image/*'
}
});
}

// 3. 限制上传文件大小
if (permission.limitContentLength) {
Object.assign(condition, {
'numeric_less_than_equal': {
// 上传大小限制不能超过5MB(只对简单上传生效)
'cos:content-length': 5 * 1024 * 1024
},
});
}

getSts({ cosKey, condition })
.then((data) => {
res.send(
Object.assign(data, {
startTime: Math.round(Date.now() / 1000),
bucket: config.bucket,
region: config.region,
key: cosKey,
})
);
})
.catch((err) => {
console.log('sts error', err);
res.send(err);
});
});

app.all('*', function (req, res, next) {
res.send({ code: -1, message: '404 Not Found' });
});
// 启动签名服务
app.listen(3000);
console.log('app is listening at http://127.0.0.1:3000');
完整代码可参考 示例代码
package main

import (
"fmt"
"github.com/tencentyun/qcloud-cos-sts-sdk/go"
"math/rand"
"os"
"time"
)

type Config struct {
filename string
appId string
SecretId string
SecretKey string
Proxy string
DurationSeconds int
Bucket string
Region string
AllowActions []string
}

type Permission struct {
LimitExt bool `json:"limitExt"`
ExtWhiteList []string `json:"extWhiteList"`
LimitContentType bool `json:"limitContentType"`
LimitContentLength bool `json:"limitContentLength"`
}

func generateCosKey(ext string) string {
date := time.Now()
m := int(date.Month()) + 1
ymd := fmt.Sprintf("%d%02d%d", date.Year(), m, date.Day())
r := fmt.Sprintf("%06d", rand.Intn(1000000))
cosKey := fmt.Sprintf("file/%s/%s_%s.%s", ymd, ymd, r, ext)
return cosKey
}

func getPermission() Permission {
permission := Permission{
LimitExt: false,
ExtWhiteList: []string{"jpg", "jpeg", "png", "gif", "bmp"},
LimitContentType: false,
LimitContentLength: false,
}
return permission
}

func getConfig() Config {
config := Config{
filename: "test.jpg",
appId: "12500000000",
SecretId: os.Getenv("SECRETID"), // 用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考 https://www.tencentcloud.com/document/product/598/37140?from_cn_redirect=1
SecretKey: os.Getenv("SECRETKEY"), // 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考 https://www.tencentcloud.com/document/product/598/37140?from_cn_redirect=1
Proxy: os.Getenv("Proxy"),
DurationSeconds: 1800,
Bucket: "bucket-12500000000",
Region: "ap-guangzhou",
AllowActions: []string{
"name/cos:PutObject",
"name/cos:InitiateMultipartUpload",
"name/cos:ListMultipartUploads",
"name/cos:ListParts",
"name/cos:UploadPart",
"name/cos:CompleteMultipartUpload",
},
}
return config
}

func stringInSlice(str string, list []string) bool {
for _, v := range list {
if v == str {
return true
}
}
return false
}

func StructToCamelMap(input interface{}) map[string]interface{} {
v := reflect.ValueOf(input)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}

result := make(map[string]interface{})
typ := v.Type()

for i := 0; i < v.NumField(); i++ {
field := typ.Field(i)
fieldValue := v.Field(i)

// 转换字段名为小驼峰
key := toLowerCamel(field.Name)

// 处理嵌套结构体
if fieldValue.Kind() == reflect.Struct ||
(fieldValue.Kind() == reflect.Ptr && fieldValue.Elem().Kind() == reflect.Struct) {

if fieldValue.IsNil() && fieldValue.Kind() == reflect.Ptr {
result[key] = nil
continue
}

result[key] = StructToCamelMap(fieldValue.Interface())
} else {
// 处理基本类型
result[key] = fieldValue.Interface()
}
}

return result
}

// 转换为小驼峰格式(首字母小写)
func toLowerCamel(s string) string {
if s == "" {
return s
}

// 处理全大写单词(如 ID)
if strings.ToUpper(s) == s {
return strings.ToLower(s)
}

// 普通小驼峰转换
runes := []rune(s)
runes[0] = unicode.ToLower(runes[0])
return string(runes)
}

func main() {

config := getConfig()

permission := getPermission()

c := sts.NewClient(
// 通过环境变量获取密钥, os.Getenv 方法表示获取环境变量
config.SecretId, //os.Getenv("SECRETID"), // 用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考 https://www.tencentcloud.com/document/product/598/37140?from_cn_redirect=1
config.SecretKey, //os.Getenv("SECRETKEY"), // 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考 https://www.tencentcloud.com/document/product/598/37140?from_cn_redirect=1
nil,
// sts.Host("sts.internal.tencentcloudapi.com"), // 设置域名, 默认域名 sts.tencentcloudapi.com
// sts.Scheme("http"), // 设置协议, 默认为 https,公有云 sts 获取临时密钥不允许走 http,特殊场景才需要设置 http
)

condition := make(map[string]map[string]interface{})

segments := strings.Split(config.filename, ".")
if len(segments) == 0 {
//ext := ""
}
ext := segments[len(segments)-1]

if permission.LimitExt {
extInvalid := ext == "" || !stringInSlice(ext, permission.ExtWhiteList)
if extInvalid {
fmt.Printf("%+v\\n", "非法文件,禁止上传")
return
}
}

if permission.LimitContentType {
condition["string_like_if_exist"] = map[string]interface{}{
// 只允许上传 content-type 为图片类型
"cos:content-type": "image/*",
}
}

// 3. 限制上传文件大小
if permission.LimitContentLength {
condition["numeric_less_than_equal"] = map[string]interface{}{
// 上传大小限制不能超过5MB(只对简单上传生效)
"cos:content-length": 5 * 1024 * 1024,
}
}

key := generateCosKey(ext)
// 策略概述 https://www.tencentcloud.com/document/product/436/18023?from_cn_redirect=1
opt := &sts.CredentialOptions{
DurationSeconds: int64(config.DurationSeconds),
Region: config.Region,
Policy: &sts.CredentialPolicy{
Version: "2.0",
Statement: []sts.CredentialPolicyStatement{
{
// 密钥的权限列表。简单上传和分片需要以下的权限,其他权限列表请看 https://www.tencentcloud.com/document/product/436/31923?from_cn_redirect=1
Action: config.AllowActions,
Effect: "allow",
Resource: []string{
// 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径,例子: a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
// 存储桶的命名格式为 BucketName-APPID,此处填写的 bucket 必须为此格式
"qcs::cos:ap-guangzhou:uid/" + config.appId + ":" + config.Bucket + "/" + key,
},
// 开始构建生效条件 condition
// 关于 condition 的详细设置规则和 COS 支持的 condition 类型可以参考 https://www.tencentcloud.com/document/product/436/71306?from_cn_redirect=1
Condition: condition,
},
},
},
}

// case 1 请求临时密钥
res, err := c.GetCredential(opt)
if err != nil {
panic(err)
}
// 转换为小驼峰 map
resultMap := StructToCamelMap(res)
resultMap["bucket"] = config.Bucket
resultMap["region"] = config.Region
resultMap["key"] = key
// 打印结果
jsonBytes, err := json.MarshalIndent(resultMap, "", " ")
if err != nil {
panic(err)
}
// 转换为字符串并打印
fmt.Println(string(jsonBytes))
}
完整代码可参考 示例代码
<?php
require_once __DIR__ . '/vendor/autoload.php';

use QCloud\\COSSTS\\Sts;

// 生成要上传的 COS 文件路径文件名
function generateCosKey($ext) {
$ymd = date('Ymd');
$r = substr('000000' . rand(), -6);
$cosKey = 'file/' . $ymd. '/' . $ymd . '_' . $r;
if ($ext) {
$cosKey = $cosKey . '.' . $ext;
}
return $cosKey;
};

// 获取单一文件上传权限的临时密钥
function getKeyAndCredentials($filename) {
// 业务自行实现 用户登录态校验,比如对 token 校验
// $canUpload = checkUserRole($userToken);
// if (!$canUpload) {
// return '当前用户没有上传权限';
// }

// 上传文件可控制类型、大小,按需开启
$permission = array(
'limitExt' => false, // 限制上传文件后缀
'extWhiteList' => ['jpg', 'jpeg', 'png', 'gif', 'bmp'], // 限制的上传后缀
'limitContentType' => false, // 限制上传 contentType
'limitContentLength' => false, // 限制上传文件大小
);
$condition = array();

// 客户端传进原始文件名,这里根据文件后缀生成随机 Key
$ext = pathinfo($filename, PATHINFO_EXTENSION);

// 1. 限制上传文件后缀
if ($permission['limitExt']) {
if ($ext === '' || array_key_exists($ext, $permission['extWhiteList'])) {
return '非法文件,禁止上传';
}
}

// 2. 限制上传文件 content-type
if ($permission['limitContentType']) {
// 只允许上传 content-type 为图片类型
$condition['string_like_if_exist'] = array('cos:content-type' => 'image/*');
}

// 3. 限制上传文件大小
if ($permission['limitContentLength']) {
// 上传大小限制不能超过5MB(只对简单上传生效)
$condition['numeric_less_than_equal'] = array('cos:content-length' => 5 * 1024 * 1024);
}

$cosKey = generateCosKey($ext);
$bucket = 'test-131234567'; // 换成你的 bucket
$region = 'ap-guangzhou'; // 换成 bucket 所在园区
$config = array(
'url' => 'https://sts.tencentcloudapi.com/', // url 和 domain保持一致
'domain' => 'sts.tencentcloudapi.com', // 域名,非必须,默认为 sts.tencentcloudapi.com
'proxy' => '',
'secretId' => getenv('GROUP_SECRET_ID'), // 固定密钥,若为明文密钥,请直接以'xxx'形式填入,不要填写到 getenv()函数中
'secretKey' => getenv('GROUP_SECRET_KEY'), // 固定密钥,若为明文密钥,请直接以'xxx'形式填入,不要填写到 getenv()函数中
'bucket' => $bucket, // 换成你的 bucket
'region' => $region, // 换成 bucket 所在园区
'durationSeconds' => 1800, // 密钥有效期
'allowPrefix' => array($cosKey), // 只分配当前 key 的路径权限
// 密钥的权限列表。简单上传和分片需要以下的权限,其他权限列表请看 https://www.tencentcloud.com/document/product/436/31923?from_cn_redirect=1
'allowActions' => array (
// 简单上传
'name/cos:PutObject',
// 分片上传
'name/cos:InitiateMultipartUpload',
'name/cos:ListMultipartUploads',
'name/cos:ListParts',
'name/cos:UploadPart',
'name/cos:CompleteMultipartUpload'
),
);

if (!empty($condition)) {
$config['condition'] = $condition;
}

$sts = new Sts();
$tempKeys = $sts->getTempKeys($config);
$resTemp = array_merge(
$tempKeys,
[
'startTime' => time(),
'bucket' => $bucket,
'region' => $region,
'key' => $cosKey,
]
);
echo json_encode($resTemp, JSON_UNESCAPED_SLASHES);
return $resTemp;
}
完整代码可参考 示例代码
#!/usr/bin/env python
# coding=utf-8
import json
import os
import datetime
import random

from sts.sts import Sts

if __name__ == '__main__':

# 配置参数
config = {
"filename":"test.jpg",
"appId": "125000000",
"secretId": os.getenv("SecretId"),
"secretKey": os.getenv("SecretKey"),
"proxy": os.getenv("Proxy"),
"durationSeconds": 1800,
"bucket": "bucket-125000000",
"region": "ap-guangzhou",
# 密钥的上传操作权限列表
"allowActions": [
# 简单上传
"name/cos:PutObject",
# 分块上传
"name/cos:InitiateMultipartUpload",
"name/cos:ListMultipartUploads",
"name/cos:ListParts",
"name/cos:UploadPart",
"name/cos:CompleteMultipartUpload",
],
}

permission = {
"limitExt": False, # 限制上传文件后缀
"extWhiteList": ["jpg", "jpeg", "png", "gif", "bmp"], # 限制的上传后缀
"limitContentType": False, # 限制上传 contentType
"limitContentLength": False, # 限制上传文件大小
}


# 生成要上传的 COS 文件路径文件名
def generate_cos_key(ext=None):
date = datetime.datetime.now()
ymd = date.strftime('%Y%m%d')
r = str(int(random.random() * 1000000)).zfill(6)
cos_key = f"file/{ymd}/{ymd}_{r}.{ext if ext else ''}"
return cos_key


segments = config['filename'].split(".")
ext = segments[-1] if segments else ""
key = generate_cos_key(ext)
resource = f"qcs::cos:{config['region']}:uid/{config['appId']}:{config['bucket']}/{key}"

condition = {}

# 1. 限制上传文件后缀
if permission["limitExt"]:
ext_invalid = not ext or ext not in permission["extWhiteList"]
if ext_invalid:
print('非法文件,禁止上传')

# 2. 限制上传文件 content-type
if permission["limitContentType"]:
condition.update({
"string_like_if_exist": {
# 只允许上传 content-type 为图片类型
"cos:content-type": "image/*"
}
})

# 3. 限制上传文件大小
if permission["limitContentLength"]:
condition.update({
"numeric_less_than_equal": {
# 上传大小限制不能超过5MB(只对简单上传生效)
"cos:content-length": 5 * 1024 * 1024
}
})


def get_credential_demo():
credentialOption = {
# 临时密钥有效时长,单位是秒
'duration_seconds': config.get('durationSeconds'),
'secret_id': config.get("secretId"),
# 固定密钥
'secret_key': config.get("secretKey"),
# 换成你的 bucket
'bucket': config.get("bucket"),
'proxy': config.get("proxy"),
# 换成 bucket 所在地区
'region': config.get("region"),
"policy": {
"version": '2.0',
"statement": [
{
"action": config.get("allowActions"),
"effect": "allow",
"resource": [
resource
],
"condition": condition
}
],
},
}

try:

sts = Sts(credentialOption)
response = sts.get_credential()
credential_dic = dict(response)
credential_info = credential_dic.get("credentials")
credential = {
"bucket": config.get("bucket"),
"region": config.get("region"),
"key": key,
"startTime": credential_dic.get("startTime"),
"expiredTime": credential_dic.get("expiredTime"),
"requestId": credential_dic.get("requestId"),
"expiration": credential_dic.get("expiration"),
"credentials": {
"tmpSecretId": credential_info.get("tmpSecretId"),
"tmpSecretKey": credential_info.get("tmpSecretKey"),
"sessionToken": credential_info.get("sessionToken"),
},
}
print('get data : ' + json.dumps(credential, indent=4))
except Exception as e:
print(e)

get_credential_demo()
完整代码可参考 示例代码
package com.tencent.cloud;

import com.tencent.cloud.assumerole.AssumeRoleParam;
import com.tencent.cloud.cos.util.Jackson;
import org.junit.Test;

import java.io.File;
import java.io.FileInputStream;
import java.text.SimpleDateFormat;
import java.util.*;

public class GetKeyAndCredentialsTest {

public static String generateCosKey(String ext) {
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
String ymd = dateFormat.format(date);

Random random = new Random();
int r = random.nextInt(1000000);
String rStr = String.format("%06d", r);

String cosKey = String.format("file/%s/%s_%s.%s", ymd, ymd, rStr, ext != null ? ext : "");
return cosKey;
}

// 获取配置信息
public TreeMap<String,Object> getConfig(){

String bucket = "test-12500000000";
String appId = "12500000000";
String filename = "test.jpg";
String region = "ap-guangzhou";
String secretId = "";
String secretKey = "";
String proxy = "";
int durationSeconds = 1800;

String[] segments = filename.split("\\\\.");
String ext = segments.length > 0 ? segments[segments.length - 1] : "";

// 临时密钥限制
Boolean limitExt = false; // 限制上传文件后缀
List extWhiteList = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp"); // 限制的上传后缀
Boolean limitContentType = false; // 限制上传 contentType
Boolean limitContentLength = false; // 限制上传文件大小


Map<String, Object> condition = new HashMap();

// 1. 限制上传文件后缀
if (limitExt) {
boolean extInvalid = ext == null || !extWhiteList.contains(ext);
if (extInvalid) {
System.out.println("非法文件,禁止上传");
return null;
}
}

// 2. 限制上传文件 content-type
if (limitContentType) {
condition.put("string_like_if_exist", new HashMap<String, String>() {{
put("cos:content-type", "image/*");
}});
}

// 3. 限制上传文件大小(只对简单上传生效)
if (limitContentLength) {
condition.put("numeric_less_than_equal", new HashMap<String, Long>() {{
put("cos:content-length", 5L * 1024 * 1024);
}});
}
String key = generateCosKey(ext);
String resource = "qcs::cos:" + region + ":uid/" + appId + ':' + bucket + '/' + key;
List allowActions = Arrays.asList(
// 简单上传
"name/cos:PutObject",
// 分块上传
"name/cos:InitiateMultipartUpload",
"name/cos:ListMultipartUploads",
"name/cos:ListParts",
"name/cos:UploadPart",
"name/cos:CompleteMultipartUpload"
);

// 构建policy
Map<String, Object> policy = new HashMap();
policy.put("version", "2.0");
Map<String, Object> statement = new HashMap();
statement.put("action", allowActions);
statement.put("effect", "allow");
List<String> resources = Arrays.asList(
resource
);
statement.put("resource", resources);
statement.put("condition", condition);
policy.put("statement", Arrays.asList(statement));


// 构建config
TreeMap <String,Object> config = new TreeMap<String, Object>();
config.put("secretId",secretId);
config.put("secretKey",secretKey);
config.put("proxy",proxy);
config.put("duration",durationSeconds);
config.put("bucket",bucket);
config.put("region",region);
config.put("key",key);
config.put("policy",Jackson.toJsonPrettyString(policy));
return config;
}

/**
* 基本的临时密钥申请示例,适合对一个桶内的一批对象路径,统一授予一批操作权限
*/
@Test
public void testGetKeyAndCredentials() {
TreeMap config = this.getConfig();
try {
Response response = CosStsClient.getCredential(config);
TreeMap <String,Object> credential = new TreeMap<String, Object>();
TreeMap <String,Object> credentials = new TreeMap<String, Object>();
credentials.put("tmpSecretId",response.credentials.tmpSecretId);
credentials.put("tmpSecretKey",response.credentials.tmpSecretKey);
credentials.put("sessionToken",response.credentials.sessionToken);
credential.put("startTime",response.startTime);
credential.put("expiredTime",response.expiredTime);
credential.put("requestId",response.requestId);
credential.put("expiration",response.expiration);
credential.put("credentials",credentials);
credential.put("bucket",config.get("bucket"));
credential.put("region",config.get("region"));
credential.put("key",config.get("key"));
System.out.println(Jackson.toJsonPrettyString(credential));
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException("no valid secret !");
}
}
}
完整代码可参考 示例代码
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Mail;
using COSSTS;
using Newtonsoft.Json;
using Formatting = System.Xml.Formatting;


namespace COSSnippet
{
public class GetKeyAndCredentials
{
//永久密钥
string secretId = "";
string secretKey = "";

string bucket = "bucket-125000000";
string appId = "125000000";
string region = "ap-guangzhou";
string filename = "test.jpg";
int time = 1800;
// 限制
Boolean limitExt = false; // 限制上传文件后缀
List<string> extWhiteList = new List<String> { "jpg", "jpeg", "png", "gif", "bmp" }; // 限制的上传后缀
Boolean limitContentType = false; // 限制上传 contentType
Boolean limitContentLength = false; // 限制上传文件大小

public string generateCosKey(string ext)
{
DateTime date = DateTime.Now;
int m = date.Month;
string ymd = $"{date.Year}{(m < 10 ? $"0{m}" : m.ToString())}{date.Day}";
Random random = new Random();
string r = random.Next(0, 1000000).ToString("D6"); // 生成6位随机数,前面补零
string cosKey = $"file/{ymd}/{ymd}_{r}.{(string.IsNullOrEmpty(ext) ? "" : ext)}";
return cosKey;
}
public Dictionary<string, object> getConfig()
{
Dictionary<string, object> config = new Dictionary<string, object>();
string[] allowActions = new string[] { // 允许的操作范围,这里以上传操作为例
"name/cos:PutObject",
"name/cos:PostObject",
"name/cos:InitiateMultipartUpload",
"name/cos:ListMultipartUploads",
"name/cos:ListParts",
"name/cos:UploadPart",
"name/cos:CompleteMultipartUpload",
};
string[] segments = filename.Split(".");
string ext = segments.Length > 0 ? segments[segments.Length - 1] : string.Empty;
string key = generateCosKey(ext);
string resource = $"qcs::cos:{region}:uid/{appId}:{bucket}/{key}";

var condition = new Dictionary<string, object>();
// 1. 限制上传文件后缀
if (limitExt)
{
var extInvalid = string.IsNullOrEmpty(ext) || !extWhiteList.Contains(ext);
if (extInvalid)
{
Console.WriteLine("非法文件,禁止上传");
return null;
}
}

// 2. 限制上传文件 content-type
if (limitContentType)
{
condition["string_like_if_exist"] = new Dictionary<string, string>
{
{ "cos:content-type", "image/*" } // 只允许上传 content-type 为图片类型
};
}

// 3. 限制上传文件大小(只对简单上传生效)
if (limitContentLength)
{
condition["numeric_less_than_equal"] = new Dictionary<string, long>
{
{ "cos:content-length", 5 * 1024 * 1024 } // 上传大小限制不能超过5MB
};
}

var policy = new Dictionary<string, object>
{
{ "version", "2.0" },
{ "statement", new List<Dictionary<string, object>>
{
new Dictionary<string, object>
{
{ "action", allowActions },
{ "effect", "allow" },
{ "resource", new List<string>
{
resource,
}
},
{ "condition", condition }
}
}
}
};

// 序列化为 JSON 并输出
string jsonPolicy = JsonConvert.SerializeObject(policy);
config.Add("bucket", bucket);
config.Add("region", region);
config.Add("durationSeconds", time);

config.Add("secretId", secretId);
config.Add("secretKey", secretKey);
config.Add("key", key);
config.Add("policy", jsonPolicy);
return config;
}
// 获取联合身份临时访问凭证 https://www.tencentcloud.com/document/product/1312/48195?from_cn_redirect=1
public Dictionary<string, object> GetCredential()
{

var config = getConfig();
//获取临时密钥
Dictionary<string, object> credential = STSClient.genCredential(config);
Dictionary<string, object> credentials = JsonConvert.DeserializeObject<Dictionary<string, object>>(JsonConvert.SerializeObject((object) credential["Credentials"]));
Dictionary<string, object> credentials1 = new Dictionary<string, object>();
credentials1.Add("tmpSecretId",credentials["TmpSecretId"]);
credentials1.Add("tmpSecretKey",credentials["TmpSecretKey"]);
credentials1.Add("sessionToken",credentials["Token"]);
Dictionary<string, object> dictionary1 = new Dictionary<string, object>();
dictionary1.Add("credentials",credentials1);
dictionary1.Add("startTime",credential["StartTime"]);
dictionary1.Add("requestId",credential["RequestId"]);
dictionary1.Add("expiration",credential["Expiration"]);
dictionary1.Add("expiredTime",credential["ExpiredTime"]);
dictionary1.Add("bucket",config["bucket"]);
dictionary1.Add("region",config["region"]);
dictionary1.Add("key",config["key"]);
return dictionary1;
}
static void Main(string[] args)
{
GetKeyAndCredentials m = new GetKeyAndCredentials();
Dictionary<string, object> result = m.GetCredential();
string Credentials = JsonConvert.SerializeObject(result);
Console.WriteLine($"{Credentials}");
}
}
}

客户端发起上传

详细使用步骤请查看 快速入门

// 一、初始化 COS SDK ,初始化一次即可,建议放在 app 已启动 就进行。
QCloudServiceConfiguration *configuration = [QCloudServiceConfiguration new];
configuration.appID = @"1250000000";
QCloudCOSXMLEndPoint *endpoint = [[QCloudCOSXMLEndPoint alloc] init];
endpoint.regionName = @"ap-guangzhou";
configuration.endpoint = endpoint;
[QCloudCOSXMLService registerDefaultCOSXMLWithConfiguration:configuration];
[QCloudCOSTransferMangerService registerDefaultCOSTransferMangerWithConfiguration:configuration];

// 二、调用业务服务 sts 接口,获取临时密钥

__block NSDictionary * credentialsInfo;
[[[NSURLSession sharedSession]dataTaskWithURL:[NSURL URLWithString:@"http://******"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 从业务服务接口请求临时密钥。
NSDictionary * dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingTopLevelDictionaryAssumed error:nil];
credentialsInfo = dic;
NSDictionary * credentials = dic[@"credentials"];
QCloudCredential *credential = [QCloudCredential new];
credential.secretID = credentials[@"tmpSecretId"];
credential.secretKey = credentials[@"tmpSecretKey"];
credential.token = credentials[@"sessionToken"];
//签名过期时间
credential.expirationDate = [NSDate dateWithTimeIntervalSince1970:[dic[@"expiredTime"] integerValue]];
credential.startDate = [NSDate dateWithTimeIntervalSince1970:[dic[@"startTime"] integerValue]];;
QCloudAuthentationV5Creator *creator = [[QCloudAuthentationV5Creator alloc] initWithCredential:credential];
continueBlock(creator, nil);
}] resume];

// 三、使用 QCloudCOSXMLUploadObjectRequest 开始上传。
QCloudCOSXMLUploadObjectRequest* put = [QCloudCOSXMLUploadObjectRequest new];

// 从业务接口请求到的临时凭证
QCloudCredential *credential = [QCloudCredential new];
// 将请求到的临时密钥设置给 QCloudCOSXMLUploadObjectRequest 实例对象。
put.credential = credential;
/** 本地文件路径,请确保 URL 是以 file:// 开头,格式如下 :
1. [NSURL URLWithString:@"file:////var/mobile/Containers/Data/Application/DBPF7490-D5U8-4ABF-A0AF-CC49D6A60AEB/Documents/exampleobject"]
2. [NSURL fileURLWithPath:@"/var/mobile/Containers/Data/Application/DBPF7490-D5U8-4ABF-A0AF-CC49D6A60AEB/Documents/exampleobject"]
*/
NSURL* url = [NSURL fileURLWithPath:@"文件的URL"];
// 存储桶名称,由BucketName-Appid 组成,可以在 COS 控制台查看 https://console.tencentcloud.com/cos5/bucket
put.bucket = credentialsInfo[@"bucket"];
// 对象键,是对象在 COS 上的完整路径,如果带目录的话,格式为 "video/xxx/movie.mp4"
put.object = credentialsInfo[@"key"];
put.regionName = credentialsInfo[@"region"];
// 需要上传的对象内容。可以传入 NSData*或者 NSURL*类型的变量
put.body = url;
// 监听上传进度
[put setSendProcessBlock:^(int64_t bytesSent,
int64_t totalBytesSent,
int64_t totalBytesExpectedToSend) {
// bytesSent 本次要发送的字节数(一个大文件可能要分多次发送)
// totalBytesSent 已发送的字节数
// totalBytesExpectedToSend 本次上传要发送的总字节数(即一个文件大小)
}];
// 监听上传结果
[put setFinishBlock:^(QCloudUploadObjectResult *result, NSError *error) {
// 在上传结果 result.location 中获取上传文件的下载链接
NSString * fileUrl = result.location;
// 获取文件 crc64
NSString * crc64 = [[outputObject __originHTTPURLResponse__].allHeaderFields valueForKey:@"x-cos-hash-crc64ecma"];
}];
[put setInitMultipleUploadFinishBlock:^(QCloudInitiateMultipartUploadResult *
multipleUploadInitResult,
QCloudCOSXMLUploadObjectResumeData resumeData) {
// 在初始化分块上传完成以后会回调该 block,在这里可以获取 resumeData,uploadid
NSString* uploadId = multipleUploadInitResult.uploadId;
}];
[[QCloudCOSTransferMangerService defaultCOSTransferManager] UploadObject:put];

客户端完整代码可参考 示例代码

相关文档

上传报错问题可参考 iOS SDK 常见问题
如何安全上传对象可参考 上传安全限制

帮助和支持

本页内容是否解决了您的问题?

填写满意度调查问卷,共创更好文档体验。

文档反馈