すべてのプロダクト
Search
ドキュメントセンター

Object Storage Service:クライアントから OSS にファイルを直接アップロードする

最終更新日:May 28, 2026

クライアントからの直接アップロードを利用すると、クライアントから Object Storage Service (OSS) にファイルを直接アップロードできます。アプリケーションサーバーをバイパスすることで、アップロード速度を向上させ、サーバーリソースを節約します。

クライアントからの直接アップロードを使用する理由

一般的なサーバークライアントアーキテクチャでは、クライアントはアプリケーションサーバーにファイルをアップロードし、アプリケーションサーバーがそれを OSS に転送します。この方法ではデータがネットワーク上を 2 回転送されるため、不要な帯域幅を消費し、サーバーのオーバーヘッドが増加します。クライアントからの直接アップロードは、クライアントを直接 OSS に接続することで、この問題を解消します。

クライアントからの直接アップロードの実装方法

クライアントからの直接アップロードを実装するには、2 つの課題に対処する必要があります:

クロスオリジンアクセス

Web アプリケーションやミニプログラムは、ブラウザやミニプログラムコンテナによるクロスオリジンの制限を受けるため、OSS への直接接続ができません。指定されたドメインを許可するには、バケットで クロスオリジン設定 を構成します。

安全な権限付与

OSS へのアップロードには、Resource Access Management (RAM) ユーザーの AccessKey ペアによる署名認証が必要ですが、クライアントに長期間有効な認証情報を保存すると、セキュリティリスクが生じます。以下のいずれかのソリューションを使用してください:

  • サーバーで STS の一時的なアクセス認証情報を生成する

    ほとんどのシナリオで推奨されます。サーバー上で STS SDK を使用して一時的なアクセス認証情報を取得し、それをクライアントに渡して OSS SDK で直接アップロードします。マルチパートアップロードと再開可能なアップロードをサポートしています。STS の速度制限を避けるため、認証情報をキャッシュし、有効期限が切れる前にリフレッシュしてください。アクセスポリシーを追加して、認証情報の権限をさらに制限します。

  • サーバーで PostObject に必要な署名と Post ポリシーを生成する

    ファイル制限のある HTML フォームでのアップロードに最適です。サーバーで Post 署名と Post ポリシーを生成します。クライアントは OSS SDK を使用せずに直接アップロードします。Post ポリシーを使用して、ファイルサイズ、種類、その他の属性を制限します。マルチパートアップロードや再開可能なアップロードはサポートしていません。詳細については、「PostObject」をご参照ください。

  • サーバーで PutObject に必要な署名付き URL を生成する

    単純な単一ファイルのアップロードに最適です。サーバーで OSS SDK を使用して署名付き URL を生成します。クライアントは PutObject を介して直接アップロードします。マルチパートアップロードや再開可能なアップロードには適していません。パートごとに URL を生成するとサーバーのラウンドトリップが増加し、クライアントがパートの内容や順序を変更してマージエラーを引き起こす可能性があります。詳細については、「署名 V1」をご参照ください。

サーバーで STS の一時的なアクセス認証情報を生成する

STS の一時的なアクセス認証情報を使用したアップロードフロー:

  1. クライアントがアプリケーションサーバーに一時的なアクセス認証情報をリクエストします。

  2. アプリケーションサーバーが STS SDK を使用して AssumeRole 操作を呼び出し、一時的なアクセス認証情報を取得します。

  3. STS がアプリケーションサーバーに一時的なアクセス認証情報を返します。

  4. アプリケーションサーバーがクライアントに一時的なアクセス認証情報を返します。

  5. クライアントが OSS SDK と一時的なアクセス認証情報を使用して、ファイルを OSS にアップロードします。

  6. OSS がクライアントに成功の応答を返します。

サンプルコード

以下に主要なコードスニペットを示します。完全なコードは sts.zip をご参照ください。

サーバーサイドのサンプルコード

サーバーで一時的なアクセス認証情報を生成します:

説明

Function Compute (FC) へのワンクリックデプロイをサポートしています。oss-upload-sts-app

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

# <YOUR_ROLE_ARN> を、指定された OSS バケットにファイルをアップロードする権限を持つ RAM ロールの ARN に置き換えます。
role_arn_for_oss_upload = '<YOUR_ROLE_ARN>'
# <YOUR_REGION_ID> を STS サービスのリージョン (例: cn-hangzhou) に設定します。
region_id = '<YOUR_REGION_ID>'

def get_sts_token():
    # パラメーターを指定せずに CredentialClient を初期化すると、デフォルトの認証情報チェーンが使用されます。
    # プログラムをローカルで実行する場合、ALIBABA_CLOUD_ACCESS_KEY_ID と ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数を使用して AccessKey ペアを指定できます。
    # プログラムを ECS インスタンス、ECI インスタンス、または Container Service で実行する場合、ALIBABA_CLOUD_ECS_METADATA 環境変数を使用してアタッチされたインスタンス RAM ロールを指定できます。SDK は自動的に 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,
        # <YOUR_ROLE_SESSION_NAME> をカスタムセッション名 (例: 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

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 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)
            // <YOUR_ROLE_SESSION_NAME> をカスタムセッション名 (例: my-website-server) に設定します。
            .setRoleSessionName("<YOUR_ROLE_SESSION_NAME>")
            // <YOUR_ROLE_ARN> を、指定された OSS バケットにファイルをアップロードする権限を持つ RAM ロールの ARN に置き換えます。ロールの ARN は RAM ロールの詳細から取得できます。
            .setRoleArn("<YOUR_ROLE_ARN>");
        RuntimeOptions runtime = new RuntimeOptions();
        try {
            AssumeRoleResponse response = stsClient.assumeRoleWithOptions(assumeRoleRequest, runtime);
            return response.body.credentials;
        } catch (TeaException error) {
            // 必要に応じてエラーを出力します。
            assertAsString(error.message);
            return null;
        } catch (Exception error) {
            assertAsString(error.getMessage());
            return null;
        }
    }
}

@Configuration
public class StsClientConfiguration {

    @Bean
    public Client stsClient() {
        // パラメーターを渡さずに認証情報クライアントを初期化すると、Credentials ツールはデフォルトの認証情報チェーンを使用してクライアントを初期化します。
        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;
        }
    }
}

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"
)

/**
 * AccessKey ペアでクライアントを初期化します。
 * @param accessKeyId
 * @param accessKeySecret
 * @return Client
 * @throws Exception
 */
func CreateClient(accessKeyId *string, accessKeySecret *string) (*sts20150401.Client, error) {
    config := &openapi.Config{
    // 必須。ご利用の AccessKey ID。
    AccessKeyId: accessKeyId,
    // 必須。ご利用の AccessKey Secret。
    AccessKeySecret: accessKeySecret,
    }
    // エンドポイントの詳細については、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

<?php
require_once 'vendor/autoload.php';
use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
use AlibabaCloud\Sts\Sts;
// Alibaba Cloud クライアントを初期化します。
AlibabaCloud::accessKeyClient(getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'), getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'))
    ->regionId('cn-hangzhou')
    ->asDefaultClient();
// STS リクエストを作成します。
$request = Sts::v20150401()->assumeRole();
// STS リクエストを開始し、結果を取得します。
// <YOUR_ROLE_SESSION_NAME> をカスタムセッション名 (例: oss-role-session) に設定します。
// <YOUR_ROLE_ARN> を、指定された OSS バケットにファイルをアップロードする権限を持つ RAM ロールの ARN に置き換えます。
$result = $request
    ->withRoleSessionName("<YOUR_ROLE_SESSION_NAME>")
    ->withDurationSeconds(3600)
    ->withRoleArn("<YOUR_ROLE_ARN>")
    ->request();
// STS リクエストの結果から認証情報を取得します。
$credentials = $result->get('Credentials');
// 返す JSON データを構築します。
$response = [
    'AccessKeyId' => $credentials['AccessKeyId'],
    'AccessKeySecret' => $credentials['AccessKeySecret'],
    'SecurityToken' => $credentials['SecurityToken'],
];
// レスポンスヘッダーを application/json に設定します。
header('Content-Type: application/json');
// 結果を JSON 形式に変換して出力します。
echo json_encode(['Credentials' => $response]);
?>

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")));
// ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数を設定します。
const accessKeyId = process.env.ALIBABA_CLOUD_ACCESS_KEY_ID;
// ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数を設定します。
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
 });
   // roleArn をステップ 2 で取得したロールの ARN (例: acs:ram::175708322470****:role/ramtest) に設定します。
   // policy をカスタムアクセスポリシーに設定して、STS 一時アクセス認証情報の権限をさらに制限します。ポリシーを指定しない場合、返される STS 一時アクセス認証情報は、デフォルトで指定されたロールのすべての権限を持ちます。
   // 3000 は有効期限 (秒) です。
   // sessionName を使用して、異なるトークンを区別するためのカスタムロールセッション名 (例: 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");
});

Ruby

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

# パブリックフォルダーのパスを現在のディレクトリの templates フォルダーに設定します。
set :public_folder, File.dirname(__FILE__) + '/templates'

def get_sts_token_for_oss_upload()
  client = RPCClient.new(
    # ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数を設定します。
    access_key_id: ENV['ALIBABA_CLOUD_ACCESS_KEY_ID'],
    # ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数を設定します。
    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: {
      # RoleArn をステップ 2 で取得したロールの ARN (例: acs:ram::175708322470****:role/ramtest) に設定します。
      "RoleArn": "acs:ram::175708322470****:role/ramtest",
      # 3600 は有効期限 (秒) です。
      "DurationSeconds": 3600,
      # RoleSessionName を使用して、異なるトークンを区別するためのカスタムロールセッション名 (例: 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

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,
                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();
                // <YOUR_ROLE_SESSION_NAME> をカスタムセッション名 (例: oss-role-session) に設定します。
                assumeRoleRequest.RoleSessionName = "<YOUR_ROLE_SESSION_NAME>";
                // <YOUR_ROLE_ARN> を、指定された OSS バケットにファイルをアップロードする権限を持つ RAM ロールの ARN に置き換えます。
                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;
        }
    }
}

クライアントサイドのサンプルコード

Web クライアントが一時的なアクセス認証情報を使用してファイルをアップロードします:

let credentials = null;
const form = document.querySelector("form");
form.addEventListener("submit", async (event) => {
  event.preventDefault();
  // STS サービスへの呼び出しを減らすため、現在の認証情報が期限切れの場合にのみ再度取得します。
  if (isCredentialsExpired(credentials)) {
    const response = await fetch("/get_sts_token_for_oss_upload", {
      method: "GET",
    });
    if (!response.ok) {
      // エラーの HTTP ステータスコードを処理します。
      throw new Error(
        `Failed to obtain STS token: ${response.status} ${response.statusText}`
      );
    }
    credentials = await response.json();
  }
  const client = new OSS({
    // <YOUR_BUCKET> をご利用の OSS バケット名に設定します。
    bucket: "<YOUR_BUCKET>",
    // <YOUR_REGION> を OSS バケットが配置されているリージョンに設定します (例: region: '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);
});

/**
 * 一時的な認証情報が期限切れかどうかを確認します。
 **/
function isCredentialsExpired(credentials) {
  if (!credentials) {
    return true;
  }
  const expireDate = new Date(credentials.Expiration);
  const now = new Date();
  // 有効期間が 1 分未満の場合、認証情報は期限切れと見なされます。
  return expireDate.getTime() - now.getTime() <= 60000;
}

サーバーで PostObject に必要な署名と Post ポリシーを生成する

Post 署名と Post ポリシーを使用したアップロードフロー:

  1. クライアントがアプリケーションサーバーに Post 署名、Post ポリシー、およびその他の情報をリクエストします。

  2. アプリケーションサーバーが Post 署名、Post ポリシー、および関連情報を生成し、クライアントに返します。

  3. クライアントが Post 署名と Post ポリシーを使用して PostObject を呼び出し、HTML フォームを介してファイルを OSS にアップロードします。

  4. OSS がクライアントに成功の応答を返します。

サンプルコード

以下に主要なコードスニペットを示します。完全なコードは postsignature.zip をご参照ください。

サーバーサイドのサンプルコード

サーバーで Post 署名と Post ポリシーを生成します:

説明

Function Compute (FC) へのワンクリックデプロイをサポートしています。oss-upload-post-signature-app

Python

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

# OSS_ACCESS_KEY_ID 環境変数を設定します。
access_key_id = os.environ.get('OSS_ACCESS_KEY_ID')
# OSS_ACCESS_KEY_SECRET 環境変数を設定します。
access_key_secret = os.environ.get('OSS_ACCESS_KEY_SECRET')
# <YOUR_BUCKET> をご利用のバケット名に置き換えます。
bucket = '<YOUR_BUCKET>'
# ホストの形式は bucketname.endpoint です。<YOUR_BUCKET> をご利用のバケット名に、<YOUR_ENDPOINT> を OSS エンドポイント (例: oss-cn-hangzhou.aliyuncs.com) に置き換えます。
host = 'https://<YOUR_BUCKET>.<YOUR_ENDPOINT>'
# OSS にアップロードするファイルのプレフィックスを指定します。
upload_dir = 'user-dir-prefix/'
# 有効期限を秒単位で指定します。
expire_time = 3600


def generate_expiration(seconds):
    """
    有効期間を秒単位で指定して、有効期限を生成します。
    :param seconds: 有効期間 (秒)。
    :return: ISO 8601 形式の時間文字列 (例: "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):
    """
    署名文字列を生成します。
    :param access_key_secret: 送信先バケットへのアクセス権限を持つアカウントの AccessKey Secret。
    :param expiration: 署名の有効期限。ISO 8601 形式、UTC。例: "2014-12-01T12:00:00.000Z"。
    :param conditions: フォームのアップロード時に許可される値を制限するために使用されるポリシー条件。
    :param policy_extra_props: 追加のポリシーパラメーター。ポリシーに新しいパラメーターが追加された場合、辞書として渡すことができます。
    :return: signature, 署名文字列。
    """
    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 = {
        # 有効期間。
        "expiration": generate_expiration(expire_time),
        # 制約。
        "conditions": [
            # success_action_redirect が指定されていない場合に、アップロード成功後に返すステータスコード。デフォルト値は 204 です。
            ["eq", "$success_action_status", "200"],
            # フォームフィールドの値は、指定されたプレフィックスで始まる必要があります。例えば、key の値が user/user1 で始まることを指定するには、["starts-with", "$key", "user/user1"] と記述します。
            ["starts-with", "$key", upload_dir],
            # アップロードされるオブジェクトの最小および最大許容サイズをバイト単位で制限します。
            ["content-length-range", 1, 1000000],
            # アップロードするファイルを指定された画像タイプに制限します。
            ["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
        # ここに他のパラメーターを追加できます。
    }
    return json.dumps(response)

Java

package com.aliyun.sample;

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;

@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.");
            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());
        } catch (Exception e) {
            System.out.println("Caught an unexpected exception: " + e.getMessage());
        }
        return response.toString();
    }
}

Go

package main

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

var (
	// OSS_ACCESS_KEY_ID 環境変数を設定します。
	accessKeyId = os.Getenv("OSS_ACCESS_KEY_ID")
	// OSS_ACCESS_KEY_SECRET 環境変数を設定します。
	accessKeySecret = os.Getenv("OSS_ACCESS_KEY_SECRET")
	// ホストの形式は bucketname.endpoint です。${your-bucket} をご利用のバケット名に、${your-endpoint} を OSS エンドポイント (例: oss-cn-hangzhou.aliyuncs.com) に置き換えます。
	host = "http://${your-bucket}.${your-endpoint}"
	// OSS にアップロードするファイルのプレフィックスを指定します。
	uploadDir = "user-dir-prefix/"
	// 有効期限を秒単位で指定します。
	expireTime = int64(3600)
)

type ConfigStruct struct {
	Expiration string          `json:"expiration"`
	Conditions [][]interface{} `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

	// ファイルプレフィックスの制限を追加します。
	config.Conditions = append(config.Conditions, []interface{}{"starts-with", "$key", uploadDir})

	// ファイルサイズの制限を追加します (例: 1 KB から 10 MB)。
	minSize := int64(1024)
	maxSize := int64(10 * 1024 * 1024)
	config.Conditions = append(config.Conditions, []interface{}{"content-length-range", minSize, maxSize})

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

// 環境変数からアクセス認証情報を取得します。このサンプルコードを実行する前に、ALIBABA_CLOUD_ACCESS_KEY_ID と ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。
$accessKeyId = getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
$accessKeySecret = getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// $host の形式は '<YOUR-BUCKET>.<YOUR-ENDPOINT>' です。変数を実際の情報に置き換えてください。
$host = 'http://<YOUR-BUCKET>.<YOUR-ENDPOINT>';
// ユーザーがファイルをアップロードする際に指定したプレフィックス。
$dir = 'user-dir-prefix/';          

$now = time();
// ポリシーの有効期限を 10 秒に設定します。この期間を過ぎると、ポリシーは無効になります。
$expire = 30;  
$end = $now + $expire;
$expiration = gmt_iso8601($end);

// 最大ファイルサイズ。自分で設定できます。
$condition = array(0 => 'content-length-range', 1 => 0, 2 => 1048576000);
$conditions[] = $condition;

// アップロードされたデータは $dir で始まる必要があります。そうでない場合、アップロードは失敗します。このステップは必須ではありませんが、ユーザーがポリシーを通じて他のディレクトリにアップロードするのを防ぐため、セキュリティ上推奨されます。
$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);

Node.js

const express = require("express");
const { Buffer } = require("buffer");
const OSS = require("ali-oss");
const app = express();
const path = require("path");
const config = {
  // ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数を設定します。
  accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID,
  // ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数を設定します。
  accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET,
  // <YOUR-BUCKET> をご利用のバケット名に置き換えます。
  bucket: "<YOUR-BUCKET>",
  // 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();
  // 署名の有効期間を秒単位で設定します。
  date.setSeconds(date.getSeconds() + 3600);
  const policy = {
    expiration: date.toISOString(),
    conditions: [
      // アップロードするファイルのサイズ制限を設定します。
      ["content-length-range", 0, 1048576000],
      // ファイルをアップロードできるバケットを制限します。
      { 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");
});

Ruby

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

# パブリックフォルダーのパスを現在のディレクトリの templates フォルダーに設定します。
set :public_folder, File.dirname(__FILE__) + '/templates'

# ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数を設定します。
$access_key_id = ENV['ALIBABA_CLOUD_ACCESS_ID']
# ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数を設定します。
$access_key_secret = ENV['ALIBABA_CLOUD_ACCESS_SECRET']

# $host の形式は <bucketname>.<endpoint> です。<bucketname> と <endpoint> を実際の情報に置き換えてください。
$host = 'http://<bucketname>.<endpoint>';

# ユーザーがアップロードしたファイルのプレフィックス。
$upload_dir = 'user-dir-prefix/'
# 有効期限 (秒)。
$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

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;
        // ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数を設定します。
        public string AccessKeyId { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID");
        // ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数を設定します。
        public string AccessKeySecret { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
        // ホストの形式は bucketname.endpoint です。<YOUR-BUCKET> をご利用のバケット名に、<YOUR-ENDPOINT> を OSS エンドポイント (例: oss-cn-hangzhou.aliyuncs.com) に置き換えます。
        public string Host { get; set; } = "<YOUR-BUCKET>.<YOUR-ENDPOINT>";
        // OSS にアップロードするファイルのプレフィックスを指定します。
        public string UploadDir { get; set; } = "user-dir-prefix/";
        // 有効期限を秒単位で指定します。
        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)));
            }
        }
    }
}

クライアントサイドのサンプルコード

Web クライアントが Post 署名と Post ポリシーを使用してファイルをアップロードします:

const form = document.querySelector("form");
const fileInput = document.querySelector("#file");
form.addEventListener("submit", (event) => {
  event.preventDefault();
  const file = fileInput.files[0];
  if (!fileInput.files[0]) {
    alert('アップロードするファイルを選択してください。');
    return;
  }
  const filename = fileInput.files[0].name;
  fetch("/get_post_signature_for_oss_upload", { method: "GET" })
    .then((response) => {
      if (!response.ok) {
        throw new Error("署名の取得に失敗しました。");
      }
      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("アップロード成功");
        alert("ファイルがアップロードされました。");
      } else {
        console.log("アップロード失敗", response);
        alert("アップロードに失敗しました。後でもう一度お試しください。");
      }
    })
    .catch((error) => {
      console.error("エラーが発生しました:", error);
    });
});

サーバーで PutObject に必要な署名付き URL を生成する

署名付き URL を使用したアップロードフロー:

  1. クライアントがアプリケーションサーバーに署名付き URL をリクエストします。

  2. アプリケーションサーバーが OSS SDK を使用して PUT 署名付き URL を生成し、クライアントに返します。

  3. クライアントが署名付き URL を使用して PutObject を呼び出し、ファイルを OSS にアップロードします。

  4. OSS がクライアントに成功の応答を返します。

サンプルコード

以下に主要なコードスニペットを示します。完全なコードは presignedurl.zip をご参照ください。

サーバーサイドのサンプルコード

サーバーで署名付き URL を生成します:

説明

Function Compute (FC) へのワンクリックデプロイをサポートしています。oss-upload-presigned-url-app

Python

import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider

# 環境変数からアクセス認証情報を取得します。このサンプルコードを実行する前に、OSS_ACCESS_KEY_ID と OSS_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。
auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider())
# <YOUR_ENDPOINT> をバケットのエンドポイントに置き換えます。
# 例えば、バケットが中国 (杭州) リージョンにある場合、
# エンドポイントを https://oss-cn-hangzhou.aliyuncs.com に設定します。
# <YOUR_BUCKET> をバケット名に置き換えます。
bucket = oss2.Bucket(auth, '<YOUR_ENDPOINT>', '<YOUR_BUCKET>')
# 有効期限を秒単位で指定します。
expire_time = 3600
# オブジェクトの完全なパスを指定します (例: exampledir/exampleobject.png)。完全なパスにバケット名を含めることはできません。
object_name = 'exampledir/exampleobject.png'

def generate_presigned_url():
    # ヘッダーを指定します。
    headers = dict()
    # Content-Type を指定します。
    headers['Content-Type'] = 'image/png'
    # ストレージクラスを指定します。
    # headers["x-oss-storage-class"] = "Standard"
    # 署名付き URL を生成する際、OSS はデフォルトでオブジェクトの完全なパスに含まれるスラッシュ (/) をエスケープします。これにより、生成された署名付き URL が使用できなくなります。
    # slash_safe を True に設定します。OSS はオブジェクトの完全なパスに含まれるスラッシュ (/) をエスケープしなくなります。生成された署名付き URL は直接使用できます。
    url = bucket.sign_url('PUT', object_name, expire_time, slash_safe=True, headers=headers)
    return url

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 {

    /**
     * <your-endpoint> を OSS エンドポイント (例: oss-cn-hangzhou.aliyuncs.com) に置き換えます。
     */
    private static final String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";

    /**
     * ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数を使用して accessKeyId を設定します。
     */
    @Value("${ALIBABA_CLOUD_ACCESS_KEY_ID}")
    private String accessKeyId;

    /**
     * ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数を使用して accessKeySecret を設定します。
     */
    @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();
    }
}

@Controller
public class PresignedURLController {

    /**
     * <your-bucket> をバケット名に置き換えます。
     * OSS にアップロードするファイルのプレフィックスを指定します。
     * <your-object> をオブジェクトの完全なパス (例: exampleobject.txt) に置き換えます。完全なパスにバケット名を含めることはできません。
     * 有効期限をミリ秒単位で指定します。
     */
    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;
    }
}

Go

package main

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

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

func getURL() string {
	// yourEndpoint をバケットのエンドポイントに設定します。例えば、バケットが中国 (杭州) リージョンにある場合、エンドポイントを https://oss-cn-hangzhou.aliyuncs.com に設定します。他のリージョンの場合は、それに応じてエンドポイントを設定します。
	endpoint := "https://oss-cn-beijing.aliyuncs.com"
	// バケット名を指定します (例: examplebucket)。
	bucketName := "examplebucket"
	// ファイルの完全なパスを指定します (例: exampledir/exampleobject.txt)。完全なパスにバケット名を含めることはできません。
	objectName := "exampledir/exampleobject.txt"
	// 環境変数からアクセス認証情報を取得します。このサンプルコードを実行する前に、ALIBABA_CLOUD_ACCESS_KEY_ID と ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。
	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

<?php
require_once __DIR__ . '/vendor/autoload.php';
use OSS\OssClient;
use OSS\Core\OssException;
use OSS\Http\RequestCore;
use OSS\Http\ResponseCore;
// このサンプルコードを実行する前に、ALIBABA_CLOUD_ACCESS_KEY_ID と ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。
$accessKeyId = getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
$accessKeySecret = getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// yourEndpoint をバケットが配置されているリージョンのエンドポイントに設定します。例えば、バケットが中国 (杭州) リージョンにある場合、エンドポイントを https://oss-cn-hangzhou.aliyuncs.com に設定します。
$endpoint = "<YOUR-ENDPOINT>";
// バケット名を指定します。
$bucket= "<YOUR-BUCKET>";
// バケット名を含まないオブジェクトの完全なパスを指定します。
$object = "test.png";
// 署名付き URL の有効期間を 3600 秒に設定します。
$timeout = 3600;
try {
    $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false);
    // 署名付き URL を生成します。
    $signedUrl = $ossClient->signUrl($bucket, $object, $timeout, "PUT", array('Content-Type' => 'image/png'));
    // 返されたデータを出力します。
    echo $signedUrl;
} catch (OssException $e) {
    printf($e->getMessage() . "\n");
    return;
}

Node.js

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 = {
  // <YOURREGION> をバケットが配置されているリージョンに設定します。例えば、バケットが中国 (杭州) リージョンにある場合、リージョンを oss-cn-hangzhou に設定します。
  region: '<YOURREGION>',
  // ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数を設定します。
  accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID,
  // ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数を設定します。
  accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET,
  // <YOUR-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");
});

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

# パブリックフォルダーのパスを現在のディレクトリの templates フォルダーに設定します。
set :public_folder, File.dirname(__FILE__) + '/templates'

# ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数を設定します。
$access_key_id = ENV['ALIBABA_CLOUD_ACCESS_KEY_ID']
# ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数を設定します。
$access_key_secret = ENV['ALIBABA_CLOUD_ACCESS_KEY_SECRET']

# オブジェクトの完全なパスを指定します (例: exampledir/exampleobject.png)。完全なパスにバケット名を含めることはできません。
object_key = 'exampledir/exampleobject.png'

def get_presigned_url(client, object_key)
  # <YOUR-BUCKET> をバケット名に置き換えます。
  bucket = client.get_bucket('<YOUR-BUCKET>')
  # 署名付き URL を生成し、有効期間を 1 時間 (3600 秒) に指定します。
  bucket.object_url(object_key, 3600)
end

client = Aliyun::OSS::Client.new(
  # <YOUR-ENDPOINT> をバケットが配置されているリージョンのエンドポイントに置き換えます。例えば、バケットが中国 (杭州) リージョンにある場合、エンドポイントを https://oss-cn-hangzhou.aliyuncs.com に設定します。
  endpoint: '<YOUR-ENDPOINT>',
  # 環境変数からアクセス認証情報を取得します。このサンプルコードを実行する前に、OSS_ACCESS_KEY_ID と OSS_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。
  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

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;

        // ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数を設定します。
        public string AccessKeyId { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID");
        // ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数を設定します。
        public string AccessKeySecret { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
        // <YOUR-ENDPOINT> をバケットが配置されているリージョンのエンドポイントに置き換えます。例えば、バケットが中国 (杭州) リージョンにある場合、エンドポイントを https://oss-cn-hangzhou.aliyuncs.com に設定します。
        private string EndPoint { get; set; } = "<YOUR-ENDPOINT>";
        // <YOUR-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();

            // ログ記録を追加します。
            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_presigned_url_for_oss_upload", async (context) =>
            {
                var program = new Program(logger);
                var signedUrl = program.GetSignedUrl();

                logger.LogInformation($"SignedUrl: {signedUrl}"); // トークンの値を出力します。
                await context.Response.WriteAsync(signedUrl);
            });

            app.Run();
        }

        // ILogger インジェクション用のコンストラクタ。
        public Program(ILogger<Program> logger)
        {
            _logger = logger;
        }

        private string GetSignedUrl()
        {
            // OSSClient インスタンスを作成します。
            var ossClient = new OssClient(EndPoint, AccessKeyId, AccessKeySecret);

            // 署名付き 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();
        }
    }
}

クライアントサイドのサンプルコード

Web クライアントが署名付き URL を使用してファイルをアップロードします:

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("署名付き URL の取得に失敗しました。");
      }
      return response.text();
    })
    .then((url) => {
      fetch(url, {
        method: "PUT",
        headers: new Headers({
          "Content-Type": "image/png",
        }),
        body: file,
      }).then((response) => {
        if (!response.ok) {
          throw new Error("OSS へのファイルのアップロードに失敗しました。");
        }
        console.log(response);
        alert("ファイルがアップロードされました。");
      });
    })
    .catch((error) => {
      console.error("エラーが発生しました:", error);
      alert(error.message);
    });
});

実践ガイド

クライアントタイプ別のアップロード実践ガイド: