直接クライアントアップロードを使用すると、クライアントからObject Storage Service (OSS) に直接データをアップロードできます。 ダイレクトクライアントアップロードソリューションは、アプリケーションサーバーとの間でオブジェクトを転送する必要をなくすことにより、アップロードを高速化し、アプリケーションサーバーのリソース使用量を削減します。 このトピックでは、このソリューションの利点、実装方法、およびベストプラクティスについて説明します。
メリット
従来のサーバーおよびクライアントアーキテクチャでは、アプリケーションサーバーからOSSにデータをアップロードするのが一般的なアップロード方法です。 クライアントはオブジェクトをアプリケーションサーバーにアップロードし、アプリケーションサーバーはオブジェクトをOSSにアップロードします。 アップロードプロセス中、データはネットワーク経由で2回転送する必要があります。 これは、ネットワークリソースの浪費及びアプリケーションサーバのリソース使用の増加をもたらす。 この問題を解決するには、オブジェクトをアプリケーションサーバーにアップロードすることなく、オブジェクトをOSSに直接アップロードします。
実装方法
クライアントからOSSに直接データをアップロードするには、次の問題を解決する必要があります。
CORS
クライアントがwebクライアントまたはミニプログラムの場合、クロスオリジンリソース共有 (CORS) を許可する必要があります。 ほとんどの場合、ブラウザとミニプログラムはCORSを禁止してデータセキュリティを確保します。 これにより、クライアントコードを使用してOSSに接続できなくなります。 CORSルールを設定して、特定のドメイン名のwebアプリケーションまたはミニプログラムがOSSにアクセスできるようにすることができます。 詳細については、「CORS」をご参照ください。
セキュリティ権限付与
オブジェクトをOSSにアップロードするには、RAMユーザーのAccessKeyペアを使用して、認証の署名を完了する必要があります。 ただし、クライアントが永続的なAccessKeyペアを使用している場合、AccessKeyペアのリークが発生し、データセキュリティリスクが発生する可能性があります。 この問題を解決するには、次のいずれかのソリューションを使用して安全なアップロードを実装します。
アプリケーションサーバーのSTSから一時的なアクセス資格情報を取得
ほとんどのオブジェクトアップロードのシナリオでは、Security Token Service (STS) SDKを使用してアプリケーションサーバーの一時的なアクセス資格情報を取得し、一時的なアクセス資格情報とOSS SDKを使用してクライアントからOSSに直接オブジェクトをアップロードすることを推奨します。 クライアントは、アプリケーションサーバ上の一時的なアクセス資格情報を再利用して署名を生成することができる。 これは、マルチパートアップロードと再開可能アップロードを使用してラージオブジェクトをアップロードするシナリオに適しています。 ただし、STSへの頻繁な呼び出しにより、スロットリングが発生する場合があります。 この場合、一時的なアクセス資格情報をキャッシュし、有効期限が切れる前に更新することをお勧めします。 クライアントでの一時的なアクセス資格情報の悪用をさらに防ぐために、一時的なアクセス資格情報の使用を制限する追加のポリシーを構成することを推奨します。 詳細については、「STSとは何ですか? 」をご参照ください。
アプリケーションサーバーからPostObjectの署名とPOSTポリシーを取得する
アップロードするオブジェクトの属性を制限するシナリオでは、PostObject操作を呼び出すために必要な情報をアプリケーションサーバーから取得する必要があります。 必要な情報には、署名とPOSTポリシーが含まれます。 その後、クライアントは取得した情報を使用して、OSS SDKを使用せずにオブジェクトをOSSに直接アップロードできます。 アプリケーションサーバーによって生成されたポリシーを使用して、オブジェクトのサイズやタイプなどのオブジェクトの属性を制限できます。 このソリューションは、HTMLフォームを使用してオブジェクトをアップロードするシナリオに適しています。 このソリューションは、マルチパートアップロードと再開可能アップロードをサポートしていません。 詳細は、「PostObject」をご参照ください。
アプリケーションサーバーからPutObjectの署名付きURLを取得
単純なオブジェクトアップロードのシナリオでは、アプリケーションサーバーでOSS SDKを使用して、PutObject操作を呼び出すために必要な署名付きURLを取得できます。 その後、クライアントは署名付きURLを使用して、OSS SDKを使用せずにオブジェクトをアップロードできます。 このソリューションは、マルチパートアップロードと再開可能アップロードには適していません。 アプリケーションサーバは、各部分の署名付きURLを生成し、署名付きURLをクライアントに返す。 これは、アプリケーションサーバとの対話の数およびネットワーク要求の複雑さを増大させる。 さらに、クライアントは、部分の内容または順序を修正することができ、その結果、無効な結合オブジェクトが生じる。 詳細については、「URLにV1署名を含める」をご参照ください。
アプリケーションサーバーのSTSから一時的なアクセス資格情報を取得する
次の図は、アプリケーションサーバーから返されたSTSの一時的なアクセス資格情報を使用して、クライアントからオブジェクトをOSSにアップロードする方法を示しています。
クライアントは、アプリケーションサーバーに一時的なアクセス資格情報を要求します。
アプリケーションサーバーはSTS SDKを使用してAssumeRole操作を呼び出し、一時的なアクセス資格情報を取得します。
STSは一時的なアクセス資格情報を生成してアプリケーションサーバーに返します。
アプリケーションサーバーは、一時的なアクセス資格情報をクライアントに返します。
クライアントは、OSS SDKを使用して、一時的なアクセス資格情報を使用してオブジェクトをOSSにアップロードします。
OSSは成功応答をクライアントに返します。
例
以下の例は、コアコードブロックのみを提供する。 完全なコードサンプルについては、sts.zipパッケージをダウンロードしてください。
サーバー側のサンプルコード
次のサンプルコードは、アプリケーションサーバーがSTSから一時的なアクセス資格情報を取得する方法の例を示しています。
サンプルコードはFunction Computeにデプロイできます。 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
# 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 obtain the AccessKey pair from the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables.
# If you run the program on Elastic Compute Service (ECS) or Elastic Container Instance, you can specify the role of the bound instance by configuring the ALIBABA_CLOUD_ECS_METADATA environment variable. The SDK automatically obtains the temporary access credentials 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 your 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
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)
// Replace <YOUR_ROLE_SESSION_NAME> with your 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 the 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;
}
}
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"
)
/**
* Use your AccessKey ID and AccessKey secret to initialize your 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
<?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 your 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]);
?>
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")));
// 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 the full permissions of the specified RAM role.
// Specify the validity period of the temporary access credentials. In this example, the validity period is set to 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");
});
Ruby
require 'sinatra'
require 'base64'
require 'open-uri'
require 'cgi'
require 'openssl'
require 'json'
require 'sinatra/reloader'
require 'sinatra/content_for'
require 'aliyunsdkcore'
# Set the path of the public-folder 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 RAM role that was obtained in Step 2. Example: acs:ram::175708322470****:role/ramtest.
"RoleArn": "acs:ram::175708322470****:role/ramtest",
# Specify the validity period of the temporary access credentials. In this example, the validity period is set to 3,600 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
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();
// Replace <YOUR_ROLE_SESSION_NAME> with your 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;
}
}
}
クライアント側のサンプルコード
次のサンプルコードは、一時的なアクセス資格情報を使用して、webクライアントからオブジェクトをOSSにアップロードする方法の例を示しています。
let credentials = null;
const form = document.querySelector("form");
form.addEventListener("submit", async (event) => {
event.preventDefault();
// Reobtain temporary access credentials 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",
});
if (!response.ok) {
// Handle HTTP error codes.
throw new Error(
'Failed to obtain STS token: ${response.status} ${response.statusText}'
);
}
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;
}
アプリケーションサーバーからPostObjectの署名とPOSTポリシーを取得する
次の図は、アプリケーションサーバーから取得した署名とPOSTポリシーを使用して、クライアントからOSSにオブジェクトをアップロードする方法を示しています。
クライアントは、署名やPOSTポリシーなどの情報をアプリケーションサーバーに要求します。
アプリケーションサーバーは、署名やPOSTポリシーなどの情報を生成してクライアントに返します。
クライアントは、署名やPOSTポリシーなどの情報を使用してPostObject操作を呼び出し、HTMLフォームを使用してオブジェクトをOSSにアップロードします。
OSSは成功応答をクライアントに返します。
例
以下の例は、コアコードブロックのみを提供する。 完全なコードサンプルについては、postsignature.zipパッケージをダウンロードしてください。
サーバー側のサンプルコード
次のサンプルコードは、アプリケーションサーバーがPostObjectの署名とPOSTポリシーを生成する方法の例を示しています。
サンプルコードはFunction Computeにデプロイできます。 oss-upload-post-signature-app
Python
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.
"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],
# Specify allowable file types.
["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)
Java
import com.aliyun.help.demo.uploading_to_oss_directly_postsignature.config.OssConfig;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.codehaus.jettison.json.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 validity period. 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();
}
}
Go
package main
import (
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
var (
// Obtain the AccessKey ID from the OSS_ACCESS_KEY_ID environment variable.
accessKeyId = os.Getenv("OSS_ACCESS_KEY_ID")
// Obtain the AccessKey secret from the OSS_ACCESS_KEY_SECRET environment variable.
accessKeySecret = os.Getenv("OSS_ACCESS_KEY_SECRET")
// Specify the host. Format: BucketName.Endpoint. 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 validity period. Unit: seconds.
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
var condition []string
condition = append(condition, "starts-with")
condition = append(condition, "$key")
condition = append(condition, uploadDir)
config.Conditions = append(config.Conditions, condition)
result, err := json.Marshal(config)
if err != nil {
fmt.Println("callback json err:", err)
return ""
}
encodedResult := base64.StdEncoding.EncodeToString(result)
h := hmac.New(sha1.New, []byte(accessKeySecret))
io.WriteString(h, encodedResult)
signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
policyToken := PolicyToken{
AccessKeyId: accessKeyId,
Host: host,
Signature: signedStr,
Policy: encodedResult,
Directory: uploadDir,
}
response, err := json.Marshal(policyToken)
if err != nil {
fmt.Println("json err:", err)
return ""
}
return string(response)
}
func handler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
http.ServeFile(w, r, "templates/index.html")
return
} else if r.URL.Path == "/get_post_signature_for_oss_upload" {
policyToken := getPolicyToken()
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(policyToken))
return
}
http.NotFound(w, r)
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
PHP
<?php
function gmt_iso8601($time)
{
return str_replace('+00:00', '.000Z', gmdate('c', $time));
}
// Obtain 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 host. Format: <YOUR-BUCKET>.<YOUR-ENDPOINT>.
$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, 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 the object from being uploaded 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);
Node.js
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");
});
Ruby
require 'sinatra'
require 'base64'
require 'open-uri'
require 'cgi'
require 'openssl'
require 'json'
require 'sinatra/reloader'
require 'sinatra/content_for'
# Set the path of the public-folder 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
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;
// 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. Format: BucketName.Endpoint. 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 validity period. 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)));
}
}
}
}
クライアント側のサンプルコード
次のサンプルコードは、署名とPOSTポリシーを使用して、webクライアントからOSSにオブジェクトをアップロードする方法の例を示しています。
const form = document.querySelector("form");
const fileInput = document.querySelector("#file");
form.addEventListener("submit", (event) => {
event.preventDefault();
const file = fileInput.files[0];
const filename = fileInput.files[0].name;
fetch("/get_post_signature_for_oss_upload", { method: "GET" })
.then((response) => {
if (!response.ok) {
throw new Error("Failed to obtain signature");
}
return response.json();
})
.then((data) => {
const formData = new FormData();
formData.append("name", filename);
formData.append("policy", data.policy);
formData.append("OSSAccessKeyId", data.ossAccessKeyId);
formData.append("success_action_status", "200");
formData.append("signature", data.signature);
formData.append("key", data.dir + filename);
formData.append("file", file);
return fetch(data.host, { method: "POST", body: formData });
})
.then((response) => {
if (response.ok) {
console.log("Uploaded");
alert("Object uploaded");
} else {
console.log("Upload failed", response);
alert("Failed to upload the object. Try to re-upload the object");
}
})
.catch((error) => {
console.error("An error occurred:", error);
});
});
アプリケーションサーバーからPutObjectの署名付きURLを取得する
次の図は、アプリケーションサーバーから取得した署名付きURLを使用して、クライアントからオブジェクトをOSSにアップロードする方法を示しています。
クライアントは、アプリケーションサーバーに署名付きURLを要求します。
アプリケーションサーバーは、OSS SDKを使用してPUTリクエストの署名付きURLを生成し、署名付きURLをクライアントに返します。
クライアントは署名付きURLを使用してPutObject操作を呼び出し、オブジェクトをOSSにアップロードします。
OSSは成功応答をクライアントに返します。
例
以下の例は、コアコードブロックのみを提供する。 完全なコードサンプルについては、presignedurl.zipパッケージをダウンロードしてください。
サーバー側のサンプルコード
次のサンプルコードは、アプリケーションサーバーから署名付きURLを取得する方法の例を示しています。
サンプルコードはFunction Computeにデプロイできます。 oss-upload-presigned-url-app
Python
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 to upload the object.
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 {
/**
* Replace <your-endpoint> with the endpoint of the region in which the bucket is located, such as 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();
}
}
@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;
}
}
Go
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.
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
<?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 3,600 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'));
// Display the returned data.
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 = {
// 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");
});
Ruby
require 'sinatra'
require 'base64'
require 'open-uri'
require 'cgi'
require 'openssl'
require 'json'
require 'sinatra/reloader'
require 'sinatra/content_for'
require 'aliyun/oss'
include Aliyun::OSS
# Set the path of the public-folder 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
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;
// 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);
// Display 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}"); // Display 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();
}
}
}
クライアント側のサンプルコード
次のサンプルコードは、署名付きURLを使用してwebクライアントからOSSにオブジェクトをアップロードする方法の例を示しています。
const form = document.querySelector("form");
form.addEventListener("submit", (event) => {
event.preventDefault();
const fileInput = document.querySelector("#file");
const file = fileInput.files[0];
fetch("/get_presigned_url_for_oss_upload", { method: "GET" })
.then((response) => {
if (!response.ok) {
throw new Error("Failed to obtain the signed 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("Failed to upload the object to OSS");
}
console.log(response);
alert("Object uploaded");
});
})
.catch((error) => {
console.error("An error occurred:", error);
alert(error.message);
});
});
ベストプラクティス
クライアントからOSSにデータをアップロードするためのベストプラクティスについては、以下のトピックを参照してください。