本文档介绍如何搭建安全的临时密钥服务以及如何使用 iOS SDK 初始化并上传。
方案优势
权限安全:可以有效限定安全的权限范围,只能用于上传指定的一个文件路径。
路径安全:由服务端决定随机的 COS 文件路径,可以有效避免已有文件被覆盖的问题和安全风险。
上传流程
1. 客户端选择文件,客户端将原始文件名发送给服务端。
2. 服务端根据文件名后缀,生成带时间的随机 COS 文件路径,并申请对应权限的临时密钥和 cos key 返回给前端。
3. iOS 端使用高级上传接口上传文件到 COS。
临时密钥搭建
临时密钥(临时访问凭证) 是通过 CAM 云 API 提供的接口,获取到权限受限的密钥。当需要发起 COS API 请求时,需要用到获取临时密钥接口返回信息中 的 TmpSecretId、TmpSecretKey 和 Token 三个字段,用于计算签名。
getKeyAndCredentials 接口会返回临时密钥信息和上传 需要的 Bucket、 Region、 cosKey。
以下示例各语言示例代码:
// 临时密钥服务例子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, // 限制上传 contentTypelimitContentLength: false, // 限制上传文件大小};// 客户端传进原始文件名,这里根据文件后缀生成随机 Keyconst filename = req.query.filename;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-typeif (permission.limitContentType) {Object.assign(condition, {'string_like': {// 只允许上传 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({TmpSecretId: data.credentials.tmpSecretId,TmpSecretKey: data.credentials.tmpSecretKey,SessionToken: data.credentials.sessionToken,StartTime: Math.round(Date.now() / 1000),ExpiredTime: data.expiredTime,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 mainimport ("fmt""github.com/tencentyun/qcloud-cos-sts-sdk/go""math/rand""os""time")type Config struct {filename stringappId stringSecretId stringSecretKey stringProxy stringDurationSeconds intBucket stringRegion stringAllowActions []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()) + 1ymd := 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://cloud.tencent.com/document/product/598/37140SecretKey: os.Getenv("SECRETKEY"), // 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140Proxy: 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 main() {config := getConfig()permission := getPermission()c := sts.NewClient(// 通过环境变量获取密钥, os.Getenv 方法表示获取环境变量config.SecretId, //os.Getenv("SECRETID"), // 用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140config.SecretKey, //os.Getenv("SECRETKEY"), // 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140nil,// 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"] = 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,}}// 策略概述 https://cloud.tencent.com/document/product/436/18023opt := &sts.CredentialOptions{DurationSeconds: int64(config.DurationSeconds),Region: config.Region,Policy: &sts.CredentialPolicy{Version: "2.0",Statement: []sts.CredentialPolicyStatement{{// 密钥的权限列表。简单上传和分片需要以下的权限,其他权限列表请看 https://cloud.tencent.com/document/product/436/31923Action: config.AllowActions,Effect: "allow",Resource: []string{// 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径,例子: a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)// 存储桶的命名格式为 BucketName-APPID,此处填写的 bucket 必须为此格式"qcs::cos:ap-guangzhou:uid/" + config.appId + ":" + config.Bucket + "/" + generateCosKey(ext),},// 开始构建生效条件 condition// 关于 condition 的详细设置规则和COS支持的condition类型可以参考https://cloud.tencent.com/document/product/436/71306Condition: condition,},},},}// 请求临时密钥res, err := c.GetCredential(opt)if err != nil {panic(err)}fmt.Printf("%+v\\n", res)fmt.Printf("%+v\\n", res.Credentials)}
<?phprequire_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-typeif ($permission['limitContentType']) {// 只允许上传 content-type 为图片类型$condition['string_like'] = 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://cloud.tencent.com/document/product/436/31923'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('TmpSecretId' => $tempKeys['credentials']['tmpSecretId'],'TmpSecretKey' => $tempKeys['credentials']['tmpSecretKey'],'SessionToken' => $tempKeys['credentials']['sessionToken'],'StartTime' => time(),'ExpiredTime' => $tempKeys['expiredTime'],'Bucket' => $bucket,'Region' => $region,'Key' => $cosKey,);echo json_encode($resTemp);return $resTemp;}
#!/usr/bin/env python# coding=utf-8import jsonimport osimport datetimeimport randomfrom sts.sts import Stsif __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_keysegments = config['filename'].split(separator)ext = segments[-1] if segments else ""resource = f"qcs::cos:{config['region']}:uid/{config['appId']}:{config['bucket']}/{generate_cos_key(ext)}"condition = {}# 1. 限制上传文件后缀if permission["limitExt"]:ext_invalid = not ext or ext not in permission["extWhiteList"]if ext_invalid:print('非法文件,禁止上传')# 2. 限制上传文件 content-typeif permission["limitContentType"]:condition.update({"string_like": {# 只允许上传 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()print('get data : ' + json.dumps(dict(response), 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 = "bucket-1250000000";String appId = "1250000000";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; // 限制上传 contentTypeBoolean 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-typeif (limitContentType) {condition.put("string_like", 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 resource = "qcs::cos:" + region + ":uid/" + appId + ':' + bucket + '/' + generateCosKey(ext);List allowActions = Arrays.asList(// 简单上传"name/cos:PutObject",// 分块上传"name/cos:InitiateMultipartUpload","name/cos:ListMultipartUploads","name/cos:ListParts","name/cos:UploadPart","name/cos:CompleteMultipartUpload");// 构建policyMap<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));// 构建configTreeMap <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("policy",Jackson.toJsonPrettyString(policy));return config;}/*** 基本的临时密钥申请示例,适合对一个桶内的一批对象路径,统一授予一批操作权限*/@Testpublic void testGetKeyAndCredentials() {TreeMap config = this.getConfig();try {Response response = CosStsClient.getCredential(config);System.out.println(response.credentials.tmpSecretId);System.out.println(response.credentials.tmpSecretKey);System.out.println(response.credentials.sessionToken);} 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; // 限制上传 contentTypeBoolean 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 resource = $"qcs::cos:{region}:uid/{appId}:{bucket}/{generateCosKey(ext)}";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-typeif (limitContentType){condition["string_like"] = 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("policy", jsonPolicy);return config;}// 获取联合身份临时访问凭证 https://cloud.tencent.com/document/product/1312/48195public Dictionary<string, object> GetCredential(){var config = getConfig();//获取临时密钥Dictionary<string, object> credential = STSClient.genCredential(config);return credential;}static void Main(string[] args){GetKeyAndCredentials m = new GetKeyAndCredentials();Dictionary<string, object> result = m.GetCredential();Console.WriteLine("Credentials:" + result["Credentials"]);Console.WriteLine("ExpiredTime:" + result["ExpiredTime"]);Console.WriteLine("StartTime:" + result["StartTime"]);}}}
客户端发起上传
// 一、初始化 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接口,获取临时密钥[[[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];NSDictionary * credentials = dic[@"credentials"];QCloudCredential *credential = [QCloudCredential new];credential.secretID = credentials[@"tmpSecretId"];credential.secretKey = credentials[@"tmpSecretKey"];;credential.token = credentials[@"sessionToken"];;//签名过期时间credential.expirationDate = nil;credential.startDate = nil;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.cloud.tencent.com/cos5/bucketput.bucket = @"examplebucket-1250000000";// 对象键,是对象在 COS 上的完整路径,如果带目录的话,格式为 "video/xxx/movie.mp4"put.object = @"exampleobject";// 需要上传的对象内容。可以传入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;// 获取文件crc64NSString * crc64 = [[outputObject __originHTTPURLResponse__].allHeaderFields valueForKey:@"x-cos-hash-crc64ecma"];}];[put setInitMultipleUploadFinishBlock:^(QCloudInitiateMultipartUploadResult *multipleUploadInitResult,QCloudCOSXMLUploadObjectResumeData resumeData) {// 在初始化分块上传完成以后会回调该 block,在这里可以获取 resumeData,uploadidNSString* uploadId = multipleUploadInitResult.uploadId;}];[[QCloudCOSTransferMangerService defaultCOSTransferManager] UploadObject:put];
相关文档
上传报错问题可参考 iOS SDK 常见问题。
如何安全上传对象可参考 上传安全限制。