All Products
Search
Document Center

Object Storage Service:Authorized third-party upload

Last Updated:Oct 10, 2023

This topic describes how to use a signed URL or temporary access credentials from Security Token Service (STS) to grant a third-party user permissions to upload objects directly to Object Storage Service (OSS).

Background information

In a standard client/server system architecture, the server is responsible for receiving and processing requests from the client, and OSS is used as a backend storage service. In that case, the client sends the objects to upload to the application server. Then, the server forwards the objects to OSS. In this process, objects are transmitted twice: from the client to the server and from the server to OSS. In the case of bursts of access requests, the server must provide sufficient bandwidth resources for multiple clients to upload objects simultaneously. This presents a challenge to the architecture scalability.

Benefits

To address the challenge in the preceding scenario, OSS allows you to authorize third-party uploads. This way, each client can upload objects directly to OSS without transmitting them to the server. This reduces the costs of application servers and maximizes the OSS capability to process large amounts of data. Furthermore, you can focus on your business without worries about bandwidth and concurrency limits. You have the option to use a signed URL or temporary access credentials to grant a third-party user permissions to upload objects directly to OSS. In most cases, we recommend that you use temporary access credentials.

Use temporary access credentials to authorize third-party users to upload objects to OSS

In most object upload scenarios, we recommend that you use Security Token Service (STS) SDKs to obtain temporary access credentials on the application server, and then use the temporary access credentials and OSS SDKs on the client to upload objects. The client can reuse the temporary access credentials on the application server to generate a signature. This is suitable for scenarios in which multipart upload and resumable upload are used to upload large objects. However, frequent calls to STS may cause throttling. In this case, we recommend that you cache the temporary access credentials and renew them before the validity period ends. For more information, see What is STS?

  1. Obtain temporary access credentials.

    The temporary access credentials consist of an AccessKey pair and a security token. The AccessKey pair consists of an AccessKey ID and an AccessKey secret. The minimum validity period of temporary access credentials is 900 seconds. The maximum validity period of temporary access credentials is the maximum session duration specified for the current role. For more information, see Specify the maximum session duration for a RAM role.

    You can use one of the following methods to obtain temporary access credentials:

    • Method 1:

      Call the AssumeRole operation to obtain temporary access credentials.

    • Method 2:

      Use STS SDKs to obtain temporary access credentials. For more information, see Overview.

  2. Use temporary access credentials to authorize a third-party user to upload objects.

    The following sample code provides examples on how to use temporary access credentials to authorize a third-party user to upload objects to OSS by using OSS SDKs for common programming languages. For more information about how to perform this operation by using OSS SDKs for other programming languages, see Overview.

    Java

    import com.aliyun.oss.*;
    import com.aliyun.oss.model.PutObjectRequest;
    import com.aliyun.oss.common.auth.*;
    import java.io.File;
    
    public class Demo {
        public static void main(String[] args) throws Throwable {
            // In this example, the endpoint of the China (Hangzhou) region is used. Specify your actual endpoint. 
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET, and OSS_SESSION_TOKEN environment variables are configured. 
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            // Specify the name of the bucket. Example: examplebucket. 
            String bucketName = "examplebucket";
            // Specify the full path of the object. Example: exampleobject.txt. Do not include the bucket name in the full path. 
            String objectName = "exampleobject.txt";
            // Specify the full path of the local file. 
            String pathName = "D:\\examplefile.txt";
    
            // Create an OSSClient instance. 
            OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
    
            try {
                // Specify the full path of the local file. By default, if you do not specify the full path of the local file, the local file is uploaded from the path of the project to which the sample program belongs. 
                PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new File(pathName));
                ossClient.putObject(putObjectRequest);
            } 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());
            } finally {
                if (ossClient != null) {
                    ossClient.shutdown();
                }
            }
        }
    }

    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\OssClient;
    use OSS\Core\OssException;
    
    // Specify the temporary AccessKey pair obtained from STS. 
    $accessKeyId = "yourAccessKeyId";
    $accessKeySecret = "yourAccessKeySecret";
    // Specify the security token obtained from STS. 
    $securityToken = "yourSecurityToken";
    // Specify the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. 
    $endpoint = "yourEndpoint";
    // Specify the name of the bucket. 
    $bucket= "examplebucket";
    // Specify the full path of the object. Do not include the bucket name in the full path. 
    $object = "exampleobject.txt";
    // Specify the string that you want to upload. 
    $content = "Hello OSS";
    
    try {
        $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false, $securityToken);
        // Upload the object by using the temporary access credentials obtained from STS. 
        $ossClient->putObject($bucket, $object, $content);
    } catch (OssException $e) {
        print $e->getMessage();
    }           

    Python

    # -*- coding: utf-8 -*-
    import oss2
    from oss2.credentials import EnvironmentVariableCredentialsProvider
    
    # Specify the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. 
    endpoint = 'https://oss-cn-hangzhou.aliyuncs.com'
    # Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET, and OSS_SESSION_TOKEN environment variables are configured. 
    auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider())
    # Specify the name of the bucket. 
    bucket_name = 'examplebucket'
    # Specify the full path of the object and the string that you want to upload. Do not include the bucket name in the full path. 
    object_name = 'exampleobject.txt'
    
    # Initialize the bucket based on the StsAuth instance. 
    bucket = oss2.Bucket(auth, endpoint, bucket_name)
    
    # Upload the object. 
    result = bucket.put_object(object_name, "hello world")
    print(result.status)

    Go

    package main
    
    import (
        "fmt"
        "github.com/aliyun/aliyun-oss-go-sdk/oss"
        "os"
    )
    
    func main() {
        // Obtain temporary access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET, and OSS_SESSION_TOKEN environment variables are configured. 
        provider, err := oss.NewEnvironmentVariableCredentialsProvider()
        if err != nil {
            fmt.Println("Error:", err)
            os.Exit(-1)
        }
        // Create an OSSClient instance. 
        // Specify the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. Specify your actual endpoint. 
        client, err := oss.New("yourEndpoint", "", "", oss.SetCredentialsProvider(&provider))
        if err != nil {
            fmt.Println("Error:", err)
            os.Exit(-1)
        }
        // Specify the name of the bucket. Example: examplebucket. 
        bucketName := "examplebucket"
        // Specify the full path of the object. Do not include the bucket name in the full path. Example: exampledir/exampleobject.txt. 
        objectName := "exampledir/exampleobject.txt"
        // Specify the full path of the local file. Example: D:\\localpath\\examplefile.txt. 
        filepath := "D:\\localpath\\examplefile.txt"
        bucket,err := client.Bucket(bucketName)
        // Use the temporary access credentials obtained from STS to grant the third-party user permissions to upload objects. 
        err = bucket.PutObjectFromFile(objectName,filepath)
        if err != nil {
            fmt.Println("Error:", err)
            os.Exit(-1)
        }
        fmt.Println("upload success")
    }

Use a signed URL to authorize third-party users to upload objects to OSS

For simple object upload scenarios, you can use OSS SDKs on the application server to obtain a signed URL that is required to call the PutObject operation. Then, the client can use the signed URL to upload an object without using OSS SDKs. This solution is not suitable for multipart upload and resumable upload. The application server generates a signed URL for each part and returns the signed URL to the client. This increases the number of interactions with the application server and the complexity of network requests. In addition, the client may modify the content or order of the parts. This results in an invalid combined object. For more information, see Add signatures to URLs.

Important A validity period must be specified for temporary access credentials and a signed URL. When you use temporary access credentials to generate a signed URL that is used to perform operations, such as object upload and download, the minimum validity period takes precedence. For example, you can set the validity period of the temporary access credentials that are provided by STS to 1,200 seconds and the validity period of the signed URL generated by using the credentials to 3,600 seconds. In this case, you cannot use the signed URL to upload objects after the temporary access credentials expire, even if the signed URL is within the validity period.

Use OSS SDKs

The following sample code provides examples on how to use a signed URL to authorize a third-party user to upload objects to OSS by using OSS SDKs for common programming languages. For more information about how to perform this operation by using OSS SDKs for other programming languages, see Overview.

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 {
        // In this example, the endpoint of the China (Hangzhou) region is used. Specify your actual endpoint. 
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured. 
        EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
        // Specify the name of the bucket. Example: examplebucket. 
        String bucketName = "examplebucket";
        // Specify the full path of the object. Example: exampleobject.txt. Do not include the bucket name in the full path. 
        String objectName = "exampleobject.txt";
        // Specify the full path of the local file that you want to upload. By default, if you do not specify the full path of the local file, the local file is uploaded from the path of the project to which the sample program belongs. 
        String pathName = "D:\\localpath\\examplefile.txt";

        // After you obtain the temporary access credentials (security token and temporary AccessKey pair) from STS, you can use the temporary access credentials to create an OSSClient instance. 
        // Use the temporary access credentials obtained from STS to create an OSSClient instance. 
        OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
        // Do not use the temporary access credentials obtained from STS to create an OSSClient instance. 
        // OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // Specify request headers. 
        Map<String, String> headers = new HashMap<String, String>();
        /*// Specify the storage class of the object. 
        headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
        // Specify ContentType. 
        headers.put(OSSHeaders.CONTENT_TYPE, "text/txt");*/

        // Specify user metadata. 
        Map<String, String> userMetadata = new HashMap<String, String>();
        /*userMetadata.put("key1","value1");
        userMetadata.put("key2","value2");*/

        URL signedUrl = null;
        try {
            // Specify the validity period of the signed URL. Unit: milliseconds. In this example, the validity period is set to 1 hour. 
            Date expiration = new Date(new Date().getTime() + 3600 * 1000L);

            // Generate the signed URL. 
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
            // Specify the expiration time. 
            request.setExpiration(expiration);

            // Add the headers to the request. 
            request.setHeaders(headers);
            // Specify user metadata. 
            request.setUserMetadata(userMetadata);

            // Generate a signed URL that allows HTTP PUT requests. 
            signedUrl = ossClient.generatePresignedUrl(request);
            // Display the signed 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());
        }

        // Use the signed URL to authorize third-party users to upload the object. In this example, HttpClients is used. 
        putObjectWithHttp(signedUrl, pathName, headers, userMetadata);
    }

    public static void putObjectWithHttp(URL 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.toString());
            HttpEntity entity = new FileEntity(new File(pathName));
            put.setEntity(entity);
            // If you configure header parameters such as the user metadata and storage class when a signed URL is generated, these parameters must be sent to the server when the signed URL is used to upload the object. If the parameters for the signature are inconsistent with those sent to the server, a signature error is reported. 
            for(Map.Entry header: headers.entrySet()){
                put.addHeader(header.getKey().toString(),header.getValue().toString());
            }
            for(Map.Entry meta: userMetadata.entrySet()){
                // If userMeta is used, the x-oss-meta- prefix is added to userMeta. If you use other methods to generate a signed URL for object upload, the x-oss-meta- prefix is also added to userMata. 
                put.addHeader("x-oss-meta-"+meta.getKey().toString(), meta.getValue().toString());
            }

            httpClient = HttpClients.createDefault();

            response = httpClient.execute(put);

            System.out.println("Upload status code:"+response.getStatusLine().getStatusCode());
            if(response.getStatusLine().getStatusCode() == 200){
                System.out.println("Successfully upload the object by using the network library");
            }
            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\OssClient;
use OSS\Core\OssException;
use OSS\Http\RequestCore;
use OSS\Http\ResponseCore;

// Specify the temporary AccessKey pair obtained from STS. An AccessKey pair consists of an AccessKey ID and an AccessKey secret. 
$accessKeyId = "yourAccessKeyId";
$accessKeySecret = "yourAccessKeySecret";
// Specify the security token obtained from STS. 
$securityToken = "yourSecurityToken";
// Specify the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. 
$endpoint = "yourEndpoint";
// Specify the name of the bucket. 
$bucket= "examplebucket";
// Specify the full path of the object. Do not include the bucket name in the full path. 
$object = "exampleobject.txt";
// Set the validity period of a signed URL to 3,600 seconds. 
$timeout = 3600;
try {
    $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false, $securityToken);

    // Generate a signed 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");               

Python

# -*- coding: utf-8 -*-
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider
import requests

# Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured. 
auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider())

# Specify the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. 
# Specify the bucket name. Example: examplebucket. 
bucket = oss2.Bucket(auth, 'https://oss-cn-hangzhou.aliyuncs.com', 'examplebucket')
# Specify the full path of the object. Example: exampledir/exampleobject.txt. Do not include the bucket name in the full path. 
object_name = 'exampledir/exampleobject.txt'

# Specify the headers. 
headers = dict()
# Specify the Content-Type parameter. 
# headers['Content-Type'] = 'text/txt'
# Specify the storage class. 
# headers["x-oss-storage-class"] = "Standard"

# Generate a signed URL that is used to upload the object. Set the validity period of the URL to 60 seconds. 
# By default, OSS identifies the forward slashes (/) in the full path of an object as escape characters when a signed URL is generated. Therefore, the signed URL cannot be used directly. 
# Set the slash_safe parameter to True. This way, OSS does not identify the forward slashes (/) in the full path of the object as escape characters. Then, you can use the generated signed URL to upload the object. 
url = bucket.sign_url('PUT', object_name, 60, slash_safe=True, headers=headers)
print('Signed URL:', url)

# Use the signed URL to upload an object. requests is used as an example. 
# Specify the full path of the local file. Example: D:\\exampledir\\examplefile.txt. 
requests.put(url, data=open('D:\\exampledir\\examplefile.txt', 'rb').read(), headers=headers)      

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() {
	// Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured. 
	provider, err := oss.NewEnvironmentVariableCredentialsProvider()
	if err != nil {
		fmt.Println("Error:", err)
		os.Exit(-1)
	}
	// Create an OSSClient instance. 
	// Specify the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. Specify your actual endpoint. 
	client, err := oss.New("yourEndpoint", "", "", oss.SetCredentialsProvider(&provider))
	if err != nil {
		fmt.Println("Error:", err)
		os.Exit(-1)
	}
    // Specify the name of the bucket. Example: examplebucket. 
    bucketName := "examplebucket"
    // Specify the full path of the object. Example: exampledir/exampleobject.txt. Do not include the bucket name in the full path. 
    objectName := "exampledir/exampleobject.txt"
    bucket, err := client.Bucket(bucketName)
    if err != nil {
        HandleError(err)
    }
    // Generate a signed URL with a specified validity period for uploading the object. In this example, the validity period of the URL is 60 seconds. 
    signedURL, err := bucket.SignURL(objectName, oss.HTTPPut, 60)
    if err != nil {
        HandleError(err)
    }

    // To use a signed URL that contains custom parameters to access an object from a browser, make sure that the value of the ContentType parameter contained in the URL is the same as the ContentType value specified in the request. 
    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)
}