简介
本文档介绍如何不依赖 SDK,用简单的代码,在APP端直传文件到对象存储(Cloud Object Storage,COS)的存储桶。
注意
前提条件
2. 登录 访问管理控制台, 获取您的项目 SecretId 和 SecretKey。
实践步骤
整体步骤为:
1. 客户端调用服务端接口传入文件后缀,服务端根据后缀和时间戳等生成 cos key 以及直传的 url。
2. 服务端通过 sts sdk 获取临时密钥。
3. 服务端用获取到的临时密钥对直传 url 进行签名并返回 url、签名、token 等信息。
4. 客户端获取到步骤 3 中的信息后,直接发起 put 请求并携带签名、token 等 header 进行上传。
配置服务端实现签名
注意
正式部署时服务端请加一层您的网站本身的权限检验。
1. 通过sts sdk获取临时密钥。
2. 根据后缀名生成 cos key 以及直传 url相关。
3. 使用临时密钥对直传 url 进行签名并返回直传 url、签名、token 等信息
服务端配置步骤:
1. 配置好密钥、bucket以及region。
var config = {// 获取腾讯云密钥,建议使用限定权限的子用户的密钥 https://console.cloud.tencent.com/cam/capisecretId: process.env.COS_SECRET_ID,secretKey: process.env.COS_SECRET_KEY,// 密钥有效期durationSeconds: 1800,// 这里填写存储桶、地域,例如:test-1250000000、ap-guangzhoubucket: 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 字段里。
客户端上传示例
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");//生成上传的urlURL 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 文档: