對外共用大量資料時,可開啟儲存空間(Bucket)的要求者付費模式,將訪問資料產生的流量、請求等費用轉由資料要求者承擔,Bucket擁有者僅支付儲存費用等固定成本。為Bucket開啟要求者付費模式後,匿名訪問將被禁用,所有請求需經過身分識別驗證。
適用範圍
僅有地區屬性的Bucket支援開啟要求者付費模式。
工作原理
OSS 按以下邏輯處理請求:
請求中攜帶
x-oss-request-payer頭,OSS 對要求者進行身分識別驗證,驗證通過後,流量和請求等費用由要求者承擔。請求中不攜帶
x-oss-request-payer頭:要求者為 Bucket 擁有者:請求正常處理,所有費用由Bucket擁有者承擔。
要求者非 Bucket 擁有者:請求被拒絕。
Bucket 擁有者配置要求者付費
步驟一:開啟要求者付費
登入OSS管理主控台。
單擊Bucket 列表,然後單擊目標Bucket名稱。
在左側導覽列,選擇。
在要求者付費頁面,開啟請求者付費開關。
在彈出的對話方塊,單擊確定。
步驟二:為要求者授予存取權限
通過 Bucket Policy 為要求者授權訪問,否則要求者將無法訪問資料。
在Bucket列表,單擊目標Bucket名稱。
在左側導覽列,選擇。
在Bucket 授權策略頁面的按圖形策略添加頁簽,單擊新增授權。
在新增授權面板,填寫授權策略。其中,授權用戶選擇其他帳號,填寫要求者的帳號ID或RAM角色ARN(格式為
arn:sts::{RoleOwnerUid}:assumed-role/{RoleName}/{RoleSessionName})。單擊確定。
要求者發起付費請求
作為要求者,訪問已開啟要求者付費模式的Bucket時,必須在請求中聲明將承擔本次請求產生的費用。
控制台
登入OSS管理主控台。
在左側導覽列,單擊我收藏的路徑右側的加號(+)。
在添加收藏路徑對話方塊,按以下說明配置各項參數。
參數
說明
添加方式
選中從其他已授權bucket添加,將授權訪問的Bucket添加到收藏路徑。
地區
下拉選擇授權訪問的Bucket所在地區。
Bucket
添加授權訪問的Bucket名稱。
請求者付費
選中我已知曉並同意後,即聲明付費意願。可以正常訪問檔案路徑下的指定資源,訪問該Bucket產生的流量、請求次數等費用將由您支付。
SDK
以下代碼以PutObject、GetObject和DeleteObject為例,用於指定第三方付費訪問Object。其他用於指定第三方付費的Object讀寫操作介面設定方法類似。
第三方操作Object時需在HTTP Header中攜帶x-oss-request-payer:requester參數,否則會報錯。
Java
import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.*;
import java.io.ByteArrayInputStream;
public class Demo {
public static void main(String[] args) throws Exception{
// Endpoint以華東1(杭州)為例,其它Region請按實際情況填寫。關於其他Region對應的Endpoint資訊,請參見訪問網域名稱和資料中心。
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// 填寫Bucket名稱,例如examplebucket。
String bucketName = "examplebucket";
// 填寫Object完整路徑,例如exampledir/exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
String objectName = "exampledir/exampleobject.txt";
Payer payer = Payer.Requester;
// 填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為cn-hangzhou。
String region = "cn-hangzhou";
// 建立OSSClient執行個體。
// 當OSSClient執行個體不再使用時,調用shutdown方法以釋放資源。
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
// PutObject介面指定付費者。
String content = "hello";
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new ByteArrayInputStream(content.getBytes()));
putObjectRequest.setRequestPayer(payer);
ossClient.putObject(putObjectRequest);
// GetObject介面指定付費者。
GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, objectName);
getObjectRequest.setRequestPayer(payer);
OSSObject ossObject = ossClient.getObject(getObjectRequest);
ossObject.close();
// DeleteObject介面指定付費者。
GenericRequest genericRequest = new GenericRequest(bucketName, objectName);
genericRequest.setRequestPayer(payer);
ossClient.deleteObject(genericRequest);
} 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("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 (Throwable 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 {
// 關閉OSSClient。
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}Python
# -*- coding: utf-8 -*-
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider
from oss2.headers import OSS_REQUEST_PAYER
# 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
# 填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
# 填寫Endpoint對應的Region資訊,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數
region = "cn-hangzhou"
# yourBucketName填寫儲存空間名稱。
bucket = oss2.Bucket(auth, endpoint, "yourBucketName", region=region)
# 填寫Object完整路徑,完整路徑中不能包含Bucket名稱,例如exampledir/exampleobject.txt。
object_name = 'exampledir/exampleobject.txt'
headers = dict()
headers[OSS_REQUEST_PAYER] = "requester"
# 上傳檔案時指定header。
result = bucket.put_object(object_name, 'test-content', headers=headers)
# 下載檔案時指定header。
result = bucket.get_object(object_name, headers=headers)
# 刪除檔案時指定header。
result = bucket.delete_object(object_name, headers=headers);Go
package main
import (
"fmt"
"io"
"os"
"strings"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)
func main() {
// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
provider, err := oss.NewEnvironmentVariableCredentialsProvider()
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// 建立OSSClient執行個體。
// yourEndpoint填寫Bucket對應的Endpoint,以華東1(杭州)為例,填寫為https://oss-cn-hangzhou.aliyuncs.com。其它Region請按實際情況填寫。
// yourRegion填寫Bucket所在地區,以華東1(杭州)為例,填寫為cn-hangzhou。其它Region請按實際情況填寫。
clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
clientOptions = append(clientOptions, oss.Region("yourRegion"))
// 設定簽名版本
clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
payerClient, err := oss.New("yourEndpoint", "", "", clientOptions...)
if err != nil {
fmt.Println("New Error:", err)
os.Exit(-1)
}
// 填寫Bucket名稱。
payerBucket, err := payerClient.Bucket("examplebucket")
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// 當Bucket擁有者開啟要求者付費模式後,外部存取者必須設定oss.RequestPayer(oss.Requester)參數才可以訪問授權的內容。
// 當Bucket擁有者沒有開啟付費模式時,外部存取者可以不用攜帶oss.RequestPayer(oss.Requester)參數即可訪問授權的內容。
// 上傳Object。
// 填寫Object完整路徑,完整路徑中不能包含Bucket名稱,例如exampledir/exampleobject.txt。
key := "exampledir/exampleobject.txt"
err = payerBucket.PutObject(key, strings.NewReader("objectValue"), oss.RequestPayer("requester"))
if err != nil {
fmt.Println("put Error:", err)
os.Exit(-1)
}
// 列舉Bucket下的所有Object。
lor, err := payerBucket.ListObjects(oss.RequestPayer(oss.Requester))
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// 列印Object名稱列表。
for _, l := range lor.Objects {
fmt.Println("the Key name is :", l.Key)
}
// 下載Object。
body, err := payerBucket.GetObject(key, oss.RequestPayer(oss.Requester))
if err != nil {
fmt.Println("Get Error:", err)
os.Exit(-1)
}
// 資料讀取完成後,擷取的流必須關閉,否則會造成串連泄漏,導致請求無串連可用,程式無法正常工作。
defer body.Close()
// 讀取並列印擷取的內容。
data, err := io.ReadAll(body)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
fmt.Println("data:", string(data))
// 刪除Object。
err = payerBucket.DeleteObject(key, oss.RequestPayer(oss.Requester))
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
}
Node.js
const OSS = require('ali-oss');
const bucket = 'bucket-name';
const payer = 'Requester';
const client = new OSS({
// yourregion填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為oss-cn-hangzhou。
region: 'yourregion',
// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
accessKeyId: process.env.OSS_ACCESS_KEY_ID,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
authorizationV4: true,
// yourBucketName填寫Bucket名稱。
bucket: 'yourBucketName',
});
async function main() {
await put();
await get();
await del();
}
async function put() {
const result = await client.putBucketRequestPayment(bucket, payer);
console.log('putBucketRequestPayment:', result);
// PutObject介面指定付費者。
const response = await client.put('fileName', path.normalize('D:\\localpath\\examplefile.txt'), {
headers: {
'x-oss-request-payer': 'requester'
}
});
console.log('put:', response);
}
async function get() {
const result = await client.putBucketRequestPayment(bucket, payer);
console.log('putBucketRequestPayment:', result);
// GetObject介面指定付費者。
const response = await client.get('fileName', {
headers: {
'x-oss-request-payer': 'requester'
}
});
console.log('get:', response);
}
async function del() {
const result = await client.putBucketRequestPayment(bucket, payer);
console.log('putBucketRequestPayment:', result);
// DeleteObject介面指定付費者。
const response = await client.delete('fileName', {
headers: {
'x-oss-request-payer': 'requester'
}
});
console.log('delete:', response);
}
main();
C#
using System;
using System.IO;
using System.Text;
using Aliyun.OSS;
using Aliyun.OSS.Common;
namespace Samples
{
public class Program
{
public static void Main(string[] args)
{
// yourEndpoint填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
var endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
var accessKeyId = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_ID");
var accessKeySecret = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_SECRET");
// 填寫Bucket名稱,例如examplebucket。
var bucketName = "examplebucket";
var objectName = "example.txt";
var objectContent = "More than just cloud.";
// 填寫Bucket所在地區對應的Region。以華東1(杭州)為例,Region填寫為cn-hangzhou。
const string region = "cn-hangzhou";
// 建立ClientConfiguration執行個體,按照您的需要修改預設參數。
var conf = new ClientConfiguration();
// 設定v4簽名。
conf.SignatureVersion = SignatureVersion.V4;
// 建立OssClient執行個體。
var client = new OssClient(endpoint, accessKeyId, accessKeySecret, conf);
try
{
byte[] binaryData = Encoding.ASCII.GetBytes(objectContent);
MemoryStream requestContent = new MemoryStream(binaryData);
// PutObject介面指定付費者。
var putRequest = new PutObjectRequest(bucketName, objectName, requestContent);
putRequest.RequestPayer = RequestPayer.Requester;
var result = client.PutObject(putRequest);
// GetObject介面指定付費者。
var getRequest = new GetObjectRequest(bucketName, objectName);
getRequest.RequestPayer = RequestPayer.Requester;
var getResult = client.GetObject(getRequest);
// DeleteObject介面指定付費者。
var delRequest = new DeleteObjectRequest(bucketName, objectName);
delRequest.RequestPayer = RequestPayer.Requester;
client.DeleteObject(delRequest);
}
catch (OssException ex)
{
Console.WriteLine("Failed with error code: {0}; Error info: {1}. \nRequestID:{2}\tHostID:{3}",
ex.ErrorCode, ex.Message, ex.RequestId, ex.HostId);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}
}
}
}PHP
<?php
if (is_file(__DIR__ . '/../autoload.php')) {
require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
}
use OSS\Credentials\EnvironmentVariableCredentialsProvider;
use OSS\OssClient;
use OSS\Core\OssException;
// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
$provider = new EnvironmentVariableCredentialsProvider();
// Endpoint以杭州為例,其它Region請按實際情況填寫。
$endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
// 填寫Bucket名稱,例如examplebucket。
$bucket= "examplebucket";
// 填寫Object的完整路徑,完整路徑中不能包含Bucket名稱,例如exampledir/exampleobject.txt。
$object = "exampledir/exampleobject.txt";
// 指定為要求者付費模式。
$options = array(
OssClient::OSS_HEADERS => array(
OssClient::OSS_REQUEST_PAYER => 'requester',
));
try {
$config = array(
"provider" => $provider,
"endpoint" => $endpoint,
"signatureVersion" => OssClient::OSS_SIGNATURE_VERSION_V4,
"region"=> "cn-hangzhou"
);
$ossClient = new OssClient($config);
// PutObject介面指定付費者。
$content = "hello";
$ossClient->putObject($bucket, $object, $content, $options);
// GetObject介面指定付費者。
$ossClient->getObject($bucket, $object, $options);
// DeleteObject介面指定付費者。
$ossClient->deleteObject($bucket, $object, $options);
} catch (OssException $e) {
printf(__FUNCTION__ . ": FAILED\n");
printf($e->getMessage() . "\n");
return;
}
print(__FUNCTION__ . ": OK" . "\n"); C++
#include <alibabacloud/oss/OssClient.h>
#include <fstream>
using namespace AlibabaCloud::OSS;
int main(void)
{
/*初始化OSS帳號資訊。*/
/*yourEndpoint填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。*/
std::string Endpoint = "yourEndpoint";
/ *yourRegion填寫Bucket所在地區對應的Region。以華東1(杭州)為例,Region填寫為cn - hangzhou。 * /
std::string Region = "yourRegion";
/*填寫要求者訪問的Bucket名稱,例如examplebucket。*/
std::string BucketName = "examplebucket";
/*填寫要求者訪問的Object的完整路徑,完整路徑中不能包含Bucket名稱,例如exampledir/exampleobject.txt。*/
std::string ObjectName = "exampleobject.txt";
/* 初始化網路等資源。*/
InitializeSdk();
ClientConfiguration conf;
conf.signatureVersion = SignatureVersionType::V4;
/* 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已通過環境變數佈建要求者的OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
OssClient client(Endpoint, credentialsProvider, conf);
client.SetRegion(Region);
/* 上傳檔案時佈建要求者付費模式。*/
std::shared_ptr<std::iostream> content = std::make_shared<std::stringstream>();
*content << "test cpp sdk";
PutObjectRequest putrequest(BucketName, ObjectName, content);
putrequest.setRequestPayer(RequestPayer::Requester);
auto putoutcome = client.PutObject(putrequest);
/* 下載檔案到本地記憶體時佈建要求者付費模式。*/
GetObjectRequest getrequest(BucketName, ObjectName);
getrequest.setRequestPayer(RequestPayer::Requester);
auto getoutcome = client.GetObject(getrequest);
/* 刪除檔案時佈建要求者付費模式。*/
DeleteObjectRequest delrequest(BucketName, ObjectName);
delrequest.setRequestPayer(RequestPayer::Requester);
auto deloutcome = client.DeleteObject(delrequest);
/* 釋放網路等資源。*/
ShutdownSdk();
return 0;
}ossutil
使用命令列工具ossutil前,請先安裝ossutil。
以使用 cp 命令下載對象為例,請指定 --request-payer=requester參數
ossutil cp oss://examplebucket/examplefile.txt /localpath --request-payer=requesterAPI
可直接發起REST API請求,需在要求標頭中加入 x-oss-request-payer: requester,並確保此要求標頭包含在簽名計算中,簽名的計算方法見在Header中包含簽名。
GET /oss.jpg HTTP/1.1
Host: oss-example.oss-cn-hangzhou.aliyuncs.com
Date: Fri, 24 Feb 2012 06:38:30 GMT
Authorization: OSS4-HMAC-SHA256 Credential=LTAI********************/20250417/cn-hangzhou/oss/aliyun_v4_request,Signature=a7c3554c729d71929e0b84489addee6b2e8d5cb48595adfc51868c299c0c218e應用於生產環境
RAM 角色訪問的計費歸屬:當要求者通過扮演阿里雲RAM角色來訪問資料時,該角色所屬的賬戶將為此請求付費。
錯誤做法:讓要求者扮演Bucket 擁有者帳號下的 RAM 角色來擷取訪問資料的許可權。此情境下,所有請求是以 Bucket 擁有者的身份執行,產生的請求和流量費用仍將由 Bucket 擁有者支付,無法實現成本轉移。
正確做法:通過Bucket Policy 直接為要求者授予訪問資料的許可權。
預簽名 URL 陷阱:
錯誤做法:由 Bucket 擁有者使用身份憑證(AccessKey 或 STS 臨時憑證)產生預簽名 URL 並對外分享,此時請求是以Bucket 擁有者身份發起,相關費用由Bucket 擁有者承擔。
正確做法:由請求方使用身份憑證(AccessKey 或 STS 臨時憑證)來產生預簽名 URL,並在產生時包含
x-oss-request-payer=requester參數,簽名的計算方法見在URL中包含簽名。請求方將此 URL 對外分享使用時,費用由請求方承擔。
相容性風險:開啟要求者付費會影響靜態網站託管依賴的匿名訪問機制,導致網站無法正常工作,建議將網站前端資源(HTML/CSS/JS)與需要要求者付費的資料分別部署在不同的Bucket中。
計費說明
Bucket開啟要求者付費前,所有費用均由Bucket擁有者支付。開啟要求者付費後,以下付費項由要求者的帳號支付,其餘計費項目還是由Bucket擁有者的帳號支付,完整計費項目參見OSS產品定價。
費用 | 計費項目 |
外網流出流量 | |
CDN回源流出流量 | |
Put類型請求 | |
Get類型請求 | |
低頻訪問資料取回容量 | |
Archive Storage資料取回容量 |