使用文件URL上傳文件
簽名URL允許第三方用戶在沒有安全憑證或者授權(quán)的情況下進(jìn)行上傳操作。第三方用戶使用簽名URL上傳文件后,OSS將在指定的Bucket生成該文件。
注意事項(xiàng)
生成簽名URL過程中,SDK利用本地存儲的密鑰信息,根據(jù)特定算法計(jì)算出簽名(signature),然后將其附加到URL上,以確保URL的有效性和安全性。這一系列計(jì)算和構(gòu)造URL的操作都是在客戶端完成,不涉及網(wǎng)絡(luò)請求到服務(wù)端。因此,生成簽名URL時(shí)不需要授予調(diào)用者特定權(quán)限。但是,為避免第三方用戶無法對簽名URL授權(quán)的資源執(zhí)行相關(guān)操作,需要確保調(diào)用生成簽名URL接口的身份主體被授予對應(yīng)的權(quán)限。
例如,通過簽名URL上傳文件時(shí),需要授予oss:PutObject權(quán)限。
生成私有文件URL時(shí)涉及設(shè)置URL的有效時(shí)長。超出簽名URL設(shè)置的有效時(shí)長后,通過簽名URL上傳文件時(shí)會提示簽名URL已過期,導(dǎo)致無法正常上傳文件。如果您希望繼續(xù)使用簽名URL上傳文件,需要重新獲取簽名URL。
操作步驟
生成簽名URL。
以下僅列舉常見SDK的生成簽名URL的代碼示例。關(guān)于其他SDK的生成簽名URL代碼示例,請參見SDK簡介。
Java
import com.aliyun.oss.*; import com.aliyun.oss.common.auth.*; import com.aliyun.oss.internal.OSSHeaders; import com.aliyun.oss.model.GeneratePresignedUrlRequest; import com.aliyun.oss.model.StorageClass; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.FileEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import java.io.*; import java.net.URL; import java.util.*; import java.util.Date; public class Demo { public static void main(String[] args) throws Throwable { // 以華東1(杭州)的外網(wǎng)Endpoint為例,其它Region請按實(shí)際情況填寫。 String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // 從環(huán)境變量中獲取訪問憑證。運(yùn)行本代碼示例之前,請確保已設(shè)置環(huán)境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider(); // 填寫B(tài)ucket名稱,例如examplebucket。 String bucketName = "examplebucket"; // 填寫Object完整路徑,例如exampleobject.txt。Object完整路徑中不能包含Bucket名稱。 String objectName = "exampleobject.txt"; // 創(chuàng)建OSSClient實(shí)例 OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider); // 設(shè)置請求頭。 Map<String, String> headers = new HashMap<String, String>(); /*// 指定Object的存儲類型。 headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString()); // 指定ContentType。 headers.put(OSSHeaders.CONTENT_TYPE, "text/txt");*/ // 設(shè)置用戶自定義元數(shù)據(jù)。 Map<String, String> userMetadata = new HashMap<String, String>(); /*userMetadata.put("key1","value1"); userMetadata.put("key2","value2");*/ URL signedUrl = null; try { // 指定生成的簽名URL過期時(shí)間,單位為毫秒。本示例以設(shè)置過期時(shí)間為1小時(shí)為例。 Date expiration = new Date(new Date().getTime() + 3600 * 1000L); // 生成簽名URL。 GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT); // 設(shè)置過期時(shí)間。 request.setExpiration(expiration); // 將請求頭加入到request中。 request.setHeaders(headers); // 添加用戶自定義元數(shù)據(jù)。 request.setUserMetadata(userMetadata); // 通過HTTP PUT請求生成簽名URL。 signedUrl = ossClient.generatePresignedUrl(request); // 打印簽名URL。 System.out.println("signed url for putObject: " + signedUrl); } catch (OSSException oe) { System.out.println("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason."); System.out.println("Error Message:" + oe.getErrorMessage()); System.out.println("Error Code:" + oe.getErrorCode()); System.out.println("Request ID:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); } catch (ClientException ce) { System.out.println("Caught an ClientException, which means the client encountered " + "a serious internal problem while trying to communicate with OSS, " + "such as not being able to access the network."); System.out.println("Error Message:" + ce.getMessage()); } } }
PHP
<?php if (is_file(__DIR__ . '/../autoload.php')) { require_once __DIR__ . '/../autoload.php'; } if (is_file(__DIR__ . '/../vendor/autoload.php')) { require_once __DIR__ . '/../vendor/autoload.php'; } use OSS\Credentials\EnvironmentVariableCredentialsProvider; use OSS\OssClient; use OSS\Core\OssException; use OSS\Http\RequestCore; use OSS\Http\ResponseCore; // 運(yùn)行本代碼示例之前,請確保已設(shè)置環(huán)境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 $provider = new EnvironmentVariableCredentialsProvider(); // yourEndpoint填寫B(tài)ucket所在地域?qū)?yīng)的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。 $endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // 填寫B(tài)ucket名稱。 $bucket= "examplebucket"; // 填寫不包含Bucket名稱在內(nèi)的Object完整路徑。 $object = "exampleobject.txt"; // 設(shè)置簽名URL的有效時(shí)長為3600秒。 $timeout = 3600; try { $config = array( "provider" => $provider, "endpoint" => $endpoint, ); $ossClient = new OssClient($config); // 生成簽名URL。 $signedUrl = $ossClient->signUrl($bucket, $object, $timeout, "PUT"); } catch (OssException $e) { printf(__FUNCTION__ . ": FAILED\n"); printf($e->getMessage() . "\n"); return; } print(__FUNCTION__ . ": signedUrl: " . $signedUrl . "\n");
Node.js
const OSS = require("ali-oss"); const { default: axios } = require("axios"); const fs = require("fs"); const client = new OSS({ // yourregion填寫B(tài)ucket所在地域。以華東1(杭州)為例,Region填寫為oss-cn-hangzhou。 region: "oss-cn-hangzhou", // 從環(huán)境變量中獲取訪問憑證。運(yùn)行本代碼示例之前,請確保已設(shè)置環(huán)境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 accessKeyId: process.env.OSS_ACCESS_KEY_ID, accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET, // 填寫B(tài)ucket名稱。 bucket: "examplebucket", }); // 生成文件的簽名URL。 const url = client.signatureUrl("exampleobject.txt", { method: "PUT", "Content-Type": "application/x-www-form-urlencoded", }); console.log(url);
Python
# -*- coding: utf-8 -*- import oss2 from oss2.credentials import EnvironmentVariableCredentialsProvider import requests # 從環(huán)境變量中獲取訪問憑證。運(yùn)行本代碼示例之前,請確保已設(shè)置環(huán)境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider()) # yourEndpoint填寫B(tài)ucket所在地域?qū)?yīng)的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。 # 填寫B(tài)ucket名稱,例如examplebucket。 bucket = oss2.Bucket(auth, 'https://oss-cn-hangzhou.aliyuncs.com', 'examplebucket') # 填寫Object完整路徑,Object完整路徑中不能包含Bucket名稱。 object_name = 'exampleobject.txt' # 指定Header。 headers = dict() # 指定Content-Type。 headers['Content-Type'] = 'text/txt' # 指定存儲類型。 headers["x-oss-storage-class"] = "Standard" # 生成上傳文件的簽名URL,有效時(shí)間為60秒。 # 生成簽名URL時(shí),OSS默認(rèn)會對Object完整路徑中的正斜線(/)進(jìn)行轉(zhuǎn)義,從而導(dǎo)致生成的簽名URL無法直接使用。 # 設(shè)置slash_safe為True,OSS不會對Object完整路徑中的正斜線(/)進(jìn)行轉(zhuǎn)義,此時(shí)生成的簽名URL可以直接使用。 url = bucket.sign_url('PUT', object_name, 60, slash_safe=True, headers=headers) print('簽名URL的地址為:', url)
Browser.js
// 生成臨時(shí)訪問憑證。 const OSS = require("ali-oss"); const STS = require("ali-oss").STS; // const cors = require("cors"); const stsClient = new STS({ // 從環(huán)境變量中獲取訪問憑證。運(yùn)行本代碼示例之前,請確保已設(shè)置環(huán)境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 accessKeyId: process.env.OSS_ACCESS_KEY_ID, accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET, }); // 填寫存儲空間名稱,例如examplebucket。 const bucket = "examplebucket"; // yourRegion填寫B(tài)ucket所在地域。以華東1(杭州)為例,Region填寫為oss-cn-hangzhou。 const region = "yourRegion"; // 指定角色ARN。 const roleArn = "acs:ram::137918634953****:role/ossram"; const getSts = () => { stsClient .assumeRole( roleArn, `{ "Statement": [ { "Effect": "Allow", "Action": "*", "Resource": [ "acs:oss:*:*:examplebucket/*" ] } ] }`, 3000 //指定SecurityToken過期時(shí)間。 ) .then((r) => { console.log("send:", r.credentials); const { SecurityToken, AccessKeyId, AccessKeySecret } = r.credentials; const client = new OSS({ bucket, region, accessKeyId: AccessKeyId, accessKeySecret: AccessKeySecret, stsToken: SecurityToken, refreshSTSTokenInterval: 9000, }); // 指定上傳至Bucket的文件名稱。 const url = client.asyncSignatureUrl("example.txt", { expires: 3600, method: "PUT", // 設(shè)置Content-Type。 "Content-Type": "text/plain;charset=UTF-8", }); console.log("url:", url); // client.put("example.txt", Buffer.from("body")).then((res) => { // console.log("res", res.url); // }); }); }; getSts();
Android
// 填寫B(tài)ucket名稱,例如examplebucket。 String bucketName = "examplebucket"; // 填寫不包含Bucket名稱在內(nèi)源Object的完整路徑,例如exampleobject.txt。 String objectKey = "exampleobject.txt"; // 設(shè)置content-type。 String contentType = "application/octet-stream"; String url = null; try { // 生成用于上傳文件的簽名URL。 GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectKey); // 設(shè)置簽名URL的過期時(shí)間為30分鐘。 request.setExpiration(30*60); request.setContentType(contentType); request.setMethod(HttpMethod.PUT); url = oss.presignConstrainedObjectURL(request); Log.d("url", url); } catch (ClientException e) { e.printStackTrace(); }
Go
package main import ( "fmt" "os" "github.com/aliyun/aliyun-oss-go-sdk/oss" ) func HandleError(err error) { fmt.Println("Error:", err) os.Exit(-1) } func main() { // 從環(huán)境變量中獲取訪問憑證。運(yùn)行本代碼示例之前,請確保已設(shè)置環(huán)境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 provider, err := oss.NewEnvironmentVariableCredentialsProvider() if err != nil { fmt.Println("Error:", err) os.Exit(-1) } // 創(chuàng)建OSSClient實(shí)例。 // yourEndpoint填寫B(tài)ucket對應(yīng)的Endpoint,以華東1(杭州)為例,填寫為https://oss-cn-hangzhou.aliyuncs.com。其它Region請按實(shí)際情況填寫。 client, err := oss.New("yourEndpoint", "", "", oss.SetCredentialsProvider(&provider)) if err != nil { fmt.Println("Error:", err) os.Exit(-1) } // 填寫B(tài)ucket名稱,例如examplebucket。 bucketName := "examplebucket" // 填寫文件完整路徑,例如exampledir/exampleobject.txt。文件完整路徑中不能包含Bucket名稱。 objectName := "exampledir/exampleobject.txt" bucket, err := client.Bucket(bucketName) if err != nil { HandleError(err) } // 生成用于上傳的簽名URL,并指定簽名URL的有效時(shí)間為60秒。 signedURL, err := bucket.SignURL(objectName, oss.HTTPPut, 60) if err != nil { HandleError(err) } // 如果要在前端使用帶可選參數(shù)的簽名URL,請確保在服務(wù)端生成該簽名URL時(shí)設(shè)置的ContentType與在前端使用時(shí)設(shè)置的ContentType一致。 options := []oss.Option{ oss.Meta("myprop", "mypropval"), oss.ContentType("text/plain"), } signedURL, err = bucket.SignURL(objectName, oss.HTTPPut, 60, options...) if err != nil { HandleError(err) } fmt.Printf("Sign Url:%s\n", signedURL) }
iOS
// 填寫B(tài)ucket名稱。 NSString *bucketName = @"examplebucket"; // 填寫Object名稱。 NSString *objectKey = @"exampleobject.txt"; NSURL *file = [NSURL fileURLWithPath:@"<filePath>"]; NSString *contentType = [OSSUtil detemineMimeTypeForFilePath:file.absoluteString uploadName:objectKey]; __block NSString *urlString; // 生成用于上傳的簽名URL,并指定簽名URL過期時(shí)間為30分鐘。 OSSTask *task = [client presignConstrainURLWithBucketName:bucketName withObjectKey:objectKey httpMethod:@"PUT" withExpirationInterval:30 * 60 withParameters:@{} contentType:contentType contentMd5:nil]; [task continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) { if (task.error) { NSLog(@"presign error: %@", task.error); } else { urlString = task.result; NSLog(@"url: %@", urlString); } return nil; }];
使用簽名URL上傳文件。
以下僅列舉常見SDK使用簽名URL簡單上傳的代碼示例。關(guān)于其他SDK使用簽名URL簡單上傳的代碼示例,請參見SDK簡介。
Java
import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.FileEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import java.io.*; import java.util.*; public class Demo { public static void main(String[] args) throws Throwable { // 填寫本地文件的完整路徑。如果未指定本地路徑,則默認(rèn)從示例程序所屬項(xiàng)目對應(yīng)本地路徑中上傳文件。 String pathName = "D:\\examplefile.txt"; // 填寫步驟1生成的簽名URL。 String signedUrl = "yourSignedUrl"; Map<String, String> headers = new HashMap<String, String>(); Map<String, String> userMetadata = new HashMap<String, String>(); // 通過簽名URL臨時(shí)授權(quán)簡單上傳文件,以HttpClients為例說明。 putObjectWithHttp(signedUrl, pathName, headers, userMetadata); } public static void putObjectWithHttp(String signedUrl, String pathName, Map<String, String> headers, Map<String, String> userMetadata) throws IOException { CloseableHttpClient httpClient = null; CloseableHttpResponse response = null; try { HttpPut put = new HttpPut(signedUrl); HttpEntity entity = new FileEntity(new File(pathName)); put.setEntity(entity); // 如果生成簽名URL時(shí)設(shè)置了header參數(shù),例如用戶元數(shù)據(jù),存儲類型等,則調(diào)用簽名URL上傳文件時(shí),也需要將這些參數(shù)發(fā)送至服務(wù)端。如果簽名和發(fā)送至服務(wù)端的不一致,會報(bào)簽名錯(cuò)誤。 for(Map.Entry header: headers.entrySet()){ put.addHeader(header.getKey().toString(),header.getValue().toString()); } for(Map.Entry meta: userMetadata.entrySet()){ // 如果使用userMeta,sdk內(nèi)部會為userMeta拼接"x-oss-meta-"前綴。當(dāng)您使用其他方式生成簽名URL進(jìn)行上傳時(shí),userMeta也需要拼接"x-oss-meta-"前綴。 put.addHeader("x-oss-meta-"+meta.getKey().toString(), meta.getValue().toString()); } httpClient = HttpClients.createDefault(); response = httpClient.execute(put); System.out.println("返回上傳狀態(tài)碼:"+response.getStatusLine().getStatusCode()); if(response.getStatusLine().getStatusCode() == 200){ System.out.println("使用網(wǎng)絡(luò)庫上傳成功"); } System.out.println(response.toString()); } catch (Exception e){ e.printStackTrace(); } finally { response.close(); httpClient.close(); } } }
PHP
<?php if (is_file(__DIR__ . '/../autoload.php')) { require_once __DIR__ . '/../autoload.php'; } if (is_file(__DIR__ . '/../vendor/autoload.php')) { require_once __DIR__ . '/../vendor/autoload.php'; } use OSS\Http\RequestCore; use OSS\Http\ResponseCore; // 填寫步驟1生成的簽名URL。 $signedUrl = 'yourSignedUrl'; $content = "Hello OSS"; $request = new RequestCore($signedUrl); // 使用簽名URL上傳文件。 $request->set_method('PUT'); $request->add_header('Content-Type', ''); $request->add_header('Content-Length', strlen($content)); $request->set_body($content); $request->send_request(); $res = new ResponseCore($request->get_response_header(), $request->get_response_body(), $request->get_response_code()); if ($res->isOK()) { print(__FUNCTION__ . ": OK" . "\n"); } else { print(__FUNCTION__ . ": FAILED" . "\n"); };
Node.js
const OSS = require("ali-oss"); const { default: axios } = require("axios"); const fs = require("fs"); // 填寫步驟1生成的簽名URL。 const url = "yourSignedUrl"; // 指定本地文件的完整路徑。 const file = fs.readFileSync("D:\\examplefile.txt"); // 使用簽名URL上傳文件。 axios({ url, method: "PUT", data: file, }) .then((r) => console.log(r)) .catch((e) => console.log(e));
Python
# -*- coding: utf-8 -*- import oss2 import requests # 填寫步驟1生成的簽名URL。 url = 'yourSignedUrl' # 指定Header。 headers = dict() # 指定Content-Type。 headers['Content-Type'] = 'text/txt' # 指定存儲類型。 headers["x-oss-storage-class"] = "Standard" # 通過簽名URL上傳文件。 # 填寫本地文件路徑,例如D:\\examplefile.txt。 requests.put(url, data=open('D:\\examplefile.txt', 'rb').read(), headers=headers)
Browser.js
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> </head> <body> <script src="https://gosspublic.alicdn.com/aliyun-oss-sdk-6.18.0.min.js"></script> <script> // 填寫步驟1生成的簽名URL。 const url = "yourSignatureUrl"; var xhr = new XMLHttpRequest(); xhr.open("PUT", url, true); xhr.onload = function () { // 請求結(jié)束后,在此處編寫處理代碼。 }; // xhr.send(null); xhr.send("string"); // xhr.send(new Blob()); // xhr.send(new Int8Array()); // xhr.send({ form: 'data' }); // xhr.send(document); </script> </body> </html>
Android
// 填寫生成的簽名URL。 String url = ""; // 指定本地文件的完整路徑。 String localFile = "/storage/emulated/0/oss/examplefile"; // 設(shè)置content-type。 String contentType = "application/octet-stream"; // 通過簽名URL上傳文件。 OkHttpClient client = new OkHttpClient(); Request putRequest = new Request.Builder() .url(url) .put(RequestBody.create(MediaType.parse(contentType), new File(localFile))) .build(); client.newCall(putRequest).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); } @Override public void onResponse(Call call, Response response) throws IOException { Log.d("response", response.body().string()); } });
Go
package main import ( "fmt" "os" "strings" "github.com/aliyun/aliyun-oss-go-sdk/oss" ) func main() { client, err := oss.New("oss-cn-hangzhou.aliyuncs.com", "", "") if err != nil { fmt.Println("Error:", err) os.Exit(-1) } // 填寫B(tài)ucket名稱,例如examplebucket。 bucketName := "examplebucket" bucket, err := client.Bucket(bucketName) if err != nil { fmt.Println("Error:", err) os.Exit(-1) } // 填寫步驟1獲取的簽名URL。 signedURL := "yourSignedUrl" var val = "上云就上阿里云" err = bucket.PutObjectWithURL(signedURL, strings.NewReader(val)) if err != nil { fmt.Println("Error:", err) os.Exit(-1) } }
iOS
// 通過簽名URL上傳文件。 NSURL * url = [NSURL URLWithString:urlString]; NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url]; request.HTTPMethod = @"PUT"; request.allHTTPHeaderFields = @{OSSHttpHeaderContentType: contentType}; NSURLSession * session = [NSURLSession sharedSession]; NSURLSessionTask * sessionTask = [session uploadTaskWithRequest:request fromFile:file completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error) { NSLog(@"upload error: %@", error); return; } else if (((NSHTTPURLResponse*)response).statusCode == 203 || ((NSHTTPURLResponse*)response).statusCode >= 300) { NSString *body = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"upload error: %@", body); return; } NSLog(@"upload success"); }]; [sessionTask resume];
相關(guān)文檔
當(dāng)您希望使用簽名URL以分片上傳的方式上傳大文件到OSS時(shí),您需要先初始化分片上傳,然后把每一個(gè)分片生成一個(gè)對應(yīng)的上傳簽名URL,并返回給第三方應(yīng)用。第三方應(yīng)用可以使用這些簽名URL上傳所有的分片信息,然后合并分片來達(dá)到通過簽名URL實(shí)現(xiàn)分片上傳的目的。關(guān)于生成分片上傳的簽名URL以及使用簽名URL臨時(shí)授權(quán)分片上傳的代碼示例,請參見使用簽名URL臨時(shí)授權(quán)上傳或下載文件。