一、简介
注意:这篇档内容基于 XML 版本的 API。
二、前提条件
1. 登录 COS 控制台 并创建存储桶,得到 Bucket(存储桶名称) 和 Region(地域名称),详情请参见 创建存储桶 文档。
2. 登录 访问管理控制台, 获取你的项目 SecretId 和 SecretKey。
三、实践步骤
整体步骤为:
1. 客户端调用服务端接口传入文件后缀,服务端根据后缀和时间戳等生成 cos key 以及直传的 url。
2. 服务端通过 sts sdk 获取临时密钥。
3. 服务端用获取到的临时密钥对直传 url 进行签名并返回 url、签名、token 等信息。
4. 客户端获取到步骤 3 中的信息后,直接发起 put 请求并携带签名、token 等 header 进行上传。
具体代码可参考:iOS 示例 和 Android 示例。
四、配置服务端实现签名
注意:正式部署时服务端请加一层你的网站本身的权限检验。
出于安全考虑,后端获取临时密钥后生成直传 url 并直接对其进行签名,可参考 Server 示例。
具体步骤为:
1. 通过 sts sdk 获取临时密钥。
2. 根据后缀名生成 cos key 以及直传 url 相关。
3. 使用临时密钥对直传 url 进行签名并返回直传 url、签名、token 等信息
服务端配置步骤:
4. 配置好密钥、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'],};
5. 终端执行
npm install
6. 启动服务
node app.js
到这里服务端就启动成功了,可以开始客户端的流程。
如有其他语言或自行实现可以参考以下流程:
7. 向服务端获取临时密钥,服务端首先使用固定密钥 SecretId、SecretKey 向 STS 服务获取临时密钥,得到临时密钥 tmpSecretId、tmpSecretKey、sessionToken,详情请参考 临时密钥生成及使用指引 或 cos-sts-sdk 文档。
8. 对直传 url 进行签名,生成 authorization。
9. 返回直传 url、authorization、sessionToken 等信息,客户端上传文件时将得到的签名和 sessionToken,分别放到发请求时 header 的 authorization 和 x-cos-security-token 字段里。
五、iOS 客户端上传
1. 从服务端请求直传和签名信息。
这里的服务端地址为上一步启动的 node 服务。
// ext 为需要上传的文件后缀名,例如 jpg、png 等http://127.0.0.1:3000/sts-direct-sign?ext=jpg
iOS 请求代码
NSURL * stsURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:3000/sts-direct-sign?ext=%@",@"文件后缀"]];NSMutableURLRequest * stsRequest = [NSMutableURLRequest requestWithURL:stsURL];[stsRequest setHTTPMethod:@"GET"];NSURLSession * session = [NSURLSession sharedSession];NSURLSessionDataTask * task = [session dataTaskWithRequest:stsRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSDictionary * dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil]; NSDictionary * params = dic[@"data"]; // 这里包含签名信息。}];[task resume];
2. 使用获取到的签名信息开始上传文件
// params 为上一步请求到的 dic[@"data"]NSString * cosHost = [params objectForKey:@"cosHost"]; NSString * cosKey = [params objectForKey:@"cosKey"];NSString * authorization = [params objectForKey:@"authorization"];NSString * securityToken = [params objectForKey:@"securityToken"];NSURL * stsURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@/%@",cosHost,cosKey]];NSMutableURLRequest * stsRequest = [NSMutableURLRequest requestWithURL:stsURL];[stsRequest setHTTPMethod:@"PUT"];[stsRequest setAllHTTPHeaderFields:@{ @"Content-Type":@"application/octet-stream", @"Content-Length":@(data.length).stringValue,// data 为待上传的文件NSData数据 @"Authorization":authorization, @"x-cos-security-token":securityToken, @"Host":cosHost}];NSURLSession * session = [NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];NSURLSessionDataTask * task = [session uploadTaskWithRequest:stsRequest fromData:self.body completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if(error){ NSLog(@"上传失败"); }else{ NSLog(@"上传成功"); }}];[task resume];
六、Android 客户端上传
1. 从服务端请求直传和签名信息。
/** * 获取直传的 url 和签名等 * * @param ext 文件后缀 直传后端会根据后缀生成 cos key * @return 直传 url 和签名等 */ private JSONObject getStsDirectSign(String ext) { // 获取上传路径和签名 HttpURLConnection getConnection = null; try { //直传签名业务服务端 url(正式环境 请替换成正式的直传签名业务 url) URL url = new URL("http://127.0.0.1:3000/sts-direct-sign?ext=" + ext); getConnection = (HttpURLConnection) url.openConnection(); getConnection.setRequestMethod("GET"); int responseCode = getConnection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { BufferedReader reader = new BufferedReader(new InputStreamReader(getConnection.getInputStream())); StringBuilder stringBuilder = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { stringBuilder.append(line); } reader.close(); JSONObject jsonObject; try { jsonObject = new JSONObject(stringBuilder.toString()); if(jsonObject.has("code") && jsonObject.optInt("code") == 0){ return jsonObject.optJSONObject("data"); } else { Log.e(TAG, String.format("getStsDirectSign error code: %d, error message: %s", jsonObject.optInt("code"), jsonObject.optString("message"))); } } catch (JSONException e) { e.printStackTrace(); } } else { Log.e(TAG, "getStsDirectSign HTTP error code: " + responseCode); } } catch (IOException e) { e.printStackTrace(); Log.e(TAG, "getStsDirectSign Error sending GET request: " + e.getMessage()); } finally { if (getConnection != null) { getConnection.disconnect(); } } return null; }
2. 使用获取到的直传和签名信息开始上传文件
/** * 上传文件 * @param filePath 本地文件路径 * @param listener 进度回调 */ private void uploadFile(String filePath, final ProgressListener listener) { File file = new File(filePath); // 获取直传签名等数据 JSONObject directTransferData = getStsDirectSign(FilePathHelper.getFileExtension(file)); if (directTransferData == null) { toastMessage("getStsDirectSign fail"); return; } String cosHost = directTransferData.optString("cosHost"); String cosKey = directTransferData.optString("cosKey"); String authorization = directTransferData.optString("authorization"); String securityToken = directTransferData.optString("securityToken"); //生成上传的 url URL url; try { url = new URL(String.format("https://%s/%s", cosHost, cosKey)); } catch (MalformedURLException e) { e.printStackTrace(); return; } HttpURLConnection conn = null; try { conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("PUT"); conn.setDoOutput(true); // 设置请求头 conn.setRequestProperty("Content-Type", "application/octet-stream"); conn.setRequestProperty("Content-Length", String.valueOf(file.length())); conn.setRequestProperty("Authorization", authorization); conn.setRequestProperty("x-cos-security-token", securityToken); conn.setRequestProperty("Host", cosHost); // 获取输出流 OutputStream outputStream = conn.getOutputStream(); // 读取文件并上传 FileInputStream inputStream = new FileInputStream(file); byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead; long totalBytesRead = 0; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); totalBytesRead += bytesRead; if (listener != null) { listener.onProgress(totalBytesRead, file.length()); } } // 关闭流 inputStream.close(); outputStream.flush(); outputStream.close(); // 获取响应码 int responseCode = conn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { // 上传成功 } else { Log.e(TAG, "uploadFile HTTP error code: " + responseCode); } } catch (IOException e) { e.printStackTrace(); Log.e(TAG, "uploadFile Error sending PUT request: " + e.getMessage()); } finally { if (conn != null) { conn.disconnect(); } } }
七、相关文档
若你有更丰富的接口调用需求,请参考以下 客户端 SDK 文档:
·iOS SDK
·Android SDK