全部产品
Search
文档中心

对象存储 OSS:使用文件URL上传文件

更新时间:Sep 04, 2024

签名URL允许第三方用户在没有安全凭证或者授权的情况下进行上传操作。第三方用户使用签名URL上传文件后,OSS将在指定的Bucket生成该文件。

注意事项

  • 生成签名URL过程中,SDK利用本地存储的密钥信息,根据特定算法计算出签名(signature),然后将其附加到URL上,以确保URL的有效性和安全性。这一系列计算和构造URL的操作都是在客户端完成,不涉及网络请求到服务端。因此,生成签名URL时不需要授予调用者特定权限。但是,为避免第三方用户无法对签名URL授权的资源执行相关操作,需要确保调用生成签名URL接口的身份主体被授予对应的权限。

    例如,通过签名URL上传文件时,需要授予oss:PutObject权限。

  • 生成私有文件URL时涉及设置URL的有效时长。超出签名URL设置的有效时长后,通过签名URL上传文件时会提示签名URL已过期,导致无法正常上传文件。如果您希望继续使用签名URL上传文件,需要重新获取签名URL。

操作步骤

  1. 生成签名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;
    }];
  2. 使用签名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临时授权上传或下载文件