签名URL允许第三方用户在没有安全凭证或者授权的情况下进行上传操作。第三方用户使用签名URL上传文件后,OSS将在指定的Bucket生成该文件。
注意事项
生成PUT方法的签名URL时,您必须具有
oss:PutObject
权限。具体操作,请参见RAM Policy常见示例。说明生成签名URL过程中,SDK利用本地存储的密钥信息,根据特定算法计算出签名(signature),然后将其附加到URL上,以确保URL的有效性和安全性。这一系列计算和构造URL的操作都是在客户端完成,不涉及网络请求到服务端。因此,生成签名URL时不需要授予调用者特定权限。但是,为避免第三方用户无法对签名URL授权的资源执行相关操作,需要确保调用生成签名URL接口的身份主体被授予对应的权限。
生成私有文件URL时涉及设置URL的有效时长。超出签名URL设置的有效时长后,通过签名URL上传文件时会提示签名URL已过期,导致无法正常上传文件。如果您希望继续使用签名URL上传文件,需要重新获取签名URL。
签名URL上传不支持上传FormData格式,若需使用FormData上传数据,建议使用OSS表单上传。
操作步骤
生成签名URL。
以下仅列举常见SDK的生成签名URL的代码示例。关于其他SDK的生成签名URL代码示例,请参见SDK简介。
Java
import com.aliyun.oss.*; import com.aliyun.oss.common.auth.*; import com.aliyun.oss.common.comm.SignVersion; import com.aliyun.oss.model.GeneratePresignedUrlRequest; import java.net.URL; import java.util.*; import java.util.Date; public class GetSignUrl { 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"; // 填写Bucket所在地域。以华东1(杭州)为例,Region填写为cn-hangzhou。 String region = "cn-hangzhou"; // 创建OSSClient实例。 ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration(); clientBuilderConfiguration.setSignatureVersion(SignVersion.V4); OSS ossClient = OSSClientBuilder.create() .endpoint(endpoint) .credentialsProvider(credentialsProvider) .clientConfiguration(clientBuilderConfiguration) .region(region) .build(); 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); // 通过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 # 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider()) # 填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。 endpoint = "https://oss-cn-hangzhou.aliyuncs.com" # 填写Endpoint对应的Region信息,例如cn-hangzhou。注意,v4签名下,必须填写该参数 region = "cn-hangzhou" # yourBucketName填写存储空间名称。 bucket = oss2.Bucket(auth, endpoint, "yourBucketName", region=region) # 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。 object_name = 'exampledir/exampleobject.txt' # 生成上传文件的签名URL,有效时间为60秒。 # 生成签名URL时,OSS默认会对Object完整路径中的正斜线(/)进行转义,从而导致生成的签名URL无法直接使用。 # 设置slash_safe为True,OSS不会对Object完整路径中的正斜线(/)进行转义,此时生成的签名URL可以直接使用。 url = bucket.sign_url('PUT', object_name, 60, slash_safe=True) print('签名URL的地址为:', url)
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 ( "context" "flag" "log" "time" "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss" "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials" ) // 定义全局变量 var ( region string // 存储区域 bucketName string // 存储空间名称 objectName string // 对象名称 ) // init函数用于初始化命令行参数 func init() { flag.StringVar(®ion, "region", "", "The region in which the bucket is located.") flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.") flag.StringVar(&objectName, "object", "", "The name of the object.") } func main() { // 解析命令行参数 flag.Parse() // 检查bucket名称是否为空 if len(bucketName) == 0 { flag.PrintDefaults() log.Fatalf("invalid parameters, bucket name required") } // 检查region是否为空 if len(region) == 0 { flag.PrintDefaults() log.Fatalf("invalid parameters, region required") } // 检查object名称是否为空 if len(objectName) == 0 { flag.PrintDefaults() log.Fatalf("invalid parameters, object name required") } // 加载默认配置并设置凭证提供者和区域 cfg := oss.LoadDefaultConfig(). WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()). WithRegion(region) // 创建OSS客户端 client := oss.NewClient(cfg) // 生成PutObject的预签名URL result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{ Bucket: oss.Ptr(bucketName), Key: oss.Ptr(objectName), //ContentType: oss.Ptr("text/txt"), // 请确保在服务端生成该签名URL时设置的ContentType与在使用URL时设置的ContentType一致 //Metadata: map[string]string{"key1": "value1", "key2": "value2"}, // 请确保在服务端生成该签名URL时设置的Metadata与在使用URL时设置的Metadata一致 }, oss.PresignExpires(10*time.Minute), ) if err != nil { log.Fatalf("failed to put object presign %v", err) } log.Printf("request method:%v\n", result.Method) log.Printf("request expiration:%v\n", result.Expiration) log.Printf("request url:%v\n", result.URL) if len(result.SignedHeaders) > 0 { //当返回结果包含签名头时,使用签名URL发送Put请求时,需要设置相应的请求头 log.Printf("signed headers:\n") for k, v := range result.SignedHeaders { log.Printf("%v: %v\n", k, v) } } }
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.net.URL; import java.util.*; public class SignUrlUpload { public static void main(String[] args) throws Throwable { CloseableHttpClient httpClient = null; CloseableHttpResponse response = null; // 将<signedUrl>替换为授权URL。 URL signedUrl = new URL("<signedUrl>"); // 填写本地文件的完整路径。如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。 String pathName = "C:\\Users\\demo.txt"; try { HttpPut put = new HttpPut(signedUrl.toString()); System.out.println(put); HttpEntity entity = new FileEntity(new File(pathName)); put.setEntity(entity); 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
import requests def upload_file(signed_url, file_path): try: # 打开文件 with open(file_path, 'rb') as file: # 发送PUT请求上传文件 response = requests.put(signed_url, data=file) print(f"返回上传状态码:{response.status_code}") if response.status_code == 200: print("使用网络库上传成功") print(response.text) except Exception as e: print(f"发生错误:{e}") if __name__ == "__main__": # 将<signedUrl>替换为授权URL。 signed_url = "<signedUrl>" # 填写本地文件的完整路径。如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。 file_path = "C:\\Users\\demo.txt" upload_file(signed_url, file_path)
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" "io" "net/http" "os" ) func uploadFile(signedUrl, filePath string) error { // 打开文件 file, err := os.Open(filePath) if err != nil { return fmt.Errorf("无法打开文件: %w", err) } defer file.Close() // 创建一个新的HTTP客户端 client := &http.Client{} // 创建一个PUT请求 req, err := http.NewRequest("PUT", signedUrl, file) if err != nil { return fmt.Errorf("创建请求失败: %w", err) } // 发送请求 resp, err := client.Do(req) if err != nil { return fmt.Errorf("发送请求失败: %w", err) } defer resp.Body.Close() // 读取响应 body, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("读取响应失败: %w", err) } fmt.Printf("返回上传状态码: %d\n", resp.StatusCode) if resp.StatusCode == 200 { fmt.Println("使用网络库上传成功") } fmt.Println(string(body)) return nil } func main() { // 将<signedUrl>替换为授权URL。 signedUrl := "<signedUrl>" // 填写本地文件的完整路径。如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。 filePath := "C:\\Users\\demo.txt" err := uploadFile(signedUrl, filePath) if err != nil { fmt.Println("发生错误:", err) } }
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下载。