签名URL允许第三方用户在没有安全凭证或者授权的情况下进行上传操作。第三方用户使用签名URL上传文件后,OSS将在指定的Bucket生成该文件。
注意事项
生成签名URL过程中,SDK利用本地存储的密钥信息,根据特定算法计算出签名(signature),然后将其附加到URL上,以确保URL的有效性和安全性。这一系列计算和构造URL的操作都是在客户端完成,不涉及网络请求到服务端。因此,生成签名URL时不需要授予调用者特定权限。但是,为避免第三方用户无法对签名URL授权的资源执行相关操作,需要确保调用生成签名URL接口的身份主体被授予对应的权限。
例如,通过签名URL上传文件时,需要授予oss:PutObject权限。
生成私有文件URL时涉及设置URL的有效时长。超出签名URL设置的有效时长后,通过签名URL上传文件时会提示签名URL已过期,导致无法正常上传文件。如果您希望继续使用签名URL上传文件,需要重新获取签名URL。
操作步骤
生成签名URL。
以下仅列举常见SDK的生成签名URL的代码示例。关于其他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(杭州)的外网Endpoint为例,其它Region请按实际情况填写。 String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider(); // 填写Bucket名称,例如examplebucket。 String bucketName = "examplebucket"; // 填写Object完整路径,例如exampleobject.txt。Object完整路径中不能包含Bucket名称。 String objectName = "exampleobject.txt"; // 创建OSSClient实例 OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider); // 设置请求头。 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");*/ // 设置用户自定义元数据。 Map<String, String> userMetadata = new HashMap<String, String>(); /*userMetadata.put("key1","value1"); userMetadata.put("key2","value2");*/ URL signedUrl = null; try { // 指定生成的签名URL过期时间,单位为毫秒。本示例以设置过期时间为1小时为例。 Date expiration = new Date(new Date().getTime() + 3600 * 1000L); // 生成签名URL。 GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT); // 设置过期时间。 request.setExpiration(expiration); // 将请求头加入到request中。 request.setHeaders(headers); // 添加用户自定义元数据。 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; // 运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 $provider = new EnvironmentVariableCredentialsProvider(); // yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。 $endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // 填写Bucket名称。 $bucket= "examplebucket"; // 填写不包含Bucket名称在内的Object完整路径。 $object = "exampleobject.txt"; // 设置签名URL的有效时长为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填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。 region: "oss-cn-hangzhou", // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 accessKeyId: process.env.OSS_ACCESS_KEY_ID, accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET, // 填写Bucket名称。 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 # 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider()) # yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。 # 填写Bucket名称,例如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,有效时间为60秒。 # 生成签名URL时,OSS默认会对Object完整路径中的正斜线(/)进行转义,从而导致生成的签名URL无法直接使用。 # 设置slash_safe为True,OSS不会对Object完整路径中的正斜线(/)进行转义,此时生成的签名URL可以直接使用。 url = bucket.sign_url('PUT', object_name, 60, slash_safe=True, headers=headers) print('签名URL的地址为:', url)
Browser.js
// 生成临时访问凭证。 const OSS = require("ali-oss"); const STS = require("ali-oss").STS; // const cors = require("cors"); const stsClient = new STS({ // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量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填写Bucket所在地域。以华东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过期时间。 ) .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", // 设置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
// 填写Bucket名称,例如examplebucket。 String bucketName = "examplebucket"; // 填写不包含Bucket名称在内源Object的完整路径,例如exampleobject.txt。 String objectKey = "exampleobject.txt"; // 设置content-type。 String contentType = "application/octet-stream"; String url = null; try { // 生成用于上传文件的签名URL。 GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectKey); // 设置签名URL的过期时间为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() { // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 provider, err := oss.NewEnvironmentVariableCredentialsProvider() if err != nil { fmt.Println("Error:", err) os.Exit(-1) } // 创建OSSClient实例。 // yourEndpoint填写Bucket对应的Endpoint,以华东1(杭州)为例,填写为https://oss-cn-hangzhou.aliyuncs.com。其它Region请按实际情况填写。 client, err := oss.New("yourEndpoint", "", "", oss.SetCredentialsProvider(&provider)) if err != nil { fmt.Println("Error:", err) os.Exit(-1) } // 填写Bucket名称,例如examplebucket。 bucketName := "examplebucket" // 填写文件完整路径,例如exampledir/exampleobject.txt。文件完整路径中不能包含Bucket名称。 objectName := "exampledir/exampleobject.txt" bucket, err := client.Bucket(bucketName) if err != nil { HandleError(err) } // 生成用于上传的签名URL,并指定签名URL的有效时间为60秒。 signedURL, err := bucket.SignURL(objectName, oss.HTTPPut, 60) if err != nil { HandleError(err) } // 如果要在前端使用带可选参数的签名URL,请确保在服务端生成该签名URL时设置的ContentType与在前端使用时设置的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
// 填写Bucket名称。 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过期时间为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简单上传的代码示例。关于其他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 { // 填写本地文件的完整路径。如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。 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临时授权简单上传文件,以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时设置了header参数,例如用户元数据,存储类型等,则调用签名URL上传文件时,也需要将这些参数发送至服务端。如果签名和发送至服务端的不一致,会报签名错误。 for(Map.Entry header: headers.entrySet()){ put.addHeader(header.getKey().toString(),header.getValue().toString()); } for(Map.Entry meta: userMetadata.entrySet()){ // 如果使用userMeta,sdk内部会为userMeta拼接"x-oss-meta-"前缀。当您使用其他方式生成签名URL进行上传时,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("返回上传状态码:"+response.getStatusLine().getStatusCode()); if(response.getStatusLine().getStatusCode() == 200){ System.out.println("使用网络库上传成功"); } 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 () { // 请求结束后,在此处编写处理代码。 }; // 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"; // 设置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) } // 填写Bucket名称,例如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];
相关文档
当您希望使用签名URL以分片上传的方式上传大文件到OSS时,您需要先初始化分片上传,然后把每一个分片生成一个对应的上传签名URL,并返回给第三方应用。第三方应用可以使用这些签名URL上传所有的分片信息,然后合并分片来达到通过签名URL实现分片上传的目的。关于生成分片上传的签名URL以及使用签名URL临时授权分片上传的代码示例,请参见使用签名URL临时授权上传或下载文件。