All Products
Search
Document Center

Object Storage Service:Upload objects directly to OSS from clients

Last Updated:Apr 24, 2024

A direct client upload allows you to upload data directly to Object Storage Service (OSS) from clients. The direct client upload solution accelerates uploads and reduces the resource usage of the application server by eliminating the need to transfer objects to and from the application server. This topic describes the benefits, implementation methods, and best practices of this solution.

Benefits

In the traditional server and client architecture, uploading data to OSS from the application server is the common upload method. The client uploads objects to the application server, and then the application server uploads the objects to OSS. During the upload process, data must be transferred twice over the network. This results in a waste of network resources and an increase in the resource usage of the application server. To resolve this issue, you can upload objects directly to OSS without the need to upload objects to the application server.

image

Implementation

To upload data directly to OSS from clients, you must resolve the following issues:

CORS

If your client is a web client or mini program, you must allow cross-origin resource sharing (CORS). To ensure data security, browsers and mini programs usually prohibit CORS. This also prevents your client code from being used to connect to OSS. You can configure CORS rules to allow web applications or mini programs on a specific domain name to access OSS. For more information about how to configure CORS rules, see CORS.

Security authorization

To upload an object to OSS, you need to use the AccessKey pair of a RAM user to complete signing for authentication. However, if the client uses a permanent AccessKey pair, AccessKey pair leaks are likely, causing data security threats. To resolve this issue, you can use one of the following solutions to implement secure upload:

  • Obtain temporary access credentials from STS on the application server

    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 to upload objects from the client directly to OSS. 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. To further prevent abuses of temporary access credentials on the client, we recommend that you configure additional policies to restrict the use of the temporary access credentials. For more information, see What is STS?

  • Obtain a signature and POST policy for PostObject from the application server

    In scenarios in which you want to limit the attributes of an object to upload, you need to obtain information required to call the PostObject operation from the application server. The required information includes a signature and a POST policy. The client can then use the obtained information to upload the object directly to OSS without using OSS SDKs. You can use the policy generated by the server to limit the attributes of an object that can be uploaded, such as the object size and type. This solution is suitable for uploading objects by using HTML forms. This solution does not support multipart upload and resumable upload. For more information, see PostObject.

  • Obtain a signed URL for PutObject from the application server

    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, which results in an invalid combined object. For more information, see Create a signed URL by using signature V1.

Obtain temporary access credentials from STS on the application server

The following figure shows how to upload an object to OSS from a client by using STS temporary access credentials returned from the application server.

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

  2. The application server uses STS SDKs 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 OSS SDKs to upload an object to OSS by using the temporary access credentials.

  6. OSS returns a success response to the client.

Demo project

sts.zip

Examples

Server-side sample code

The following sample code provides an example on how the application server obtains temporary access credentials from STS:

Note

The sample code can be deployed in Function Compute. oss-upload-sts-app

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 Alibaba Cloud Resource Name (ARN) of the RAM role that has the permissions to upload objects to the OSS bucket. 
role_arn_for_oss_upload = '<YOUR_ROLE_ARN>'
# Replace <YOUR_REGION_ID> with the ID of the region of the STS service. Example: cn-hangzhou. 
region_id = '<YOUR_REGION_ID>'

def get_sts_token():
    # If you do not specify parameters when you initialize CredentialClient, the default credential chain is used. 
    # If you run the program on your local computer, you can specify the AccessKey pair by configuring the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables.
    # If you run the program on an Elastic Compute Service (ECS) instance, an elastic container instance, or a node in a Container Service for Kubernetes (ACK) cluster, you can specify the bound instance or node by configuring the ALIBABA_CLOUD_ECS_METADATA environment variable. The SDK automatically exchanges the temporary access credentials obtained from STS. 
    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,
        # Replace <YOUR_ROLE_SESSION_NAME> with a custom session name, such as oss-role-session. 
        role_session_name='<YOUR_ROLE_SESSION_NAME>'
    )
    response = sts_client.assume_role(assume_role_request)
    token = json.dumps(response.body.credentials.to_map())
    return token
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 com.aliyun.teaopenapi.models.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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() {
        AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest()
            .setDurationSeconds(3600L)
            // Replace <YOUR_ROLE_SESSION_NAME> with a custom session name, such as my-website-server. 
            .setRoleSessionName("<YOUR_ROLE_SESSION_NAME>")
            // Replace <YOUR_ROLE_ARN> with the ARN of the RAM role that has the permissions to upload objects to the specified bucket. You can obtain the ARN of a RAM role on the RAM role details page. 
        RuntimeOptions runtime = new RuntimeOptions();
        try {
            AssumeRoleResponse response = stsClient.assumeRoleWithOptions(assumeRoleRequest, runtime);
            return response.body.credentials;
        } catch (TeaException error) {
            // Print the error if necessary.
            assertAsString(error.message);
            return null;
        } catch (Exception error) {
            TeaException error = new TeaException(_error.getMessage(), _error);
            // Print the error if necessary.
            assertAsString(error.message);
            return null;
        }
    }
}

@Configuration
public class StsClientConfiguration {

    @Bean
    public Client stsClient() {
        // If you do not specify any parameters when you initialize the credentials client, the Credentials tool uses the default credential chain to initialize the client. 
        Config config = new Config();
        config.endpoint = "sts.cn-hangzhou.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;
        }
    }
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"
)

/**
 * Use your AccessKey ID and AccessKey secret to initialize the client.
 * @param accessKeyId
 * @param accessKeySecret
 * @return Client
 * @throws Exception
 */
func CreateClient(accessKeyId *string, accessKeySecret *string) (*sts20150401.Client, error) {
    config := &openapi.Config{
    // (Required) Specify your AccessKey ID.
    AccessKeyId: accessKeyId,
    // (Required) Specify your AccessKey secret.
    AccessKeySecret: accessKeySecret,
    }
    // For more information about endpoints, visit https://api.aliyun.com/product/Sts.
    config.Endpoint = tea.String("sts.cn-hangzhou.aliyuncs.com")
    return sts20150401.NewClient(config)
}

func AssumeRole(client *sts20150401.Client) (*sts20150401.AssumeRoleResponse, error) {
    assumeRoleRequest := &sts20150401.AssumeRoleRequest{
    DurationSeconds: tea.Int64(3600),
    RoleArn:         tea.String("acs:ram::1379186349531844:role/admin-oss"),
    RoleSessionName: tea.String("peiyu-demo"),
    }
    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)
    }
    responseBytes, err := json.Marshal(assumeRoleResponse)
    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
require_once 'vendor/autoload.php';
use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
use AlibabaCloud\Sts\Sts;
// Initialize the AlibabaCloud client. 
AlibabaCloud::accessKeyClient(getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'), getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'))
    ->regionId('cn-hangzhou')
    ->asDefaultClient();
// Create a request to obtain temporary access credentials from STS. 
$request = Sts::v20150401()->assumeRole();
// Initiate the STS request and obtain the result. 
// Replace <YOUR_ROLE_SESSION_NAME> with a custom session name, such as oss-role-session. 
// Replace <YOUR_ROLE_ARN> with the ARN of the RAM role that has the permissions to upload objects to the specified bucket. 
$result = $request
    ->withRoleSessionName("<YOUR_ROLE_SESSION_NAME>")
    ->withDurationSeconds(3600)
    ->withRoleArn("<YOUR_ROLE_ARN>")
    ->request();
// Obtain the credential information in the STS request result. 
$credentials = $result->get('Credentials');
// Construct JSON data from the returned result. 
$response = [
    'AccessKeyId' => $credentials['AccessKeyId'],
    'AccessKeySecret' => $credentials['AccessKeySecret'],
    'SecurityToken' => $credentials['SecurityToken'],
];
// Set the Content-Type response header to application/json. 
header('Content-Type: application/json');
// Convert the result to JSON data and print the result. 
echo json_encode(['Credentials' => $response]);
?>
const express = require("express");
const { STS } = require('ali-oss');

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

app.use(express.static(path.join(__dirname, "templates")));
// Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable. 
const accessKeyId = process.env.ALIBABA_CLOUD_ACCESS_KEY_ID;
// Obtain the AccessKey secret from the ALIBABA_CLOUD_ACCESS_SECRET environment variable. 
const accessKeySecret = process.env.ALIBABA_CLOUD_ACCESS_SECRET;

app.get('/get_sts_token_for_oss_upload', (req, res) => {
  let sts = new STS({
   accessKeyId: accessKeyId,
   accessKeySecret: accessKeySecret
 });
   // Specify the ARN of the role that was obtained in Step 2. Example: acs:ram::175708322470****:role/ramtest. 
   // Specify a custom policy to limit the permissions of the temporary access credentials. If you do not specify a custom policy, the returned temporary access credentials have full permissions of the specified RAM role. 
   // Specify the expiration time in seconds for the access credentials. In this example, the expiration time is 3,000 seconds. 
   // Specify a custom role session name, which is used to distinguish different tokens. Example: sessiontest. 
   sts.assumeRole('<YOUR_ROLE_ARN>', ``, '3000', 'sessiontest').then((result) => {
     console.log(result);
     res.json({
       AccessKeyId: result.credentials.AccessKeyId,
       AccessKeySecret: result.credentials.AccessKeySecret,
       SecurityToken: result.credentials.SecurityToken,
     });
   }).catch((err) => {
     console.log(err);
     res.status(400).json(err.message);
   });
 });

app.listen(8000, () => {
  console.log("http://127.0.0.1:8000");
});
require 'sinatra'
require 'base64'
require 'open-uri'
require 'cgi'
require 'openssl'
require 'json'
require 'sinatra/reloader'
require 'sinatra/content_for'
require 'aliyunsdkcore'

# Specify the path of the public-folder folder to the templates subfolder within the current folder.
set :public_folder, File.dirname(__FILE__) + '/templates'

def get_sts_token_for_oss_upload()
  client = RPCClient.new(
    # Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable. 
    access_key_id: ENV['ALIBABA_CLOUD_ACCESS_KEY_ID'],
    # Obtain the AccessKey secret from the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable. 
    access_key_secret: ENV['ALIBABA_CLOUD_ACCESS_KEY_SECRET'],
    endpoint: 'https://sts.cn-hangzhou.aliyuncs.com',
    api_version: '2015-04-01'
  )
  response = client.request(
    action: 'AssumeRole',
    params: {
      # Specify the ARN of the role that was obtained in Step 2. Example: acs:ram::175708322470****:role/ramtest. 
      "RoleArn": "acs:ram::175708322470****:role/ramtest",
      # Specify the expiration time in seconds for the access credentials. In this example, the expiration time is 3,000 seconds. 
      "DurationSeconds": 3600,
      # Specify a custom role session name, which is used to distinguish different tokens. Example: sessiontest. 
      "RoleSessionName": "sessiontest"
    },
    opts: {
      method: 'POST',
      format_params: true
    }
  )
end

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

$server_ip = "0.0.0.0"
$server_port = 8000

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()
  response = {
    "AccessKeyId" => token["Credentials"]["AccessKeyId"],
    "AccessKeySecret" => token["Credentials"]["AccessKeySecret"],
    "SecurityToken" => token["Credentials"]["SecurityToken"]
  }
  response.to_json
end

get '/*' do
  puts "********************* GET "
  send_file File.join(settings.public_folder, 'index.html')
end
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,
                Endpoint = "sts.cn-hangzhou.aliyuncs.com"
            };
            return new AlibabaCloud.SDK.Sts20150401.Client(config);
        }
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            var app = builder.Build();
            builder.Logging.AddConsole();
            var serviceProvider = builder.Services.BuildServiceProvider();
            var logger = serviceProvider.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 program = new Program(logger);
                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();
                // Replace <YOUR_ROLE_SESSION_NAME> with a custom session name, such as oss-role-session. 
                assumeRoleRequest.RoleSessionName = "<YOUR_ROLE_SESSION_NAME>";
                // Replace <YOUR_ROLE_ARN> with the ARN of the RAM role that has the permissions to upload objects to the specified 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;
        }
    }
}

Client-side sample code

The following sample code provides an example on how to use the temporary access credentials to upload an object to OSS from a web client:

let credentials = null;
const form = document.querySelector("form");
form.addEventListener("submit", async (event) => {
  event.preventDefault();
  // Obtain temporary access credentials again only if they expire. This reduces the number of calls to STS. 
  if (isCredentialsExpired(credentials)) {
    const response = await fetch("/get_sts_token_for_oss_upload", {
      method: "GET",
    });
    credentials = await response.json();
  }
  const client = new OSS({
    // Replace <YOUR_BUCKET> with 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. Example: oss-cn-hangzhou. 
    region: "oss-<YOUR_REGION>",
    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);
});

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

Obtain a signature and POST policy for PostObject from the application server

The following figure shows how to upload an object from a client to OSS by using the signature and POST policy that are obtained from the application server.

image
  1. The client requests information, such as a signature and POST policy, from the application server.

  2. The application server generates and returns information, such as the signature and POST policy, to the client.

  3. The client uses information, such as the signature and POST policy, to call the PostObject operation to upload the object to OSS by using an HTML form.

  4. OSS returns a success response to the client.

Demo project

postsignature.zip

Examples

Server-side sample code

The following sample code provides examples on how the application server generates a signature and POST policy for PostObject:

Note

The sample code can be deployed in Function Compute. oss-upload-post-signature-app

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

# Obtain the AccessKey ID from the OSS_ACCESS_KEY_ID environment variable. 
access_key_id = os.environ.get('OSS_ACCESS_KEY_ID')
# Obtain the AccessKey secret from the OSS_ACCESS_KEY_SECRET environment variable. 
access_key_secret = os.environ.get('OSS_ACCESS_KEY_SECRET')
# Replace <YOUR_BUCKET> with the name of the bucket. 
bucket = '<YOUR_BUCKET>'
# Set host to a value that is in the BucketName.Endpoint format. Replace <YOUR_BUCKET> with the name of the bucket. Replace <YOUR_ENDPOINT> with the endpoint of the region in which the bucket is located, such as oss-cn-hangzhou.aliyuncs.com. 
host = 'https://<YOUR_BUCKET>.<YOUR_ENDPOINT>'
# Specify the prefix in the name of the object that you want to upload to OSS. 
upload_dir = 'user-dir-prefix/'
# Specify the validity period. Unit: seconds. 
expire_time = 3600


def generate_expiration(seconds):
    """
    The expiration time is calculated based on the specified validity period. Unit: seconds. 
    :param seconds: the validity period (seconds). 
    :return: the time string in the ISO 8601 standard. Example: 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 a signature string. 
    :param access_key_secret: the AccessKey secret that has the permissions to access the bucket. 
    :param expiration: the time when the signature expires. Specify the time in the ISO 8601 standard in the yyyy-MM-ddTHH:mm:ssZ format. Example: 2014-12-01T12:00:00.000Z. 
    :param conditions: the policy conditions that can be used to limit the values that you can specify during form upload. 
    :param policy_extra_props: the additional policy parameters. You can pass additional parameters as a dict. 
    :return: 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 = {
        # Specify the validity period of the policy. 
        "expiration": generate_expiration(expire_time),
        # Specify the policy conditions. 
        "conditions": [
            # If success_action_redirect is not specified, HTTP status code 204 is returned after the object is uploaded. 
            ["eq", "$success_action_status", "200"],
            # The value of the form field must start with the specified prefix. For example, if the value of the key form field starts with user/user1, the condition is ["starts-with", "$key", "user/user1"]. 
            ["starts-with", "$key", upload_dir],
            # Specify the minimum and maximum sizes of the object that can be uploaded. Unit: bytes. 
            ["content-length-range", 1, 1000000],
            # Set the type of the object that you want to upload to the specified image type.
            ["in", "$content-type", ["image/jpg", "image/png"]]
        ]
    }
    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
        # Specify additional parameters.
    }
    return json.dumps(response)
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.JSONObject;
import java.util.Date;
import com.aliyun.oss.OSSClientBuilder;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import javax.annotation.PreDestroy;

@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("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.");
        // Assume that the method exists.
        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 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();
        }
        return response.toString();
    }
  }
}

@Configuration
public class OssConfig {
    /**
     * Replace <YOUR-ENDPOINT> with the endpoint of the region in which the bucket is located, such as oss-cn-hangzhou.aliyuncs.com.
     */
    private String endpoint = "<YOUR-ENDPOINT>";
    /**
     * Replace <YOUR-BUCKET> with the name of the bucket.
     */
    private String bucket = "<YOUR-BUCKET>";
    /**
     * Specify the prefix in the name of the object in OSS.
     */
    private String dir = "user-dir-prefix/";
    /**
     * Specify the expiration time. Unit: seconds.
     */
    private long expireTime = 3600;
    /**
     * Create a host.
     */
    private String host = "http://" + bucket + "." + endpoint;
    /**
     * Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable.
     */
    private String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
    /**
     * Obtain the AccessKey secret from 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();
    }
}

package main

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

var (
    // Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable. 
    accessKeyId = os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
    // Obtain the AccessKey secret from the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable. 
    accessKeySecret = os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
    // specify the host. The host is in the BucketName.Endpoint format. Replace ${your-bucket} with the name of the bucket. Replace ${your-endpoint} with the OSS endpoint, such as oss-cn-hangzhou.aliyuncs.com. 
    host = "http://${your-bucket}.${your-endpoint}"
    // Specify the prefix in the name of the object that you want to upload to OSS. 
    uploadDir = "user-dir-prefix/"
    // Specify the expiration time. Unit: 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
function gmt_iso8601($time)
{
    return str_replace('+00:00', '.000Z', gmdate('c', $time));
}

// Obtain access credentials from environment variables. Before you run the 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");
// Specify a value for the $host parameter. The host is in the <YOUR-BUCKET>.<YOUR-ENDPOINT> format. 
$host = 'http://<YOUR-BUCKET>.<YOUR-ENDPOINT>';
// Specify the prefix in the name of the object you want to upload. 
$dir = 'user-dir-prefix/';          

$now = time();
// Specify the expiration time of the policy. If the expiration time elapses, the access is denied. In this example, the expiration time is set to 10 seconds. 
$expire = 30;  
$end = $now + $expire;
$expiration = gmt_iso8601($end);

// Specify the maximum size of the object that can be uploaded. 
$condition = array(0 => 'content-length-range', 1 => 0, 2 => 1048576000);
$conditions[] = $condition;

// Specify the object that can be uploaded. In this example, only an object whose name starts with the value of the $dir parameter can be uploaded. This is an optional setting that prevents an upload to a different directory in the bucket by using the policy. 
$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);
const express = require("express");
const { Buffer } = require("buffer");
const OSS = require("ali-oss");
const app = express();
const path = require("path");
const config = {
  // Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable. 
  accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID,
  // Obtain the AccessKey secret from the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable. 
  accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET,
  // Replace <YOUR-BUCKET> with the name of the bucket. 
  bucket: "<YOUR-BUCKET>",
  // Specify the prefix in the name of the object that you want 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();
  // Specify the validity period of the signature. Unit: seconds. 
  date.setSeconds(date.getSeconds() + 3600);
  const policy = {
    expiration: date.toISOString(),
    conditions: [
      // Specify the size limit on the object that can be uploaded. 
      ["content-length-range", 0, 1048576000],
      // Specify the bucket to which the object can be uploaded. 
      { bucket: client.options.bucket },
    ],
  };
  const formData = await client.calculatePostSignature(policy);
  const host = `http://${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");
});
require 'sinatra'
require 'base64'
require 'open-uri'
require 'cgi'
require 'openssl'
require 'json'
require 'sinatra/reloader'
require 'sinatra/content_for'

# Specify the path of the public-folder folder to the templates subfolder within the current folder.
set :public_folder, File.dirname(__FILE__) + '/templates'

# Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable. 
$access_key_id = ENV['ALIBABA_CLOUD_ACCESS_ID']
# Obtain the AccessKey secret from the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable. 
$access_key_secret = ENV['ALIBABA_CLOUD_ACCESS_SECRET']

# Specify the host. The host is in the <bucketname>.<endpoint> format. 
$host = 'http://<bucketname>.<endpoint>';

# Specify the prefix in the name of the object you want to upload. 
$upload_dir = 'user-dir-prefix/'
# Specify the validity period. Unit: seconds. 
$expire_time = 30
$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_jason(source_hash)
  jason_string = source_hash.to_json;    

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

  jason_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_jason(policy_dict)
  policy_encode = Base64.strict_encode64(policy).chomp;
  h = OpenSSL::HMAC.digest('sha1', $access_key_secret, policy_encode)
  hs = Digest::MD5.hexdigest(h)
  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_jason(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

end

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

$server_ip = "0.0.0.0"
$server_port = 8000

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()
  response = {
    "AccessKeyId" => token["Credentials"]["AccessKeyId"],
    "AccessKeySecret" => token["Credentials"]["AccessKeySecret"],
    "SecurityToken" => token["Credentials"]["SecurityToken"]
  }
  response.to_json
end

get '/*' do
  puts "********************* GET "
  send_file File.join(settings.public_folder, 'index.html')
end
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;
        // Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable. 
        public string AccessKeyId { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID");
        // Obtain the AccessKey secret from the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable. 
        public string AccessKeySecret { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
        // Specify the host. The host is in the BucketName.Endpoint format. Replace <YOUR-BUCKET> with the name of the bucket. Replace <YOUR-ENDPOINT> with the endpoint of the region in which the bucket is located, such as oss-cn-hangzhou.aliyuncs.com. 
        public string Host { get; set; } = "<YOUR-BUCKET>.<YOUR-ENDPOINT>";
        // Specify the prefix in the name of the object that you want to upload to OSS. 
        public string UploadDir { get; set; } = "user-dir-prefix/";
        // Specify the expiration time. Unit: 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);
            var app = builder.Build();

            builder.Logging.AddConsole();
            var logger = builder.Services.BuildServiceProvider().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 provides an example on how to use the POST signature and POST policy to upload an object to OSS from a web client:

const form = document.querySelector('form');
const fileInput = document.querySelector('#file');
form.addEventListener('submit', (event) => {
  event.preventDefault();
  let file = fileInput.files[0];
  let filename = fileInput.files[0].name;
  fetch('/get_post_signature_for_oss_upload', { method: 'GET' })
    .then(response => 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);
      // file must be the last form field. No particular order is required for other form fields. 
      formData.append('file', file);
      fetch(data.host, { method: 'POST', body: formData },).then((res) => {
        console.log(res);
        alert ('Object Uploaded');
      });
    })
    .catch(error => {
      console.log('Error occurred while getting OSS upload parameters:', error);
    });
});

Obtain a signed URL for PutObject from the application server

The following figure shows how to upload an object to OSS from a client by using a signed URL obtained from the application server.

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

  2. The application server uses OSS SDKs to generate a signed URL for PUT requests and then returns the signed URL to the client.

  3. The client uses the signed URL to call the PutObject operation to upload an object to OSS.

  4. OSS returns a success response to the client.

Demo project

presignedurl.zip

Examples

Server-side sample code

The following sample code provides examples on how to obtain a signed URL from the application server:

Note

The sample code can be deployed in Function Compute. oss-upload-presigned-url-app

import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider

# 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())
# Replace <YOUR_ENDPOINT> with 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. 
# Replace <YOUR_BUCKET> with the name of the bucket. 
bucket = oss2.Bucket(auth, '<YOUR_ENDPOINT>', '<YOUR_BUCKET>')
# Specify the validity period. Unit: seconds. 
expire_time = 3600
# Specify the full path of the object. Example: exampledir/exampleobject.png. Do not include the bucket name in the full path. 
object_name = 'exampledir/exampleobject.png'

def generate_presigned_url():
    # Specify headers. 
    headers = dict()
    # Specify the Content-Type header. 
    headers['Content-Type'] = 'image/png'
    # Specify the storage class. 
    # headers["x-oss-storage-class"] = "Standard"
    # By default, OSS identifies the forward slashes (/) in the full path of an object as escape characters when a signed URL is generated. Therefore, you cannot use the signed URL 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. In this case, you can use the generated signed URL. 
    url = bucket.sign_url('PUT', object_name, expire_time, slash_safe=True, headers=headers)
    return url
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSS;
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 com.aliyun.oss.OSSClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;

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

/**
 * Applicationx
 *
 * @author peiyu
 * @date 2023/10/24
 */
@Controller
public class PresignedURLController {

    /**
     * Replace <YOUR-BUCKET> with the name of the bucket. 
     * Specify the prefix in the name of the object that you want to upload to OSS. 
     * Replace <YOUR-OBJECT> with the full path of the object. Example: exampleobject.txt. Do not include the bucket name in the full path. 
     * Specify the expiration time. Unit: 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;
    }
}

@Configuration
public class OssConfig {

    /**
     * Specify the endpoint of the region in which the bucket is located. Example: oss-cn-hangzhou.aliyuncs.com. 
     */
    private static final String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";

    /**
     * Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable.
     */
    @Value("${ALIBABA_CLOUD_ACCESS_KEY_ID}")
    private String accessKeyId;

    /**
     * Obtain the AccessKey secret from the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable.
     */
    @Value("${ALIBABA_CLOUD_ACCESS_KEY_SECRET}")
    private String accessKeySecret;

    private OSS ossClient;


    @Bean
    public OSS getSssClient() {
        ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        return ossClient;
    }

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

import (
	"fmt"
	"net/http"
	"os"

	"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, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. Specify your actual endpoint. 
	endpoint := "https://oss-cn-beijing.aliyuncs.com"
	// 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 of the object. 
	objectName := "exampledir/exampleobject.txt"
	// Obtain access credentials from environment variables. Before you run the sample code, make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables are configured. 
	accessKeyID := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
	accessKeySecret := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")

	client, err := oss.New(endpoint, accessKeyID, accessKeySecret)
	if err != nil {
		fmt.Println("json err:", err)
	}
	bucket, err := client.Bucket(bucketName)
	if err != nil {
		fmt.Println("json err:", err)
	}
	options := []oss.Option{
		oss.ContentType("image/png"),
	}
	signedURL, err := bucket.SignURL(objectName, oss.HTTPPut, 60, options...)
	if err != nil {
		fmt.Println("json err:", 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)
}
<?php
require_once __DIR__ . '/vendor/autoload.php';
use OSS\OssClient;
use OSS\Core\OssException;
use OSS\Http\RequestCore;
use OSS\Http\ResponseCore;
// Before you run the 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");
// 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 = "<YOUR-ENDPOINT>";
// Specify the name of the bucket. 
$bucket= "<YOUR-BUCKET>";
// Specify the full path of the object. Do not include the bucket name in the full path. 
$object = "test.png";
// Set the validity period of the signed URL to 3600. Unit: seconds. 
$timeout = 3600;
try {
    $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false);
    // Generate the signed URL. 
    $signedUrl = $ossClient->signUrl($bucket, $object, $timeout, "PUT", array('Content-Type' => 'image/png'));
    // Print the returned data.
    echo $signedUrl;
} catch (OssException $e) {
    printf($e->getMessage() . "\n");
    return;
}
const express = require("express");
const { Buffer } = require("buffer");
const OSS = require("ali-oss");
const app = express();
const path = require("path");
const fs = require("fs");
const axios = require("axios");

const config = {
  // Specify the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the region to oss-cn-hangzhou. 
  region: '<YOURREGION>',
  // Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable. 
  accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID,
  // Obtain the AccessKey secret from the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable. 
  accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET,
  // Replace <YOUR-BUCKET> with the name of the bucket. 
  bucket: "<YOUR-BUCKET>",
}
const object = "examplefile.png";

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

app.get("/get_presigned_url_for_oss_upload", async (req, res) => {
    const client = new OSS(config);
    const url = client.signatureUrl(object, {
        method: "PUT",
        "Content-Type": "application/x-www-form-urlencoded",
    });
    res.send(url); 
    console.log(url);
  });

app.listen(8000, () => {
  console.log("http://127.0.0.1:8000");
});
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

# Specify the path of the public-folder folder to the templates subfolder within the current folder.
set :public_folder, File.dirname(__FILE__) + '/templates'

# Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable. 
$access_key_id = ENV['ALIBABA_CLOUD_ACCESS_KEY_ID']
# Obtain the AccessKey secret from the ALIBABA_CLOUD_ACCESS_SECRET environment variable. 
$access_key_secret = ENV['ALIBABA_CLOUD_ACCESS_KEY_SECRET']

# Specify the full path of the object. Example: exampledir/exampleobject.png. Do not include the bucket name in the full path. 
object_key = 'exampledir/exampleobject.png'

def get_presigned_url(client, object_key)
  # Replace <YOUR-BUCKET> with the name of the bucket. 
  bucket = client.get_bucket('<YOUR-BUCKET>')
  # Generate a signed URL and set the validity period of the URL to 3600. Unit: seconds. 
  bucket.object_url(object_key, 3600)
end

client = Aliyun::OSS::Client.new(
  # 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: '<YOUR-ENDPOINT>',
  # 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. 
  access_key_id: $access_key_id,
  access_key_secret: $access_key_secret
)


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

$server_ip = "0.0.0.0"
$server_port = 8000

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
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;

        // Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable. 
        public string AccessKeyId { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID");
        // Obtain the AccessKey secret from 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 where 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. 
        private string EndPoint { get; set; } = "<YOUR-ENDPOINT>";
        // Replace <YOUR-BUCKET> with the name of the bucket. 
        private string BucketName { get; set; } = "<YOUR-BUCKET>";
        private string ObjectName { get; set; } = "exampledir/exampleobject2.png";

        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            var app = builder.Build();

            // Specify logging.
            builder.Logging.AddConsole();
            var logger = builder.Services.BuildServiceProvider().GetRequiredService<ILogger<Program>>();

            app.UseStaticFiles(); // Add this line to enable static file middleware

            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 logs.
                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 the ILogger.
        public Program(ILogger<Program> logger)
        {
            _logger = logger;
        }

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

            // Create a request to generate the 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 provides an example on how to use a signed URL to upload an object 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?filename=${file.name}`, { method: "GET" })
    .then((response) => {
        return response.text();
     })
    .then((url) => {
      fetch(url, {
        method: "PUT",
        headers: new Headers({
          'Content-Type': 'image/png',
        }),
        body: file,
       }).then((res) => {
            console.log(res);
            alert ('Object Uploaded');
       });
   });
});

Best practices

For information about best practices for uploading data to OSS from clients, see the following topics: