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

Object Storage Service:署名付き URL を使用したファイルのアップロード

最終更新日:Dec 17, 2025

デフォルトでは、Object Storage Service (OSS) バケット内のファイルは非公開であり、バケットのオーナーのみがアップロードできます。ただし、バケットのオーナーは、特定のファイルの署名付き URL を生成して、一時的なアップロード権限を付与できます。この署名付き URL を持つユーザーは誰でも、URL の有効期間内にファイルをアップロードできます。この機能は、パートナーに契約書のアップロードを許可したり、ユーザーにプロフィール写真のアップロードを許可したりするなどのシナリオで役立ちます。

注意事項

  • このトピックでは、中国 (杭州) リージョンのパブリックエンドポイントを使用します。同じリージョン内の他の Alibaba Cloud サービスから OSS にアクセスするには、内部エンドポイントを使用します。サポートされているリージョンとエンドポイントの詳細については、「リージョンとエンドポイント」をご参照ください。

  • 署名付き URL の生成に権限は必要ありません。ただし、サードパーティが署名付き URL を使用してファイルを正常にアップロードできるのは、URL を生成したユーザーが oss:PutObject 権限を持っている場合に限られます。権限の付与方法の詳細については、「RAM ユーザーへのカスタム権限の付与」をご参照ください。

  • 署名付き URL は、FormData 形式でのアップロードをサポートしていません。FormData を使用してデータをアップロードするには、OSS フォームアップロードを使用します。

重要

コンプライアンスとセキュリティを向上させるためのポリシー変更により、2025 年 3 月 20 日以降、新規の OSS ユーザーは、中国本土リージョンにある OSS バケットでデータ API 操作を実行するために、カスタムドメイン名 (CNAME ドメイン名) を使用する必要があります。デフォルトのパブリックエンドポイントは、これらの操作では制限されます。影響を受ける操作の完全なリストについては、公式発表をご参照ください。HTTPS 経由でデータにアクセスする場合は、カスタムドメインに有効な SSL 証明書をバインドする必要があります。これは、コンソールが HTTPS を強制するため、OSS コンソールへのアクセスには必須です。

プロセスの概要

署名付き URL を使用してファイルをアップロードするプロセスは次のとおりです。

image

署名付き URL を使用した単一ファイルのアップロード

  1. バケットのオーナーが PUT リクエスト用の署名付き URL を生成します。

    説明

    SDK を使用して生成された署名付き URL の最大有効期間は 7 日間です。セキュリティトークンサービス (STS) トークンを使用して署名付き URL を生成する場合、最大有効期間は 43,200 秒 (12 時間) です。

    Java

    SDK の詳細については、「Java で署名付き URL を使用してファイルをアップロード」をご参照ください。

    import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.*;
    import com.aliyun.oss.common.comm.SignVersion;
    import com.aliyun.oss.model.GeneratePresignedUrlRequest;
    import java.net.URL;
    import java.util.*;
    import java.util.Date;
    
    public class GetSignUrl {
        public static void main(String[] args) throws Throwable {
            // この例では、中国 (杭州) リージョンのパブリックエンドポイントを使用します。実際のエンドポイントを指定してください。
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // 環境変数からアクセス認証情報を取得します。サンプルコードを実行する前に、OSS_ACCESS_KEY_ID および OSS_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            // バケット名を指定します (例: examplebucket)。
            String bucketName = "examplebucket";
            // オブジェクトの完全なパスを指定します (例: exampleobject.txt)。オブジェクトの完全なパスにバケット名を含めることはできません。
            String objectName = "exampleobject.txt";
            // バケットが配置されているリージョンを指定します。たとえば、バケットが中国 (杭州) リージョンにある場合、リージョンを 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();
    
            URL signedUrl = null;
            try {
                // 生成された署名付き URL の有効期限をミリ秒単位で指定します。この例では、有効期限を 1 時間に設定します。
                Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
    
                // 署名付き URL を生成します。
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
                // 有効期限を設定します。
                request.setExpiration(expiration);
                // HTTP PUT リクエスト用の署名付き URL を生成します。
                signedUrl = ossClient.generatePresignedUrl(request);
                // 署名付き URL を出力します。
                System.out.println("signed url for putObject: " + signedUrl);
    
            } 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 (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());
            }
        }
    }       

    Go

    SDK の詳細については、「Go で署名付き URL を使用してファイルをアップロード」をご参照ください。

    package main
    
    import (
    	"context"
    	"flag"
    	"log"
    	"time"
    
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
    )
    
    // グローバル変数を指定します。
    var (
    	region     string // バケットが配置されているリージョン。
    	bucketName string // バケットの名前。
    	objectName string // オブジェクトの名前。
    )
    
    // コマンドラインパラメーターを初期化するために使用される init 関数を指定します。
    func init() {
    	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
    	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
    	flag.StringVar(&objectName, "object", "", "The name of the object.")
    }
    
    func main() {
    	// コマンドラインパラメーターを解析します。
    	flag.Parse()
    
    	// バケット名が空かどうかを確認します。
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, bucket name required")
    	}
    
    	// リージョンが空かどうかを確認します。
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, region required")
    	}
    
    	// オブジェクト名が空かどうかを確認します。
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, object name required")
    	}
    
    	// デフォルトの構成をロードし、認証情報プロバイダーとリージョンを指定します。
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	// OSS クライアントを作成します。
    	client := oss.NewClient(cfg)
    
    	// PutObject リクエスト用の署名付き URL を生成します。
    	result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
    		Bucket: oss.Ptr(bucketName),
    		Key:    oss.Ptr(objectName),
    		//ContentType: oss.Ptr("text/txt"),                                 // URL に含まれる ContentType パラメーターの値が、リクエストで指定された ContentType の値と同じであることを確認してください。
    		//Metadata:    map[string]string{"key1": "value1", "key2": "value2"}, // URL に含まれる Metadata パラメーターの値が、リクエストで指定された Metadata の値と同じであることを確認してください。
    	},
    		oss.PresignExpires(10*time.Minute),
    	)
    	if err != nil {
    		log.Fatalf("failed to put object presign %v", err)
    	}
    
    	log.Printf("request method:%v\n", result.Method)
    	log.Printf("request expiration:%v\n", result.Expiration)
    	log.Printf("request url:%v\n", result.URL)
    	if len(result.SignedHeaders) > 0 {
    		// HTTP PUT リクエストを許可する署名付き URL を生成する際にリクエストヘッダーを指定する場合は、そのリクエストヘッダーが署名付き URL を使用して開始される PUT リクエストに含まれていることを確認してください。
    		log.Printf("signed headers:\n")
    		for k, v := range result.SignedHeaders {
    			log.Printf("%v: %v\n", k, v)
    		}
    	}
    }
    

    Python

    SDK の詳細については、「Python で署名付き URL を使用してファイルをアップロード」をご参照ください。

    import argparse
    import requests
    import alibabacloud_oss_v2 as oss
    
    from datetime import datetime, timedelta
    
    # コマンドラインパラメーターパーサーを作成し、スクリプトの目的を記述します。この例では、HTTP PUT リクエストを許可する署名付き URL を生成する方法を説明します。
    parser = argparse.ArgumentParser(description="presign put object sample")
    
    # 必須のリージョン、バケット名、エンドポイント、オブジェクト名を含むコマンドラインパラメーターを指定します。
    parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
    parser.add_argument('--bucket', help='The name of the bucket.', required=True)
    parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
    parser.add_argument('--key', help='The name of the object.', required=True)
    
    def main():
        # コマンドラインパラメーターを解析して、ユーザーが指定した値を取得します。
        args = parser.parse_args()
    
        # 認証のために環境変数からアクセス認証情報を取得します。
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    
        # SDK のデフォルト構成を使用して構成オブジェクトを作成し、認証情報プロバイダーを指定します。
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
    
        # ユーザーが指定したコマンドラインパラメーターに基づいて、構成オブジェクトのリージョン属性を指定します。
        cfg.region = args.region
    
        # カスタムエンドポイントが提供されている場合は、構成オブジェクトのエンドポイントパラメーターを変更します。
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
    
        # 上記の構成を使用して OSSClient インスタンスを初期化し、インスタンスが OSS と対話できるようにします。
        client = oss.Client(cfg)
    
        # PUT リクエストを開始し、指定されたオブジェクトの署名付き URL を生成するリクエストを送信します。
        pre_result = client.presign(oss.PutObjectRequest(
            bucket=args.bucket, # バケットの名前。
            key=args.key, # オブジェクトの名前。
        ),expires=timedelta(seconds=3600)) # リクエストの有効期間を指定します。この例では、有効期間は 3,600 秒に設定されています。
    
        # リクエストで指定されたメソッド、有効期限、署名付き URL を表示して、署名付き URL の有効性を確認します。
        print(f'method: {pre_result.method},'
              f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
              f' url: {pre_result.url}'
              )
    
        # リクエストの署名付きヘッダーを表示します。これは、リクエストが送信されるときに HTTP ヘッダーに含まれます。
        for key, value in pre_result.signed_headers.items():
            print(f'signed headers key: {key}, signed headers value: {value}')
    
    # スクリプトが直接実行されるときに処理ロジックを開始するために main 関数を呼び出します。
    if __name__ == "__main__":
        main() # スクリプトの関数のエントリポイントを指定します。制御フローはここから始まります。

    Node.js

    このセクションでは、一般的なシナリオの例を示します。画像処理パラメーターまたはバージョン ID を持つ署名付き URL を生成する方法の詳細については、「Node.js で署名付き URL を使用してファイルをアップロード」をご参照ください。

    const OSS = require("ali-oss");
    
    // 署名付き URL を生成する関数を定義します。
    async function generateSignatureUrl(fileName) {
      // 署名付き URL を取得します。
      const client = await new OSS({
          accessKeyId: 'yourAccessKeyId',
          accessKeySecret: 'yourAccessKeySecret',
          bucket: 'examplebucket',
          region: 'oss-cn-hangzhou',
          authorizationV4: true
      });
    
      return await client.signatureUrlV4('PUT', 3600, {
          headers: {} // 実際のリクエストヘッダーに基づいてリクエストヘッダーを設定します。
      }, fileName);
    }
    // 関数を呼び出し、ファイル名を渡します。
    generateSignatureUrl('yourFileName').then(url => {
      console.log('Generated Signature URL:', url);
    }).catch(err => {
      console.error('Error generating signature URL:', err);
    });

    PHP

    このセクションでは、一般的なシナリオの例を示します。バージョン ID を持つ署名付き URL を生成する方法の詳細については、「PHP で署名付き URL を使用してファイルをアップロード」をご参照ください。

    <?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\OssClient;
    use OSS\Core\OssException;
    use OSS\Http\RequestCore;
    use OSS\Http\ResponseCore;
    use OSS\Credentials\EnvironmentVariableCredentialsProvider;
    
    // 環境変数からアクセス認証情報を取得します。サンプルコードを実行する前に、OSS_ACCESS_KEY_ID および OSS_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。
    $provider = new EnvironmentVariableCredentialsProvider();
    // バケットが配置されているリージョンのエンドポイントを指定します。たとえば、バケットが中国 (杭州) リージョンにある場合、エンドポイントを https://oss-cn-hangzhou.aliyuncs.com に設定します。
    $endpoint = "yourEndpoint";
    // バケットの名前を指定します。
    $bucket= "examplebucket";
    // オブジェクトの完全なパスを指定します。オブジェクトの完全なパスにバケット名を含めないでください。
    $object = "exampleobject.txt";
    // 署名付き URL の有効期間を 600 秒に設定します。最大値: 32400。
    $timeout = 600;
    try {
        $config = array(  
            "provider" => $provider,
            "endpoint" => $endpoint,
            'signatureVersion'=>OssClient::OSS_SIGNATURE_VERSION_V4,
            "region"=> "cn-hangzhou"
        );
        $ossClient = new OssClient($config);
        // 署名付き URL を生成します。
        $signedUrl = $ossClient->signUrl($bucket, $object, $timeout, "PUT");
        print_r($signedUrl);
    } catch (OssException $e) {
        printf(__FUNCTION__ . ": FAILED\n");
        printf($e->getMessage() . "\n");
        return;
    }           

    Android

    SDK の詳細については、「Android で署名付き URL を使用してファイルをアップロード」をご参照ください。

    // バケット名を指定します (例: examplebucket)。
    String bucketName = "examplebucket";
    // ソースオブジェクトの完全なパスを、バケット名なしで指定します (例: exampleobject.txt)。
    String objectKey = "exampleobject.txt";
    // content-type を設定します。
    String contentType = "application/octet-stream";
    String url = null;
    try {
        // ファイルをアップロードするための署名付き URL を生成します。
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectKey);
        // 署名付き URL の有効期限を 30 分に設定します。
        request.setExpiration(30*60);
        request.setContentType(contentType);    
        request.setMethod(HttpMethod.PUT);
        url = oss.presignConstrainedObjectURL(request);
        Log.d("url", url);
    } catch (ClientException e) {
        e.printStackTrace();
    }

    C++

    SDK の詳細については、「C++ で署名付き URL を使用してファイルをアップロード」をご参照ください。

    #include <alibabacloud/oss/OssClient.h>
    using namespace AlibabaCloud::OSS;
    
    int main(void)
    {
        /* OSS へのアクセスに使用するアカウントに関する情報を初期化します。*/
                
        /* バケットが配置されているリージョンのエンドポイントを指定します。たとえば、バケットが中国 (杭州) リージョンにある場合、エンドポイントを https://oss-cn-hangzhou.aliyuncs.com に設定します。*/
        std::string Endpoint = "yourEndpoint";
        /* バケットが配置されているリージョンを指定します。たとえば、バケットが中国 (杭州) リージョンにある場合、リージョンを cn-hangzhou に設定します。* /
        std::string Region = "yourRegion";
        /* バケットの名前を指定します。例: examplebucket。*/
        std::string BucketName = "examplebucket";
        /* オブジェクトの完全なパスを指定します。完全なパスにバケット名を含めないでください。例: exampledir/exampleobject.txt。*/   
        std::string PutobjectUrlName = "exampledir/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);
    
        /* 署名付き URL の有効期間を指定します。最大有効期間は 32,400 です。単位: 秒。*/
        std::time_t t = std::time(nullptr) + 1200;
        /* 署名付き URL を生成します。*/
        auto genOutcome = client.GeneratePresignedUrl(BucketName, PutobjectUrlName, t, Http::Put);
        if (genOutcome.isSuccess()) {
            std::cout << "GeneratePresignedUrl success, Gen url:" << genOutcome.result().c_str() << std::endl;
        }
        else {
            /* 例外を処理します。*/
            std::cout << "GeneratePresignedUrl fail" <<
            ",code:" << genOutcome.error().Code() <<
            ",message:" << genOutcome.error().Message() <<
            ",requestId:" << genOutcome.error().RequestId() << std::endl;
            return -1;
        }
    
        /* ネットワークリソースなどのリソースを解放します。*/
        ShutdownSdk();
        return 0;
    }

    iOS

    SDK の詳細については、「iOS で署名付き URL を使用してファイルをアップロード」をご参照ください。

    // バケットの名前を指定します。
    NSString *bucketName = @"examplebucket";
    // オブジェクトの名前を指定します。
    NSString *objectKey = @"exampleobject.txt";
    NSURL *file = [NSURL fileURLWithPath:@"<filePath>"];
    NSString *contentType = [OSSUtil detemineMimeTypeForFilePath:file.absoluteString uploadName:objectKey];
    __block NSString *urlString;
    // オブジェクトをアップロードするための有効期間付きの署名付き URL を生成します。この例では、URL の有効期間は 30 分です。
    OSSTask *task = [client presignConstrainURLWithBucketName:bucketName
                                                withObjectKey:objectKey
                                                   httpMethod:@"PUT"
                                       withExpirationInterval:30 * 60
                                               withParameters:@{}
                                                  contentType:contentType
                                                   contentMd5:nil];
    [task continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
        if (task.error) {
            NSLog(@"presign error: %@", task.error);
        } else {
            urlString = task.result;
            NSLog(@"url: %@", urlString);
        }
        return nil;
    }];

    .NET

    SDK の詳細については、「.NET で署名付き URL を使用してファイルをアップロード」をご参照ください。

    using Aliyun.OSS;
    using Aliyun.OSS.Common;
    // バケットが配置されているリージョンのエンドポイントを指定します。たとえば、バケットが中国 (杭州) リージョンにある場合、エンドポイントを 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");
    // バケット名を指定します (例: examplebucket)。
    var bucketName = "examplebucket";
    // オブジェクトの完全なパスを指定します。完全なパスにはバケット名を含めません。たとえば、exampledir/exampleobject.txt です。
    var objectName = "exampledir/exampleobject.txt";
    var objectContent = "More than just cloud.";
    // バケットが配置されているリージョンを指定します。たとえば、バケットが中国 (杭州) リージョンにある場合、リージョンを 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);
    client.SetRegion(region);
    try
    {
        // 署名付き URL を生成します。
        var generatePresignedUriRequest = new GeneratePresignedUriRequest(bucketName, objectName, SignHttpMethod.Put)
        {
            // 署名付き URL の有効期限を設定します。デフォルト値は 3600 秒です。
            Expiration = DateTime.Now.AddHours(1),
        };
        var signedUrl = client.GeneratePresignedUri(generatePresignedUriRequest);
    }
    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);
    }

    C

    SDK の詳細については、「C で署名付き URL を使用してファイルをダウンロード」をご参照ください。

    #include "oss_api.h"
    #include "aos_http_io.h"
    /* yourEndpoint をバケットが配置されているリージョンのエンドポイントに設定します。たとえば、バケットが中国 (杭州) リージョンにある場合、エンドポイントを https://oss-cn-hangzhou.aliyuncs.com に設定します。*/
    const char *endpoint = "yourEndpoint";
    
    /* バケット名を指定します。たとえば、examplebucket です。*/
    const char *bucket_name = "examplebucket";
    /* オブジェクトの完全なパスを指定します。完全なパスにバケット名を含めることはできません。たとえば、exampledir/exampleobject.txt です。*/
    const char *object_name = "exampledir/exampleobject.txt";
    /* ローカルファイルの完全なパスを指定します。*/
    const char *local_filename = "yourLocalFilename";
    void init_options(oss_request_options_t *options)
    {
        options->config = oss_config_create(options->pool);
        /* aos_string_t 型を char* 文字列で初期化します。*/
        aos_str_set(&options->config->endpoint, endpoint);
        /* 環境変数からアクセス認証情報を取得します。このサンプルコードを実行する前に、OSS_ACCESS_KEY_ID および OSS_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。*/
        aos_str_set(&options->config->access_key_id, getenv("OSS_ACCESS_KEY_ID"));
        aos_str_set(&options->config->access_key_secret, getenv("OSS_ACCESS_KEY_SECRET"));
        /* CNAME を使用して OSS にアクセスするかどうかを指定します。値 0 は CNAME を使用しないことを示します。*/
        options->config->is_cname = 0;
        /* タイムアウト期間などのネットワークパラメーターを設定します。*/
        options->ctl = aos_http_controller_create(options->pool, 0);
    }
    int main(int argc, char *argv[])
    {
        /* プログラムエントリで aos_http_io_initialize メソッドを呼び出して、ネットワークやメモリなどのグローバルリソースを初期化します。*/
        if (aos_http_io_initialize(NULL, 0) != AOSE_OK) {
            exit(1);
        }
        /* メモリ管理用のメモリプール (pool)。apr_pool_t と同等です。その実装コードは APR ライブラリにあります。*/
        aos_pool_t *pool;
        /* 新しいメモリプールを作成します。2 番目のパラメーターは NULL で、プールが他のメモリプールから継承しないことを示します。*/
        aos_pool_create(&pool, NULL);
        /* オプションを作成して初期化します。このパラメーターには、endpoint、access_key_id、access_key_secret、is_cname、curl などのグローバル構成情報が含まれます。*/
        oss_request_options_t *oss_client_options;
        /* メモリプール内のオプションにメモリを割り当てます。*/
        oss_client_options = oss_request_options_create(pool);
        /* クライアントオプション oss_client_options を初期化します。*/
        init_options(oss_client_options);
        /* パラメーターを初期化します。*/
        aos_string_t bucket;
        aos_string_t object;
        aos_string_t file;    
        aos_http_request_t *req;
        apr_time_t now;
        char *url_str;
        aos_string_t url;
        int64_t expire_time; 
        int one_hour = 3600;
        aos_str_set(&bucket, bucket_name);
        aos_str_set(&object, object_name);
        aos_str_set(&file, local_filename);
        expire_time = now / 1000000 + one_hour;    
        req = aos_http_request_create(pool);
        req->method = HTTP_PUT;
        now = apr_time_now(); 
        /* 単位: マイクロ秒。*/
        expire_time = now / 1000000 + one_hour;
        /* 署名付き URL を生成します。*/
        url_str = oss_gen_signed_url(oss_client_options, &bucket, &object, expire_time, req);
        aos_str_set(&url, url_str);
        printf("Temporary upload URL: %s\n", url_str);    
        /* メモリプールを解放します。これは、リクエスト中にさまざまなリソースに割り当てられたメモリを解放することと同じです。*/
        aos_pool_destroy(pool);
        /* 以前に割り当てられたグローバルリソースを解放します。*/
        aos_http_io_deinitialize();
        return 0;
    }
  1. サードパーティが生成された署名付き URL を使用して、PUT リクエストでファイルをアップロードします。

    重要

    署名付き URL は、有効期限が切れる前に複数回アクセスできます。ただし、複数のアップロード操作を実行すると、ファイルが上書きされるリスクがあります。URL の有効期限が切れた後、ファイルへのアクセスを継続するには、ステップ 1 に従って署名付き URL を再生成する必要があります。

    curl

    curl -X PUT -T /path/to/local/file "https://exampleobject.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T083238Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI****************%2F20241112%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-signature=ed5a******************************************************"

    Java

    import org.apache.http.HttpEntity;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPut;
    import org.apache.http.entity.FileEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import java.io.*;
    import java.net.URL;
    import java.util.*;
    
    public class SignUrlUpload {
        public static void main(String[] args) throws Throwable {
            CloseableHttpClient httpClient = null;
            CloseableHttpResponse response = null;
    
            // <signedUrl> を承認済み URL に置き換えます。
            URL signedUrl = new URL("<signedUrl>");
    
            // ローカルファイルの完全なパスを指定します。ローカルパスを指定しない場合、ファイルはサンプルプログラムのプロジェクトパスからデフォルトでアップロードされます。
            String pathName = "C:\\Users\\demo.txt";
    
            try {
                HttpPut put = new HttpPut(signedUrl.toString());
                System.out.println(put);
                HttpEntity entity = new FileEntity(new File(pathName));
                put.setEntity(entity);
                httpClient = HttpClients.createDefault();
                response = httpClient.execute(put);
    
                System.out.println("Status code returned for the upload:"+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("Upload successful using network library.");
                }
                System.out.println(response.toString());
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                response.close();
                httpClient.close();
            }
        }
    }       

    Go

    package main
    
    import (
    	"fmt"
    	"io"
    	"net/http"
    	"os"
    )
    
    func uploadFile(signedUrl, filePath string) error {
    	// ファイルを開きます。
    	file, err := os.Open(filePath)
    	if err != nil {
    		return fmt.Errorf("Unable to open file: %w", err)
    	}
    	defer file.Close()
    
    	// 新しい HTTP クライアントを作成します。
    	client := &http.Client{}
    
    	// PUT リクエストを作成します。
    	req, err := http.NewRequest("PUT", signedUrl, file)
    	if err != nil {
    		return fmt.Errorf("Failed to create request: %w", err)
    	}
    
    	// リクエストを送信します。
    	resp, err := client.Do(req)
    	if err != nil {
    		return fmt.Errorf("Failed to send request: %w", err)
    	}
    	defer resp.Body.Close()
    
    	// 応答を読み取ります。
    	body, err := io.ReadAll(resp.Body)
    	if err != nil {
    		return fmt.Errorf("Failed to read response: %w", err)
    	}
    
    	fmt.Printf("Status code returned for the upload: %d\n", resp.StatusCode)
    	if resp.StatusCode == 200 {
    		fmt.Println("Upload successful using network library.")
    	}
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// <signedUrl> を承認済み URL に置き換えます。
    	signedUrl := "<signedUrl>"
    
    	// ローカルファイルの完全なパスを指定します。ローカルパスを指定しない場合、ファイルはサンプルプログラムのプロジェクトパスからデフォルトでアップロードされます。
    	filePath := "C:\\Users\\demo.txt"
    
    	err := uploadFile(signedUrl, filePath)
    	if err != nil {
    		fmt.Println("An error occurred:", err)
    	}
    }
    

    python

    import requests
    
    def upload_file(signed_url, file_path):
        try:
            # ファイルを開きます。
            with open(file_path, 'rb') as file:
                # PUT リクエストを送信してファイルをアップロードします。
                response = requests.put(signed_url, data=file)
         
            print(f"Status code returned for the upload: {response.status_code}")
            if response.status_code == 200:
                print("Upload successful using network library.")
            print(response.text)
     
        except Exception as e:
            print(f"An error occurred: {e}")
    
    if __name__ == "__main__":
        # <signedUrl> を承認済み URL に置き換えます。
        signed_url = "<signedUrl>"
        
        // ローカルファイルの完全なパスを指定します。ローカルパスを指定しない場合、ファイルはサンプルプログラムのプロジェクトパスからデフォルトでアップロードされます。
        file_path = "C:\\Users\\demo.txt"
    
        upload_file(signed_url, file_path)
    

    Node.js

    const fs = require('fs');
    const axios = require('axios');
    
    async function uploadFile(signedUrl, filePath) {
        try {
            // 読み取りストリームを作成します。
            const fileStream = fs.createReadStream(filePath);
            
            // PUT リクエストを送信してファイルをアップロードします。
            const response = await axios.put(signedUrl, fileStream, {
                headers: {
                    'Content-Type': 'application/octet-stream' // 必要に応じて Content-Type を調整します。
                }
            });
    
            console.log(`Status code returned for the upload: ${response.status}`);
            if (response.status === 200) {
                console.log('Upload successful using network library.');
            }
            console.log(response.data);
        } catch (error) {
            console.error(`An error occurred: ${error.message}`);
        }
    }
    
    // メイン関数。
    (async () => {
        // <signedUrl> を承認済み URL に置き換えます。
        const signedUrl = '<signedUrl>';
        
        // ローカルファイルの完全なパスを指定します。ローカルパスを指定しない場合、ファイルはサンプルプログラムのプロジェクトパスからデフォルトでアップロードされます。
        const filePath = 'C:\\Users\\demo.txt';
    
        await uploadFile(signedUrl, filePath);
    })();

    browser.js

    重要

    Browser.js を使用してファイルをアップロードする際に 403 署名不一致エラーが発生した場合、通常はブラウザが自動的に Content-Type リクエストヘッダーを追加するものの、署名付き URL の生成時にこのヘッダーが指定されていなかったことが原因です。これにより、署名検証が失敗します。この問題を解決するには、署名付き URL を生成する際に Content-Type リクエストヘッダーを指定する必要があります。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>File Upload Example</title>
    </head>
    <body>
        <h1>File Upload Example</h1>
    
        <!-- ファイルを選択 -->
        <input type="file" id="fileInput" />
        <button id="uploadButton">Upload File</button>
    
        <script>
            // これをステップ 1 で生成された署名付き URL に置き換えます。
            const signedUrl = "<signedUrl>"; 
    
    
            document.getElementById('uploadButton').addEventListener('click', async () => {
                const fileInput = document.getElementById('fileInput');
                const file = fileInput.files[0];
    
                if (!file) {
                    alert('Please select a file to upload.');
                    return;
                }
    
                try {
                    await upload(file, signedUrl);
                    alert('File uploaded successfully!');
                } catch (error) {
                    console.error('Error during upload:', error);
                    alert('Upload failed: ' + error.message);
                }
            });
    
            /**
             * ファイルを OSS にアップロードします。
             * @param {File} file - アップロードするファイル。
             * @param {string} presignedUrl - 署名付き URL。
             */
            const upload = async (file, presignedUrl) => {
                const response = await fetch(presignedUrl, {
                    method: 'PUT',
                    body: file,  // ファイル全体を直接アップロードします。
                });
    
                if (!response.ok) {
                    throw new Error(`Upload failed, status: ${response.status}`);
                }
    
                console.log('File uploaded successfully');
            };
        </script>
    </body>
    </html>

    C#

    using System.Net.Http.Headers;
    
    // ローカルファイルの完全なパスを指定します。ローカルパスを指定しない場合、ファイルはサンプルプログラムのプロジェクトパスからデフォルトでアップロードされます。
    var filePath = "C:\\Users\\demo.txt";
    // <signedUrl> を承認済み URL に置き換えます。
    var presignedUrl = "<signedUrl>";
    
    // HTTP クライアントを作成し、ローカルファイルストリームを開きます。
    using var httpClient = new HttpClient(); 
    using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
    using var content = new StreamContent(fileStream);
                
    // PUT リクエストを作成します。
    var request = new HttpRequestMessage(HttpMethod.Put, presignedUrl);
    request.Content = content;
    
    // リクエストを送信します。
    var response = await httpClient.SendAsync(request);
    
    // 応答を処理します。
    if (response.IsSuccessStatusCode)
    {
        Console.WriteLine($"Upload successful! Status code: {response.StatusCode}");
        Console.WriteLine("Response headers:");
        foreach (var header in response.Headers)
        {
            Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
        }
    }
    else
    {
        string responseContent = await response.Content.ReadAsStringAsync();
        Console.WriteLine($"Upload failed! Status code: {response.StatusCode}");
        Console.WriteLine("Response content: " + responseContent);
    }

    C++

    #include <iostream>
    #include <fstream>
    #include <curl/curl.h>
    
    void uploadFile(const std::string& signedUrl, const std::string& filePath) {
        CURL *curl;
        CURLcode res;
    
        curl_global_init(CURL_GLOBAL_DEFAULT);
        curl = curl_easy_init();
    
        if (curl) {
            // URL を設定します。
            curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str());
    
            // リクエストメソッドを PUT に設定します。
            curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    
            // ファイルを開きます。
            FILE *file = fopen(filePath.c_str(), "rb");
            if (!file) {
                std::cerr << "Unable to open file: " << filePath << std::endl;
                return;
            }
    
            // ファイルサイズを取得します。
            fseek(file, 0, SEEK_END);
            long fileSize = ftell(file);
            fseek(file, 0, SEEK_SET);
    
            // ファイルサイズを設定します。
            curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
    
            // 入力ファイルハンドルを設定します。
            curl_easy_setopt(curl, CURLOPT_READDATA, file);
    
            // リクエストを実行します。
            res = curl_easy_perform(curl);
    
            if (res != CURLE_OK) {
                std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
            } else {
                long httpCode = 0;
                curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
                std::cout << "Status code returned for the upload: " << httpCode << std::endl;
    
                if (httpCode == 200) {
                    std::cout << "Upload successful using network library." << std::endl;
                }
            }
    
            // ファイルを閉じます。
            fclose(file);
    
            // クリーンアップします。
            curl_easy_cleanup(curl);
        }
    
        curl_global_cleanup();
    }
    
    int main() {
        // <signedUrl> を承認済み URL に置き換えます。
        std::string signedUrl = "<signedUrl>";
    
        // ローカルファイルの完全なパスを指定します。ローカルパスを指定しない場合、ファイルはサンプルプログラムのプロジェクトパスからデフォルトでアップロードされます。
        std::string filePath = "C:\\Users\\demo.txt";
    
        uploadFile(signedUrl, filePath);
    
        return 0;
    }
    

    Android

    package com.example.signurlupload;
    
    import android.os.AsyncTask;
    import android.util.Log;
    
    import java.io.DataOutputStream;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    public class SignUrlUploadActivity {
    
        private static final String TAG = "SignUrlUploadActivity";
    
        public void uploadFile(String signedUrl, String filePath) {
            new UploadTask().execute(signedUrl, filePath);
        }
    
        private class UploadTask extends AsyncTask<String, Void, String> {
    
            @Override
            protected String doInBackground(String... params) {
                String signedUrl = params[0];
                String filePath = params[1];
    
                HttpURLConnection connection = null;
                DataOutputStream dos = null;
                FileInputStream fis = null;
    
                try {
                    URL url = new URL(signedUrl);
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("PUT");
                    connection.setDoOutput(true);
                    connection.setRequestProperty("Content-Type", "application/octet-stream");
    
                    fis = new FileInputStream(filePath);
                    dos = new DataOutputStream(connection.getOutputStream());
    
                    byte[] buffer = new byte[1024];
                    int length;
    
                    while ((length = fis.read(buffer)) != -1) {
                        dos.write(buffer, 0, length);
                    }
    
                    dos.flush();
                    dos.close();
                    fis.close();
    
                    int responseCode = connection.getResponseCode();
                    Log.d(TAG, "Status code returned for the upload: " + responseCode);
    
                    if (responseCode == 200) {
                        Log.d(TAG, "Upload successful using network library.");
                    }
    
                    return "Upload complete. Status code: " + responseCode;
    
                } catch (IOException e) {
                    e.printStackTrace();
                    return "Upload failed: " + e.getMessage();
                } finally {
                    if (connection != null) {
                        connection.disconnect();
                    }
                }
            }
    
            @Override
            protected void onPostExecute(String result) {
                Log.d(TAG, result);
            }
        }
    
        public static void main(String[] args) {
            SignUrlUploadActivity activity = new SignUrlUploadActivity();
            // <signedUrl> を承認済み URL に置き換えます。
            String signedUrl = "<signedUrl>";
            // ローカルファイルの完全なパスを指定します。ローカルパスを指定しない場合、ファイルはサンプルプログラムのプロジェクトパスからデフォルトでアップロードされます。
            String filePath = "C:\\Users\\demo.txt";
            activity.uploadFile(signedUrl, filePath);
        }
    }
    

署名付き URL を使用したマルチパートアップロードの実行

クライアントを OSS SDK と統合できないが、大きなファイルをアップロードする必要がある場合は、署名付き URL を使用してマルチパートアップロードを実行できます。この方法では、サーバーがアップロードタスクを開始し、各パートの権限を持つ署名付き URL を生成します。その後、クライアントまたはサードパーティのアプリケーションがこれらの URL を使用してパートをアップロードします。すべてのパートがアップロードされた後、サーバーはパートを完全なファイルにマージするリクエストを開始します。

重要

署名付き URL を使用したマルチパートアップロードは比較的複雑です。クライアントは、URL で指定された `partNumber` に対応する正しいパートをアップロードする必要があります。パートが欠落していたり、不一致があったりすると、最終的なファイルが正しくなくなります。したがって、クライアントを OSS SDK と統合できる場合は、STS 権限付与を使用してクライアントサイドの直接アップロードを実装することを推奨します。この方法は開発がより簡単で、より安定したアップロードを提供します。

image

  1. クライアントがアップロードリクエストを開始

    クライアントは、ファイル名、ファイルサイズ、および希望するパートサイズを含むアップロードリクエストをサーバーに送信します。パートサイズは 5 GB を超えることはできません。パートサイズは 5 MB を推奨します。

    次の例はリクエストを示しています。この例では、https://yourserver.com/init-upload はサーバーが提供する初期化 API エンドポイントです。実際のエンドポイントに置き換えてください。

    curl -X POST https://yourserver.com/init-upload \
      -H "Content-Type: application/json" \
      -d '{
        "fileName": "exampleobject.jpg",
        "fileSize": 104857600,
        "partSize": 5242880
    }'
  2. サーバーがアップロードタスクを開始し、署名付き URL を生成

    1. InitiateMultipartUpload 操作を呼び出してアップロードタスクを初期化し、UploadId を取得します。

    2. ファイルサイズと希望するパートサイズに基づいてパートの数を計算します。

    3. 各パートに対応する署名付き URL を生成します。

    4. URL のリストを JSON 形式で返します。

    説明

    サーバーは UploadId を記録し、アップロードされたファイルとの一意のマッピングを確保する必要があります。これは、後続のパートの検証とマージに必要です。必要に応じて、キャッシュまたはデータベースを使用して UploadId を保存できます。

    サンプルコード:アップロードリクエストの初期化と署名付き URL の生成

    Java

    import com.aliyun.oss.ClientBuilderConfiguration;
    import com.aliyun.oss.HttpMethod;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.common.auth.CredentialsProviderFactory;
    import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
    import com.aliyun.oss.common.comm.SignVersion;
    import com.aliyun.oss.model.GeneratePresignedUrlRequest;
    import com.aliyun.oss.model.InitiateMultipartUploadRequest;
    import com.aliyun.oss.model.InitiateMultipartUploadResult;
    
    import java.net.URL;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    public class InitAndGenerateURL {
        public static void main(String[] args) throws Throwable {
            // クライアントから渡されたパラメーターをシミュレートします (単位: バイト)。
            long fileSize = 15 * 1024 * 1024L;   // 15 MB のファイルをシミュレートします。
            long partSize = 5 * 1024 * 1024L;    // 5 MB の希望パートサイズをシミュレートします。
    
            // パートの数を計算します。
            int totalParts = (int) ((fileSize + partSize - 1) / partSize);
    
            // この例では、中国 (杭州) リージョンのパブリックエンドポイントを使用します。
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            String region = "cn-hangzhou";
            String bucketName = "exampleBucket";
            String objectName = "exampleObject.jpeg";
            long expireTime = 3600 * 1000L; // URL は 1 時間で有効期限が切れます。
    
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            ClientBuilderConfiguration config = new ClientBuilderConfiguration();
            config.setSignatureVersion(SignVersion.V4);
    
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .region(region)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(config)
                    .build();
    
            // アップロードタスクを初期化します。
            InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(bucketName, objectName);
            InitiateMultipartUploadResult initResult = ossClient.initiateMultipartUpload(initRequest);
            String uploadId = initResult.getUploadId();
            System.out.println("Upload ID: " + uploadId);
            System.out.println("Total parts: " + totalParts);
    
            // 各パートの署名付き URL を生成します。
            for (int i = 1; i <= totalParts; i++) {
                Map<String, String> headers = new HashMap<>();
                String signedUrl = generatePresignedUrl(ossClient, bucketName, objectName, HttpMethod.PUT,
                        expireTime, i, uploadId, headers);
                System.out.println("Part " + i + " URL: " + signedUrl);
            }
    
            ossClient.shutdown();
        }
    
        public static String generatePresignedUrl(OSS ossClient, String bucketName, String objectName, HttpMethod method,
                                                  long expireTime, int partNum, String uploadId, Map<String, String> headers) {
            Date expiration = new Date(System.currentTimeMillis() + expireTime);
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, method);
            request.setExpiration(expiration);
            request.setHeaders(headers);
            request.addQueryParameter("partNumber", String.valueOf(partNum));
            request.addQueryParameter("uploadId", uploadId);
            URL url = ossClient.generatePresignedUrl(request);
            return url.toString();
        }
    }
    

    Go

    package main
    
    import (
    	"context"
    	"encoding/json"
    	"flag"
    	"fmt"
    	"log"
    	"time"
    
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
    )
    
    type SignedPart struct {
    	PartNumber int    `json:"part_number"`
    	URL        string `json:"url"`
    	Method     string `json:"method"`
    }
    
    type UploadInitResponse struct {
    	UploadId string       `json:"upload_id"`
    	Parts    []SignedPart `json:"parts"`
    }
    
    // グローバルパラメーター変数。
    var (
    	region     string
    	bucketName string
    	objectName string
    )
    
    func init() {
    	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
    	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
    	flag.StringVar(&objectName, "object", "", "The name of the object.")
    }
    
    func main() {
    	flag.Parse()
    
    	// ファイルサイズが 15 MB、パートサイズが 5 MB であると仮定します。
    	fileSize := int64(15 * 1024 * 1024)
    	partSize := int64(5 * 1024 * 1024)
    	totalParts := int((fileSize + partSize - 1) / partSize)
    
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	client := oss.NewClient(cfg)
    
    	// マルチパートアップロードを開始します。
    	resp, err := client.InitiateMultipartUpload(context.TODO(), &oss.InitiateMultipartUploadRequest{
    		Bucket: oss.Ptr(bucketName),
    		Key:    oss.Ptr(objectName),
    	})
    	if err != nil {
    		log.Fatalf("Failed to initiate multipart upload: %v", err)
    	}
    	uploadId := resp.UploadId
    	fmt.Println("Upload ID:", *uploadId)
    	fmt.Println("Total parts:", totalParts)
    
    	// 各パートの署名付き URL を生成します。
    	expire := time.Hour // 署名付き URL の有効期間を 1 時間に設定します。
    	var parts []SignedPart
    	for i := 1; i <= totalParts; i++ {
    		req := &oss.UploadPartRequest{
    			Bucket:     oss.Ptr(bucketName),
    			Key:        oss.Ptr(objectName),
    			PartNumber: int32(i),
    			UploadId:   uploadId,
    		}
    		presignResult, err := client.Presign(context.TODO(), req, oss.PresignExpiration(time.Now().Add(expire)))
    		if err != nil {
    			log.Fatalf("Failed to generate signed URL for part %d: %v", i, err)
    		}
    		parts = append(parts, SignedPart{
    			PartNumber: i,
    			URL:        presignResult.URL,
    			Method:     "PUT",
    		})
    	}
    
    	// JSON を出力します。
    	out := UploadInitResponse{
    		UploadId: *uploadId,
    		Parts:    parts,
    	}
    	outJSON, _ := json.MarshalIndent(out, "", "  ")
    	fmt.Println(string(outJSON))
    }
    

    Python

    import argparse
    import json
    import alibabacloud_oss_v2 as oss
    
    # ファイルパラメーターをシミュレートします。
    FILE_SIZE = 15 * 1024 * 1024  # アップロードするファイルのサイズをシミュレートします (15 MB)。
    PART_SIZE = 5 * 1024 * 1024   # 各パートのサイズ: 5 MB。
    EXPIRE_TIME = 3600            # 署名付き URL の有効期間 (秒単位)。
    
    def main():
        # コマンドライン引数パーサーを作成し、スクリプトの目的を記述します。
        parser = argparse.ArgumentParser(description="presign multipart upload sample")
    
        # バケットが配置されているリージョンを指定する --region コマンドライン引数を追加します。これは必須パラメーターです。
        parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
        # オブジェクトがアップロードされるバケットの名前を指定する --bucket コマンドライン引数を追加します。これは必須パラメーターです。
        parser.add_argument('--bucket', help='The name of the bucket.', required=True)
        # 他のサービスが OSS にアクセスするために使用できるドメイン名を指定する --endpoint コマンドライン引数を追加します。これはオプションのパラメーターです。
        parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
        # OSS 内のオブジェクト (ファイル) のキーを指定する --key コマンドライン引数を追加します。これは必須パラメーターです。
        parser.add_argument('--key', help='The name of the object.', required=True)
    
        args = parser.parse_args()
    
        # OSS クライアントを構成します。
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
        cfg.region = args.region
        # カスタムエンドポイントが提供されている場合は、構成オブジェクトのエンドポイントプロパティを更新します。
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
        
        # OSS クライアントを初期化します。
        client = oss.Client(cfg)
    
        # マルチパートアップロードを開始し、upload_id を取得するリクエストを送信します。
        init_result = client.presign(oss.InitiateMultipartUploadRequest(
            bucket=args.bucket,
            key=args.key,
        ))
    
        # 署名付き URL を使用してリクエストを開始し、upload_id を取得します。
        import requests
        with requests.post(init_result.url, headers=init_result.signed_headers) as resp:
            obj = oss.InitiateMultipartUploadResult()
            oss.serde.deserialize_xml(xml_data=resp.content, obj=obj)
            upload_id = obj.upload_id
    
        # ファイルサイズに基づいてパートの数を計算し、各パートの署名付き URL を生成します。
        total_parts = (FILE_SIZE + PART_SIZE - 1) // PART_SIZE
        parts = []
    
        for part_number in range(1, total_parts + 1):
            req = oss.UploadPartRequest(
                bucket=args.bucket,
                key=args.key,
                part_number=part_number,
                upload_id=upload_id,
                expiration_in_seconds=EXPIRE_TIME
            )
            presign_result = client.presign(req)
            parts.append({
                "part_number": part_number,
                "url": presign_result.url,
                "method": "PUT"
            })
    
        # upload_id とすべてのパートの署名付き URL を JSON 形式で出力します。
        output = {
            "upload_id": upload_id,
            "parts": parts
        }
    
        print(json.dumps(output, indent=2))
    
    if __name__ == "__main__":
        main()
  3. クライアントがパートデータをアップロード

    クライアントは、サーバーから返された署名付き URL のリストを使用して、HTTP PUT リクエストで各パートをアップロードします。この方法は、単一ファイルのアップロードと同じです。すべてのパートがアップロードされた後、クライアントはサーバーにマージ操作を開始するよう通知する必要があります。

    説明

    同時アップロードがサポートされています。ただし、各 URL を使用してアップロードされるコンテンツが正しいパートに対応していることを確認する必要があります。つまり、`partNumber=N` の URL では、ファイルの N 番目のパートのみをアップロードできます。パートを混在させたり、スキップしたりすることはできません。

    次の例は、最初のパートをアップロードする方法を示しています。/path/to/local/file はローカルパートのパスです。

    curl -X PUT -T /path/to/local/file "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.jpg?partNumber=1&uploadId=BE2D0BC931BE4DE1B23F339AABFA49EE&x-oss-credential=LTAI********************%2F20250520%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-date=20250520T082728Z&x-oss-expires=3600&x-oss-signature=81f3d2e5eaa67c432291577ed20af3b3f60df05ab3cddedcdce168ef707f7ad0&x-oss-signature-version=OSS4-HMAC-SHA256"
  4. (オプション) サーバーがアップロードされたパートを検証

    サーバーが「アップロード完了」通知を受け取った後、ListParts 操作を呼び出して以下を検証できます。

    • パートの数が完全であるかどうか。

    • 各パートのサイズが期待どおりであるかどうか。

    説明

    この操作では、以前に記録された UploadId を使用します。

    サンプルコード:サーバーサイドの検証

    Java

    import com.aliyun.oss.ClientBuilderConfiguration;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.common.auth.CredentialsProviderFactory;
    import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
    import com.aliyun.oss.common.comm.SignVersion;
    import com.aliyun.oss.model.*;
    
    import java.util.ArrayList;
    import java.util.List;
    public class complete {
    
        public static void main(String[] args) throws Exception {
            // OSS サービスのエンドポイント。実際のリージョンに置き換えてください。この例では中国 (杭州) を使用します。
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // ご利用のバケット名に置き換えてください。この例では examplebucket を使用します。
            String bucketName = "examplebucket";
            // オブジェクトの完全なパスに置き換えてください。完全なパスにバケット名を含めることはできません。例: exampledir/exampleobject.jpeg。
            String objectName = "exampleobject.jpeg";
            // マルチパートアップロードタスクの ID。
            String uploadId = "4B78****************************";
            // ご利用のリージョンに置き換えてください。この例では中国 (杭州) を使用します。
            String region = "cn-hangzhou";
    
            // 環境変数から認証情報を取得し、OSSClient を構築します。
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            ClientBuilderConfiguration config = new ClientBuilderConfiguration();
            config.setSignatureVersion(SignVersion.V4);
    
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .region(region)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(config)
                    .build();
    
            try {
                // 現在のアップロードタスク内のすべてのアップロード済みパートをリストします。
                ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, objectName, uploadId);
                PartListing partListing = ossClient.listParts(listPartsRequest);
    
                // 後続のマージのためにパートの ETag 情報を収集します。
                List<PartETag> partETags = new ArrayList<>();
                for (PartSummary part : partListing.getParts()) {
                    partETags.add(new PartETag(part.getPartNumber(), part.getETag()));
                }
    
                if (partETags.isEmpty()) {
                    System.out.println("No uploaded parts found. Aborting merge.");
                    return;
                }
    
                // マージリクエストを送信して、マルチパートアップロードを完了します。
                System.out.println("Starting to merge parts...");
                CompleteMultipartUploadRequest completeRequest =
                        new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
    
                CompleteMultipartUploadResult result = ossClient.completeMultipartUpload(completeRequest);
                System.out.println("Parts merged successfully!");
                System.out.println("ETag: " + result.getETag());
    
            } catch (Exception e) {
                System.err.println("Failed to merge parts: " + e.getMessage());
                e.printStackTrace();
            } finally {
                if (ossClient != null) {
                    ossClient.shutdown();
                }
            }
        }
    }
    

    Go

    package main
    
    import (
    	"context"
    	"flag"
    	"log"
    
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
    )
    
    var (
    	region     string
    	bucketName string
    	objectName string
    	uploadId   string
    )
    
    func init() {
    	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
    	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
    	flag.StringVar(&objectName, "object", "", "The name of the object.")
    	flag.StringVar(&uploadId, "uploadId", "", "The upload ID.")
    }
    
    func main() {
    	flag.Parse()
    
    	if region == "" || bucketName == "" || objectName == "" || uploadId == "" {
    		flag.PrintDefaults()
    		log.Fatal("Missing required parameters.")
    	}
    
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	client := oss.NewClient(cfg)
    
    	// アップロードされたパートをリストし、パートの完全なリストを構築します。
    	partListResp, err := client.ListParts(context.TODO(), &oss.ListPartsRequest{
    		Bucket:   oss.Ptr(bucketName),
    		Key:      oss.Ptr(objectName),
    		UploadId: oss.Ptr(uploadId),
    	})
    	if err != nil {
    		log.Fatalf("failed to list parts: %v", err)
    	}
    
    	var parts []oss.UploadPart
    	for _, p := range partListResp.Parts {
    		parts = append(parts, oss.UploadPart{
    			PartNumber: p.PartNumber,
    			ETag:       p.ETag,
    		})
    	}
    
    	// アップロードされたパートをマージします。
    	completeResult, err := client.CompleteMultipartUpload(context.TODO(), &oss.CompleteMultipartUploadRequest{
    		Bucket:   oss.Ptr(bucketName),
    		Key:      oss.Ptr(objectName),
    		UploadId: oss.Ptr(uploadId),
    		CompleteMultipartUpload: &oss.CompleteMultipartUpload{
    			Parts: parts,
    		},
    	})
    	if err != nil {
    		log.Fatalf("failed to complete multipart upload: %v", err)
    	}
    
    	// マージ結果を出力します。
    	log.Println("Upload completed successfully.")
    	log.Printf("Bucket:   %s\n", oss.ToString(completeResult.Bucket))
    	log.Printf("Key:      %s\n", oss.ToString(completeResult.Key))
    	log.Printf("ETag:     %s\n", oss.ToString(completeResult.ETag))
    	log.Printf("Status:   %s\n", completeResult.Status)
    }
    

    Python

    # -*- coding: utf-8 -*-
    import argparse
    import alibabacloud_oss_v2 as oss
    
    def main():
        # コマンドライン引数パーサーを作成し、スクリプトの目的を記述します。
        parser = argparse.ArgumentParser(description="presign multipart upload sample")
        # バケットが配置されているリージョンを指定する --region コマンドライン引数を追加します。これは必須パラメーターです。
        parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
        # オブジェクトがアップロードされるバケットの名前を指定する --bucket コマンドライン引数を追加します。これは必須パラメーターです。
        parser.add_argument('--bucket', help='The name of the bucket.', required=True)
        # 他のサービスが OSS にアクセスするために使用できるドメイン名を指定する --endpoint コマンドライン引数を追加します。これはオプションのパラメーターです。
        parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
        # OSS 内のオブジェクト (ファイル) のキーを指定する --key コマンドライン引数を追加します。これは必須パラメーターです。
        parser.add_argument('--key', help='The name of the object.', required=True)
        # マルチパートアップロードタスクのアップロード ID を指定する --upload_id コマンドライン引数を追加します。これは必須パラメーターです。
        parser.add_argument('--upload_id', help='The upload ID to list parts for.',required=True)
    
        args = parser.parse_args()
    
        # クライアント構成と認証情報を初期化します。
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
        cfg.region = args.region
        if args.endpoint:
            cfg.endpoint = args.endpoint
    
        client = oss.Client(cfg)
    
        try:
            # すべてのアップロード済みパートを取得します。
            list_parts_result = client.list_parts(oss.ListPartsRequest(
                bucket=args.bucket,
                key=args.key,
                upload_id=args.upload_id
            ))
    
            # パートのリストを構築します。
            upload_parts = [
                oss.UploadPart(part_number=part.part_number, etag=part.etag)
                for part in list_parts_result.parts
            ]
    
            if not upload_parts:
                print("No uploaded parts found. Aborting operation.")
                return
    
            # マージリクエストを構築して送信します。
            request = oss.CompleteMultipartUploadRequest(
                bucket=args.bucket,
                key=args.key,
                upload_id=args.upload_id,
                complete_multipart_upload=oss.CompleteMultipartUpload(parts=upload_parts)
            )
    
            result = client.complete_multipart_upload(request)
    
            print("Merge successful!")
            print(f"ETag: {result.etag}")
            print(f"Bucket: {result.bucket}")
            print(f"Key: {result.key}")
    
        except Exception as e:
            print("Failed to merge parts:", e)
    
    if __name__ == "__main__":
        main()
  5. サーバーがパートをマージし、結果を返す

    検証が成功した後、サーバーは CompleteMultipartUpload 操作を呼び出してパートをマージし、アップロード結果をクライアントに返します。

    サンプルコード:サーバーがパートをマージする

    Java

    import com.aliyun.oss.ClientBuilderConfiguration;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.common.auth.CredentialsProviderFactory;
    import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
    import com.aliyun.oss.common.comm.SignVersion;
    import com.aliyun.oss.model.*;
    
    import java.util.ArrayList;
    import java.util.List;
    public class complete {
    
        public static void main(String[] args) throws Exception {
            // OSS サービスのエンドポイント。実際のリージョンに置き換えてください。この例では中国 (杭州) を使用します。
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // ご利用のバケット名に置き換えてください。この例では examplebucket を使用します。
            String bucketName = "examplebucket";
            // オブジェクトの完全なパスに置き換えてください。完全なパスにバケット名を含めることはできません。例: exampledir/exampleobject.jpeg。
            String objectName = "exampleobject.jpeg";
            // マルチパートアップロードタスクの ID。
            String uploadId = "4B78****************************";
            // ご利用のリージョンに置き換えてください。この例では中国 (杭州) を使用します。
            String region = "cn-hangzhou";
    
            // 環境変数から認証情報を取得し、OSSClient を構築します。
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            ClientBuilderConfiguration config = new ClientBuilderConfiguration();
            config.setSignatureVersion(SignVersion.V4);
    
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .region(region)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(config)
                    .build();
    
            try {
                // 現在のアップロードタスク内のすべてのアップロード済みパートをリストします。
                ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, objectName, uploadId);
                PartListing partListing = ossClient.listParts(listPartsRequest);
    
                // 後続のマージのためにパートの ETag 情報を収集します。
                List<PartETag> partETags = new ArrayList<>();
                for (PartSummary part : partListing.getParts()) {
                    partETags.add(new PartETag(part.getPartNumber(), part.getETag()));
                }
    
                if (partETags.isEmpty()) {
                    System.out.println("No uploaded parts found. Aborting merge.");
                    return;
                }
    
                // マージリクエストを送信して、マルチパートアップロードを完了します。
                System.out.println("Starting to merge parts...");
                CompleteMultipartUploadRequest completeRequest =
                        new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
    
                CompleteMultipartUploadResult result = ossClient.completeMultipartUpload(completeRequest);
                System.out.println("Parts merged successfully!");
                System.out.println("ETag: " + result.getETag());
    
            } catch (Exception e) {
                System.err.println("Failed to merge parts: " + e.getMessage());
                e.printStackTrace();
            } finally {
                if (ossClient != null) {
                    ossClient.shutdown();
                }
            }
        }
    }
    

    Go

    package main
    
    import (
    	"context"
    	"flag"
    	"log"
    
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
    )
    
    var (
    	region     string
    	bucketName string
    	objectName string
    	uploadId   string
    )
    
    func init() {
    	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
    	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
    	flag.StringVar(&objectName, "object", "", "The name of the object.")
    	flag.StringVar(&uploadId, "uploadId", "", "The upload ID.")
    }
    
    func main() {
    	flag.Parse()
    
    	if region == "" || bucketName == "" || objectName == "" || uploadId == "" {
    		flag.PrintDefaults()
    		log.Fatal("Missing required parameters.")
    	}
    
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	client := oss.NewClient(cfg)
    
    	// アップロードされたパートをリストし、パートの完全なリストを構築します。
    	partListResp, err := client.ListParts(context.TODO(), &oss.ListPartsRequest{
    		Bucket:   oss.Ptr(bucketName),
    		Key:      oss.Ptr(objectName),
    		UploadId: oss.Ptr(uploadId),
    	})
    	if err != nil {
    		log.Fatalf("failed to list parts: %v", err)
    	}
    
    	var parts []oss.UploadPart
    	for _, p := range partListResp.Parts {
    		parts = append(parts, oss.UploadPart{
    			PartNumber: p.PartNumber,
    			ETag:       p.ETag,
    		})
    	}
    
    	// アップロードされたパートをマージします。
    	completeResult, err := client.CompleteMultipartUpload(context.TODO(), &oss.CompleteMultipartUploadRequest{
    		Bucket:   oss.Ptr(bucketName),
    		Key:      oss.Ptr(objectName),
    		UploadId: oss.Ptr(uploadId),
    		CompleteMultipartUpload: &oss.CompleteMultipartUpload{
    			Parts: parts,
    		},
    	})
    	if err != nil {
    		log.Fatalf("failed to complete multipart upload: %v", err)
    	}
    
    	// マージ結果を出力します。
    	log.Println("Upload completed successfully.")
    	log.Printf("Bucket:   %s\n", oss.ToString(completeResult.Bucket))
    	log.Printf("Key:      %s\n", oss.ToString(completeResult.Key))
    	log.Printf("ETag:     %s\n", oss.ToString(completeResult.ETag))
    	log.Printf("Status:   %s\n", completeResult.Status)
    }
    

    Python

    # -*- coding: utf-8 -*-
    import argparse
    import alibabacloud_oss_v2 as oss
    
    def main():
        # コマンドライン引数パーサーを作成し、スクリプトの目的を記述します。
        parser = argparse.ArgumentParser(description="presign multipart upload sample")
        # バケットが配置されているリージョンを指定する --region コマンドライン引数を追加します。これは必須パラメーターです。
        parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
        # オブジェクトがアップロードされるバケットの名前を指定する --bucket コマンドライン引数を追加します。これは必須パラメーターです。
        parser.add_argument('--bucket', help='The name of the bucket.', required=True)
        # 他のサービスが OSS にアクセスするために使用できるドメイン名を指定する --endpoint コマンドライン引数を追加します。これはオプションのパラメーターです。
        parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
        # OSS 内のオブジェクト (ファイル) のキーを指定する --key コマンドライン引数を追加します。これは必須パラメーターです。
        parser.add_argument('--key', help='The name of the object.', required=True)
        # マルチパートアップロードタスクのアップロード ID を指定する --upload_id コマンドライン引数を追加します。これは必須パラメーターです。
        parser.add_argument('--upload_id', help='The upload ID to list parts for.',required=True)
    
        args = parser.parse_args()
    
        # クライアント構成と認証情報を初期化します。
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
        cfg.region = args.region
        if args.endpoint:
            cfg.endpoint = args.endpoint
    
        client = oss.Client(cfg)
    
        try:
            # すべてのアップロード済みパートを取得します。
            list_parts_result = client.list_parts(oss.ListPartsRequest(
                bucket=args.bucket,
                key=args.key,
                upload_id=args.upload_id
            ))
    
            # パートのリストを構築します。
            upload_parts = [
                oss.UploadPart(part_number=part.part_number, etag=part.etag)
                for part in list_parts_result.parts
            ]
    
            if not upload_parts:
                print("No uploaded parts found. Aborting operation.")
                return
    
            # マージリクエストを構築して送信します。
            request = oss.CompleteMultipartUploadRequest(
                bucket=args.bucket,
                key=args.key,
                upload_id=args.upload_id,
                complete_multipart_upload=oss.CompleteMultipartUpload(parts=upload_parts)
            )
    
            result = client.complete_multipart_upload(request)
    
            print("Merge successful!")
            print(f"ETag: {result.etag}")
            print(f"Bucket: {result.bucket}")
            print(f"Key: {result.key}")
    
        except Exception as e:
            print("Failed to merge parts:", e)
    
    if __name__ == "__main__":
        main()

アップロードポリシーを定義するためのヘッダーの設定

署名付き URL を生成する際に、ヘッダーパラメーターを指定してアップロードポリシーを定義できます。たとえば、次のコード例に示すように、ファイルのストレージクラス x-oss-storage-class とコンテンツタイプ Content-Type を設定できます。

重要

署名付き URL の生成時にヘッダーパラメーターを指定した場合、署名付き URL を使用してファイルをアップロードする際にも同じヘッダーを渡す必要があります。そうしないと、署名検証が失敗するため、OSS は 403 エラーを返します。

設定可能な OSS システムヘッダーの詳細については、「PutObject」をご参照ください。また、カスタムヘッダーを設定してファイルを管理することもできます。詳細については、「ファイルメタデータの管理」をご参照ください。

  1. バケットのオーナーがヘッダーパラメーター付きの署名付き URL を生成します。

    Java

    import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.*;
    import com.aliyun.oss.common.comm.SignVersion;
    import com.aliyun.oss.internal.OSSHeaders;
    import com.aliyun.oss.model.GeneratePresignedUrlRequest;
    import com.aliyun.oss.model.StorageClass;
    
    import java.net.URL;
    import java.util.*;
    import java.util.Date;
    
    public class GetSignUrl {
        public static void main(String[] args) throws Throwable {
            // この例では、中国 (杭州) リージョンのパブリックエンドポイントを使用します。実際のエンドポイントを指定してください。
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // 環境変数からアクセス認証情報を取得します。サンプルコードを実行する前に、OSS_ACCESS_KEY_ID および OSS_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            // バケット名を指定します (例: examplebucket)。
            String bucketName = "examplebucket";
            // オブジェクトの完全なパスを指定します (例: exampleobject.txt)。オブジェクトの完全なパスにバケット名を含めることはできません。
            String objectName = "exampleobject.txt";
            // バケットが配置されているリージョンを指定します。たとえば、バケットが中国 (杭州) リージョンにある場合、リージョンを 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();
    
            // リクエストヘッダーを設定します。
            Map<String, String> headers = new HashMap<String, String>();
            // StorageClass を指定します。
            headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
            // ContentType を指定します。
            headers.put(OSSHeaders.CONTENT_TYPE, "text/plain; charset=utf8");
    
            // カスタムユーザーメタデータを設定します。
            Map<String, String> userMetadata = new HashMap<String, String>();
            userMetadata.put("key1","value1");
            userMetadata.put("key2","value2");
    
            URL signedUrl = null;
            try {
                // 生成された署名付き URL の有効期限をミリ秒単位で指定します。この例では、有効期限を 1 時間に設定します。
                Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
    
                // 署名付き URL を生成します。
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
                // 有効期限を設定します。
                request.setExpiration(expiration);
    
                // リクエストにリクエストヘッダーを追加します。
                request.setHeaders(headers);
                // カスタムユーザーメタデータを追加します。
                request.setUserMetadata(userMetadata);
    
                // HTTP PUT リクエスト用の署名付き URL を生成します。
                signedUrl = ossClient.generatePresignedUrl(request);
                // 署名付き URL を出力します。
                System.out.println("signed url for putObject: " + signedUrl);
    
            } 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 (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());
            }
        }
    }       

    Go

    package main
    
    import (
    	"context"
    	"flag"
    	"log"
    	"time"
    
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
    )
    
    // グローバル変数を定義します。
    var (
    	region     string // バケットが配置されているリージョン。
    	bucketName string // バケットの名前。
    	objectName string // オブジェクトの名前。
    )
    
    // コマンドラインパラメーターを初期化するために使用される init 関数を指定します。
    func init() {
    	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
    	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
    	flag.StringVar(&objectName, "object", "", "The name of the object.")
    }
    
    func main() {
    	// コマンドラインパラメーターを解析します。
    	flag.Parse()
    
    	// バケットの名前が指定されているかどうかを確認します。
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, bucket name required")
    	}
    
    	// リージョンが指定されているかどうかを確認します。
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, region required")
    	}
    
    	// オブジェクトの名前が指定されているかどうかを確認します。
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, object name required")
    	}
    
    	// デフォルトの構成をロードし、認証情報プロバイダーとリージョンを指定します。
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	// OSS クライアントを作成します。
    	client := oss.NewClient(cfg)
    
    	// PutObject リクエスト用の署名付き URL を生成します。
    	result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
    		Bucket:      oss.Ptr(bucketName),
    		Key:         oss.Ptr(objectName),
    		ContentType:  oss.Ptr("text/plain;charset=utf8"),                    // URL に含まれる ContentType パラメーターの値が、リクエストで指定された ContentType の値と同じであることを確認してください。
    		StorageClass: oss.StorageClassStandard,                              // URL に含まれる StorageClass パラメーターの値が、リクエストで指定された StorageClass の値と同じであることを確認してください。
    		Metadata:    map[string]string{"key1": "value1", "key2": "value2"}, // URL に含まれる Metadata の値が、リクエストで指定された Metadata の値と同じであることを確認してください。
    	},
    		oss.PresignExpires(10*time.Minute),
    	)
    	if err != nil {
    		log.Fatalf("failed to put object presign %v", err)
    	}
    
    	log.Printf("request method:%v\n", result.Method)
    	log.Printf("request expiration:%v\n", result.Expiration)
    	log.Printf("request url:%v\n", result.URL)
    	if len(result.SignedHeaders) > 0 {
    		// HTTP PUT リクエストを許可する署名付き URL を生成する際にリクエストヘッダーを指定する場合は、そのリクエストヘッダーが URL を使用して開始される PUT リクエストに含まれていることを確認してください。
    		log.Printf("signed headers:\n")
    		for k, v := range result.SignedHeaders {
    			log.Printf("%v: %v\n", k, v)
    		}
    	}
    }
    

    Python

    import argparse
    import requests
    import alibabacloud_oss_v2 as oss
    
    from datetime import datetime, timedelta
    
    # コマンドラインパラメーターパーサーを作成し、スクリプトの目的を記述します。この例では、HTTP PUT リクエストを許可する署名付き URL を生成する方法を説明します。
    parser = argparse.ArgumentParser(description="presign put object sample")
    
    # 必須のリージョン、バケット名、エンドポイント、オブジェクト名を含むコマンドラインパラメーターを指定します。
    parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
    parser.add_argument('--bucket', help='The name of the bucket.', required=True)
    parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
    parser.add_argument('--key', help='The name of the object.', required=True)
    
    def main():
        # コマンドラインパラメーターを解析して、ユーザーが指定した値を取得します。
        args = parser.parse_args()
    
        # 認証のために環境変数からアクセス認証情報を取得します。
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    
        # SDK のデフォルト構成を使用して構成オブジェクトを作成し、認証情報プロバイダーを指定します。
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
    
        # ユーザーが指定したコマンドラインパラメーターに基づいて、構成オブジェクトのリージョン属性を指定します。
        cfg.region = args.region
    
        # カスタムエンドポイントが提供されている場合は、構成オブジェクトのエンドポイントパラメーターを変更します。
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
    
        # 上記の構成を使用して OSSClient インスタンスを初期化し、インスタンスが OSS と対話できるようにします。
        client = oss.Client(cfg)
    
        # PUT リクエストを開始し、指定されたオブジェクトの署名付き URL を生成するリクエストを送信します。
        pre_result = client.presign(oss.PutObjectRequest(
            bucket=args.bucket, # バケットの名前。
            key=args.key, # オブジェクトの名前。
            content_type='text/plain;charset=utf8', # オブジェクトのタイプを指定します。
            storage_class='Standard', # オブジェクトのストレージクラスを指定します。
            metadata={
                'key1': 'value1', # オブジェクトのメタデータを指定します。
                'key2': 'value2' # オブジェクトのメタデータを指定します。
            }
        ),expires=timedelta(seconds=3600)) # リクエストの有効期間を指定します。この例では、有効期間は 3,600 秒に設定されています。
    
    
        # リクエストで指定されたメソッド、有効期限、署名付き URL を表示して、署名付き URL の有効性を確認します。
        print(f'method: {pre_result.method},'
              f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
              f' url: {pre_result.url}'
              )
    
        # リクエストの署名付きヘッダーを表示します。これは、リクエストが送信されるときに HTTP ヘッダーに含まれます。
        for key, value in pre_result.signed_headers.items():
            print(f'signed headers key: {key}, signed headers value: {value}')
    
    # スクリプトが直接実行されるときに処理ロジックを開始するために main 関数を呼び出します。
    if __name__ == "__main__":
        main() # スクリプトの関数のエントリポイントを指定します。制御フローはここから始まります。
  2. サードパーティが署名付き URL を使用してファイルをアップロードし、同じヘッダーを渡します。

    curl

    curl -X PUT \
         -H "Content-Type: text/plain;charset=utf8" \
         -H "x-oss-storage-class: Standard" \
         -H "x-oss-meta-key1: value1" \
         -H "x-oss-meta-key2: value2" \
         -T "C:\\Users\\demo.txt" \
         "https://exampleobject.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T083238Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI****************%2F20241112%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-signature=ed5a******************************************************"

    Java

    import com.aliyun.oss.internal.OSSHeaders;
    import com.aliyun.oss.model.StorageClass;
    import org.apache.http.HttpEntity;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPut;
    import org.apache.http.entity.FileEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import java.io.*;
    import java.net.URL;
    import java.util.*;
    
    public class SignUrlUpload {
        public static void main(String[] args) throws Throwable {
            CloseableHttpClient httpClient = null;
            CloseableHttpResponse response = null;
    
            // <signedUrl> を承認済み URL に置き換えます。
            URL signedUrl = new URL("<signedUrl>");
    
            // ローカルファイルの完全なパスを指定します。ローカルパスを指定しない場合、ファイルはサンプルプログラムのプロジェクトパスからデフォルトでアップロードされます。
            String pathName = "C:\\Users\\demo.txt";
    
            // リクエストヘッダーを設定します。リクエストヘッダー情報は、URL が生成されたときに使用された情報と同じでなければなりません。
            Map<String, String> headers = new HashMap<String, String>();
            //オブジェクトのストレージクラスを指定します。
            headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
            //ContentType を指定します。
            headers.put(OSSHeaders.CONTENT_TYPE, "text/plain;charset=utf8");
    
            // カスタムユーザーメタデータを設定します。カスタムユーザーメタデータは、URL が生成されたときに使用された情報と同じでなければなりません。
            Map<String, String> userMetadata = new HashMap<String, String>();
            userMetadata.put("key1","value1");
            userMetadata.put("key2","value2");
    
            try {
                HttpPut put = new HttpPut(signedUrl.toString());
                System.out.println(put);
                HttpEntity entity = new FileEntity(new File(pathName));
                put.setEntity(entity);
                // 署名付き URL を生成する際にユーザーメタデータやストレージクラスなどのヘッダーパラメーターを設定した場合、署名付き URL を使用してファイルをアップロードする際にもこれらのパラメーターをサーバーに送信する必要があります。サーバーに送信されたパラメーターが署名付きパラメーターと一致しない場合、署名エラーが報告されます。
                for(Map.Entry header: headers.entrySet()){
                    put.addHeader(header.getKey().toString(),header.getValue().toString());
                }
                for(Map.Entry meta: userMetadata.entrySet()){
                    // userMeta を使用する場合、SDK は内部的に 'x-oss-meta-' プレフィックスを追加します。他の方法でアップロード用の署名付き URL を生成する場合も、userMeta に 'x-oss-meta-' プレフィックスを追加する必要があります。
                    put.addHeader("x-oss-meta-"+meta.getKey().toString(), meta.getValue().toString());
                }
    
                httpClient = HttpClients.createDefault();
    
                response = httpClient.execute(put);
    
                System.out.println("Status code returned for the upload:"+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("Upload successful using network library.");
                }
                System.out.println(response.toString());
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                response.close();
                httpClient.close();
            }
        }
    }       

    Go

    package main
    
    import (
    	"bytes"
    	"fmt"
    	"io/ioutil"
    	"net/http"
    	"os"
    )
    
    func uploadFile(signedUrl string, filePath string, headers map[string]string, metadata map[string]string) error {
    	// ファイルを開きます。
    	file, err := os.Open(filePath)
    	if err != nil {
    		return err
    	}
    	defer file.Close()
    
    	// ファイルの内容を読み取ります。
    	fileBytes, err := ioutil.ReadAll(file)
    	if err != nil {
    		return err
    	}
    
    	// リクエストを作成します。
    	req, err := http.NewRequest("PUT", signedUrl, bytes.NewBuffer(fileBytes))
    	if err != nil {
    		return err
    	}
    
    	// リクエストヘッダーを設定します。
    	for key, value := range headers {
    		req.Header.Set(key, value)
    	}
    
    	// カスタムユーザーメタデータを設定します。
    	for key, value := range metadata {
    		req.Header.Set(fmt.Sprintf("x-oss-meta-%s", key), value)
    	}
    
    	// リクエストを送信します。
    	client := &http.Client{}
    	resp, err := client.Do(req)
    	if err != nil {
    		return err
    	}
    	defer resp.Body.Close()
    
    	// 応答を処理します。
    	fmt.Printf("Status code returned for the upload: %d\n", resp.StatusCode)
    	if resp.StatusCode == 200 {
    		fmt.Println("Upload successful using network library.")
    	} else {
    		fmt.Println("Upload failed.")
    	}
    	body, _ := ioutil.ReadAll(resp.Body)
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// <signedUrl> を承認済み URL に置き換えます。
    	signedUrl := "<signedUrl>"
    
    	// ローカルファイルの完全なパスを指定します。ローカルパスを指定しない場合、ファイルはサンプルプログラムのプロジェクトパスからデフォルトでアップロードされます。
    	filePath := "C:\\Users\\demo.txt"
    
    	// リクエストヘッダーを設定します。リクエストヘッダー情報は、URL が生成されたときに使用された情報と同じでなければなりません。
    	headers := map[string]string{
    		"Content-Type": "text/plain;charset=utf8",
    		"x-oss-storage-class": "Standard",
    	}
    
    	// カスタムユーザーメタデータを設定します。カスタムユーザーメタデータは、URL が生成されたときに使用された情報と同じでなければなりません。
    	metadata := map[string]string{
    		"key1": "value1",
    		"key2": "value2",
    	}
    
    	err := uploadFile(signedUrl, filePath, headers, metadata)
    	if err != nil {
    		fmt.Printf("An error occurred: %v\n", err)
    	}
    }
    

    Python

    import requests
    from requests.auth import HTTPBasicAuth
    import os
    
    def upload_file(signed_url, file_path, headers=None, metadata=None):
        """
        署名付き URL を使用してファイルを OSS にアップロードします。
    
        :param signed_url: 署名付き URL。
        :param file_path: アップロードするファイルの完全なパス。
        :param headers: オプション。カスタム HTTP ヘッダー。
        :param metadata: オプション。カスタムメタデータ。
        :return: None
        """
        if not headers:
            headers = {}
        if not metadata:
            metadata = {}
    
        # ヘッダーを更新し、メタデータプレフィックスを追加します。
        for key, value in metadata.items():
            headers[f'x-oss-meta-{key}'] = value
    
        try:
            with open(file_path, 'rb') as file:
                response = requests.put(signed_url, data=file, headers=headers)
                print(f"Status code returned for the upload: {response.status_code}")
                if response.status_code == 200:
                    print("Upload successful using network library.")
                else:
                    print("Upload failed.")
                print(response.text)
        except Exception as e:
            print(f"An error occurred: {e}")
    
    if __name__ == "__main__":
        // <signedUrl> を承認済み URL に置き換えます。
        signed_url = "<signedUrl>"
       
        // ローカルファイルの完全なパスを指定します。指定しない場合、ファイルはデフォルトでスクリプトが配置されているディレクトリからアップロードされます。
        file_path = "C:\\Users\\demo.txt"
    
        // リクエストヘッダーを設定します。リクエストヘッダー情報は、URL が生成されたときに使用された情報と同じでなければなりません。
        headers = {
             "Content-Type": "text/plain;charset=utf8",
             "x-oss-storage-class": "Standard"
        }
    
        // カスタムユーザーメタデータを設定します。カスタムユーザーメタデータは、URL が生成されたときに使用された情報と同じでなければなりません。
        metadata = {
             "key1": "value1",
             "key2": "value2"
        }
    
        upload_file(signed_url, file_path, headers, metadata)
    

    Node.js

    const fs = require('fs');
    const axios = require('axios');
    
    async function uploadFile(signedUrl, filePath, headers = {}, metadata = {}) {
        try {
            // ヘッダーを更新し、メタデータプレフィックスを追加します。
            for (const [key, value] of Object.entries(metadata)) {
                headers[`x-oss-meta-${key}`] = value;
            }
    
            // ファイルストリームを読み取ります。
            const fileStream = fs.createReadStream(filePath);
    
            // PUT リクエストを送信します。
            const response = await axios.put(signedUrl, fileStream, {
                headers: headers
            });
    
            console.log(`Status code returned for the upload: ${response.status}`);
            if (response.status === 200) {
                console.log("Upload successful using network library.");
            } else {
                console.log("Upload failed.");
            }
            console.log(response.data);
        } catch (error) {
            console.error(`An error occurred: ${error.message}`);
        }
    }
    
    // メイン関数。
    (async () => {
        // <signedUrl> を承認済み URL に置き換えます。
        const signedUrl = "<signedUrl>";
    
        // ローカルファイルの完全なパスを指定します。指定しない場合、ファイルはデフォルトでスクリプトが配置されているディレクトリからアップロードされます。
        const filePath = "C:\\Users\\demo.txt";
    
        // リクエストヘッダーを設定します。リクエストヘッダー情報は、URL が生成されたときに使用された情報と同じでなければなりません。
        const headers = {
             "Content-Type": "text/plain;charset=utf8",
             "x-oss-storage-class": "Standard"
        };
    
        // カスタムユーザーメタデータを設定します。カスタムユーザーメタデータは、URL が生成されたときに使用された情報と同じでなければなりません。
        const metadata = {
             "key1": "value1",
             "key2": "value2"
        };
    
        await uploadFile(signedUrl, filePath, headers, metadata);
    })();
    

    Browser.js

    重要

    Browser.js を使用してファイルをアップロードする際に 403 署名不一致エラーが発生した場合、通常はブラウザが自動的に Content-Type リクエストヘッダーを追加するものの、署名付き URL の生成時にこのヘッダーが指定されていなかったことが原因です。これにより、署名検証が失敗します。この問題を解決するには、署名付き URL を生成する際に Content-Type リクエストヘッダーを指定する必要があります。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>File Upload Example</title>
    </head>
    <body>
        <h1>File Upload Example (Java SDK)</h1>
    
        <input type="file" id="fileInput" />
        <button id="uploadButton">Upload File</button>
    
        <script>
            // これを実際の署名付き URL に置き換えます。
            const signedUrl = "<signedUrl>"; 
    
            document.getElementById('uploadButton').addEventListener('click', async () => {
                const fileInput = document.getElementById('fileInput');
                const file = fileInput.files[0];
    
                if (file) {
                    try {
                        await upload(file, signedUrl);
                    } catch (error) {
                        console.error('Error during upload:', error);
                        alert('Upload failed: ' + error.message);
                    }
                } else {
                    alert('Please select a file to upload.');
                }
            });
    
            const upload = async (file, presignedUrl) => {
                const headers = {
                    "Content-Type": "text/plain;charset=utf8",
                    'x-oss-storage-class': 'Standard',
                    'x-oss-meta-key1': 'value1',
                    'x-oss-meta-key2': 'value2'
                };
    
                const response = await fetch(presignedUrl, {
                    method: 'PUT',
                    headers: headers,
                    body: file
                });
    
                if (!response.ok) {
                    throw new Error(`Upload failed, status: ${response.status}`);
                }
    
                alert('File uploaded successfully');
                console.log('File uploaded successfully');
            };
        </script>
    </body>
    </html>

    C#

    using System.Net.Http.Headers;
    
    // ローカルファイルの完全なパスを指定します。ローカルパスを指定しない場合、ファイルはサンプルプログラムのプロジェクトパスからデフォルトでアップロードされます。
    var filePath = "C:\\Users\\demo.txt";
    // <signedUrl> を承認済み URL に置き換えます。
    var presignedUrl = "<signedUrl>";
    
    // HTTP クライアントを作成し、ローカルファイルストリームを開きます。
    using var httpClient = new HttpClient(); 
    using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
    using var content = new StreamContent(fileStream);
                
    // PUT リクエストを作成します。
    var request = new HttpRequestMessage(HttpMethod.Put, presignedUrl);
    request.Content = content;
    
    // 署名付き URL を生成する際にユーザーメタデータやストレージクラスなどのヘッダーパラメーターを設定した場合、署名付き URL を使用してファイルをアップロードする際にもこれらのパラメーターをサーバーに送信する必要があります。サーバーに送信されたパラメーターが署名付きパラメーターと一致しない場合、署名エラーが報告されます。
    // リクエストヘッダーを設定します。リクエストヘッダー情報は、URL が生成されたときに使用された情報と同じでなければなりません。
    request.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain") { CharSet = "utf8" };  // ContentType を指定します。       
    // カスタムユーザーメタデータを設定します。カスタムユーザーメタデータは、URL が生成されたときに使用された情報と同じでなければなりません。
    request.Content.Headers.Add("x-oss-meta-key1", "value1");
    request.Content.Headers.Add("x-oss-meta-key2", "value2");
    // オブジェクトのストレージクラスを指定します。
    request.Content.Headers.Add("x-oss-storage-class", "Standard");
    
    // リクエストヘッダーを出力します。
    Console.WriteLine("Request headers:");
    foreach (var header in request.Content.Headers)
    {
        Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
    }
    
    // リクエストを送信します。
    var response = await httpClient.SendAsync(request);
    
    // 応答を処理します。
    if (response.IsSuccessStatusCode)
    {
        Console.WriteLine($"Upload successful! Status code: {response.StatusCode}");
        Console.WriteLine("Response headers:");
        foreach (var header in response.Headers)
        {
            Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
        }
    }
    else
    {
        string responseContent = await response.Content.ReadAsStringAsync();
        Console.WriteLine($"Upload failed! Status code: {response.StatusCode}");
        Console.WriteLine("Response content: " + responseContent);
    }

    C++

    #include <iostream>
    #include <fstream>
    #include <curl/curl.h>
    #include <map>
    #include <string>
    
    // HTTP 応答を処理するコールバック関数。
    size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) {
        size_t totalSize = size * nmemb;
        output->append((char*)contents, totalSize);
        return totalSize;
    }
    
    void uploadFile(const std::string& signedUrl, const std::string& filePath, const std::map<std::string, std::string>& headers, const std::map<std::string, std::string>& metadata) {
        CURL* curl;
        CURLcode res;
        std::string readBuffer;
    
        curl_global_init(CURL_GLOBAL_DEFAULT);
        curl = curl_easy_init();
    
        if (curl) {
            // URL を設定します。
            curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str());
    
            // リクエストメソッドを PUT に設定します。
            curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    
            // ファイルを開きます。
            FILE* file = fopen(filePath.c_str(), "rb");
            if (!file) {
                std::cerr << "Unable to open file: " << filePath << std::endl;
                return;
            }
    
            // ファイルサイズを設定します。
            fseek(file, 0, SEEK_END);
            long fileSize = ftell(file);
            rewind(file);
    
            // ファイル読み取りコールバックを設定します。
            curl_easy_setopt(curl, CURLOPT_READDATA, file);
            curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
    
            // リクエストヘッダーを設定します。
            struct curl_slist* chunk = nullptr;
            for (const auto& header : headers) {
                std::string headerStr = header.first + ": " + header.second;
                chunk = curl_slist_append(chunk, headerStr.c_str());
            }
            for (const auto& meta : metadata) {
                std::string metaStr = "x-oss-meta-" + meta.first + ": " + meta.second;
                chunk = curl_slist_append(chunk, metaStr.c_str());
            }
            curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
    
            // 応答処理コールバックを設定します。
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
    
            // リクエストを実行します。
            res = curl_easy_perform(curl);
    
            // 応答を確認します。
            if (res != CURLE_OK) {
                std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
            } else {
                long responseCode;
                curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
                std::cout << "Status code returned for the upload: " << responseCode << std::endl;
                if (responseCode == 200) {
                    std::cout << "Upload successful using network library." << std::endl;
                } else {
                    std::cout << "Upload failed." << std::endl;
                }
                std::cout << readBuffer << std::endl;
            }
    
            // クリーンアップします。
            fclose(file);
            curl_slist_free_all(chunk);
            curl_easy_cleanup(curl);
        }
    
        curl_global_cleanup();
    }
    
    int main() {
        // <signedUrl> を承認済み URL に置き換えます。
        std::string signedUrl = "<signedUrl>";
    
        // ローカルファイルの完全なパスを指定します。ローカルパスを指定しない場合、ファイルはサンプルプログラムのプロジェクトパスからデフォルトでアップロードされます。
        std::string filePath = "C:\\Users\\demo.txt";
    
        // リクエストヘッダーを設定します。リクエストヘッダー情報は、URL が生成されたときに使用された情報と同じでなければなりません。
        std::map<std::string, std::string> headers = {
             {"Content-Type", "text/plain;charset=utf8"},
             {"x-oss-storage-class", "Standard"}
        };
    
        // カスタムユーザーメタデータを設定します。カスタムユーザーメタデータは、URL が生成されたときに使用された情報と同じでなければなりません。
        std::map<std::string, std::string> metadata = {
             {"key1", "value1"},
             {"key2", "value2"}
        };
    
        uploadFile(signedUrl, filePath, headers, metadata);
    
        return 0;
    }
    

    Android

    import android.os.AsyncTask;
    import android.util.Log;
    
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Map.Entry;
    
    public class SignUrlUploadActivity extends AppCompatActivity {
    
        private static final String TAG = "SignUrlUploadActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // <signedUrl> を承認済み URL に置き換えます。
            String signedUrl = "<signedUrl>";
    
            // ローカルファイルの完全なパスを指定します。ローカルパスを指定しない場合、ファイルはサンプルプログラムのプロジェクトパスからデフォルトでアップロードされます。
            String pathName = "/storage/emulated/0/demo.txt";
    
            // リクエストヘッダーを設定します。リクエストヘッダー情報は、URL が生成されたときに使用された情報と同じでなければなりません。
            Map<String, String> headers = new HashMap<>();
            headers.put("Content-Type", "text/plain;charset=utf8");
            headers.put("x-oss-storage-class", "Standard");
    
            // カスタムユーザーメタデータを設定します。カスタムユーザーメタデータは、URL が生成されたときに使用された情報と同じでなければなりません。
            Map<String, String> userMetadata = new HashMap<>();
            userMetadata.put("key1", "value1");
            userMetadata.put("key2", "value2");
    
            new UploadTask().execute(signedUrl, pathName, headers, userMetadata);
        }
    
        private class UploadTask extends AsyncTask<Object, Void, Integer> {
            @Override
            protected Integer doInBackground(Object... params) {
                String signedUrl = (String) params[0];
                String pathName = (String) params[1];
                Map<String, String> headers = (Map<String, String>) params[2];
                Map<String, String> userMetadata = (Map<String, String>) params[3];
    
                try {
                    URL url = new URL(signedUrl);
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("PUT");
                    connection.setDoOutput(true);
                    connection.setUseCaches(false);
    
                    // リクエストヘッダーを設定します。
                    for (Entry<String, String> header : headers.entrySet()) {
                        connection.setRequestProperty(header.getKey(), header.getValue());
                    }
    
                    // カスタムユーザーメタデータを設定します。
                    for (Entry<String, String> meta : userMetadata.entrySet()) {
                        connection.setRequestProperty("x-oss-meta-" + meta.getKey(), meta.getValue());
                    }
    
                    // ファイルを読み取ります。
                    File file = new File(pathName);
                    FileInputStream fileInputStream = new FileInputStream(file);
                    DataOutputStream dos = new DataOutputStream(connection.getOutputStream());
    
                    byte[] buffer = new byte[1024];
                    int count;
                    while ((count = fileInputStream.read(buffer)) != -1) {
                        dos.write(buffer, 0, count);
                    }
    
                    fileInputStream.close();
                    dos.flush();
                    dos.close();
    
                    // 応答を取得します。
                    int responseCode = connection.getResponseCode();
                    Log.d(TAG, "Status code returned for the upload: " + responseCode);
                    if (responseCode == 200) {
                        Log.d(TAG, "Upload successful using network library.");
                    } else {
                        Log.d(TAG, "Upload failed.");
                    }
    
                    InputStream is = connection.getInputStream();
                    byte[] responseBuffer = new byte[1024];
                    StringBuilder responseStringBuilder = new StringBuilder();
                    while ((count = is.read(responseBuffer)) != -1) {
                        responseStringBuilder.append(new String(responseBuffer, 0, count));
                    }
                    Log.d(TAG, responseStringBuilder.toString());
    
                    return responseCode;
                } catch (IOException e) {
                    e.printStackTrace();
                    return -1;
                }
            }
    
            @Override
            protected void onPostExecute(Integer result) {
                super.onPostExecute(result);
                if (result == 200) {
                    Toast.makeText(SignUrlUploadActivity.this, "Upload successful.", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(SignUrlUploadActivity.this, "Upload failed.", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }
    

アップロードコールバックの設定

ファイルをアップロードする際に、コールバックパラメーターを追加して、アップロードが成功した後にアプリケーションサーバーに自動的に通知することができます。コールバックの原則の詳細については、「コールバック」をご参照ください。

  1. バケットのオーナーが、アップロードコールバックパラメーターを含む PUT リクエスト用の署名付き URL を生成します。

    Python

    import argparse
    import base64
    import requests
    import alibabacloud_oss_v2 as oss
    
    from datetime import datetime, timedelta
    
    # コマンドラインパラメーターパーサーを作成し、スクリプトの目的を記述します。この例では、HTTP PUT リクエストを許可する署名付き URL を生成する方法を説明します。
    parser = argparse.ArgumentParser(description="presign put object sample")
    
    # 必須のリージョン、バケット名、エンドポイント、オブジェクト名を含むコマンドラインパラメーターを指定します。
    parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
    parser.add_argument('--bucket', help='The name of the bucket.', required=True)
    parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
    parser.add_argument('--key', help='The name of the object.', required=True)
    
    def main():
        # コマンドラインパラメーターを解析します。
        args = parser.parse_args()
    
        # 認証のために環境変数からアクセス認証情報を取得します。
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    
        # デフォルト構成を使用して cfg オブジェクトを作成し、認証情報プロバイダーを指定します。
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
    
        # cfg オブジェクトのリージョン属性をコマンドラインで提供されたリージョンに設定します。
        cfg.region = args.region
    
        # カスタムエンドポイントが提供されている場合は、cfg オブジェクトのエンドポイント属性を更新します。
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
    
        # 上記の構成を使用して OSSClient インスタンスを初期化します。
        client = oss.Client(cfg)
    
        # カスタムコールバック URL を指定します。
        call_back_url = "http://www.example.com/callback"
        # コールバックパラメーターを構築します: コールバック URL とリクエストボディを指定し、パラメーターを Base64 でエンコードします。
        callback=base64.b64encode(str('{\"callbackUrl\":\"' + call_back_url + '\",\"callbackBody\":\"bucket=${bucket}&object=${object}&my_var_1=${x:var1}&my_var_2=${x:var2}\"}').encode()).decode()
        # カスタムコールバック変数を定義し、Base64 でエンコードします。
        callback_var=base64.b64encode('{\"x:var1\":\"value1\",\"x:var2\":\"value2\"}'.encode()).decode()
    
        # PUT リクエストを開始し、指定されたオブジェクトの署名付き URL を生成するリクエストを送信します。
        pre_result = client.presign(oss.PutObjectRequest(
            bucket=args.bucket,  # バケットの名前。
            key=args.key,  # オブジェクトの名前。
            callback=callback,
            callback_var=callback_var,
        ),expires=timedelta(seconds=3600)) # リクエストの有効期間を指定します。この例では、有効期間は 3,600 秒に設定されています。
    
        # リクエストで指定されたメソッド、有効期限、署名付き URL を表示して、署名付き URL の有効性を確認します。
        print(f'method: {pre_result.method},'
              f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
              f' url: {pre_result.url}'
              )
    
        # リクエストの署名付きヘッダーを表示します。これは、リクエストが送信されるときに HTTP ヘッダーに含まれます。
        for key, value in pre_result.signed_headers.items():
            print(f'signed headers key: {key}, signed headers value: {value}')
    
    # スクリプトが直接実行されるときに処理ロジックを開始するために main 関数を呼び出します。
    if __name__ == "__main__":
        main()  # スクリプトのエントリポイントを指定します。制御フローはここから始まります。

    Go

    package main
    
    import (
    	"context"
    	"encoding/base64"
    	"encoding/json"
    	"flag"
    	"log"
    	"time"
    
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
    )
    
    // グローバル変数を定義します。
    var (
    	region     string // バケットが配置されているリージョン。
    	bucketName string // バケットの名前。
    	objectName string // オブジェクトの名前。
    )
    
    // コマンドラインパラメーターを初期化するために使用される init 関数を指定します。
    func init() {
    	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
    	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
    	flag.StringVar(&objectName, "object", "", "The name of the object.")
    }
    
    func main() {
    	// コマンドラインパラメーターを解析します。
    	flag.Parse()
    
    	// バケットの名前が指定されているかどうかを確認します。
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, bucket name required")
    	}
    
    	// リージョンが指定されているかどうかを確認します。
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, region required")
    	}
    
    	// オブジェクトの名前が指定されているかどうかを確認します。
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, object name required")
    	}
    
    	// デフォルトの構成をロードし、認証情報プロバイダーとリージョンを指定します。
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	// OSS クライアントを作成します。
    	client := oss.NewClient(cfg)
    
    	// コールバックパラメーターを指定します。
    	callbackMap := map[string]string{
    		"callbackUrl":      "http://example.com:23450",                                                                   // コールバックサーバーの URL を指定します。例: https://example.com:23450。
    		"callbackBody":     "bucket=${bucket}&object=${object}&size=${size}&my_var_1=${x:my_var1}&my_var_2=${x:my_var2}", // コールバックリクエストボディを指定します。
    		"callbackBodyType": "application/x-www-form-urlencoded",                                                          // コールバックリクエストボディのタイプを指定します。
    	}
    
    	// コールバックパラメーターの構成を JSON 文字列に変換し、その文字列を Base64 でエンコードしてコールバック構成を渡します。
    	callbackStr, err := json.Marshal(callbackMap)
    	if err != nil {
    		log.Fatalf("failed to marshal callback map: %v", err)
    	}
    	callbackBase64 := base64.StdEncoding.EncodeToString(callbackStr)
    
    	callbackVarMap := map[string]string{}
    	callbackVarMap["x:my_var1"] = "thi is var 1"
    	callbackVarMap["x:my_var2"] = "thi is var 2"
    	callbackVarStr, err := json.Marshal(callbackVarMap)
    	if err != nil {
    		log.Fatalf("failed to marshal callback var: %v", err)
    	}
    	callbackVarBase64 := base64.StdEncoding.EncodeToString(callbackVarStr)
    
    	// PutObject リクエスト用の署名付き URL を生成します。
    	result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
    		Bucket:      oss.Ptr(bucketName),
    		Key:         oss.Ptr(objectName),
    		Callback:    oss.Ptr(callbackBase64),    // コールバックパラメーターを設定します。この場合は Base64 エンコードされた JSON 文字列です。
    		CallbackVar: oss.Ptr(callbackVarBase64), // コールバックリクエストのカスタムパラメーターを指定します。この例では Base64 エンコードされた JSON 文字列です。
    	},
    		oss.PresignExpires(10*time.Minute),
    	)
    	if err != nil {
    		log.Fatalf("failed to put object presign %v", err)
    	}
    
    	log.Printf("request method:%v\n", result.Method)
    	log.Printf("request expiration:%v\n", result.Expiration)
    	log.Printf("request url:%v\n", result.URL)
    	if len(result.SignedHeaders) > 0 {
    		// HTTP PUT リクエストを許可する署名付き URL を生成する際にリクエストヘッダーを指定する場合は、そのリクエストヘッダーが署名付き URL を使用して開始される PUT リクエストに含まれていることを確認してください。
    		log.Printf("signed headers:\n")
    		for k, v := range result.SignedHeaders {
    			log.Printf("%v: %v\n", k, v)
    		}
    	}
    }
    

    Java

    import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.CredentialsProviderFactory;
    import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
    import com.aliyun.oss.common.comm.SignVersion;
    import com.aliyun.oss.internal.OSSHeaders;
    import com.aliyun.oss.model.GeneratePresignedUrlRequest;
    
    import java.net.URL;
    import java.text.SimpleDateFormat;
    import java.util.*;
    
    public class OssPresignExample {
        public static void main(String[] args) throws Throwable {
            // この例では、中国 (杭州) リージョンのパブリックエンドポイントを使用します。実際のエンドポイントを指定してください。
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // 環境変数からアクセス認証情報を取得します。サンプルコードを実行する前に、OSS_ACCESS_KEY_ID および OSS_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            // バケット名を指定します (例: examplebucket)。
            String bucketName = "examplebucket";
            // オブジェクトの完全なパスを指定します (例: exampleobject.txt)。オブジェクトの完全なパスにバケット名を含めることはできません。
            String objectName = "exampleobject.txt";
            // バケットが配置されているリージョンを指定します。たとえば、バケットが中国 (杭州) リージョンにある場合、リージョンを 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();
    
            URL signedUrl = null;
            try {
                // コールバックパラメーターを構築します。
                String callbackUrl = "http://www.example.com/callback";
                String callbackBody = "{\"callbackUrl\":\"" + callbackUrl + "\",\"callbackBody\":\"bucket=${bucket}&object=${object}&my_var_1=${x:var1}&my_var_2=${x:var2}\"}";
                String callbackBase64 = Base64.getEncoder().encodeToString(callbackBody.getBytes());
    
                String callbackVarJson = "{\"x:var1\":\"value1\",\"x:var2\":\"value2\"}";
                String callbackVarBase64 = Base64.getEncoder().encodeToString(callbackVarJson.getBytes());
                // リクエストヘッダーを設定します。
                Map<String, String> headers = new HashMap<String, String>();
                // CALLBACK を指定します。
                headers.put(OSSHeaders.OSS_HEADER_CALLBACK, callbackBase64);
                // CALLBACK-VAR を指定します。
                headers.put(OSSHeaders.OSS_HEADER_CALLBACK_VAR, callbackVarBase64);
    
                // 有効期限を設定します (3600 秒後)。
                Date expiration = new Date(new Date().getTime() + 3600 * 1000);
    
                // 有効期限をフォーマットします。
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
                dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
                String expirationStr = dateFormat.format(expiration);
    
                // リクエストを構築します。
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName);
                request.setMethod(HttpMethod.PUT);
                request.setExpiration(expiration);
                // リクエストにリクエストヘッダーを追加します。
                request.setHeaders(headers);
    
                // callback および callback-var パラメーターを出力します。
                System.out.println("callback:"+callbackBase64);
                System.out.println("callback-var:"+callbackVarBase64);
    
                // 署名付き URL を生成します。
                URL url = ossClient.generatePresignedUrl(request);
    
                // 結果を出力します。
                System.out.println("method: PUT,");
                System.out.println(" expiration: " + expirationStr + ",");
                System.out.println(" url: " + url);
    
            } 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 (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());
            }
        }
    }

    PHP

    <?php
    
    // 依存関係をロードするために autoload ファイルをインクルードします。
    require_once __DIR__ . '/../vendor/autoload.php';
    
    use AlibabaCloud\Oss\V2 as Oss;
    
    // コマンドラインオプションを定義し、記述します。
    $optsdesc = [
        "region" => ['help' => 'The region in which the bucket is located.', 'required' => True], // (必須) バケットが配置されているリージョンを指定します。
        "endpoint" => ['help' => 'The domain names that other services can use to access OSS.', 'required' => False], // (オプション) OSS にアクセスするためのエンドポイントを指定します。
        "bucket" => ['help' => 'The name of the bucket', 'required' => True], // (必須) バケットの名前を指定します。
        "key" => ['help' => 'The name of the object', 'required' => True], // (必須) オブジェクトの名前を指定します。
    ];
    
    // 説明を getopt で必要なロングオプションのリストに変換します。
    // 各オプションの末尾にコロン (:) を追加して、値が必要であることを示します。
    $longopts = \array_map(function ($key) {
        return "$key:";
    }, array_keys($optsdesc));
    
    // コマンドラインオプションを解析します。
    $options = getopt("", $longopts);
    
    // 必須オプションが設定されているかどうかを確認します。
    foreach ($optsdesc as $key => $value) {
        if ($value['required'] === True && empty($options[$key])) {
            $help = $value['help']; // ヘルプ情報を取得します。
            echo "Error: the following arguments are required: --$key, $help" . PHP_EOL;
            exit(1); // 必須オプションが欠落している場合はプログラムを終了します。
        }
    }
    
    // コマンドラインオプションから解析された値を対応する変数に割り当てます。
    $region = $options["region"]; // バケットが配置されているリージョン。
    $bucket = $options["bucket"]; // バケットの名前。
    $key = $options["key"];       // オブジェクトの名前。
    
    // 環境変数からアクセス認証情報をロードします。
    // EnvironmentVariableCredentialsProvider を使用して、環境変数から AccessKey ID と AccessKey Secret を取得します。
    $credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();
    
    // SDK のデフォルト構成を使用します。
    $cfg = Oss\Config::loadDefault();
    $cfg->setCredentialsProvider($credentialsProvider); // 認証情報プロバイダーを指定します。
    $cfg->setRegion($region); // バケットが配置されているリージョンを指定します。
    if (isset($options["endpoint"])) {
        $cfg->setEndpoint($options["endpoint"]); // エンドポイントが提供されている場合は指定します。
    }
    
    // OSSClient インスタンスを作成します。
    $client = new Oss\Client($cfg);
    
    // x-oss-callback および x-oss-callback-var ヘッダーを含めます。
    // コールバック URL を指定します。
    $call_back_url = "http://www.example.com/callback";
    
    // コールバックパラメーターを構築します: コールバック URL とリクエストボディを指定し、両方とも Base64 でエンコードする必要があります。
    // ${x:var1} と ${x:var2} をプレースホルダー {var1} と {var2} に置き換えます。
    $callback_body_template = "bucket={bucket}&object={object}&my_var_1={var1}&my_var_2={var2}";
    $callback_body_replaced = str_replace(
        ['{bucket}', '{object}', '{var1}', '{var2}'],
        [$bucket, $key, 'value1', 'value2'],
        $callback_body_template
    );
    $callback = base64_encode(json_encode([
        "callbackUrl" => $call_back_url,
        "callbackBody" => $callback_body_replaced
    ]));
    
    // カスタムコールバック変数を定義し、Base64 でエンコードします。
    $callback_var = base64_encode(json_encode([
        "x:var1" => "value1",
        "x:var2" => "value2"
    ]));
    
    // データをアップロードするための PutObjectRequest オブジェクトを作成します。
    // Content-Type、メタデータ、およびヘッダーが署名計算に含まれることに注意してください。
    $request = new Oss\Models\PutObjectRequest(
        bucket: $bucket,
        key: $key,
        callback:$callback,
        callbackVar:$callback_var,
    );
    
    // presign メソッドを呼び出して、署名付きリクエストを生成します。
    $result = $client->presign($request);
    
    // 指定されたオブジェクトをアップロードするために使用できる署名付き URL を表示します。
    print(
        'put object presign result:' . var_export($result, true) . PHP_EOL .
        'put object url:' . $result->url . PHP_EOL
    );
    
  2. サードパーティが PUT リクエスト用の署名付き URL を使用してファイルをアップロードします。

    curl

    curl -X PUT \
         -H "x-oss-callback: eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9" \
         -H "x-oss-callback-var: eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==" \
         -T "C:\\Users\\demo.txt" \
         "https://exampleobject.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T083238Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI****************%2F20241112%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-signature=ed5a******************************************************"

    Python

    import requests
    
    def upload_file(signed_url, file_path, headers=None):
        """
        署名付き URL を使用してオブジェクトを OSS にアップロードします。
    
        :param signed_url: 署名付き URL。
        :param file_path: アップロードするローカルファイルの完全なパス。
        :param headers: リクエストヘッダー。このパラメーターはオプションです。
        :return: None
        """
        if not headers:
            headers = {}
    
        try:
            with open(file_path, 'rb') as file:
                response = requests.put(signed_url, data=file, headers=headers)
                print(f"Status code: {response.status_code}")
                if response.status_code == 200:
                    print("The object is uploaded by using the library.")
                else:
                    print("Upload failed.")
                print(response.text)
        except Exception as e:
            print(f"An error occurred: {e}")
    
    if __name__ == "__main__":
        # <signedUrl> を署名付き URL に置き換えます。
        signed_url = "<signedUrl>"
    
        # ローカルファイルの完全なパスを指定します。デフォルトでは、ローカルファイルの完全なパスを指定しない場合、ローカルファイルはスクリプトが保存されているディレクトリからアップロードされます。
        file_path = "C:\\Users\\demo.txt"
    
        headers = {
            "x-oss-callback": "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9",
            "x-oss-callback-var": "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==",
        }
    
        upload_file(signed_url,  file_path, headers)

    Go

    package main
    
    import (
    	"bytes"
    	"fmt"
    	"io"
    
    	"net/http"
    	"os"
    )
    
    func uploadFile(signedUrl string, filePath string, headers map[string]string) error {
    	// ファイルを開きます。
    	file, err := os.Open(filePath)
    	if err != nil {
    		return err
    	}
    	defer file.Close()
    
    	// コンテンツを読み取ります。
    	fileBytes, err := io.ReadAll(file)
    	if err != nil {
    		return err
    	}
    
    	// リクエストを作成します。
    	req, err := http.NewRequest("PUT", signedUrl, bytes.NewBuffer(fileBytes))
    	if err != nil {
    		return err
    	}
    
    	// リクエストヘッダーを指定します。
    	for key, value := range headers {
    		req.Header.Add(key, value)
    	}
    
    	// リクエストを送信します。
    	client := &http.Client{}
    	resp, err := client.Do(req)
    	if err != nil {
    		return err
    	}
    	defer resp.Body.Close()
    
    	// 応答を処理します。
    	fmt.Printf("Status code: %d\n", resp.StatusCode)
    	if resp.StatusCode == 200 {
    		fmt.Println("The object is uploaded by using the library.")
    	} else {
    		fmt.Println("Upload failed.")
    	}
    	body, _ := io.ReadAll(resp.Body)
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// <signedUrl> を署名付き URL に置き換えます。
    	signedUrl := "<signedUrl>"
    	// ローカルファイルの完全なパスを指定します。ローカルファイルのパスが指定されていない場合、ローカルファイルはサンプルプログラムが属するプロジェクトのパスからアップロードされます。
    	filePath := "C:\\Users\\demo.txt"
    
    	// リクエストヘッダーを指定します。リクエストヘッダーの値が、署名付き URL が生成されたときに指定された値と同じであることを確認してください。
    	headers := map[string]string{
    		"x-oss-callback":     "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9",
    		"x-oss-callback-var": "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==",
    	}
    
    	err := uploadFile(signedUrl, filePath, headers)
    	if err != nil {
    		fmt.Printf("An error occurred: %v\n", err)
    	}
    }
    

    Java

    import org.apache.http.HttpEntity;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPut;
    import org.apache.http.entity.FileEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import java.io.*;
    import java.net.URL;
    import java.util.*;
    
    public class SignUrlUpload {
        public static void main(String[] args) throws Throwable {
            CloseableHttpClient httpClient = null;
            CloseableHttpResponse response = null;
    
            // <signedUrl> を署名付き URL に置き換えます。
            URL signedUrl = new URL("<signedUrl>");
    
            // ローカルファイルの完全なパスを指定します。ローカルファイルのパスが指定されていない場合、ローカルファイルはサンプルプログラムが属するプロジェクトのパスからアップロードされます。
            String pathName = "C:\\Users\\demo.txt";
    
            // x-oss-callback および x-oss-callback-var を含むリクエストヘッダーを指定します。
            Map<String, String> headers = new HashMap<String, String>();
            headers.put("x-oss-callback", "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9");
            headers.put("x-oss-callback-var", "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==");
    
            try {
                HttpPut put = new HttpPut(signedUrl.toString());
                System.out.println(put);
                HttpEntity entity = new FileEntity(new File(pathName));
                put.setEntity(entity);
                // 署名付き URL が生成されたときにヘッダーを設定した場合、署名付き URL を使用してファイルをアップロードする際にこれらのヘッダーをサーバーに送信する必要があります。署名計算のためにサーバーに送信されるヘッダーが、署名付き URL が生成されたときに指定されたヘッダーと異なる場合、署名エラーが報告されます。
                for(Map.Entry header: headers.entrySet()){
                    put.addHeader(header.getKey().toString(),header.getValue().toString());
                }
    
                httpClient = HttpClients.createDefault();
    
                response = httpClient.execute(put);
    
                System.out.println("Status code: "+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("The object is uploaded by using the library.");
                }
                System.out.println(response.toString());
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                response.close();
                httpClient.close();
            }
        }
    }       

    PHP

    <?php
    
    function uploadFile($signedUrl, $filePath, $headers = []) {
        // ファイルが存在するかどうかを確認します。
        if (!file_exists($filePath)) {
            echo "The file does not exist: $filePath\n";
            return;
        }
    
        // cURL セッションを初期化します。
        $ch = curl_init();
    
        // cURL オプションを設定します。
        curl_setopt($ch, CURLOPT_URL, $signedUrl);
        curl_setopt($ch, CURLOPT_PUT, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_INFILE, fopen($filePath, 'rb'));
        curl_setopt($ch, CURLOPT_INFILESIZE, filesize($filePath));
        curl_setopt($ch, CURLOPT_HTTPHEADER, array_map(function($key, $value) {
            return "$key: $value";
        }, array_keys($headers), $headers));
    
        // cURL リクエストを実行します。
        $response = curl_exec($ch);
    
        // HTTP ステータスコードを照会します。
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
        // cURL セッションを閉じます。
        curl_close($ch);
    
        // 結果を出力します
        echo "Status code: $httpCode\n";
        if ($httpCode == 200) {
            echo "The object is uploaded by using the library.\n";
        } else {
            echo "Upload failed.\n";
        }
        echo $response . "\n";
    }
    
    // <signedUrl> を署名付き URL に置き換えます。
    $signedUrl = "<signedUrl>";
    
    // ローカルファイルの完全なパスを指定します。デフォルトでは、ローカルファイルの完全なパスを指定しない場合、ローカルファイルはスクリプトが保存されているディレクトリからアップロードされます。
    $filePath = "C:\\Users\\demo.txt";
    
    $headers = [
        "x-oss-callback" => "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9",
        "x-oss-callback-var" => "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==",
    ];
    
    uploadFile($signedUrl, $filePath, $headers);
    
    ?>

詳細情報

署名付き URL とは

署名付き URL は、暗号化された署名と有効期間の検証を使用して、特定の OSS ファイルへの一時的なアクセスを許可する安全なリンクです。署名付き URL が生成されると、AccessKey ペア、リソースパス、有効期限、その他のパラメーターに基づいて、暗号化された署名がローカルで計算されます。その後、署名パラメーターが URL に追加され、完全な署名付き URL が形成されます。一般的な形式は次のとおりです:https://BucketName.Endpoint/Object?signature_parameters

サードパーティがこの URL にアクセスすると、OSS は署名パラメーターを検証します。パラメーターが改ざんされているか、URL の有効期限が切れている場合、アクセスは拒否されます。

以下は署名付き URL の例です。

https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-process=image%2Fresize%2Cp_10&x-oss-date=20241115T095058Z&x-oss-expires=3600&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI****************%2F20241115%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-signature=6e7a********************************

この方法により、バケットのオーナーは AccessKey ペアを公開することなく、サードパーティのファイルへのアクセスを安全に許可できます。

シナリオ

  • 短期的なファイル共有:サードパーティが特定のファイルのアップロードまたはダウンロードをリクエストします。バックエンドは有効期間付きの署名付き URL を生成し、フロントエンドに返します。サードパーティはこの URL を使用して、有効期間内にファイルをアップロードまたはダウンロードでき、データのセキュリティが確保されます。

  • 柔軟なアクセス:バケットのオーナーは、メールやチャットを通じて署名付き URL を共有できます。サードパーティは、URL をブラウザのアドレスバーに貼り付けてファイルをダウンロードできます。

よくある質問

アップロードだけでなく、OSS リソースに対するより多くの操作をサードパーティに許可するにはどうすればよいですか?

署名付き URL に加えて、Alibaba Cloud はより柔軟な一時的な権限付与方法である STS 一時アクセス認証情報を提供しています。サードパーティにファイルのアップロードに加えて、ファイルのリスト表示やコピーなど、OSS リソースに対するより多くの管理操作を実行させたい場合は、STS 一時アクセス認証情報を使用することを推奨します。詳細については、「STS 認証情報を使用した OSS へのアクセス」をご参照ください。

指定されたウェブサイトからのみ OSS リソースへのアクセスを許可し、他のソースからのリクエストを拒否することはできますか?

はい、できます。Referer ベースのホットリンク保護を設定し、ホワイトリストをセットアップできます。この構成により、OSS リソースへのアクセスを自社のウェブサイトなどの特定のウェブサイトに制限し、他のソースが OSS ファイルを直接参照するのを防ぎます。詳細な設定手順については、「他のウェブサイトからの OSS ファイル参照を防ぐための Referer ベースのホットリンク保護の設定」をご参照ください。

CORS エラー

このエラーは、バケットのクロスドメインポリシーが設定されていないか、誤って設定されている場合に発生します。設定の確認方法については、「クロスドメイン設定の構成」をご参照ください。

405 Method Not Allowed エラー

このエラーは、リクエストメソッドが正しくない場合に発生します。署名付き URL を使用してファイルをアップロードする場合は、POST リクエストではなく、PUT リクエストを使用するようにしてください。