更新时间:2026-10-09 GMT+08:00
分享

授权第三方上传

使用场景

默认情况下,桶中对象的读写权限为私有,仅桶拥有者可以上传对象。在某些场景下,需要让第三方用户向桶中上传对象,为解决第三方用户没有上传权限的问题,可以授予给第三方用户临时访问OBS服务的权限,在授权有效期内实现对象上传。例如,部分企业拥有自己的用户管理系统,用户管理系统中的用户包括终端APP用户、企业本地用户等,这部分用户并不具有IAM用户的权限,通过授予其临时访问密钥来访问OBS。

授权方式有以下两种:

  • 通过临时访问密钥授权上传
  • 通过临时URL授权上传

注意事项

授权方式

说明

临时访问密钥授权

临时访问密钥的有效时间为15min至24h。

在获取临时访问密钥时,通过传入policy参数设置临时权限来进一步约束使用者的权限范围,方便IAM用户进一步管理使用者的权限。

临时URL授权

临时URL授权需要桶的拥有者生成临时URL,每调整一次URL有效期,都需要重新获取鉴权信息以生成新的临时链接。

临时URL授权上传,需要授予obs:PutObject权限。需要先创建具有“obs:PutObject”权限的自定义策略,然后给IAM用户授予自定义策略权限。

通过临时访问密钥授权上传

OBS可以通过IAM获取临时访问密钥(临时AK,SK和securitytoken)进行临时授权访问,为第三方应用或IAM用户颁发一个自定义时效和权限的访问凭证。

临时AK/SK和securitytoken遵循权限最小化原则,使用临时AK/SK调用API鉴权时,临时AK/SK和securitytoken必须同时使用,请求头中需要添加“x-obs-security-token”字段。

详情参见通过临时访问密钥访问OBS。

操作流程

通过临时访问密钥授权其他用户上传文件的流程如下:

图1 操作流程

为了遵循权限最小化的原则,需要通过创建IAM用户并授权、获取临时访问密钥时设置临时策略来约束临时访问密钥的权限。

  • 如果不设置临时策略,则获取的临时访问密钥具有与IAM用户相同的权限。
  • 如果设置了临时策略,则获取的临时访问密钥的权限在IAM用户原有权限的基础上,进一步约束在设置的临时策略以内。

操作示例

假设您希望IAM用户(用户名:APPServer)可以访问桶hi-company的APPClient文件夹,并希望申请到两个不同的临时访问密钥分发给终端APP:APP-1和APP-2,其中APP-1仅能向APPClient/APP-1中上传文件,APP-2仅能向APPClient/APP-2中上传文件。

  1. 创建自定义策略

    创建自定义策略,“操作”选择对象的所有权限,指定资源范围为桶hi-company的APPClient文件夹。操作详情请参见创建自定义策略。策略内容如下:

    {
        "Version": "1.1",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "obs:object:*"
                ],
                "Resource": [
                    "OBS:*:*:object:hi-company/APPClient/*"
                ]
            }
        ]
    }
  2. 创建IAM用户并授权

    创建IAM用户APPServer并给该用户授予1创建的自定义策略权限。操作详情请参见创建IAM用户和给IAM用户授权。

  3. IAM用户获取临时访问密钥

    IAM用户APPServer为终端APP-1和APP-2获取临时访问密钥(临时AK/SK和securitytoken),需针对不同终端设置临时策略,用于约束临时访问密钥的权限。操作详情请参见获取临时AK/SK和securitytoken。

    为终端APP-1获取临时访问密钥,使其仅能向APPClient/APP-1中上传文件,请求示例如下:

    {
        "auth": {
    	"identity": {
    	    "policy": {
    		"Version": "1.1",
    		"Statement": [
    		    {
    			"Action": [
    			    "obs:object:PutObject"
    			],
    			"Resource": [
    			    "obs:*:*:object:hi-company/APPClient/APP-1/*"
    			],
    			"Effect": "Allow"
    		    }
    		]
    	    },
    	    "token": {
    		"duration-seconds": 900
    		
    	    },
    	    "methods": [
    		"token"
    	    ]
    	}
        }
    }

    为终端APP-2获取临时访问密钥,使其仅能向APPClient/APP-2中上传文件,请求示例如下:

    {
        "auth": {
    	"identity": {
    	    "policy": {
    		"Version": "1.1",
    		"Statement": [
    		    {
    			"Action": [
    			    "obs:object:PutObject"
    			],
    			"Resource": [
    			    "obs:*:*:object:hi-company/APPClient/APP-2/*"
    			],
    			"Effect": "Allow"
    		    }
    		]
    	    },
    	    "token": {
    		"duration-seconds": 900
    		
    	    },
    	    "methods": [
    		"token"
    	    ]
    	}
        }
    }
  4. 第三方用户使用临时访问密钥访问OBS资源

    终端APP-1和APP-2获取对应的临时访问密钥后,使用OBS API或SDK来访问OBS,APP-1只能向APPClient/APP-1中上传文件,APP-2只能向APPClient/APP-2中上传文件。

通过临时URL授权上传

OBS客户端支持通过访问密钥、请求方法类型、请求参数等信息生成一个在Query参数中携带鉴权信息的URL,可将该URL提供给其他用户进行临时访问。在生成URL时,您需要指定URL的有效期来限制访客用户的访问时长。

临时URL允许第三方用户在没有安全凭证或者授权的情况下进行上传操作。第三方用户使用临时URL上传对象后,OBS将在指定的Bucket生成该对象。

详情参见通过临时URL访问OBS。

操作流程

通过临时URL授权其他用户上传文件的流程如下:

图2 操作流程

使用临时URL上传单个文件

以下介绍通过OBS SDK使用PUT方法的临时URL上传单个文件。

URL的有效期如下:

  • 使用Harmony SDK生成的临时URL的有效期为0~(231-1)秒。
  • 使用其他SDK生成的临时URL的有效期如下:
    • 如果您使用临时密钥调用SDK接口,则expires取值范围为0~86400秒(24小时)。
    • 如果您使用永久密钥调用SDK接口,则expires取值范围为0~630720000秒(约20年)。

临时授权访问,利用HttpMethodEnum.PUT请求上传对象到examplebucket桶下的objectname对象中,URL有效期设置为3600秒。

关于更多详细内容,请参见通过临时URL访问OBS(Java SDK)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.HttpMethodEnum;
import com.obs.services.model.TemporarySignatureRequest;
import com.obs.services.model.TemporarySignatureResponse;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import java.util.HashMap;
import java.util.Map;
public class PutObject001 {
    public static void main(String[] args) {
        // 您可以通过环境变量获取访问密钥AK/SK,也可以使用其他外部引入方式传入。如果使用硬编码可能会存在泄露风险。
        // 您可以登录访问管理控制台获取访问密钥AK/SK
        String ak = System.getenv("ACCESS_KEY_ID");
        String sk = System.getenv("SECRET_ACCESS_KEY_ID");
        // 【可选】如果使用临时AK/SK和SecurityToken访问OBS,同样建议您尽量避免使用硬编码,以降低信息泄露风险。
        // 您可以通过环境变量获取访问密钥AK/SK/SecurityToken,也可以使用其他外部引入方式传入。使用临时访问密钥调用接口可将下面一行的注释取消。
        // String securityToken = System.getenv("SECURITY_TOKEN");
        // endpoint填写桶所在的endpoint, 此处以华北-北京四为例,其他地区请按实际情况填写。
        String endPoint = "https://obs.cn-north-4.myhuaweicloud.com";
        // 您可以通过环境变量获取endPoint,也可以使用其他外部引入方式传入。
        //String endPoint = System.getenv("ENDPOINT");
        
        // 创建ObsClient实例
        // 使用永久AK/SK初始化客户端
        ObsClient obsClient = new ObsClient(ak, sk, endPoint);
        // 使用临时AK/SK和SecurityToken初始化客户端
        // ObsClient obsClient = new ObsClient(ak, sk, securityToken, endPoint);

        try {
            // URL有效期,3600秒
            long expireSeconds = 3600L;
            Map<String, String> headers = new HashMap<>();
            String contentType = "text/plain";
            headers.put("Content-Type", contentType);
            TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.PUT, expireSeconds);
            request.setBucketName("examplebucket");
            request.setObjectKey("objectname");
            request.setHeaders(headers);
            TemporarySignatureResponse response = obsClient.createTemporarySignature(request);
            System.out.println("Creating object using temporary signature url:");
            System.out.println("\t" + response.getSignedUrl());
            Request.Builder builder = new Request.Builder();
            for (Map.Entry<String, String> entry : response.getActualSignedRequestHeaders().entrySet()) {
                builder.header(entry.getKey(), entry.getValue());
            }
            // 使用PUT请求上传对象
            Request httpRequest =
                    builder.url(response.getSignedUrl())
                            .put(RequestBody.create(MediaType.parse(contentType), "Hello OBS".getBytes("UTF-8")))
                            .build();
            OkHttpClient httpClient =
                    new OkHttpClient.Builder()
                            .followRedirects(false)
                            .retryOnConnectionFailure(false)
                            .cache(null)
                            .build();
            Call c = httpClient.newCall(httpRequest);
            Response res = c.execute();
            System.out.println("Status:" + res.code());
            if (res.body() != null) {
                System.out.println("Content:" + res.body().string() + "\n");
            }
            res.close();
            System.out.println("PutObject successfully");
        } catch (ObsException e) {
            System.out.println("PutObject failed");
            // 请求失败,打印http状态码
            System.out.println("HTTP Code:" + e.getResponseCode());
            // 请求失败,打印服务端错误码
            System.out.println("Error Code:" + e.getErrorCode());
            // 请求失败,打印详细错误信息
            System.out.println("Error Message:" + e.getErrorMessage());
            // 请求失败,打印请求id
            System.out.println("Request ID:" + e.getErrorRequestId());
            System.out.println("Host ID:" + e.getErrorHostId());
            e.printStackTrace();
        } catch (Exception e) {
            System.out.println("PutObject failed");
            // 其他异常信息打印
            e.printStackTrace();
        }
    }
}

以下为生成上传对象的临时URL示例。关于更多详细内容,请参见生成带授权信息的URL(Python SDK)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import os
import traceback
import base64
from obs import ObsClient

# 推荐通过环境变量获取AK/SK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险
# 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
ak = os.getenv('AccessKeyID')
sk = os.getenv('SecretAccessKey')
# 如需使用临时AK/SK和SecurityToken访问OBS,则同样推荐通过环境变量获取
# security_token = os.getenv('SecurityToken')
# server填写Bucket对应的Endpoint,这里以华北-北京四为例,其他地区请按实际情况填写
server = 'https://obs.cn-north-4.myhuaweicloud.com'
# 创建obsClient实例
# 如果使用临时AK/SK和SecurityToken访问OBS,需要在创建实例时通过security_token参数指定securityToken值
obs_client = ObsClient(access_key_id=ak, secret_access_key=sk, server=server)
try:
    # 生成上传对象的带授权信息的URL
    res2 = obs_client.createSignedUrl(method='PUT', bucketName='bucketname', objectKey='objectkey', expires=3600,
                                     headers = {'Content-Type': 'text/plain'})
    print('signedUrl:', res2['signedUrl'])
    print('actualSignedRequestHeaders:', res2['actualSignedRequestHeaders'])
except Exception:
    print(traceback.format_exc())

以下为生成上传对象的临时URL示例。关于更多详细内容,请参见生成带授权信息的URL(Go SDK)。

package main
import (
    "fmt"
    "net/http"
    obs "github.com/huaweicloud/huaweicloud-sdk-go-obs/obs"
    "os"
    "strings"
)
func main() {
    // 推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险。
    // 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html。
    ak := os.Getenv("AccessKeyID")
    sk := os.Getenv("SecretAccessKey")
    // 【可选】如果使用临时AK/SK和SecurityToken访问OBS,同样建议您尽量避免使用硬编码,以降低信息泄露风险。您可以通过环境变量获取访问密钥AK/SK,也可以使用其他外部引入方式传入。
    // securityToken := os.Getenv("SecurityToken")
    // endpoint填写Bucket对应的Endpoint,这里以华北-北京四为例,其他地区请按实际情况填写。
    endPoint := "https://obs.cn-north-4.myhuaweicloud.com"
    // 创建obsClient实例
    // 如果使用临时AKSK和SecurityToken访问OBS,需要在创建实例时通过obs.WithSecurityToken方法指定securityToken值。
    obsClient, err := obs.New(ak, sk, endPoint /*, obs.WithSecurityToken(securityToken)*/)
    if err != nil {
        fmt.Printf("Create obsClient error, errMsg: %s", err.Error())
    }
    putObjectInput := &obs.CreateSignedUrlInput{}
    putObjectInput.Method = obs.HttpMethodPut
    putObjectInput.Bucket = "examplebucket"
    putObjectInput.Key = "example/objectname"
    putObjectInput.Expires = 3600
    // 生成上传对象的带授权信息的URL
    putObjectOutput, err := obsClient.CreateSignedUrl(putObjectInput)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("SignedUrl:%s\n", putObjectOutput.SignedUrl)
    fmt.Printf("ActualSignedRequestHeaders:%v\n", putObjectOutput.ActualSignedRequestHeaders)
    // 调用授权URL
    payload := strings.NewReader("hello OBS!")
    req, err := http.NewRequest("PUT", putObjectOutput.SignedUrl, payload)
    req.Header = putObjectOutput.ActualSignedRequestHeaders
    if err != nil {
        fmt.Printf("Create request error, errMsg: %s", err.Error())
        return
    }
    response, err := http.DefaultClient.Do(req)
    if err == nil {
        fmt.Printf("Use signed-url successful!\n")
        fmt.Printf("Status:%s,Etag:%s\n", response.Status, response.Header.Get("Etag"))
        return
    }
    fmt.Printf("Use signed-url successful!\n")
    fmt.Println("An Exception was found, which means the client encountered an internal problem when attempting to communicate with OBS, for example, the client was unable to access the network.")
    fmt.Println(err)
}

以下为生成上传对象的临时URL示例。关于更多详细内容,请参见使用临时URL进行授权访问(BrowserJS SDK)。

// 创建ObsClient实例
var obsClient = new ObsClient({
    // 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
    // 前端本身没有process对象,可以使用webpack类打包工具定义环境变量,就可以在代码中运行了。
    // 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
    access_key_id: process.env.AccessKeyID,
    secret_access_key: process.env.SecretAccessKey,
    // 这里以华北-北京四为例,其他地区请按实际情况填写
    server: 'https://obs.cn-north-4.myhuaweicloud.com'
});
// 使用PUT请求上传对象
var bucketName = 'bucketname';
var objectKey = 'objectname';
var method = 'PUT';
var headers = {
       'Content-Type' : 'text/plain'
}
var res = obsClient.createSignedUrlSync({
       Method : method,
       Bucket : bucketName,
       Key : objectKey,
       Expires : 3600,
       Headers : headers
});
var content = 'Hello OBS';

var reopt = {
       method : method,
       url : res.SignedUrl,
       withCredentials: false, 
       headers : res.ActualSignedRequestHeaders || {},
       validateStatus: function(status){
              return status >= 200;
       },
       maxRedirects : 0,
       responseType : 'text',
       data : content,
};

axios.request(reopt).then(function (response) {
       if(response.status < 300){                     
              console.log('Creating object using temporary signature succeed.');              
       }else{                     
              console.log('Creating object using temporary signature failed!');                     
              console.log('status:' + response.status);                     
              console.log('\n');              
       }              
       console.log(response.data);              
       console.log('\n');
}).catch(function (err) {
       console.log('Creating object using temporary signature failed!');       
       console.log(err);       
       console.log('\n');
});

以下为生成上传对象的临时URL示例。关于更多详细内容,请参见使用临时URL进行授权访问(.NET SDK)。

// 初始化配置参数
ObsConfig config = new ObsConfig();
config.Endpoint = "https://your-endpoint";
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
string accessKey= Environment.GetEnvironmentVariable("AccessKeyID", EnvironmentVariableTarget.Machine);
string secretKey= Environment.GetEnvironmentVariable("SecretAccessKey", EnvironmentVariableTarget.Machine);
// 创建ObsClient实例
ObsClient client = new ObsClient(accessKey, secretKey, config);

// URL有效期,3600秒
long exipreSeconds = 3600;

CreateTemporarySignatureRequest request = new CreateTemporarySignatureRequest();
request.BucketName = "bucketname";
request.ObjectKey = "objectkey";         
request.Method = HttpVerb.PUT;
request.Expires = exipreSeconds;

CreateTemporarySignatureResponse response = client.CreateTemporarySignature(request);
Console.WriteLine("Creating object using temporary signature url:");
Console.WriteLine("\t" + response.SignUrl);

// 使用PUT请求上传对象
HttpWebRequest webRequest = WebRequest.Create(response.SignUrl) as HttpWebRequest;
webRequest.Method = "PUT";


foreach (KeyValuePair<string, string> header in response.ActualSignedRequestHeaders)
{
    if (!header.Key.Equals("host", StringComparison.OrdinalIgnoreCase))
    {
        webRequest.Headers.Add(header.Key, header.Value);
    }
}
            
webRequest.SendChunked = true;
webRequest.AllowWriteStreamBuffering = false;
using (Stream requestStream = webRequest.GetRequestStream())
{
    byte[] buffer = Encoding.UTF8.GetBytes("Hello OBS");
    requestStream.Write(buffer, 0, buffer.Length);
}

HttpWebResponse webResponse = null;
try
{
    webResponse = webRequest.GetResponse() as HttpWebResponse;
}
catch (WebException ex)
{
    webResponse = ex.Response as HttpWebResponse;
}
Console.WriteLine("Response Status:" + Convert.ToInt32(webResponse.StatusCode));
using (MemoryStream dest = new MemoryStream())
{
    using (Stream stream = webResponse.GetResponseStream())
    {
        byte[] buffer = new byte[8192];
        int bytesRead;
        while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
        {
            dest.Write(buffer, 0, bytesRead);
        }

    }
    Console.WriteLine("Response Content:");
    Console.WriteLine(Encoding.UTF8.GetString(dest.ToArray()));
}

以下为生成上传对象的临时URL示例。关于更多详细内容,请参见使用临时URL进行授权访问(Android SDK)。

// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量ACCESS_KEY_ID和SECRET_ACCESS_KEY_ID。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
String ak = System.getenv("ACCESS_KEY_ID");
String sk = System.getenv("SECRET_ACCESS_KEY_ID");
String endPoint = "https://your-endpoint";
// 创建ObsClient实例
ObsClient obsClient = new ObsClient(ak, sk, endPoint);
// URL有效期,3600秒
long expireSeconds = 3600L;

Map<String, String> headers = new HashMap<String, String>();
String contentType = "text/plain";
headers.put("Content-Type", contentType);

TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.PUT, expireSeconds);
request.setBucketName("bucketname");
request.setObjectKey("objectname");
request.setHeaders(headers);

TemporarySignatureResponse response = obsClient.createTemporarySignature(request);

Log.i("CreateTemporarySignature", "Creating object using temporary signature url:");
Log.i("CreateTemporarySignature", "\t" + response.getSignedUrl());
Request.Builder builder = new Request.Builder();
for (Map.Entry<String, String> entry : response.getActualSignedRequestHeaders().entrySet()) {
       builder.header(entry.getKey(), entry.getValue());
}

//使用PUT请求上传对象
Request httpRequest = builder.url(response.getSignedUrl()).put(RequestBody.create(MediaType.parse(contentType), "Hello OBS".getBytes("UTF-8"))).build();
OkHttpClient httpClient = new OkHttpClient.Builder().followRedirects(false).retryOnConnectionFailure(false)
              .cache(null).build();

Call c = httpClient.newCall(httpRequest);
Response res = c.execute();
Log.i("CreateTemporarySignature", "\tStatus:" + res.code());
if (res.body() != null) {
       Log.i("CreateTemporarySignature", "\tContent:" + res.body().string() + "\n");
}
res.close();

以下为生成上传对象的临时URL示例。关于更多详细内容,请参见使用临时URL进行授权访问(Java SDK)。

static OBSClient *client;
NSString *endPoint = @"your-endpoint";
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
char* ak_env = getenv("AccessKeyID");
char* sk_env = getenv("SecretAccessKey");
NSString *AK = [NSString stringWithUTF8String:ak_env ?: ""];
NSString *SK = [NSString stringWithUTF8String:sk_env ?: ""];
    
// 初始化身份验证
OBSStaticCredentialProvider *credentialProvider = [[OBSStaticCredentialProvider alloc] initWithAccessKey:AK secretKey:SK];
    
//初始化服务配置
OBSServiceConfiguration *conf = [[OBSServiceConfiguration alloc] initWithURLString:endPoint credentialProvider:credentialProvider];
    
// 初始化client
client = [[OBSClient alloc] initWithConfiguration:conf];
OBSPutObjectWithFileRequest *request = [[OBSPutObjectWithFileRequest alloc] initWithBucketName:@"bucketname"];
    
// V2生成授权访问url
[client createV2PreSignedURL:request expireAfter:3600 completionHandler:^(NSString *urlString, NSString *httpVerb, NSDictionary *signedHeaders) {
    NSLog(@"%@",urlString);
}]

以下为生成上传对象的临时URL示例。关于更多详细内容,请参见使用临时URL进行授权访问(Java SDK)。

// 引入依赖库
require 'vendor/autoload.php';
// 使用源码安装时引入SDK代码库
// require 'obs-autoloader.php';
// 声明命名空间
use Obs\ObsClient;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
// 创建ObsClient实例
$obsClient = new ObsClient( [ 
    //推荐通过环境变量获取访问密钥AK/SK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险。
    //您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
    'key' => getenv('ACCESS_KEY_ID'),
    'secret' => getenv('SECRET_ACCESS_KEY'),
    'endpoint' => 'https://your-endpoint'
    'signature' => 'obs'
] );

// URL有效期,3600秒
$expires = 3600;
// 上传对象
$resp = $obsClient->createSignedUrl( [ 
              'Method' => 'PUT',
              'Bucket' => 'bucketname',
              'Key' => 'objectname',
              'Expires' => $expires
] );
printf("SignedUrl:%s\n", $resp ['SignedUrl']);
$url = $resp['SignedUrl'];
$httpClient = new Client(['verify' => false ]);
$content = 'Hello OBS'; 
try{
       $response = $httpClient -> request('PUT', $url, ['body' => $content, 'headers'=> $resp['ActualSignedRequestHeaders']]);
       printf("%s using temporary signature url:\n", 'Put object');
       printf("\t%s successfully.\n", $url);
       printf("\tStatus:%d\n", $response -> getStatusCode());
       printf("\tContent:%s\n", $response -> getBody() -> getContents());
       $response -> getBody()-> close();
}catch (ClientException $ex){
       printf("%s using temporary signature url:\n", 'Put object');
       printf("\t%s failed!\n", $url);
       printf('Exception message:%s', $ex ->getMessage());
}

以下为生成上传对象的临时URL示例。关于更多详细内容,请参见使用临时URL进行授权访问(Node.js SDK)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// 引入obs库
// 使用npm安装
const ObsClient = require("esdk-obs-nodejs");
// 使用源码安装
// var ObsClient = require('./lib/obs');

// 创建ObsClient实例
const obsClient = new ObsClient({
  // 推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险
  // 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
  access_key_id: process.env.ACCESS_KEY_ID,
  secret_access_key: process.env.SECRET_ACCESS_KEY,
  // 【可选】如果使用临时AK/SK和SecurityToken访问OBS,同样建议您尽量避免使用硬编码,以降低信息泄露风险。您可以通过环境变量获取访问密钥AK/SK,也可以使用其他外部引入方式传入
  // security_token: process.env.SECURITY_TOKEN,
  // endpoint填写Bucket对应的Endpoint, 这里以华北-北京四为例,其他地区请按实际情况填写
  server: "https://obs.cn-north-4.myhuaweicloud.com",
});

function createSignedUrlSync() {
  // 指定HTTP方法类型, 这里以PUT为例
  const method = 'PUT';
  const params = {
    // 指定存储桶名称
    Bucket: "examplebucket",
    // 指定对象名,此处以 example/objectname 为例
    Key: "example/objectname",
    // 指定HTTP方法类型
    Method: method,
    // 指定签名URL的过期时间,这里以3600为例,单位为秒
    Expires: 3600,
    // 指定请求中携带的头域
    Headers: {
      "Content-Type": "text/plain",
    }
  };

  // 生成上传对象的带授权信息的URL
  const res = obsClient.createSignedUrlSync(params);
  console.log("SignedUrl: %s", res.SignedUrl);
  console.log("ActualSignedRequestHeaders: %v", res.ActualSignedRequestHeaders);

  let content  = 'Hello OBS';
  // 使用PUT请求上传对象
  var url = urlLib.parse(res.SignedUrl);
  var req = https.request({
    method: method,
    host: url.hostname,
    port: url.port,
    path: url.path,
    rejectUnauthorized: false,
    headers: res.ActualSignedRequestHeaders || {}
  });
  console.log('Creating object using url:' + res.SignedUrl);

  req.on('response', (serverback) => {
    var buffers = [];
    serverback.on('data', (data) => {
      buffers.push(data);
    }).on('end', () => {
      if (serverback.statusCode < 300) {
        console.log('Creating object using temporary signature succeed.');
      } else {
        console.log('Creating object using temporary signature failed!');
        console.log('status:' + serverback.statusCode);
        console.log('\n');
      };
      buffers = Buffer.concat(buffers);
      if (buffers.length > 0) {
        console.log(buffers.toString());
      };
      console.log('\n');
    });
  }).on('error', (err) => {
    console.log('Creating object using temporary signature failed!');
    console.log(err);
    console.log('\n');
  });
  if (content) {
    req.write(content);
  };
  req.end();
};

createSignedUrlSync();

以下为生成上传对象的临时URL示例。关于更多详细内容,请参见生成带授权信息的URL(Harmony SDK)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// 引入依赖包
import ObsClient, { HttpMethodType } from '@obs/esdk-obs-harmony';
import { process } from '@kit.ArkTS';
const processManager = new process.ProcessManager();
import hilog from '@kit.PerformanceAnalysisKit';

// 指定存储桶名称
const bucketName = "examplebucket";
// 指定对象,此处以 example/objectname 为例
const objectKey = "example/objectname";
 // Server填写Bucket对应的Endpoint, 这里以华北-北京四为例,其他地区请按实际情况填写
const server = "https://obs.cn-north-4.myhuaweicloud.com";
// 文件的本地存储路径。仅支持"internal"协议类型,"internal://cache/"为应用的私有目录,是必填字段
const sourceFile = "internal://cache/hello.txt";

// 创建ObsClient实例
const obsClient = new ObsClient({
  // 推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险
  // 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
  AccessKeyId: processManager.getEnvironmentVar("ACCESS_KEY_ID"),
  SecretAccessKey: processManager.getEnvironmentVar("SECRET_ACCESS_KEY"),
  // 【可选】如果使用临时AK/SK和SecurityToken访问OBS,同样建议您尽量避免使用硬编码,以降低信息泄露风险。您可以通过环境变量获取访问密钥AK/SK,也可以使用其他外部引入方式传入
  // SecurityToken: processManager.getEnvironmentVar("SECURITY_TOKEN"),
  Server,
});

const putObject = async () => {
  const result = obsClient.createSignedUrlSync({
    // 指定HTTP方法类型,这里以HttpMethodType.PUT为例
    Method: HttpMethodType.PUT,
    // 指定存储桶名称
    Bucket: bucketName,
    // 指定对象名
    Key: objectKey,
    // 指定带授权信息的URL的过期时间。
    Expires: 3600,
    // 指定请求中携带的头域
    Headers: {
      "Content-Type": "text/plain"
    }
  });

  let uploadTask: request.UploadTask;
  // 指定上传的配置信息
  let uploadConfig: request.UploadConfig = {
    url: result.SignedUrl,
    method: HttpMethodType.PUT,
    header: result.ActualSignedRequestHeaders,
    files: [{ filename: objectKey, name: "file", uri: sourceFile, type: "text/plain" }],
    data: [],
  };
  try {
    request.uploadFile(getContext(), uploadConfig).then((data: request.UploadTask) => {
      uploadTask = data;

      uploadTask.on('progress',  (uploadedSize: number, totalSize: number) => {
        hilog.info(0x0001, 'OBS-Harmony', 'Upload progress notification. totalSize:' + totalSize + 'uploadedSize:' + uploadedSize);
      });
      let upCompleteCallback = (taskStates: Array<request.TaskState>) => {
        for (let i = 0; i < taskStates.length; i++) {
          hilog.info(0x0001, 'OBS-Harmony', "upOnComplete taskState:" + JSON.stringify(taskStates[i]));
        }
      };
      uploadTask.on('complete', upCompleteCallback);

      let upFailCallback = (taskStates: Array<request.TaskState>) => {
        for (let i = 0; i < taskStates.length; i++) {
          hilog.info(0x0001, 'OBS-Harmony', "upOnFail taskState:" + JSON.stringify(taskStates[i]));
        }
      };
      uploadTask.on('fail', upFailCallback);

    }).catch((err: BusinessError) => {
      hilog.error(0x0001, 'OBS-Harmony', `Failed to request the upload. Code: ${err.code}, message: ${err.message}`);
    });
  } catch (err) {
    hilog.error(0x0001, 'OBS-Harmony', `Failed to request the upload. err: ${JSON.stringify(err)}`);
  }
}

putObject();

使用临时URL分段上传文件

当因客户端无法集成OBS SDK或网络条件较差等原因而无法上传大文件到桶中时,可采用分段上传来上传大文件。由服务端初始化分段上传任务,并为每个分段生成临时URL,然后第三方上传分段,最后再由服务端合并段,生成完整的文件。使用SDK的示例代码如下:

  1. 客户端发起上传文件的请求

    客户端向服务端发起上传文件的请求,请求中明确文件名、文件大小、期望的分段大小(不超过5GB)。

    示例如下:

    curl -X POST https://example.com/uploadfile \
      -H "Content-Type: application/json" \
      -d '{
        "fileName": "testexample.jpg",
        "fileSize": 104857600,
        "partSize": 5242880
    }'
  2. 服务端初始化分段上传任务并生成临时URL
    1. 调用initiateMultipartUpload接口初始化初始化分段上传任务,获取OBS服务端创建的全局唯一标识(Upload ID),用于标识本次分段上传任务。
    2. 根据文件大小和期望的分段大小计算分段个数。
    3. 为每个分段生成对应的临时访问URL。
    4. 返回临时URL列表。

    初始化分段上传任务并为每个分段生成临时URL的示例代码如下:

    import com.obs.services.ObsClient;
    import com.obs.services.ObsConfiguration;
    import com.obs.services.model.*;
    
    import java.io.*;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.*;
    
    public class ObsExample {
    
        // 推荐通过环境变量获取AK/SK,如果使用硬编码可能会存在泄露风险
        // 您可以登录访问管理控制台获取访问密钥AK/SK
        static String ak = System.getenv("AccessKeyID");
        static String sk = System.getenv("SecretAccessKey");
        // 如需使用临时AK/SK和SecurityToken访问OBS,则同样推荐通过环境变量获取
        // static String securityToken = System.getenv("SecurityToken");
        // server填写Bucket对应的Endpoint,这里以华北-北京四为例,其他地区请按实际情况填写
        static String server = "https://obs.cn-north-4.myhuaweicloud.com";
        static final int TOTAL_PARTS = 3;     // 要上传的分片数量
        static final long EXPIRE_TIME = 3600; // 预签名URL有效期(单位:秒)
    
        public static void initMultipartUploadAndGenerateUrls() throws Exception {
            ObsClient obsClient = new ObsClient(ak, sk, server);
            try {
                InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest("bucket01", "objectkey");
                InitiateMultipartUploadResult initResult = obsClient.initiateMultipartUpload(initRequest);
                String uploadId = initResult.getUploadId();
    
                List<Map<String, Object>> parts = new ArrayList<>();
                for (int partNumber = 1; partNumber <= TOTAL_PARTS; partNumber++) {
                    Map<String, Object> queryParams = new HashMap<>();
                    queryParams.put("partNumber", String.valueOf(partNumber));
                    queryParams.put("uploadId", uploadId);
    
                    TemporarySignatureRequest sigRequest = new TemporarySignatureRequest(HttpMethodEnum.PUT, EXPIRE_TIME);
                    sigRequest.setBucketName("bucket01");
                    sigRequest.setObjectKey("objectkey");
                    sigRequest.setQueryParams(queryParams);
    
                    TemporarySignatureResponse sigResponse = obsClient.createTemporarySignature(sigRequest);
    
                    Map<String, Object> partInfo = new HashMap<>();
                    partInfo.put("part_number", partNumber);
                    partInfo.put("url", sigResponse.getSignedUrl());
                    partInfo.put("method", "PUT");
                    parts.add(partInfo);
                }
    
                System.out.println("upload_id: " + uploadId);
                System.out.println("parts:");
                for (Map<String, Object> part : parts) {
                    System.out.println("  part_number: " + part.get("part_number"));
                    System.out.println("  url: " + part.get("url"));
                    System.out.println("  method: " + part.get("method"));
                }
            } finally {
                obsClient.close();
            }
        }
    }
  3. 客户端上传分段数据

    客户端使用服务端返回的各分段的临时URL,通过PUT方式上传分段。示例代码如下:

    import com.obs.services.ObsClient;
    import com.obs.services.ObsConfiguration;
    import com.obs.services.model.*;
    
    import java.io.*;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.*;
    
    public class ObsExample {
    
        // 推荐通过环境变量获取AK/SK,如果使用硬编码可能会存在泄露风险
        // 您可以登录访问管理控制台获取访问密钥AK/SK
        static String ak = System.getenv("AccessKeyID");
        static String sk = System.getenv("SecretAccessKey");
        // 如需使用临时AK/SK和SecurityToken访问OBS,则同样推荐通过环境变量获取
        // static String securityToken = System.getenv("SecurityToken");
        // server填写Bucket对应的Endpoint,这里以华北-北京四为例,其他地区请按实际情况填写
        static String server = "https://obs.cn-north-4.myhuaweicloud.com";
        static final int TOTAL_PARTS = 3;     // 要上传的分片数量
        static final long EXPIRE_TIME = 3600; // 预签名URL有效期(单位:秒)
    
        public static String uploadPart(String url, byte[] content) throws Exception {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setRequestMethod("PUT");
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", "");
            connection.getOutputStream().write(content);
            connection.getOutputStream().flush();
    
            int statusCode = connection.getResponseCode();
            String etag = connection.getHeaderField("etag");
            connection.disconnect();
    
            if (statusCode != 200) {
                throw new RuntimeException("Upload part failed with status: " + statusCode);
            }
            return etag;
        }
    
        public static void uploadParts() throws Exception {
            try {
                List<String> etagList = new ArrayList<>();
                String url = "...";  // 服务端生成的url
                byte[][] contents = {"...".getBytes(), "...".getBytes()}; // 客户端要上传的内容
    
                for (byte[] content : contents) {
                    String etag = uploadPart(url, content);
                    etagList.add(etag);
                }
    
                System.out.println("etag list:");
                for (String etag : etagList) {
                    System.out.println("  " + etag);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    也可以通过curl指令发送请求,使用服务端返回的各分段的临时URL,通过PUT方式上传分段。

    curl -X PUT -T <local_file_path> -s '<url>' -kvs
  4. 服务端合并段

    服务端收到客户端上传分段完成的通知后,将所有分段合并为完整的文件,并将结果返回给客户端。

    import com.obs.services.ObsClient;
    import com.obs.services.ObsConfiguration;
    import com.obs.services.model.*;
    
    import java.io.*;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.*;
    
    public class ObsExample {
    
        // 推荐通过环境变量获取AK/SK,如果使用硬编码可能会存在泄露风险
        // 您可以登录访问管理控制台获取访问密钥AK/SK
        static String ak = System.getenv("AccessKeyID");
        static String sk = System.getenv("SecretAccessKey");
        // 如需使用临时AK/SK和SecurityToken访问OBS,则同样推荐通过环境变量获取
        // static String securityToken = System.getenv("SecurityToken");
        // server填写Bucket对应的Endpoint,这里以华北-北京四为例,其他地区请按实际情况填写
        static String server = "https://obs.cn-north-4.myhuaweicloud.com";
        static final int TOTAL_PARTS = 3;     // 要上传的分片数量
        static final long EXPIRE_TIME = 3600; // 预签名URL有效期(单位:秒)
    
        public static void completeMultipartUpload(String uploadId) throws Exception {
            ObsClient obsClient = new ObsClient(ak, sk, server);
            try {
                String[] etags = {"xxxxxx", "xxxxxx", "xxxxxx"}; // 来自于客户端上传分段数据后的响应,需要按段顺序排列
                CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest("bucket01", "objectkey", uploadId);
                for (int i = 0; i < etags.length; i++) {
                    CompletePart part = new CompletePart();
                    part.setPartNumber(i + 1);
                    part.setEtag(etags[i]);
                    completeRequest.addPart(part);
                }
    
                CompleteMultipartUploadResult result = obsClient.completeMultipartUpload(completeRequest);
                if (result.getStatusCode() == 200) {
                    System.out.println("UploadPart successfully");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                obsClient.close();
            }
        }
    }
  1. 客户端发起上传文件的请求

    客户端向服务端发起上传文件的请求,请求中明确文件名、文件大小、期望的分段大小(不超过5GB)。

    示例如下:

    curl -X POST https://example.com/uploadfile \
      -H "Content-Type: application/json" \
      -d '{
        "fileName": "testexample.jpg",
        "fileSize": 104857600,
        "partSize": 5242880
    }'
  2. 服务端初始化分段上传任务并生成临时URL
    1. 调用initiateMultipartUpload接口初始化初始化分段上传任务,获取OBS服务端创建的全局唯一标识(Upload ID),用于标识本次分段上传任务。
    2. 根据文件大小和期望的分段大小计算分段个数。
    3. 为每个分段生成对应的临时访问URL。
    4. 返回临时URL列表。

    初始化分段上传任务并为每个分段生成临时URL的示例代码如下:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import os
    import traceback
    import json
    from obs import ObsClient
    from obs import CompleteMultipartUploadRequest, CompletePart
    
    # 推荐通过环境变量获取AK/SK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险
    # 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.htmlhttps://support.huaweicloud.com/intl/zh-cn/usermanual-ca/ca_01_0003.htmlhttps://support.huaweicloud.com/eu/usermanual-ca/ca_01_0003.html
    ak = os.getenv('AccessKeyID')
    sk = os.getenv('SecretAccessKey')
    # 如需使用临时AK/SK和SecurityToken访问OBS,则同样推荐通过环境变量获取
    # security_token = os.getenv('SecurityToken')
    # server填写Bucket对应的Endpoint,这里以华北-北京四为例,其他地区请按实际情况填写
    server = 'https://obs.cn-north-4.myhuaweicloud.com'
    TOTAL_PARTS = 3     # 要上传的分片数量
    EXPIRE_TIME = 3600  # 预签名URL有效期(单位:秒)
    # 创建obsClient实例
    # 如果使用临时AK/SK和SecurityToken访问OBS,需要在创建实例时通过security_token参数指定securityToken值
    obs_client = ObsClient(access_key_id=ak, secret_access_key=sk, server=server)
    try:
        resp = obs_client.initiateMultipartUpload(bucketName='bucket01', objectKey='objectkey')
        upload_id = resp.body.uploadId
        parts = []
        # 生成分片url
        local_file_path = "/your_local_file_path"
        for part_number in range(1, TOTAL_PARTS + 1):
            resp2 = obs_client.createSignedUrl(method='PUT', bucketName='bucket01', objectKey='objectkey',
                                              queryParams = {"partNumber": part_number, "uploadId": upload_id}, 
                                              expires=EXPIRE_TIME)
            parts.append({
                "part_number": part_number,
                "url": resp2['signedUrl'],
                "method": "PUT",
            })
        output = {
            "upload_id": upload_id,
            "parts": parts
        }
        print(json.dumps(output, indent=2))
  3. 客户端上传分段数据

    客户端使用服务端返回的各分段的临时URL,通过PUT方式上传分段。示例代码如下:

    import traceback
    import urllib.request
    
    def upload_part(url, content):
        # Python 3 的 Request 直接支持 method 参数
        headers = {"Content-type": ""}
        request = urllib.request.Request(url, data=content, headers=headers, method="PUT")
    
        # 发送请求并获取响应
        with urllib.request.urlopen(request) as response:
            status_code = response.getcode()
            # 获取响应头中的 etag(注意 HTTP 头通常大小写不敏感,用 .get() 更安全)
            etag = response.headers.get('ETag') or response.headers.get('etag')
    
        return status_code, etag
    
    TOTAL_PARTS = 3     # 要上传的分片数量
    try:
        etag_list = []
        url = ".."  # 服务端生成的url
        # 注意:如果上传的是字符串内容,通常需要先 encode 为 bytes(例如 b"...")
        contents = [b"...", b"...."] # 客户端要上传的内容
    
        for content in contents:
            status_code, etag = upload_part(url, content)
            etag_list.append(etag)
    
        print("etag list:")
        print(etag_list)
    except Exception:
        print(traceback.format_exc())

    也可以通过curl指令发送请求,使用服务端返回的各分段的临时URL,通过PUT方式上传分段。

    curl -X PUT -T <local_file_path> -s '<url>' -kvs
  4. 服务端合并段

    服务端收到客户端上传分段完成的通知后,将所有分段合并为完整的文件,并将结果返回给客户端。

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import os
    import traceback
    import json
    from obs import ObsClient
    from obs import CompleteMultipartUploadRequest, CompletePart
    # 推荐通过环境变量获取AK/SK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险
    # 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
    ak = os.getenv('AccessKeyID')
    sk = os.getenv('SecretAccessKey')
    # 如需使用临时AK/SK和SecurityToken访问OBS,则同样推荐通过环境变量获取
    # security_token = os.getenv('SecurityToken')
    # server填写Bucket对应的Endpoint,这里以华北-北京四为例,其他地区请按实际情况填写
    server = 'https://obs.cn-north-4.myhuaweicloud.com'
    TOTAL_PARTS = 3     # 要上传的分片数量
    EXPIRE_TIME = 3600  # 预签名URL有效期(单位:秒)
    # 创建obsClient实例
    # 如果使用临时AK/SK和SecurityToken访问OBS,需要在创建实例时通过security_token参数指定securityToken值
    obs_client = ObsClient(access_key_id=ak, secret_access_key=sk, server=server)
    try:
        # 合并段
        etags = ["xxxxxx","xxxxxx","xxxxxx"] # 来自于客户端上传分段数据后的响应,需要按段顺序排列
        upload_request = CompleteMultipartUploadRequest()
        for i, etag in enumerate(etags):
            upload_request.add_part(CompletePart(i + 1, etag))
        resp = obs_client.completeMultipartUpload(bucketName='bucket01', objectKey='objectkey', uploadId=upload_id,
                                                  completeMultipartUploadRequest=upload_request)
        if (resp['status'] == 200):
            print("UploadPart successfully")
    except Exception:
        print(traceback.format_exc())

设置上传回调

您可以在上传对象请求中设置回调参数,OBS在对象上传成功之后,将上传结果回调特定服务器,并将回调结果返回给用户。关于回调的详细内容,请参见回调-Callback。

上传回调和普通上传的核心差异为:在计算签名时,将x-obs-callback头域内容计算进去。

  1. 文件拥有者生成携带上传回调参数的PUT方法的临时访问URL。
    import com.obs.services.ObsClient;
    import com.obs.services.ObsConfiguration;
    import com.obs.services.model.*;
    
    import java.io.*;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.*;
    
    public class ObsExample {
    
        // 推荐通过环境变量获取AK/SK,如果使用硬编码可能会存在泄露风险
        // 您可以登录访问管理控制台获取访问密钥AK/SK
        static String ak = System.getenv("AccessKeyID");
        static String sk = System.getenv("SecretAccessKey");
        // 如需使用临时AK/SK和SecurityToken访问OBS,则同样推荐通过环境变量获取
        // static String securityToken = System.getenv("SecurityToken");
        // server填写Bucket对应的Endpoint,这里以华北-北京四为例,其他地区请按实际情况填写
        static String server = "https://obs.cn-north-4.myhuaweicloud.com";
        public static String buildCallbackHeaderValue(String callbackUrl, String callbackBody, String callbackBodyType) throws Exception {
            String callbackPolicy = String.format(
                    "{\"callbackUrl\":\"%s\",\"callbackBody\":\"%s\",\"callbackBodyType\":\"%s\"}",
                    callbackUrl, callbackBody, callbackBodyType
            );
            return Base64.getEncoder().encodeToString(callbackPolicy.getBytes("UTF-8"));
        }
    
        static final long EXPIRE_TIME = 3600; // 预签名URL有效期(单位:秒)
    
        public static void generateUploadUrlWithCallback() throws Exception {
            ObsClient obsClient = new ObsClient(ak, sk, server);
            try {
                String callbackUrl = "http://callback"; // 回调服务器地址(需公网可访问)
                String callbackBody = "{\"key\":\"${object}\",\"etag\":\"${etag}\",\"size\":\"${size}\",\"bucket\":\"${bucket}\"}";
                String callbackHeaderValue = buildCallbackHeaderValue(callbackUrl, callbackBody, "application/json");
    
                Map<String, Object> headers = new HashMap<>();
                headers.put("Content-Type", "text/plain");
                headers.put("x-obs-callback", callbackHeaderValue);
    
                TemporarySignatureRequest sigRequest = new TemporarySignatureRequest(HttpMethodEnum.PUT, EXPIRE_TIME);
                sigRequest.setBucketName("bucketname");
                sigRequest.setObjectKey("objectkey");
                sigRequest.setHeaders(headers);
    
                TemporarySignatureResponse sigResponse = obsClient.createTemporarySignatureUrl(sigRequest);
                System.out.println("signedUrl: " + sigResponse.getSignedUrl());
                System.out.println("actualSignedRequestHeaders: " + sigResponse.getActualSignedRequestHeaders());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                obsClient.close();
            }
        }
    }
  2. 第三方用户使用PUT方法的临时访问URL上传文件。
    import com.obs.services.ObsClient;
    import com.obs.services.ObsConfiguration;
    import com.obs.services.model.*;
    
    import java.io.*;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.*;
    
    public class ObsExample {
    
        // 推荐通过环境变量获取AK/SK,如果使用硬编码可能会存在泄露风险
        // 您可以登录访问管理控制台获取访问密钥AK/SK
        static String ak = System.getenv("AccessKeyID");
        static String sk = System.getenv("SecretAccessKey");
        // 如需使用临时AK/SK和SecurityToken访问OBS,则同样推荐通过环境变量获取
        // static String securityToken = System.getenv("SecurityToken");
        // server填写Bucket对应的Endpoint,这里以华北-北京四为例,其他地区请按实际情况填写
        static String server = "https://obs.cn-north-4.myhuaweicloud.com";
        public static void uploadWithCallback(String presignedUrl, String callbackHeaderValue,
                                              byte[] content, String contentType) throws Exception {
            HttpURLConnection connection = (HttpURLConnection) new URL(presignedUrl).openConnection();
            connection.setRequestMethod("PUT");
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", contentType);
            connection.setRequestProperty("x-obs-callback", callbackHeaderValue);
            connection.setRequestProperty("Content-Length", String.valueOf(content.length));
            connection.getOutputStream().write(content);
            connection.getOutputStream().flush();
    
            int statusCode = connection.getResponseCode();
            Map<String, List<String>> responseHeaders = connection.getHeaderFields();
    
            StringBuilder responseBody = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    responseBody.append(line);
                }
            }
            connection.disconnect();
    
            System.out.println("\n响应结果");
            System.out.println("  HTTP Status: " + statusCode);
        }
    
        public static void main(String[] args) throws Exception {
            // 使用url上传 
            uploadWithCallback("presignedUrl", "callbackHeaderValue", "content".getBytes(), "text/plain");
        }
    }
  1. 文件拥有者生成携带上传回调参数的PUT方法的临时访问URL。
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import os
    import traceback
    import base64
    from obs import ObsClient
    import json
    # 推荐通过环境变量获取AK/SK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险
    # 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
    ak = os.getenv('AccessKeyID')
    sk = os.getenv('SecretAccessKey')
    # 如需使用临时AK/SK和SecurityToken访问OBS,则同样推荐通过环境变量获取
    # security_token = os.getenv('SecurityToken')
    # server填写Bucket对应的Endpoint,这里以华北-北京四为例,其他地区请按实际情况填写
    server = 'https://obs.cn-north-4.myhuaweicloud.com'
    def build_callback_header_value(callback_url, callback_body, callback_body_type):
        callback_policy = {
            "callbackUrl": callback_url,
            "callbackBody": callback_body,
            "callbackBodyType": callback_body_type
        }
        callback_json = json.dumps(callback_policy)
        callback_base64 = base64.b64encode(callback_json)
        return callback_base64
    CALLBACK_URL = "http://callback"  # 回调服务器地址(需公网可访问)
    callback_body = json.dumps({
        "key": "${object}",
        "etag": "${etag}",
        "size": "${size}",
        "bucket": "${bucket}"
    })
    callback_header_value = build_callback_header_value(
        callback_url=CALLBACK_URL,
        callback_body=callback_body,
        callback_body_type="application/json"
    )
    # 创建obsClient实例
    # 如果使用临时AK/SK和SecurityToken访问OBS,需要在创建实例时通过security_token参数指定securityToken值
    obs_client = ObsClient(access_key_id=ak, secret_access_key=sk, server=server)
    try:
        # 生成上传对象的带授权信息的URL
        res2 = obs_client.createSignedUrl(method='PUT', bucketName='bucketname', objectKey='objectkey', expires=3600,
                                         headers = {'Content-Type': 'text/plain', "x-obs-callback": callback_header_value})
        print('signedUrl:', res2['signedUrl'])
        print('actualSignedRequestHeaders:', res2['actualSignedRequestHeaders'])
    except Exception:
        print(traceback.format_exc())
  2. 第三方用户使用PUT方法的临时访问URL上传文件。
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import urllib.request
    import base64
    import json
    
    UPLOAD_CONTENT = 'upload content'
    CONTENT_TYPE = 'text/plain'
    
    def build_callback_header_value(callback_url, callback_body, callback_body_type):
        callback_policy = {
            "callbackUrl": callback_url,
            "callbackBody": callback_body,
            "callbackBodyType": callback_body_type
        }
        callback_json = json.dumps(callback_policy)
        callback_base64 = base64.b64encode(callback_json)
        return callback_base64
    
    def upload_with_callback(presigned_url, callback_header_value,
                             content, content_type):
        # 构造请求头
        headers = {
            "Content-Type": content_type,
            "x-obs-callback": callback_header_value,
            "Content-Length": str(len(content)),
        }
        # 使用 Python 3 的 urllib.request.Request 直接指定 method="PUT"
        request = urllib.request.Request(presigned_url, data=content, headers=headers, method="PUT")
        # 发送请求并获取响应
        with urllib.request.urlopen(request) as response:
            status_code = response.getcode()
            response_headers = dict(response.info())
            response_body = response.read()
        return status_code, response_headers, response_body
    
    if __name__ == '__main__':
        CALLBACK_URL = "http://callback"  # 回调服务器地址(需公网可访问)
        callback_body = json.dumps({
            "key": "${object}",
            "etag": "${etag}",
            "size": "${size}",
            "bucket": "${bucket}"
        })
        callback_header_value = build_callback_header_value(
            callback_url=CALLBACK_URL,
            callback_body=callback_body,
            callback_body_type="application/json"
        )
        presigned_url = 'https://bucketname.obs.cn-north-4.myhuaweicloud.com/objectkey?Expires=1789872566&Signature=at2xDy2Ys0X16Z9vz0cm0yTLymE%3D&AccessKeyId=XXXX'  # 文件拥有者生成携带上传回调参数的PUT方法的临时访问URL
        status_code, resp_headers, resp_body = upload_with_callback(
            presigned_url=presigned_url,
            callback_header_value=callback_header_value,
            content=UPLOAD_CONTENT,
            content_type=CONTENT_TYPE
        )
        print("\n响应结果")
        print(" HTTP Status: %s" % str(status_code))

    也可以通过curl指令发送请求,使用PUT方法的临时访问URL上传文件。

    curl -X PUT -T '<待上传的文件>' -H 'x-obs-callback:callback_value' -H 'Content-Type:text/plain' -s '<生成的url>' -kvs

相关文档

相关文档