All Products
Search
Document Center

Object Storage Service:Browser.js SDK: Authorizing access

Last Updated:Aug 26, 2026

This topic describes three ways to grant a browser-side client temporary permission to upload objects to Object Storage Service (OSS): temporary access credentials issued by Security Token Service (STS), a Post signature with a PostPolicy, and a presigned URL. In every method, your application server keeps the long-term AccessKey pair and the browser uploads the file directly to OSS.

Prerequisites

Direct upload from a browser depends on resources that you create before you run any of the sample code:

  • Bucket CORS rules — The page that hosts the upload form and the OSS endpoint are different origins, so the browser sends a preflight request before it uploads. Configure CORS rules on the destination bucket to allow the origin that serves your page. If CORS is not configured, the browser blocks the upload with a No 'Access-Control-Allow-Origin' header error before the request reaches OSS.

  • A RAM role for STS — Method 1 calls the AssumeRole operation, which requires a RAM role that has permissions to upload objects to the destination bucket. Obtain the role ARN on the details page of the RAM role, and use it wherever the sample code contains <YOUR-ROLE-ARN>.

  • Server-side access credentials — The application server calls STS and OSS with a long-term AccessKey pair. Configure the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables on the server before you start it. The OSS SDK for Python reads the AccessKey pair from OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET instead, and the affected snippets state this.

  • Access control for the credential endpoints — The sample servers expose /get_sts_token_for_oss_upload, /get_post_signature_for_oss_upload, and /get_presigned_url_for_oss_upload as GET endpoints without any authentication. Anyone who reaches one of these endpoints obtains write access to your bucket. In production, place the endpoints behind the login and authorization checks of your application, and limit the permissions of the credentials that you issue.

  • A page that hosts the upload form — Every client-side sample expects a form element and a file input whose ID is file on the page. Method 1 additionally expects the global OSS object that the Browser.js SDK provides. Both are included in templates/index.html in the sample projects that the following sections link to.

Authorization methods

OSS supports multiple methods to authorize clients. Each method keeps the long-term AccessKey pair on the application server and hands the browser only a short-lived credential, signature, or URL. The following table compares the three methods that this topic provides sample code for. Select a method based on your authentication and authorization requirements.

Authorization method

Client-side OSS SDK

Multipart and resumable upload

Restrictions that the server can apply

Typical scenario

Method 1: Use STS on the server to generate temporary access credentials (recommended)

Required. The client signs its own requests with the credentials.

Supported. The client reuses the credentials to sign every part.

An extra access policy attached to the credentials further limits their permissions.

Most upload scenarios, including large files.

Method 2: Generate signatures and PostPolicies on the server for PostObject

Not required. The client submits an HTML form.

Not supported.

A PostPolicy restricts the properties of the uploaded file, such as its size and type.

Form-based uploads that must restrict the properties of uploaded files.

Method 3: Generate signed URLs on the server for PutObject

Not required. The client sends a PUT request to the URL.

Not supported.

The signed URL fixes the object name and the signed request headers, such as Content-Type.

Simple uploads of a single object.

Method 1: Use STS on the server to generate temporary access credentials

Important

A validity period must be specified for STS 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 uploads and downloads, the minimum validity period takes precedence. For example, you can set the validity period of your temporary access credentials to 1,200 seconds and the validity period of the signed URL generated by using the credentials to 3,600 seconds. In this case, the signed URL cannot be used to upload objects after the STS temporary access credentials expire, even if the signed URL is within its validity period.

The following figure shows the process in which the server uses temporary STS access credentials to authorize a client to upload files to OSS.

image

The process consists of the following steps:

  1. The client requests temporary access credentials from the application server.

  2. The application server uses an STS SDK to call the AssumeRole operation to obtain temporary access credentials.

  3. STS generates and returns the temporary access credentials to the application server.

  4. The application server returns the temporary access credentials to the client.

  5. The client uses an OSS SDK and the temporary access credentials to upload a file to OSS.

  6. OSS returns a success response to the client.

Note

Frequent calls to the STS service may cause throttling. We recommend that you cache the temporary STS credentials and refresh them before they expire. To prevent the client from misusing the temporary STS access credentials, we recommend that you add an extra access policy to the credentials to further limit their permissions.

Sample code

The following sections provide core snippets. For the complete code, see the sample project: sts.zip.

Server-side sample code

Return the credentials to the client as a flat JSON object that contains the AccessKeyId, AccessKeySecret, SecurityToken, and Expiration fields. The client-side sample code reads Expiration to decide when to request new credentials, so the response must include it.

Each of the following snippets serves the credentials at /get_sts_token_for_oss_upload, which is the path that the client-side sample code requests. The Python and PHP snippets contain only the credential-generating logic, so mount them on that path in the web framework that your server uses.

Java

import com.aliyun.sts20150401.Client;
import com.aliyun.sts20150401.models.AssumeRoleRequest;
import com.aliyun.sts20150401.models.AssumeRoleResponse;
import com.aliyun.sts20150401.models.AssumeRoleResponseBody;
import com.aliyun.tea.TeaException;
import com.aliyun.teautil.models.RuntimeOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import static com.aliyun.teautil.Common.assertAsString;

@RestController
public class StsController {

    @Autowired
    private Client stsClient;

    @GetMapping("/get_sts_token_for_oss_upload")
    public AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials generateStsToken() {
        // Replace <YOUR-ROLE-ARN> with the ARN of the RAM role that has permissions to upload files to the specified OSS bucket. You can obtain the role ARN on the details page of the RAM role.
        // Set <YOUR-ROLE-SESSION-NAME> to a custom session name, for example, my-website-server.
        AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest()
            .setDurationSeconds(3600L)
            .setRoleSessionName("<YOUR-ROLE-SESSION-NAME>")
            .setRoleArn("<YOUR-ROLE-ARN>");
        RuntimeOptions runtime = new RuntimeOptions();
        try {
            AssumeRoleResponse response = stsClient.assumeRoleWithOptions(assumeRoleRequest, runtime);
            // The credentials contain the AccessKeyId, AccessKeySecret, SecurityToken, and Expiration fields.
            return response.body.credentials;
        } catch (TeaException error) {
            // Print the error if needed.
            assertAsString(error.message);
            return null;
        } catch (Exception _error) {
            TeaException error = new TeaException(_error.getMessage(), _error);
            // Print the error if needed.
            assertAsString(error.message);
            return null;
        }
    }
}

Declare the STS client as a Spring bean in a separate file:

import com.aliyun.sts20150401.Client;
import com.aliyun.teaopenapi.models.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class StsClientConfiguration {

    @Bean
    public Client stsClient() {
        // If you do not pass any parameters when you initialize the credential client, the Credentials tool initializes the client by using the default credential provider chain.
        Config config = new Config();
        // Replace <YOUR-REGION> with the region ID of the STS service, for example, ap-southeast-1.
        config.endpoint = "sts.<YOUR-REGION>.aliyuncs.com";
        try {
            com.aliyun.credentials.Client credentials = new com.aliyun.credentials.Client();
            config.setCredential(credentials);
            return new Client(config);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

Node.js

const express = require("express");
const { STS } = require('ali-oss');

const app = express();
const path = require("path");

app.use(express.static(path.join(__dirname, "templates")));
// Configure the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable.
const accessKeyId = process.env.ALIBABA_CLOUD_ACCESS_KEY_ID;
// Configure the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable.
const accessKeySecret = process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET;

app.get('/get_sts_token_for_oss_upload', (req, res) => {
  let sts = new STS({
   accessKeyId: accessKeyId,
   accessKeySecret: accessKeySecret
 });
   // Replace <YOUR-ROLE-ARN> with the ARN of the RAM role that you created in the prerequisites, for example, acs:ram::175708322470****:role/ramtest.
   // Set policy to a custom access policy that further limits the permissions of the temporary STS access credentials. If you do not specify a policy, the returned temporary STS access credentials have all permissions of the specified role by default.
   // 3600 is the expiration time, in seconds.
   // Use sessionName to specify a custom role session name that distinguishes different tokens, for example, sessiontest.
   sts.assumeRole('<YOUR-ROLE-ARN>', ``, '3600', '<YOUR-ROLE-SESSION-NAME>').then((result) => {
     console.log(result);
     // Return Expiration together with the credentials. The client uses it to refresh the credentials before they expire.
     res.json({
       AccessKeyId: result.credentials.AccessKeyId,
       AccessKeySecret: result.credentials.AccessKeySecret,
       SecurityToken: result.credentials.SecurityToken,
       Expiration: result.credentials.Expiration,
     });
   }).catch((err) => {
     console.log(err);
     res.status(400).json(err.message);
   });
 });

app.listen(8000, () => {
  console.log("http://127.0.0.1:8000");
});

Python

import json
from alibabacloud_tea_openapi.models import Config
from alibabacloud_sts20150401.client import Client as Sts20150401Client
from alibabacloud_sts20150401 import models as sts_20150401_models
from alibabacloud_credentials.client import Client as CredentialClient

# Replace <YOUR-ROLE-ARN> with the ARN of the RAM role that has permissions to upload files to the specified OSS bucket.
role_arn_for_oss_upload = '<YOUR-ROLE-ARN>'
# Replace <YOUR-REGION> with the region ID of the STS service, for example, ap-southeast-1.
region_id = '<YOUR-REGION>'

def get_sts_token():
    # If you do not specify parameters when you initialize CredentialClient, the default credential provider chain is used.
    # When you run the program on your computer, you can use the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables to specify the AccessKey pair.
    # When you run the program on ECS, ECI, or a container service, you can use the ALIBABA_CLOUD_ECS_METADATA environment variable to specify the attached instance RAM role. The SDK automatically obtains temporary STS credentials.
    config = Config(region_id=region_id, credential=CredentialClient())
    sts_client = Sts20150401Client(config=config)
    assume_role_request = sts_20150401_models.AssumeRoleRequest(
        role_arn=role_arn_for_oss_upload,
        # Set <YOUR-ROLE-SESSION-NAME> to a custom session name, for example, oss-role-session.
        role_session_name='<YOUR-ROLE-SESSION-NAME>'
    )
    response = sts_client.assume_role(assume_role_request)
    # The serialized credentials contain the AccessKeyId, AccessKeySecret, SecurityToken, and Expiration fields.
    token = json.dumps(response.body.credentials.to_map())
    return token

Go

package main

import (
    "encoding/json"
    "net/http"
    "os"

    openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
    sts20150401 "github.com/alibabacloud-go/sts-20150401/v2/client"
    util "github.com/alibabacloud-go/tea-utils/v2/service"
    "github.com/alibabacloud-go/tea/tea"
)

/**
 * Initialize the account client by using an AccessKey pair.
 * @param accessKeyId
 * @param accessKeySecret
 * @return Client
 * @throws Exception
 */
func CreateClient(accessKeyId *string, accessKeySecret *string) (*sts20150401.Client, error) {
    config := &openapi.Config{
        // Required. Your AccessKey ID.
        AccessKeyId: accessKeyId,
        // Required. Your AccessKey secret.
        AccessKeySecret: accessKeySecret,
    }
    // Replace <YOUR-REGION> with the region ID of the STS service, for example, ap-southeast-1. For more information about endpoints, see https://api.alibabacloud.com/product/Sts.
    config.Endpoint = tea.String("sts.<YOUR-REGION>.aliyuncs.com")
    return sts20150401.NewClient(config)
}

func AssumeRole(client *sts20150401.Client) (*sts20150401.AssumeRoleResponse, error) {
    assumeRoleRequest := &sts20150401.AssumeRoleRequest{
        DurationSeconds: tea.Int64(3600),
        // Replace <YOUR-ROLE-ARN> with the ARN of the RAM role that has permissions to upload files to the specified OSS bucket, for example, acs:ram::175708322470****:role/ramtest.
        RoleArn: tea.String("<YOUR-ROLE-ARN>"),
        // Set <YOUR-ROLE-SESSION-NAME> to a custom session name, for example, oss-role-session.
        RoleSessionName: tea.String("<YOUR-ROLE-SESSION-NAME>"),
    }
    return client.AssumeRoleWithOptions(assumeRoleRequest, &util.RuntimeOptions{})
}

func handler(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path == "/" {
        http.ServeFile(w, r, "templates/index.html")
        return
    } else if r.URL.Path == "/get_sts_token_for_oss_upload" {
        client, err := CreateClient(tea.String(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")), tea.String(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")))
        if err != nil {
            panic(err)
        }
        assumeRoleResponse, err := AssumeRole(client)
        if err != nil {
            panic(err)
        }
        // Serialize only the credentials so that the client receives a flat JSON object.
        responseBytes, err := json.Marshal(assumeRoleResponse.Body.Credentials)
        if err != nil {
            panic(err)
        }
        w.Header().Set("Content-Type", "application/json")
        w.Write(responseBytes)
        return
    }
    http.NotFound(w, r)
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

PHP

<?php
require_once 'vendor/autoload.php';
use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Sts\Sts;
// Initialize the Alibaba Cloud client.
AlibabaCloud::accessKeyClient(getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'), getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'))
    // Replace <YOUR-REGION> with the region ID of the STS service, for example, ap-southeast-1.
    ->regionId('<YOUR-REGION>')
    ->asDefaultClient();
// Create an STS request.
$request = Sts::v20150401()->assumeRole();
// Send the STS request and obtain the result.
// Set <YOUR-ROLE-SESSION-NAME> to a custom session name, for example, oss-role-session.
// Replace <YOUR-ROLE-ARN> with the ARN of the RAM role that has permissions to upload files to the specified OSS bucket.
$result = $request
    ->withRoleSessionName("<YOUR-ROLE-SESSION-NAME>")
    ->withDurationSeconds(3600)
    ->withRoleArn("<YOUR-ROLE-ARN>")
    ->request();
// Obtain the credential information from the result of the STS request.
$credentials = $result->get('Credentials');
// Construct the flat JSON data to return. Expiration lets the client refresh the credentials before they expire.
$response = [
    'AccessKeyId' => $credentials['AccessKeyId'],
    'AccessKeySecret' => $credentials['AccessKeySecret'],
    'SecurityToken' => $credentials['SecurityToken'],
    'Expiration' => $credentials['Expiration'],
];
// Set the response header to application/json.
header('Content-Type: application/json');
// Convert the result to the JSON format and print it.
echo json_encode($response);
?>

C#

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Aliyun.OSS;
using System;
using System.IO;
using AlibabaCloud.SDK.Sts20150401;
using System.Text.Json;
namespace YourNamespace
{
    public class Program
    {
        private ILogger<Program> _logger;
        public static AlibabaCloud.SDK.Sts20150401.Client CreateClient(string accessKeyId, string accessKeySecret)
        {
            var config = new AlibabaCloud.OpenApiClient.Models.Config
            {
                AccessKeyId = accessKeyId,
                AccessKeySecret = accessKeySecret,
                // Replace <YOUR-REGION> with the region ID of the STS service, for example, ap-southeast-1.
                Endpoint = "sts.<YOUR-REGION>.aliyuncs.com"
            };
            return new AlibabaCloud.SDK.Sts20150401.Client(config);
        }
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            // Register services before you build the application. The service collection is read-only after Build is called.
            builder.Logging.AddConsole();
            var app = builder.Build();
            var logger = app.Services.GetRequiredService<ILogger<Program>>();
            app.UseStaticFiles();
            app.MapGet("/", async (context) =>
            {
                var filePath = Path.Combine(Directory.GetCurrentDirectory(), "templates/index.html");
                var htmlContent = await File.ReadAllTextAsync(filePath);
                await context.Response.WriteAsync(htmlContent);
                logger.LogInformation("GET request to root path");
            });
            app.MapGet("/get_sts_token_for_oss_upload", async (context) =>
            {
                var client = CreateClient(Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID"), Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
                var assumeRoleRequest = new AlibabaCloud.SDK.Sts20150401.Models.AssumeRoleRequest();
                // Set <YOUR-ROLE-SESSION-NAME> to a custom session name, for example, oss-role-session.
                assumeRoleRequest.RoleSessionName = "<YOUR-ROLE-SESSION-NAME>";
                // Replace <YOUR-ROLE-ARN> with the ARN of the RAM role that has permissions to upload files to the specified OSS bucket.
                assumeRoleRequest.RoleArn = "<YOUR-ROLE-ARN>";
                assumeRoleRequest.DurationSeconds = 3600;
                var runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions();
                var response = client.AssumeRoleWithOptions(assumeRoleRequest, runtime);
                var credentials = response.Body.Credentials;
                var jsonResponse = JsonSerializer.Serialize(new
                {
                    AccessKeyId = credentials.AccessKeyId,
                    AccessKeySecret = credentials.AccessKeySecret,
                    Expiration = credentials.Expiration,
                    SecurityToken = credentials.SecurityToken
                });
                context.Response.ContentType = "application/json";
                await context.Response.WriteAsync(jsonResponse);
            });
            app.Run();
        }
        public Program(ILogger<Program> logger)
        {
            _logger = logger;
        }
    }
}

Ruby

require 'sinatra'
require 'base64'
require 'open-uri'
require 'cgi'
require 'openssl'
require 'json'
require 'sinatra/reloader'
require 'sinatra/content_for'
require 'aliyunsdkcore'

# Set the path of the public folder to the templates folder in the current directory.
set :public_folder, File.dirname(__FILE__) + '/templates'

def get_sts_token_for_oss_upload()
  client = RPCClient.new(
    # Configure the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable.
    access_key_id: ENV['ALIBABA_CLOUD_ACCESS_KEY_ID'],
    # Configure the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable.
    access_key_secret: ENV['ALIBABA_CLOUD_ACCESS_KEY_SECRET'],
    # Replace <YOUR-REGION> with the ID of the region in which you call STS, for example, ap-southeast-1.
    endpoint: 'https://sts.<YOUR-REGION>.aliyuncs.com',
    api_version: '2015-04-01'
  )
  response = client.request(
    action: 'AssumeRole',
    params: {
      # Replace <YOUR-ROLE-ARN> with the ARN of the RAM role that you created in the prerequisites, for example, acs:ram::175708322470****:role/ramtest.
      "RoleArn": "<YOUR-ROLE-ARN>",
      # 3600 is the expiration time, in seconds.
      "DurationSeconds": 3600,
      # Use RoleSessionName to specify a custom role session name that distinguishes different tokens, for example, sessiontest.
      "RoleSessionName": "<YOUR-ROLE-SESSION-NAME>"
    },
    opts: {
      method: 'POST',
      format_params: true
    }
  )
end

$server_ip = "0.0.0.0"
$server_port = 8000

if ARGV.length == 1
  $server_port = ARGV[0]
elsif ARGV.length == 2
  $server_ip = ARGV[0]
  $server_port = ARGV[1]
end

puts "App server is running on: http://#{$server_ip}:#{$server_port}"

set :bind, $server_ip
set :port, $server_port

get '/get_sts_token_for_oss_upload' do
  token = get_sts_token_for_oss_upload()
  # Return Expiration together with the credentials. The client uses it to refresh the credentials before they expire.
  response = {
    "AccessKeyId" => token["Credentials"]["AccessKeyId"],
    "AccessKeySecret" => token["Credentials"]["AccessKeySecret"],
    "SecurityToken" => token["Credentials"]["SecurityToken"],
    "Expiration" => token["Credentials"]["Expiration"]
  }
  response.to_json
end

get '/*' do
  puts "********************* GET "
  send_file File.join(settings.public_folder, 'index.html')
end

Client-side sample code

The following sample code shows how to use temporary access credentials to upload a file to OSS from a web client:

let credentials = null;
const form = document.querySelector("form");
form.addEventListener("submit", async (event) => {
  event.preventDefault();
  // To reduce the number of calls to the STS service, obtain the temporary credentials again only after the current credentials expire.
  if (isCredentialsExpired(credentials)) {
    const response = await fetch("/get_sts_token_for_oss_upload", {
      method: "GET",
    });
    if (!response.ok) {
      // Handle the error HTTP status code.
      throw new Error(
        `Failed to obtain the STS token: ${response.status} ${response.statusText}`
      );
    }
    credentials = await response.json();
  }
  const client = new OSS({
    // Set <YOUR-BUCKET> to the name of the OSS bucket.
    bucket: "<YOUR-BUCKET>",
    // Replace <YOUR-REGION> with the ID of the region in which the OSS bucket is located, for example, ap-southeast-1.
    region: "oss-<YOUR-REGION>",
    authorizationV4: true,
    accessKeyId: credentials.AccessKeyId,
    accessKeySecret: credentials.AccessKeySecret,
    stsToken: credentials.SecurityToken,
  });

  const fileInput = document.querySelector("#file");
  const file = fileInput.files[0];
  const result = await client.put(file.name, file);
  console.log(result);
});

/**
 * Check whether the temporary credentials have expired.
 **/
function isCredentialsExpired(credentials) {
  if (!credentials) {
    return true;
  }
  const expireDate = new Date(credentials.Expiration);
  const now = new Date();
  // If the validity period is less than one minute, the credentials are considered expired.
  return expireDate.getTime() - now.getTime() <= 60000;
}

The put method resolves after OSS stores the object, and the sample logs the result to the browser console. The object is stored in the bucket that you specify in the client configuration, under the name of the file that the user selects. If the upload fails, check the browser console for errors and confirm that the bucket CORS rules described in Prerequisites allow the origin that serves your page.

Method 2: Generate signatures and PostPolicies on the server for PostObject

In this method, the browser submits an HTML form directly to OSS. The client does not use the Browser.js SDK: the client-side sample code relies only on the FormData and fetch APIs of the browser.

The following figure shows the process in which the server uses a Post signature and a PostPolicy to authorize a client to upload files to OSS.

image

The process consists of the following steps:

  1. The client requests information such as the Post signature and the PostPolicy from the application server.

  2. The application server generates and returns information such as the Post signature and the PostPolicy to the client.

  3. The client uses the Post signature, the PostPolicy, and other information to upload a file to OSS by calling the PostObject operation from an HTML form.

  4. OSS returns a success response to the client.

    The PostPolicy that the server generates restricts the files that the client uploads. For example, you can limit the file size and the prefix of the object name.

Important

Every condition in the PostPolicy must have a matching field in the form that the client submits. If a condition is not satisfied, OSS rejects the upload.

Sample code

The following sections provide core snippets. For the complete code, see the sample project: postsignature.zip.

Server-side sample code

Each of the following snippets returns the host, policy, signature, ossAccessKeyId, and dir fields that the client-side sample code adds to the upload form. The snippets serve this response at /get_post_signature_for_oss_upload, which is the path that the client-side sample code requests. The Python and PHP snippets contain only the signature-generating logic, so mount them on that path in the web framework that your server uses.

Java

import com.aliyun.help.demo.uploading_to_oss_directly_postsignature.config.OssConfig;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import java.nio.charset.StandardCharsets;
import java.util.Date;

@Controller
public class PostSignatureController {
    @Autowired
    private OSS ossClient;

    @Autowired
    private OssConfig ossConfig;

    @GetMapping("/get_post_signature_for_oss_upload")
    @ResponseBody
    public String generatePostSignature() {
        JSONObject response = new JSONObject();
        try {
            long expireEndTime = System.currentTimeMillis() + ossConfig.getExpireTime() * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, ossConfig.getDir());
            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);
            response.put("ossAccessKeyId", ossConfig.getAccessKeyId());
            response.put("policy", encodedPolicy);
            response.put("signature", postSignature);
            response.put("dir", ossConfig.getDir());
            response.put("host", ossConfig.getHost());
        } 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("HTTP Status Code: " + oe.getRawResponseError());
            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 a 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());
        } catch (JSONException je) {
            System.out.println("Failed to construct the JSON response.");
            System.out.println("Error Message: " + je.getMessage());
        }
        // Do not shut down the injected OSS client here. It is shared by all requests and is released when the application stops.
        return response.toString();
    }
}

Declare the OSS client and the upload settings as a Spring configuration class in a separate file:

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PreDestroy;

@Configuration
public class OssConfig {
    /**
     * Replace <YOUR-ENDPOINT> with the endpoint of the region in which the bucket is located, for example, oss-ap-southeast-1.aliyuncs.com.
     */
    private String endpoint = "<YOUR-ENDPOINT>";
    /**
     * Replace <YOUR-BUCKET> with the bucket name.
     */
    private String bucket = "<YOUR-BUCKET>";
    /**
     * Specify the prefix of the files to upload to OSS.
     */
    private String dir = "user-dir-prefix/";
    /**
     * Specify the expiration time, in seconds.
     */
    private long expireTime = 3600;
    /**
     * Construct the host.
     */
    private String host = "https://" + bucket + "." + endpoint;
    /**
     * Set accessKeyId by using the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable.
     */
    private String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
    /**
     * Set accessKeySecret by using the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable.
     */
    private String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");

    private OSS ossClient;
    @Bean
    public OSS getOssClient() {
        ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        return ossClient;
    }
    @Bean
    public String getHost() {
        return host;
    }
    @Bean
    public String getAccessKeyId() {
        return accessKeyId;
    }
    @Bean
    public long getExpireTime() {
        return expireTime;
    }
    @Bean
    public String getDir() {
        return dir;
    }

    @PreDestroy
    public void onDestroy() {
        ossClient.shutdown();
    }
}

Node.js

const express = require("express");
const { Buffer } = require("buffer");
const OSS = require("ali-oss");
const app = express();
const path = require("path");
const config = {
  // Configure the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable.
  accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID,
  // Configure the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable.
  accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET,
  // Replace <YOUR-BUCKET> with the bucket name.
  bucket: "<YOUR-BUCKET>",
  // Specify the prefix of the files to upload to OSS.
  dir: "prefix/",
};

app.use(express.static(path.join(__dirname, "templates")));

app.get("/get_post_signature_for_oss_upload", async (req, res) => {
  const client = new OSS(config);
  const date = new Date();
  // Set the validity period of the signature, in seconds.
  date.setSeconds(date.getSeconds() + 3600);
  const policy = {
    expiration: date.toISOString(),
    conditions: [
      // Set the size limit for the uploaded file.
      ["content-length-range", 0, 1048576000],
      // Specify the bucket to which files can be uploaded.
      { bucket: client.options.bucket },
    ],
  };
  const formData = await client.calculatePostSignature(policy);
  const host = `https://${config.bucket}.${
    (await client.getBucketLocation()).location
  }.aliyuncs.com`.toString();
  const params = {
    policy: formData.policy,
    signature: formData.Signature,
    ossAccessKeyId: formData.OSSAccessKeyId,
    host,
    dir: config.dir,
  };
  res.json(params);
});

app.get(/^(.+)*\.(html|js)$/i, async (req, res) => {
  res.sendFile(path.join(__dirname, "./templates", req.originalUrl));
});

app.listen(8000, () => {
  console.log("http://127.0.0.1:8000");
});

Python

import os
from hashlib import sha1 as sha
import json
import base64
import hmac
import datetime
import time

# Configure the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable.
access_key_id = os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID')
# Configure the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable.
access_key_secret = os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET')
# Replace <YOUR-BUCKET> with the bucket name.
bucket = '<YOUR-BUCKET>'
# The host is in the bucketname.endpoint format. Replace <YOUR-BUCKET> with the bucket name. Replace <YOUR-ENDPOINT> with the OSS endpoint, for example, oss-ap-southeast-1.aliyuncs.com.
host = 'https://<YOUR-BUCKET>.<YOUR-ENDPOINT>'
# Specify the prefix of the files to upload to OSS.
upload_dir = 'user-dir-prefix/'
# Specify the expiration time, in seconds.
expire_time = 3600

def generate_expiration(seconds):
    """
    Generate an expiration time by specifying a validity period, in seconds.
    :param seconds: The validity period, in seconds.
    :return: An ISO 8601 time string, such as "2014-12-01T12:00:00.000Z".
    """
    now = int(time.time())
    expiration_time = now + seconds
    gmt = datetime.datetime.utcfromtimestamp(expiration_time).isoformat()
    gmt += 'Z'
    return gmt

def generate_signature(access_key_secret, expiration, conditions, policy_extra_props=None):
    """
    Generate the Signature string.
    :param access_key_secret: The AccessKey secret that has permissions to access the destination bucket.
    :param expiration: The expiration time of the signature. The value follows the ISO 8601 standard, must be in UTC, and is in the yyyy-MM-ddTHH:mm:ssZ format. Example: "2014-12-01T12:00:00.000Z".
    :param conditions: The policy conditions that limit the values allowed in the upload form.
    :param policy_extra_props: Additional policy parameters. If the policy supports new parameters later, you can pass the additional parameters as a dict.
    :return: signature, the signature string.
    """
    policy_dict = {
        'expiration': expiration,
        'conditions': conditions
    }
    if policy_extra_props is not None:
        policy_dict.update(policy_extra_props)
    policy = json.dumps(policy_dict).strip()
    policy_encode = base64.b64encode(policy.encode())
    h = hmac.new(access_key_secret.encode(), policy_encode, sha)
    sign_result = base64.b64encode(h.digest()).strip()
    return sign_result.decode()

def generate_upload_params():
    policy = {
        # The validity period.
        "expiration": generate_expiration(expire_time),
        # The constraints.
        "conditions": [
            # If success_action_redirect is not specified, the status code returned after a successful upload is 204 by default.
            ["eq", "$success_action_status", "200"],
            # The value of the form field must start with the specified prefix. For example, to specify that the value of key must start with user/user1, set this condition to ["starts-with", "$key", "user/user1"].
            ["starts-with", "$key", upload_dir],
            # Limit the minimum and maximum allowed size of the uploaded object, in bytes.
            ["content-length-range", 0, 1048576000]
            # Add a condition only if the client-side form submits a matching field. Otherwise, OSS rejects the upload.
        ]
    }
    signature = generate_signature(access_key_secret, policy.get('expiration'), policy.get('conditions'))
    response = {
        'policy': base64.b64encode(json.dumps(policy).encode('utf-8')).decode(),
        'ossAccessKeyId': access_key_id,
        'signature': signature,
        'host': host,
        'dir': upload_dir
        # You can append other parameters here as needed.
    }
    return json.dumps(response)

Go

package main

import (
    "crypto/hmac"
    "crypto/sha1"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
)

var (
    // Configure the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable.
    accessKeyId = os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
    // Configure the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable.
    accessKeySecret = os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
    // The host is in the bucketname.endpoint format. Replace <YOUR-BUCKET> with the bucket name. Replace <YOUR-ENDPOINT> with the OSS endpoint, for example, oss-ap-southeast-1.aliyuncs.com.
    host = "https://<YOUR-BUCKET>.<YOUR-ENDPOINT>"
    // Specify the prefix of the files to upload to OSS.
    uploadDir = "user-dir-prefix/"
    // Specify the expiration time, in seconds.
    expireTime = int64(3600)
)

type ConfigStruct struct {
    Expiration string     `json:"expiration"`
    Conditions [][]string `json:"conditions"`
}

type PolicyToken struct {
    AccessKeyId string `json:"ossAccessKeyId"`
    Host        string `json:"host"`
    Signature   string `json:"signature"`
    Policy      string `json:"policy"`
    Directory   string `json:"dir"`
}

func getGMTISO8601(expireEnd int64) string {
    return time.Unix(expireEnd, 0).UTC().Format("2006-01-02T15:04:05Z")
}

func getPolicyToken() string {
    now := time.Now().Unix()
    expireEnd := now + expireTime
    tokenExpire := getGMTISO8601(expireEnd)
    var config ConfigStruct
    config.Expiration = tokenExpire
    var condition []string
    condition = append(condition, "starts-with")
    condition = append(condition, "$key")
    condition = append(condition, uploadDir)
    config.Conditions = append(config.Conditions, condition)
    result, err := json.Marshal(config)
    if err != nil {
        fmt.Println("callback json err:", err)
        return ""
    }
    encodedResult := base64.StdEncoding.EncodeToString(result)
    h := hmac.New(sha1.New, []byte(accessKeySecret))
    io.WriteString(h, encodedResult)
    signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
    policyToken := PolicyToken{
        AccessKeyId: accessKeyId,
        Host:        host,
        Signature:   signedStr,
        Policy:      encodedResult,
        Directory:   uploadDir,
    }
    response, err := json.Marshal(policyToken)
    if err != nil {
        fmt.Println("json err:", err)
        return ""
    }
    return string(response)
}

func handler(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path == "/" {
        http.ServeFile(w, r, "templates/index.html")
        return
    } else if r.URL.Path == "/get_post_signature_for_oss_upload" {
        policyToken := getPolicyToken()
        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte(policyToken))
        return
    }
    http.NotFound(w, r)
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

PHP

<?php
function gmt_iso8601($time)
{
    return str_replace('+00:00', '.000Z', gmdate('c', $time));
}

// Obtain the access credentials from environment variables. Before you run this sample code, make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables are configured.
$accessKeyId = getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
$accessKeySecret = getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// The host is in the bucketname.endpoint format. Replace <YOUR-BUCKET> with the bucket name. Replace <YOUR-ENDPOINT> with the OSS endpoint, for example, oss-ap-southeast-1.aliyuncs.com.
$host = 'https://<YOUR-BUCKET>.<YOUR-ENDPOINT>';
// The prefix that the user specifies when uploading a file.
$dir = 'user-dir-prefix/';          

$now = time();
// Specify the expiration time, in seconds. After the validity period elapses, the policy can no longer be used for access.
$expire = 3600;
$end = $now + $expire;
$expiration = gmt_iso8601($end);

//The maximum file size. You can set this value as needed.
$condition = array(0 => 'content-length-range', 1 => 0, 2 => 1048576000);
$conditions[] = $condition;

// The data that the user uploads must start with $dir. Otherwise, the upload fails. This step is optional and is used for security purposes to prevent users from using the policy to upload files to other users' directories.
$start = array(0 => 'starts-with', 1 => '$key', 2 => $dir);
$conditions[] = $start;

$arr = array('expiration' => $expiration, 'conditions' => $conditions);
$policy = json_encode($arr);
$base64_policy = base64_encode($policy);
$string_to_sign = $base64_policy;
$signature = base64_encode(hash_hmac('sha1', $string_to_sign, $accessKeySecret, true));

$response = array();
$response['ossAccessKeyId'] = $accessKeyId;
$response['host'] = $host;
$response['policy'] = $base64_policy;
$response['signature'] = $signature;
$response['dir'] = $dir;  
echo json_encode($response);

Ruby

require 'sinatra'
require 'base64'
require 'open-uri'
require 'cgi'
require 'openssl'
require 'json'
require 'sinatra/reloader'
require 'sinatra/content_for'

# Set the path of the public folder to the templates folder in the current directory.
set :public_folder, File.dirname(__FILE__) + '/templates'

# Configure the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable.
$access_key_id = ENV['ALIBABA_CLOUD_ACCESS_KEY_ID']
# Configure the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable.
$access_key_secret = ENV['ALIBABA_CLOUD_ACCESS_KEY_SECRET']

# The host is in the bucketname.endpoint format. Replace <YOUR-BUCKET> with the bucket name. Replace <YOUR-ENDPOINT> with the OSS endpoint, for example, oss-ap-southeast-1.aliyuncs.com.
$host = 'https://<YOUR-BUCKET>.<YOUR-ENDPOINT>'

# The prefix that the user specifies when uploading a file.
$upload_dir = 'user-dir-prefix/'
# Specify the expiration time, in seconds.
$expire_time = 3600
$server_ip = "0.0.0.0"
$server_port = 8000

if ARGV.length == 1
  $server_port = ARGV[0]
elsif ARGV.length == 2
  $server_ip = ARGV[0]
  $server_port = ARGV[1]
end

puts "App server is running on: http://#{$server_ip}:#{$server_port}"

def hash_to_json(source_hash)
  json_string = source_hash.to_json

  json_string.gsub!("\":[", "\": [")
  json_string.gsub!("\",\"", "\", \"")
  json_string.gsub!("],\"", "], \"")
  json_string.gsub!("\":\"", "\": \"")

  json_string
end

def get_token()
  expire_syncpoint = Time.now.to_i + $expire_time
  expire = Time.at(expire_syncpoint).utc.iso8601()
  response.headers['expire'] = expire
  policy_dict = {}
  condition_arrary = Array.new
  array_item = Array.new
  array_item.push('starts-with')
  array_item.push('$key')
  array_item.push($upload_dir)
  condition_arrary.push(array_item)
  policy_dict["conditions"] = condition_arrary
  policy_dict["expiration"] = expire
  policy = hash_to_json(policy_dict)
  policy_encode = Base64.strict_encode64(policy).chomp;
  h = OpenSSL::HMAC.digest('sha1', $access_key_secret, policy_encode)
  sign_result = Base64.strict_encode64(h).strip()
  token_dict = {}
  token_dict['ossAccessKeyId'] = $access_key_id
  token_dict['host'] = $host
  token_dict['policy'] = policy_encode
  token_dict['signature'] = sign_result 
  token_dict['expire'] = expire_syncpoint
  token_dict['dir'] = $upload_dir
  result = hash_to_json(token_dict)
  result
end

set :bind, $server_ip
set :port, $server_port

get '/get_post_signature_for_oss_upload' do
  token = get_token()
  puts "Token: #{token}"
  token
end

get '/*' do
  puts "********************* GET "
  send_file File.join(settings.public_folder, 'index.html')
end

C#

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using System.IO;
using System.Collections.Generic;
using System;
using System.Globalization;
using System.Text;
using System.Security.Cryptography;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace YourNamespace
{
    public class Program
    {
        private ILogger<Program> _logger;
        // Configure the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable.
        public string AccessKeyId { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID");
        // Configure the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable.
        public string AccessKeySecret { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
        // The host is in the bucketname.endpoint format. Replace <YOUR-BUCKET> with the bucket name. Replace <YOUR-ENDPOINT> with the OSS endpoint, for example, oss-ap-southeast-1.aliyuncs.com.
        public string Host { get; set; } = "https://<YOUR-BUCKET>.<YOUR-ENDPOINT>";
        // Specify the prefix of the files to upload to OSS.
        public string UploadDir { get; set; } = "user-dir-prefix/";
        // Specify the expiration time, in seconds.
        public int ExpireTime { get; set; } = 3600;
        public class PolicyConfig
        {
            public string expiration { get; set; }
            public List<List<object>> conditions { get; set; }
        }
        public class PolicyToken
        {
            public string Accessid { get; set; }
            public string Policy { get; set; }
            public string Signature { get; set; }
            public string Dir { get; set; }
            public string Host { get; set; }
            public string Expire { get; set; }
        }
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            // Register services before you build the application. The service collection is read-only after Build is called.
            builder.Logging.AddConsole();
            var app = builder.Build();
            var logger = app.Services.GetRequiredService<ILogger<Program>>();

            app.UseStaticFiles();

            app.MapGet("/", async (context) =>
            {
                var filePath = Path.Combine(Directory.GetCurrentDirectory(), "templates/index.html");
                var htmlContent = await File.ReadAllTextAsync(filePath);
                await context.Response.WriteAsync(htmlContent);
                logger.LogInformation("GET request to root path");
            });

            app.MapGet("/get_post_signature_for_oss_upload", async (context) =>
            {
                var program = new Program(logger);
                var token = program.GetPolicyToken();

                logger.LogInformation($"Token: {token}");

                context.Response.ContentType = "application/json";
                await context.Response.WriteAsync(token);
            });

            app.Run();
        }

        public Program(ILogger<Program> logger)
        {
            _logger = logger;
        }

        private string ToUnixTime(DateTime dateTime)
        {
            return ((DateTimeOffset)dateTime).ToUnixTimeSeconds().ToString();
        }

        private string GetPolicyToken()
        {
            var expireDateTime = DateTime.Now.AddSeconds(ExpireTime);
            var config = new PolicyConfig
            {
                expiration = FormatIso8601Date(expireDateTime),
                conditions = new List<List<object>>()
            };
            config.conditions.Add(new List<object>
            {
                "content-length-range", 0, 1048576000
            });
            var policy = JsonConvert.SerializeObject(config);
            var policyBase64 = EncodeBase64("utf-8", policy);
            var signature = ComputeSignature(AccessKeySecret, policyBase64);
            var policyToken = new PolicyToken
            {
                Accessid = AccessKeyId,
                Host = Host,
                Policy = policyBase64,
                Signature = signature,
                Expire = ToUnixTime(expireDateTime),
                Dir = UploadDir
            };
            return JsonConvert.SerializeObject(policyToken);
        }

        private string FormatIso8601Date(DateTime dtime)
        {
            return dtime.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'",
                                    CultureInfo.CurrentCulture);
        }

        private string EncodeBase64(string codeType, string code)
        {
            string encode = "";
            byte[] bytes = Encoding.GetEncoding(codeType).GetBytes(code);
            try
            {
                encode = Convert.ToBase64String(bytes);
            }
            catch
            {
                encode = code;
            }
            return encode;
        }

        private string ComputeSignature(string key, string data)
        {
            using (var algorithm = new HMACSHA1(Encoding.UTF8.GetBytes(key)))
            {
                return Convert.ToBase64String(algorithm.ComputeHash(Encoding.UTF8.GetBytes(data)));
            }
        }
    }
}

Client-side sample code

The following sample code shows how to use information such as a Post signature and a PostPolicy to upload a file to OSS from a web client:

const form = document.querySelector("form");
const fileInput = document.querySelector("#file");
form.addEventListener("submit", (event) => {
  event.preventDefault();
  const file = fileInput.files[0];
  const filename = fileInput.files[0].name;
  fetch("/get_post_signature_for_oss_upload", { method: "GET" })
    .then((response) => {
      if (!response.ok) {
        throw new Error("Failed to obtain the signature");
      }
      return response.json();
    })
    .then((data) => {
      const formData = new FormData();
      formData.append("name", filename);
      formData.append("policy", data.policy);
      formData.append("OSSAccessKeyId", data.ossAccessKeyId);
      formData.append("success_action_status", "200");
      formData.append("signature", data.signature);
      formData.append("key", data.dir + filename);
      formData.append("file", file);

      return fetch(data.host, { method: "POST", body: formData });
    })
    .then((response) => {
      if (response.ok) {
        console.log("Upload successful");
        alert("The file is uploaded");
      } else {
        console.log("Upload failed", response);
        alert("The upload failed. Try again later.");
      }
    })
    .catch((error) => {
      console.error("An error occurred:", error);
    });
});

Method 3: Generate signed URLs on the server for PutObject

In this method, the browser sends a PUT request to a URL that the server signs. The client does not use the Browser.js SDK: the client-side sample code relies only on the fetch API of the browser.

The following figure shows the process in which the server uses a signed URL to authorize a client to upload files to OSS.

image

The process consists of the following steps:

  1. The client requests a signed URL from the application server.

  2. The application server uses an OSS SDK to generate a signed URL for a PUT request and returns the URL to the client.

  3. The client uses the signed URL for the PUT request to upload a file to OSS by calling the PutObject operation.

  4. OSS returns a success response to the client.

    This method is not suitable for multipart uploads of large files or resumable uploads based on multipart upload. If you generate a signed URL for each part on the server and return the signed URLs to the client, the number of interactions with the server increases and network requests become more complex. In addition, the client might modify the content or the order of the parts, which causes the final merged object to be incorrect.

Important

If you want to use a signed URL that contains optional parameters on the frontend, make sure that the Content-Type specified when the server generates the signed URL is the same as the Content-Type specified when the URL is used on the frontend. Otherwise, a SignatureDoesNotMatch error may occur. For more information about how to set the Content-Type, see How do I set the Content-Type (MIME)?.

Sample code

The following sections provide core snippets. For the complete code, see the sample project: presignedurl.zip.

Server-side sample code

Each of the following snippets returns the signed URL as plain text at /get_presigned_url_for_oss_upload, which is the path that the client-side sample code requests. The snippets sign the image/png Content-Type, which matches the Content-Type that the client-side sample code sends. The Python snippet contains only the URL-generating logic, so mount it on that path in the web framework that your server uses. The Ruby snippet requires one additional change, which the caution in the Ruby tab describes.

Java

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.net.URL;
import java.util.Date;
import javax.annotation.PreDestroy;

@Configuration
public class OssConfig {

    /**
     * Configure the OSS endpoint, for example, oss-ap-southeast-1.aliyuncs.com.
     */
    private static final String endpoint = "https://oss-ap-southeast-1.aliyuncs.com";

    /**
     * Set accessKeyId by using the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable.
     */
    @Value("${ALIBABA_CLOUD_ACCESS_KEY_ID}")
    private String accessKeyId;

    /**
     * Set accessKeySecret by using the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable.
     */
    @Value("${ALIBABA_CLOUD_ACCESS_KEY_SECRET}")
    private String accessKeySecret;

    private OSS ossClient;

    @Bean
    public OSS getOssClient() {
        // Create an OSSClient instance.
        // When the OSSClient instance is no longer used, call the shutdown method to release resources.
        ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        return ossClient;
    }

    @PreDestroy
    public void onDestroy() {
        ossClient.shutdown();
    }
}

@Controller
public class PresignedURLController {

    /**
     * Replace <YOUR-BUCKET> with the bucket name.
     * Replace <YOUR-OBJECT> with the full path of the object, for example, exampledir/exampleobject.png. The full path of the object cannot contain the bucket name.
     * Specify the expiration time, in milliseconds.
     */
    private static final String BUCKET_NAME = "<YOUR-BUCKET>";
    private static final String OBJECT_NAME = "<YOUR-OBJECT>";
    private static final long EXPIRE_TIME = 3600 * 1000L;

    @Autowired
    private OSS ossClient;

    @GetMapping("/get_presigned_url_for_oss_upload")
    @ResponseBody
    public String generatePresignedURL() {

        try {
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(BUCKET_NAME, OBJECT_NAME, HttpMethod.PUT);
            Date expiration = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            request.setExpiration(expiration);
            request.setContentType("image/png");
            URL signedUrl = ossClient.generatePresignedUrl(request);
            return signedUrl.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

Node.js

const express = require("express");
const OSS = require("ali-oss");
const app = express();

app.get("/get_presigned_url_for_oss_upload", async (req, res) => {
  const client = new OSS({
    // Obtain the access credentials from environment variables. Before you run this sample code, make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables are configured.
    accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID,
    accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET,
    // Replace <YOUR-BUCKET> with the bucket name.
    bucket: '<YOUR-BUCKET>',
    // Replace <YOUR-REGION> with the region in which the bucket is located, for example, ap-southeast-1.
    region: '<YOUR-REGION>',
    authorizationV4: true,
  });

  // Sign the same Content-Type that the client sends. Otherwise, a SignatureDoesNotMatch error occurs.
  const url = await client.signatureUrlV4('PUT', 3600, {
    headers: {
      'Content-Type': 'image/png',
    },
  }, 'exampledir/exampleobject.png');

  // Return the signed URL as plain text. The client-side sample code reads the response body by calling response.text().
  res.type('text/plain').send(url);
});

app.listen(8000, () => {
  console.log("http://127.0.0.1:8000");
});

Python

import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider

# Obtain the access credentials from environment variables. Before you run this sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured.
auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
# Specify the endpoint of the region in which the bucket is located, for example, https://oss-ap-southeast-1.aliyuncs.com.
endpoint = "https://oss-ap-southeast-1.aliyuncs.com"
# Specify the region that corresponds to the endpoint, for example, ap-southeast-1. This parameter is required for V4 signatures.
region = "ap-southeast-1"

# Replace <YOUR-BUCKET> with the bucket name.
bucket = oss2.Bucket(auth, endpoint, "<YOUR-BUCKET>", region=region)

# Specify an expiration time of 3,600s. The maximum expiration time is 32,400s.
expire_time = 3600
# Specify the full path of the object, for example, exampledir/exampleobject.png. The full path of the object cannot contain the bucket name.
object_name = 'exampledir/exampleobject.png'

def generate_presigned_url():
    # Specify the headers.
    headers = dict()
    # Specify the Content-Type.
    headers['Content-Type'] = 'image/png'
    # Specify the storage class.
    # headers["x-oss-storage-class"] = "Standard"
    # When a signed URL is generated, OSS escapes the forward slashes (/) in the full path of the object by default. As a result, the generated signed URL cannot be used directly.
    # Set slash_safe to True. This way, OSS does not escape the forward slashes (/) in the full path of the object, and the generated signed URL can be used directly.
    url = bucket.sign_url('PUT', object_name, expire_time, slash_safe=True, headers=headers)
    return url

Go

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/aliyun/aliyun-oss-go-sdk/oss"
)

func getURL() string {
    // Specify the endpoint of the region in which the bucket is located, for example, https://oss-ap-southeast-1.aliyuncs.com.
    endpoint := "https://oss-ap-southeast-1.aliyuncs.com"
    // Replace <YOUR-BUCKET> with the bucket name.
    bucketName := "<YOUR-BUCKET>"
    // Specify the full path of the file, for example, exampledir/exampleobject.png. The full path of the file cannot contain the bucket name.
    objectName := "exampledir/exampleobject.png"
    // Obtain the access credentials from environment variables. Before you run this sample code, make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables are configured.
    provider, err := oss.NewEnvironmentVariableCredentialsProvider()
    if err != nil {
        log.Fatal("Failed to obtain the access credentials: ", err)
    }
    clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
    // Replace <YOUR-REGION> with the region in which the bucket is located, for example, ap-southeast-1.
    clientOptions = append(clientOptions, oss.Region("<YOUR-REGION>"))
    // Specify the signature version.
    clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
    client, err := oss.New(endpoint, "", "", clientOptions...)
    if err != nil {
        log.Fatal("Failed to create the OSSClient instance: ", err)
    }
    bucket, err := client.Bucket(bucketName)
    if err != nil {
        log.Fatal("Failed to obtain the bucket: ", err)
    }
    options := []oss.Option{
        oss.ContentType("image/png"),
    }
    // Specify a validity period of 3,600s.
    signedURL, err := bucket.SignURL(objectName, oss.HTTPPut, 3600, options...)
    if err != nil {
        log.Fatal("Failed to generate the signed URL: ", err)
    }

    return signedURL
}

func handler(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path == "/" {
        http.ServeFile(w, r, "templates/index.html")
        return
    } else if r.URL.Path == "/get_presigned_url_for_oss_upload" {
        url := getURL()
        fmt.Fprintf(w, "%s", url)
        return
    }
    http.NotFound(w, r)
}
func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

Ruby

require 'sinatra'
require 'base64'
require 'open-uri'
require 'cgi'
require 'openssl'
require 'json'
require 'sinatra/reloader'
require 'sinatra/content_for'
require 'aliyun/oss'
include Aliyun::OSS

# Set the path of the public folder to the templates folder in the current directory.
set :public_folder, File.dirname(__FILE__) + '/templates'

# Configure the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable.
$access_key_id = ENV['ALIBABA_CLOUD_ACCESS_KEY_ID']
# Configure the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable.
$access_key_secret = ENV['ALIBABA_CLOUD_ACCESS_KEY_SECRET']

# Specify the full path of the object, for example, exampledir/exampleobject.png. The full path of the object cannot contain the bucket name.
object_key = 'exampledir/exampleobject.png'

def get_presigned_url(client, object_key)
  # Replace <YOUR-BUCKET> with the bucket name.
  bucket = client.get_bucket('<YOUR-BUCKET>')
  # Replace the following placeholder with a call that signs a PUT request for object_key on bucket and specifies a validity period. The maximum validity period is 32,400s.
  '<YOUR-PUT-SIGNED-URL>'
end

client = Aliyun::OSS::Client.new(
  # Replace <YOUR-ENDPOINT> with the endpoint of the region in which the bucket is located, for example, https://oss-ap-southeast-1.aliyuncs.com.
  endpoint: '<YOUR-ENDPOINT>',
  # Pass in the access credentials that are read from the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables.
  access_key_id: $access_key_id,
  access_key_secret: $access_key_secret
)

$server_ip = "0.0.0.0"
$server_port = 8000

if ARGV.length == 1
  $server_port = ARGV[0]
elsif ARGV.length == 2
  $server_ip = ARGV[0]
  $server_port = ARGV[1]
end

puts "App server is running on: http://#{$server_ip}:#{$server_port}"

set :bind, $server_ip
set :port, $server_port

get '/get_presigned_url_for_oss_upload' do
  url = get_presigned_url(client, object_key.to_s)
  puts "Token: #{url}"
  url
end

get '/*' do
  puts "********************* GET "
  send_file File.join(settings.public_folder, 'index.html')
end
Important

This snippet does not include a call that generates a signed URL for a PUT request. Before you use this snippet, replace the <YOUR-PUT-SIGNED-URL> placeholder with a call that signs a PUT request and sets the validity period. Otherwise, OSS rejects the PUT request from the client with a SignatureDoesNotMatch or AccessDenied error.

C#

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using System.IO;
using System;
using Microsoft.Extensions.Logging;
using Aliyun.OSS;

namespace YourNamespace
{
    public class Program
    {
        private ILogger<Program> _logger;

        // Configure the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable.
        public string AccessKeyId { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID");
        // Configure the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable.
        public string AccessKeySecret { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
        // Replace <YOUR-ENDPOINT> with the endpoint of the region in which the bucket is located, for example, https://oss-ap-southeast-1.aliyuncs.com.
        private string EndPoint { get; set; } = "<YOUR-ENDPOINT>";
        // Replace <YOUR-BUCKET> with the bucket name.
        private string BucketName { get; set; } = "<YOUR-BUCKET>";
        // Specify the full path of the object. The full path of the object cannot contain the bucket name.
        private string ObjectName { get; set; } = "exampledir/exampleobject.png";

        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            // Add logging. Register services before you build the application. The service collection is read-only after Build is called.
            builder.Logging.AddConsole();
            var app = builder.Build();
            var logger = app.Services.GetRequiredService<ILogger<Program>>();

            // Enable the static file middleware.
            app.UseStaticFiles();

            app.MapGet("/", async (context) =>
            {
                var filePath = Path.Combine(Directory.GetCurrentDirectory(), "templates/index.html");
                var htmlContent = await File.ReadAllTextAsync(filePath);
                await context.Response.WriteAsync(htmlContent);

                // Print the log.
                logger.LogInformation("GET request to root path");
            });

            app.MapGet("/get_presigned_url_for_oss_upload", async (context) =>
            {
                var program = new Program(logger);
                var signedUrl = program.GetSignedUrl();

                logger.LogInformation($"SignedUrl: {signedUrl}"); // Print the value of the token.
                await context.Response.WriteAsync(signedUrl);
            });

            app.Run();
        }

        // Inject ILogger by using the constructor.
        public Program(ILogger<Program> logger)
        {
            _logger = logger;
        }

        private string GetSignedUrl()
        {
            // Create an OSSClient instance.
            var ossClient = new OssClient(EndPoint, AccessKeyId, AccessKeySecret);

            // Generate a signed URL.
            var generatePresignedUriRequest = new GeneratePresignedUriRequest(BucketName, ObjectName, SignHttpMethod.Put)
            {
                Expiration = DateTime.Now.AddHours(1),
                ContentType = "image/png"
            };
            var signedUrl = ossClient.GeneratePresignedUri(generatePresignedUriRequest);

            return signedUrl.ToString();
        }
    }
}

Client-side sample code

The following sample code shows how to use a signed URL to upload a file to OSS from a web client:

const form = document.querySelector("form");
form.addEventListener("submit", (event) => {
  event.preventDefault();
  const fileInput = document.querySelector("#file");
  const file = fileInput.files[0];
  fetch("/get_presigned_url_for_oss_upload", { method: "GET" })
    .then((response) => {
      if (!response.ok) {
        throw new Error("Failed to obtain the presigned URL");
      }
      return response.text();
    })
    .then((url) => {
      return fetch(url, {
        method: "PUT",
        headers: new Headers({
          "Content-Type": "image/png",
        }),
        body: file,
      }).then((response) => {
        if (!response.ok) {
          throw new Error("Failed to upload the file to OSS");
        }
        console.log(response);
        alert("The file is uploaded");
      });
    })
    .catch((error) => {
      console.error("An error occurred:", error);
      alert(error.message);
    });
});

References

  • For the complete sample code for using STS to grant temporary access, seeGitHub example.

  • For the complete sample code for using signed URLs to grant temporary access, seeGitHub example.