Object Storage Service (OSS) バケットにアップロードできるファイルのタイプとサイズを制限しない場合、ユーザーが大きなオブジェクトや悪意のあるオブジェクトをアップロードする可能性があります。これにより、予期しないストレージ使用量や帯域幅の消費が発生し、場合によってはバケットドメインがブロックされることもあります。OSS には、アップロード時にファイルタイプとサイズを直接制限するための専用機能はありません。ただし、サーバーで署名を生成する際、またはクライアントサイドで制限ロジックを記述することで、これらの制限を指定できます。
サーバーサイドでの Post 署名と PostPolicy の生成
アップロードされるファイルの属性を制限する必要があるシナリオでは、サーバーで Post 署名、PostPolicy、および PostObject に必要なその他の情報を生成できます。クライアントは、OSS SDK に依存することなく、この情報を使用して特定の制限下でファイルを直接アップロードできます。サーバーで生成された PostPolicy を使用して、ファイルサイズやファイルタイプを制限するなど、クライアントがアップロードするファイルを制限できます。このソリューションは、HTML フォームを介したファイルのアップロードに適しています。このソリューションは、大きなファイルのマルチパートアップロードや再開可能なアップロードをサポートしていないことにご注意ください。詳細については、「PostObject」をご参照ください。
サンプルコード
サーバーサイドのサンプルコード
以下のサンプルコードは、サーバーで Post 署名、Post Policy、およびその他の情報を生成する方法を示しています。
このコードはワンクリックデプロイをサポートしています。ワンクリックで 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>'
# host のフォーマットは 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")
// host のフォーマットは 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'
# public フォルダのパスを現在のディレクトリの 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");
// host のフォーマットは 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 Policy、およびその他の情報を使用してファイルを OSS にアップロードする方法を示しています。
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);
});
});サーバーサイドでの署名付き URL の生成
content-length-range フィールドは URL 署名ではサポートされていません。したがって、このメソッドを使用してアップロードするファイルのサイズを制限することはできません。アップロードを特定のファイルタイプに制限したい場合は、サーバーが署名付き URL を生成するように設定する際に content-type ヘッダーを指定できます。クライアントが署名付き URL を使用してファイルをアップロードする場合、ファイルは指定されたフォーマットのいずれかである必要があります。詳細については、「URL への署名の追加」をご参照ください。
サンプルコード
サーバーサイドのサンプルコード
以下のサンプルコードは、サーバーで署名付き 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
# public フォルダのパスを現在のディレクトリの 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 を使用してファイルを 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("署名付き 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);
});
});クライアントサイドでのインターセプト
クライアントの JavaScript コードを使用して、アップロードするファイルのプロパティを制限できます。条件文を使用して、ファイルがサイズとタイプの要件を満たしているかどうかを確認します。ファイルが要件を満たしていない場合は、アップロードを中止し、警告またはエラーメッセージを表示します。クライアントサイドの制限は、Security Token Service (STS) からの一時的なアクセス認証情報を使用するアップロードには適用されないことにご注意ください。一時的なアクセス認証情報が漏洩した場合、悪意のあるユーザーはクライアントサイドの制限をバイパスして、悪意のあるファイルを OSS バケットにアップロードする可能性があります。
サンプルコード
クライアントサイドのサンプルコード
以下のサンプルコードは、<input type="file"> 要素の files 属性と accept 属性を使用して、ファイルサイズとタイプが要件を満たしているかどうかを確認する方法を示しています。
<!DOCTYPE html>
<html>
<head>
<title>File Upload</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
<input type="file" id="file-upload" accept="image/jpeg, image/png, application/pdf" />
<button onclick="uploadFile()">Upload</button>
<script>
function uploadFile() {
const fileInput = document.getElementById('file-upload');
const file = fileInput.files[0];
// ファイルサイズの制限 (バイト単位)。
const maxFileSize = 1024 * 1024; // 1 MB
// 許可されるファイルタイプ。
const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
if (file) {
// ファイルサイズを確認します。
if (file.size > maxFileSize) {
alert('ファイルサイズが制限を超えています。1 MB 未満のファイルをアップロードしてください。');
return;
}
// ファイルタイプを確認します。
if (!allowedTypes.includes(file.type)) {
alert('サポートされていないファイルタイプです。JPEG、PNG、または PDF ファイルをアップロードしてください。');
return;
}
// ファイルは検証に合格し、アップロードできます。
// ここに具体的なアップロードロジックを記述できます。
console.log('ファイルのアップロードを開始:', file.name);
// アップロードコード。
} else {
alert('アップロードするファイルを選択してください。');
}
}
</script>
</body>
</html>