All Products
Search
Document Center

Object Storage Service:Gunakan URL yang ditandatangani sebelumnya untuk menangani unggahan objek

Last Updated:Sep 03, 2026

Secara default, daftar kontrol akses (ACL) objek dalam bucket Object Storage Service (OSS) diatur ke privat, artinya hanya pemilik objek yang memiliki izin untuk mengaksesnya. Namun, pemilik dapat menghasilkan URL yang ditandatangani sebelumnya untuk memberikan akses sementara kepada pihak lain agar dapat mengunggah objek dalam jangka waktu tertentu. Fitur ini memungkinkan skenario seperti mengizinkan mitra mengunggah kontrak atau pengguna mengirimkan gambar profil.

Catatan penggunaan

  • Pada topik ini, titik akhir publik wilayah China (Hangzhou) digunakan. Untuk mengakses OSS dari layanan Alibaba Cloud lainnya di wilayah yang sama, gunakan titik akhir internal. Untuk detail tentang wilayah dan titik akhir yang didukung, lihat Wilayah dan titik akhir.

  • Izin tidak diperlukan untuk menghasilkan URL yang ditandatangani sebelumnya. Namun, pihak ketiga hanya dapat berhasil mengunggah file menggunakan URL tersebut jika Anda memiliki izin oss:PutObject. Untuk informasi lebih lanjut, lihat Berikan kebijakan akses kustom kepada Pengguna RAM.

  • URL yang telah ditandatangani tidak mendukung unggahan FormData. Untuk unggahan FormData, gunakan unggahan formulir.

Alur kerja

Gambar berikut menunjukkan proses mengunggah objek ke OSS menggunakan URL yang ditandatangani sebelumnya.

Gunakan URL yang ditandatangani sebelumnya untuk mengunggah objek

  1. Pemilik bucket menghasilkan URL yang ditandatangani sebelumnya yang mengizinkan permintaan PUT.

    Catatan

    URL yang ditandatangani sebelumnya yang dihasilkan menggunakan OSS SDK memiliki periode validitas maksimum 7 hari. Jika Anda menghasilkan URL tersebut menggunakan token Security Token Service (STS), URL tersebut dapat berlaku hingga 12 jam (43.200 detik).

    Java

    Untuk informasi tentang cara menggunakan OSS SDK untuk Java guna menghasilkan URL yang ditandatangani sebelumnya, lihat OSS SDK untuk Java.

    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 {
            // Titik akhir publik China (Hangzhou) digunakan sebagai contoh. Untuk wilayah lain, ganti nilainya dengan informasi aktual Anda.
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // Ambil kredensial akses dari variabel lingkungan. Sebelum menjalankan kode ini, pastikan variabel lingkungan OSS_ACCESS_KEY_ID dan OSS_ACCESS_KEY_SECRET telah disetel.
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            // Tentukan nama bucket, misalnya, "examplebucket".
            String bucketName = "examplebucket";
            // Tentukan jalur lengkap objek, misalnya, "exampleobject.txt". Jalur objek tidak boleh mencakup nama bucket.
            String objectName = "exampleobject.txt";
            // Tentukan wilayah tempat bucket berada. Misalnya, jika bucket berada di China (Hangzhou), atur wilayah ke cn-hangzhou.
            String region = "cn-hangzhou";
    
            // Buat instance OSSClient.
            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 {
                // Tentukan periode validitas untuk URL yang ditandatangani sebelumnya yang dihasilkan dalam milidetik. Pada contoh ini, waktu kedaluwarsa diatur ke 1 jam.
                Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
    
                // Hasilkan URL yang ditandatangani sebelumnya.
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
                // Tentukan waktu kedaluwarsa URL yang ditandatangani sebelumnya.
                request.setExpiration(expiration);
                // Hasilkan URL yang ditandatangani sebelumnya yang mengizinkan metode HTTP PUT.
                signedUrl = ossClient.generatePresignedUrl(request);
                // Tampilkan URL yang ditandatangani sebelumnya.
                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

    Untuk informasi tentang cara menggunakan OSS SDK untuk Go guna menghasilkan URL yang ditandatangani sebelumnya, lihat OSS SDK untuk 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"
    )
    
    // Definisikan variabel global.
    var (
    	region     string // Wilayah bucket.
    	bucketName string // Nama bucket.
    	objectName string // Nama objek.
    )
    
    // Definisikan fungsi init untuk menginisialisasi parameter baris perintah.
    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() {
    	// Uraikan parameter baris perintah.
    	flag.Parse()
    
    	// Periksa apakah nama bucket kosong.
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, bucket name required")
    	}
    
    	// Periksa apakah wilayah kosong.
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, region required")
    	}
    
    	// Periksa apakah nama objek kosong.
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, object name required")
    	}
    
    	// Muat konfigurasi default dan tentukan penyedia kredensial serta wilayah.
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	// Buat instance klien OSS.
    	client := oss.NewClient(cfg)
    
    	// Hasilkan URL yang ditandatangani sebelumnya dengan metode PutObject.
    	result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
    		Bucket: oss.Ptr(bucketName),
    		Key:    oss.Ptr(objectName),
    	},
    		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 {
    		//Jika hasil yang dikembalikan berisi header tanda tangan, pastikan Anda menyertakan header ini saat membuat permintaan PUT dengan URL yang ditandatangani sebelumnya.
    		log.Printf("signed headers:\n")
    		for k, v := range result.SignedHeaders {
    			log.Printf("%v: %v\n", k, v)
    		}
    	}
    }
    

    Python

    Untuk informasi tentang cara menggunakan OSS SDK untuk Python guna menghasilkan URL yang ditandatangani sebelumnya, lihat OSS SDK untuk Python.

    import argparse
    import requests
    import alibabacloud_oss_v2 as oss
    
    from datetime import datetime, timedelta
    
    # Buat parser argumen baris perintah dan jelaskan tujuan skrip: untuk menghasilkan URL permintaan PUT pratanda tangan untuk sebuah objek (Presign Put Object).
    parser = argparse.ArgumentParser(description="presign put object sample")
    
    # Tentukan argumen baris perintah, termasuk wilayah, nama bucket, titik akhir, dan kunci objek yang diperlukan.
    parser.add_argument('--region', help='Wilayah tempat bucket berada.', required=True)
    parser.add_argument('--bucket', help='Nama bucket.', required=True)
    parser.add_argument('--endpoint', help='Nama domain yang dapat digunakan layanan lain untuk mengakses OSS')
    parser.add_argument('--key', help='Nama objek.', required=True)
    
    def main():
        # Urai argumen baris perintah untuk mendapatkan nilai yang diberikan pengguna.
        args = parser.parse_args()
    
        # Muat informasi kredensial akses dari variabel lingkungan untuk verifikasi identitas.
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    
        # Buat objek konfigurasi menggunakan pengaturan default SDK dan atur penyedia otentikasi.
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
    
        # Atur properti wilayah dari objek konfigurasi berdasarkan argumen baris perintah.
        cfg.region = args.region
    
        # Jika titik akhir kustom disediakan, perbarui properti titik akhir di objek konfigurasi.
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
    
        # Inisialisasi klien OSS dengan konfigurasi di atas untuk berinteraksi dengan OSS.
        client = oss.Client(cfg)
    
        # Kirim permintaan untuk menghasilkan permintaan PUT pratanda tangan untuk objek yang ditentukan.
        pre_result = client.presign(oss.PutObjectRequest(
            bucket=args.bucket,  # Nama bucket
            key=args.key,  # Kunci objek
        ),expires=timedelta(seconds=3600)) # Atur waktu kedaluwarsa dalam detik. Di sini, diatur ke 3600 detik.
    
        # Cetak metode, waktu kedaluwarsa, dan URL permintaan pratanda tangan untuk mengonfirmasi validitas tautan.
        print(f'method: {pre_result.method},'
              f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
              f' url: {pre_result.url}'
              )
    
        # Cetak informasi header yang ditandatangani dari permintaan pratanda tangan. Informasi ini akan disertakan dalam header HTTP saat permintaan aktual dikirim.
        for key, value in pre_result.signed_headers.items():
            print(f'signed headers key: {key}, signed headers value: {value}')
    
    # Saat skrip ini dijalankan secara langsung, panggil fungsi utama untuk memulai logika pemrosesan.
    if __name__ == "__main__":
        main()  # Titik masuk skrip. Alur program dimulai di sini.

    Node.js

    Contoh berikut menghasilkan URL yang ditandatangani sebelumnya untuk unggahan reguler. Untuk informasi tentang cara menggunakan OSS SDK untuk Node.js guna menghasilkan URL yang ditandatangani sebelumnya dengan parameter pemrosesan gambar atau ID versi, lihat OSS SDK untuk Node.js.

    const OSS = require("ali-oss");
    
    // Definisikan fungsi untuk membuat URL yang ditandatangani.
    async function generateSignatureUrl(fileName) {
      // Dapatkan URL yang ditandatangani.
      const client = await new OSS({
          accessKeyId: 'yourAccessKeyId',
          accessKeySecret: 'yourAccessKeySecret',
          bucket: 'examplebucket',
          region: 'oss-cn-hangzhou',
          authorizationV4: true
      });
    
      return await client.signatureUrlV4('PUT', 3600, {
          headers: {} // Atur header permintaan berdasarkan header permintaan yang sebenarnya.
      }, fileName);
    }
    // Panggil fungsi dan teruskan nama file.
    generateSignatureUrl('yourFileName').then(url => {
      console.log('Generated Signature URL:', url);
    }).catch(err => {
      console.error('Error generating signature URL:', err);
    });

    PHP

    Contoh berikut menghasilkan URL yang ditandatangani sebelumnya untuk unggahan reguler. Untuk informasi tentang cara menggunakan OSS SDK untuk PHP guna menghasilkan URL yang ditandatangani sebelumnya dengan ID versi, lihat OSS SDK untuk PHP.

    <?php
    if (is_file(__DIR__ . '/../autoload.php')) {
        require_once __DIR__ . '/../autoload.php';
    }
    if (is_file(__DIR__ . '/../vendor/autoload.php')) {
        require_once __DIR__ . '/../vendor/autoload.php';
    }
    
    use OSS\OssClient;
    use OSS\Core\OssException;
    use OSS\Http\RequestCore;
    use OSS\Http\ResponseCore;
    use OSS\Credentials\EnvironmentVariableCredentialsProvider;
    
    // Dapatkan kredensial akses dari variabel lingkungan. Sebelum Anda menjalankan kode sampel, pastikan variabel lingkungan OSS_ACCESS_KEY_ID dan OSS_ACCESS_KEY_SECRET telah dikonfigurasi.
    $provider = new EnvironmentVariableCredentialsProvider();
    // Tentukan titik akhir wilayah tempat bucket berada. Misalnya, jika bucket berada di wilayah China (Hangzhou), atur titik akhir ke https://oss-cn-hangzhou.aliyuncs.com. 
    $endpoint = "yourEndpoint";
    // Tentukan nama bucket. 
    $bucket= "examplebucket";
    // Tentukan jalur lengkap objek. Jangan sertakan nama bucket dalam jalur lengkap objek. 
    $object = "exampleobject.txt";
    // Atur periode validitas URL yang telah ditandatangani ke 600 detik. Nilai maksimum: 32400. 
    $timeout = 600;
    try {
        $config = array(  
            "provider" => $provider,
            "endpoint" => $endpoint,
            'signatureVersion'=>OssClient::OSS_SIGNATURE_VERSION_V4,
            "region"=> "cn-hangzhou"
        );
        $ossClient = new OssClient($config);
        // Buat URL yang telah ditandatangani. 
        $signedUrl = $ossClient->signUrl($bucket, $object, $timeout, "PUT");
        print_r($signedUrl);
    } catch (OssException $e) {
        printf(__FUNCTION__ . ": FAILED\n");
        printf($e->getMessage() . "\n");
        return;
    }           

    Android

    Untuk informasi tentang cara menggunakan OSS SDK untuk Android guna menghasilkan URL yang ditandatangani sebelumnya, lihat Otorisasi akses (Android SDK).

    // Tentukan nama bucket, misalnya, examplebucket.
    String bucketName = "examplebucket";
    // Tentukan jalur lengkap objek sumber, tanpa nama bucket, misalnya, exampleobject.txt.
    String objectKey = "exampleobject.txt";
    // Atur content-type.
    String contentType = "application/octet-stream";
    String url = null;
    try {
        // Buat URL yang telah ditandatangani untuk mengunggah file.
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectKey);
        // Atur waktu kedaluwarsa URL yang telah ditandatangani menjadi 30 menit.
        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++

    Untuk informasi tentang cara menggunakan OSS SDK untuk C++ guna menghasilkan URL yang ditandatangani sebelumnya, lihat Otorisasi akses (C++ SDK).

    #include <alibabacloud/oss/OssClient.h>
    using namespace AlibabaCloud::OSS;
    
    int main(void)
    {
        /* Inisialisasi informasi tentang akun yang digunakan untuk mengakses OSS. */
                
        /* Tentukan titik akhir wilayah tempat bucket berada. Misalnya, jika bucket berada di wilayah China (Hangzhou), atur titik akhir ke https://oss-cn-hangzhou.aliyuncs.com. */
        std::string Endpoint = "yourEndpoint";
        /* Tentukan wilayah tempat bucket berada. Misalnya, jika bucket berada di wilayah China (Hangzhou), atur wilayah ke cn-hangzhou.   * /
        std::string Region = "yourRegion";
        /* Tentukan nama bucket. Contoh: examplebucket. */
        std::string BucketName = "examplebucket";
        /* Tentukan jalur lengkap objek. Jangan sertakan nama bucket di jalur lengkap. Contoh: exampledir/exampleobject.txt. */   
        std::string PutobjectUrlName = "exampledir/exampleobject.txt";
    
         /* Inisialisasi sumber daya, seperti sumber daya jaringan. */
        InitializeSdk();
    
        ClientConfiguration conf;
        conf.signatureVersion = SignatureVersionType::V4;
        /* Dapatkan kredensial akses dari variabel lingkungan. Sebelum Anda menjalankan kode sampel, pastikan bahwa variabel lingkungan OSS_ACCESS_KEY_ID dan OSS_ACCESS_KEY_SECRET telah dikonfigurasi. */
        auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
        OssClient client(Endpoint, credentialsProvider, conf);
        client.SetRegion(Region);
    
        /* Tentukan periode validitas URL pra-tanda tangan. Periode validitas maksimum adalah 32.400. Unit: detik. */
        std::time_t t = std::time(nullptr) + 1200;
        /* Hasilkan URL pra-tanda tangan. */
        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 {
            /* Tangani pengecualian. */
            std::cout << "GeneratePresignedUrl fail" <<
            ",code:" << genOutcome.error().Code() <<
            ",message:" << genOutcome.error().Message() <<
            ",requestId:" << genOutcome.error().RequestId() << std::endl;
            return -1;
        }
    
        /* Lepaskan sumber daya, seperti sumber daya jaringan. */
        ShutdownSdk();
        return 0;
    }

    iOS

    Untuk informasi tentang cara menggunakan OSS SDK untuk iOS guna menghasilkan URL yang ditandatangani sebelumnya, lihat Otorisasi akses (iOS SDK).

    // Tentukan nama bucket. 
    NSString *bucketName = @"examplebucket";
    // Tentukan nama objek. 
    NSString *objectKey = @"exampleobject.txt";
    NSURL *file = [NSURL fileURLWithPath:@"<filePath>"];
    NSString *contentType = [OSSUtil detemineMimeTypeForFilePath:file.absoluteString uploadName:objectKey];
    __block NSString *urlString;
    // Buat URL yang ditandatangani dengan periode validitas untuk mengunggah objek. Dalam contoh ini, periode validitas URL adalah 30 menit. 
    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

    Untuk informasi tentang cara menggunakan OSS SDK untuk .NET guna menghasilkan URL yang ditandatangani sebelumnya, lihat Unggah file menggunakan URL yang ditandatangani (C# SDK V1).

    using Aliyun.OSS;
    using Aliyun.OSS.Common;
    // Tentukan Titik akhir untuk wilayah tempat bucket berada. Misalnya, jika bucket berada di wilayah Tiongkok (Hangzhou), atur Titik akhir ke https://oss-cn-hangzhou.aliyuncs.com.
    var endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
    // Dapatkan kredensial akses dari variabel lingkungan. Sebelum Anda menjalankan kode ini, pastikan bahwa variabel lingkungan OSS_ACCESS_KEY_ID dan OSS_ACCESS_KEY_SECRET telah dikonfigurasi.
    var accessKeyId = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_ID");
    var accessKeySecret = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_SECRET");
    // Tentukan nama bucket, misalnya, examplebucket.
    var bucketName = "examplebucket";
    // Tentukan jalur lengkap objek. Jalur lengkap tidak menyertakan nama bucket. Misalnya, exampledir/exampleobject.txt.
    var objectName = "exampledir/exampleobject.txt";
    var objectContent = "More than just cloud.";
    // Tentukan wilayah tempat bucket berada. Misalnya, jika bucket berada di wilayah Tiongkok (Hangzhou), atur wilayah ke cn-hangzhou.
    const string region = "cn-hangzhou";
    
    // Buat instans ClientConfiguration dan ubah parameter default sesuai kebutuhan.
    var conf = new ClientConfiguration();
    
    // Atur versi tanda tangan ke V4.
    conf.SignatureVersion = SignatureVersion.V4;
    
    // Buat instans OssClient.
    var client = new OssClient(endpoint, accessKeyId, accessKeySecret, conf);
    client.SetRegion(region);
    try
    {
        // Buat URL yang ditandatangani.
        var generatePresignedUriRequest = new GeneratePresignedUriRequest(bucketName, objectName, SignHttpMethod.Put)
        {
            // Atur waktu kedaluwarsa URL yang ditandatangani. Nilai defaultnya adalah 3600 detik.
            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

    Untuk informasi tentang cara menggunakan OSS SDK untuk C guna menghasilkan URL yang ditandatangani sebelumnya, lihat Otorisasi akses (C SDK).

    #include "oss_api.h"
    #include "aos_http_io.h"
    /* Tetapkan yourEndpoint ke titik akhir wilayah tempat bucket berada. Misalnya, jika bucket berada di wilayah China (Hangzhou), tetapkan titik akhir ke https://oss-cn-hangzhou.aliyuncs.com. */
    const char *endpoint = "yourEndpoint";
    
    /* Tentukan nama bucket. Misalnya, examplebucket. */
    const char *bucket_name = "examplebucket";
    /* Tentukan path lengkap objek. Path lengkap tidak boleh mengandung nama bucket. Misalnya, exampledir/exampleobject.txt. */
    const char *object_name = "exampledir/exampleobject.txt";
    /* Tentukan path lengkap file lokal. */
    const char *local_filename = "yourLocalFilename";
    void init_options(oss_request_options_t *options)
    {
        options->config = oss_config_create(options->pool);
        /* Inisialisasi tipe aos_string_t dengan string char*. */
        aos_str_set(&options->config->endpoint, endpoint);
        /* Dapatkan kredensial akses dari variabel lingkungan. Sebelum menjalankan kode contoh ini, pastikan variabel lingkungan OSS_ACCESS_KEY_ID dan OSS_ACCESS_KEY_SECRET telah disetel. */
        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"));
        /* Tentukan apakah akan menggunakan CNAME untuk mengakses OSS. Nilai 0 menunjukkan bahwa CNAME tidak digunakan. */
        options->config->is_cname = 0;
        /* Tetapkan parameter jaringan, seperti periode timeout. */
        options->ctl = aos_http_controller_create(options->pool, 0);
    }
    int main(int argc, char *argv[])
    {
        /* Panggil metode aos_http_io_initialize pada entri program untuk menginisialisasi sumber daya global, seperti jaringan dan memori. */
        if (aos_http_io_initialize(NULL, 0) != AOSE_OK) {
            exit(1);
        }
        /* Memory pool (pool) untuk management memori, yang setara dengan apr_pool_t. Kode implementasinya berada di library APR. */
        aos_pool_t *pool;
        /* Buat memory pool baru. Parameter kedua adalah NULL, yang menunjukkan bahwa pool tersebut tidak mewarisi dari memory pool lain. */
        aos_pool_create(&pool, NULL);
        /* Buat dan inisialisasi opsi. Parameter ini mencakup informasi konfigurasi global, seperti endpoint, access_key_id, access_key_secret, is_cname, dan curl. */
        oss_request_options_t *oss_client_options;
        /* Alokasikan memori untuk opsi dalam memory pool. */
        oss_client_options = oss_request_options_create(pool);
        /* Inisialisasi opsi klien oss_client_options. */
        init_options(oss_client_options);
        /* Inisialisasi parameter. */
        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(); 
        /* Satuan: mikrodetik. */
        expire_time = now / 1000000 + one_hour;
        /* Hasilkan URL yang ditandatangani. */
        url_str = oss_gen_signed_url(oss_client_options, &bucket, &object, expire_time, req);
        aos_str_set(&url, url_str);
        printf("URL unggah sementara: %s\n", url_str);    
        /* Hapus memory pool. Ini setara dengan melepaskan memori yang dialokasikan untuk berbagai sumber daya selama permintaan. */
        aos_pool_destroy(pool);
        /* Lepaskan sumber daya global yang sebelumnya dialokasikan. */
        aos_http_io_deinitialize();
        return 0;
    }
  1. Pihak lain mengunggah objek dengan menggunakan URL yang ditandatangani sebelumnya untuk mengirim permintaan PUT.

    Penting

    URL yang ditandatangani sebelumnya dapat digunakan beberapa kali sebelum kedaluwarsa. Namun, jika Anda mengunggah objek yang sama beberapa kali, setiap unggahan baru akan menimpa unggahan sebelumnya. Setelah URL kedaluwarsa, pemilik perlu menghasilkan URL yang ditandatangani sebelumnya baru (seperti yang ditunjukkan pada Langkah 1) agar pihak lain dapat terus mengakses.

Gunakan URL yang ditandatangani sebelumnya untuk mengunggah objek secara bertahap

Jika Anda perlu mengunggah objek besar tetapi merasa sulit atau tidak mungkin mengintegrasikan OSS SDK ke klien Anda, pertimbangkan unggah multi-bagian menggunakan URL yang ditandatangani sebelumnya. Dalam pendekatan ini, server memulai proses unggahan, dan setiap bagian diberi URL yang ditandatangani sebelumnya agar klien atau layanan pihak ketiga Anda dapat mengunggahnya secara individual. Setelah semua bagian berhasil diunggah, server menggabungkannya menjadi satu objek utuh.

Penting

Mengunggah objek secara bertahap menggunakan URL yang ditandatangani sebelumnya lebih kompleks dibandingkan menggunakan OSS SDK. Saat mengunggah objek secara bertahap menggunakan URL yang ditandatangani sebelumnya, pastikan klien Anda mengunggah bagian yang sesuai dengan nomor bagian yang ditentukan dalam URL tersebut. Bagian yang hilang atau ketidakcocokan bagian akan menyebabkan kegagalan dalam menggabungkan bagian-bagian tersebut. Jika klien Anda mendukung integrasi OSS SDK, kami merekomendasikan Anda menggunakan unggahan langsung klien berdasarkan token STS untuk kesederhanaan dan stabilitas yang lebih baik.

  1. Klien mengirim permintaan unggah multi-bagian.

    Klien mengirimkan permintaan unggah multi-bagian yang mencakup nama objek, ukuran objek, dan ukuran bagian. Ukuran bagian dibatasi maksimal 5 GB, dengan nilai yang direkomendasikan sebesar 5 MB.

    Kode berikut adalah contoh unggah multi-bagian yang dikirim oleh klien. Saat menggunakan contoh ini, ingatlah untuk mengganti https://yourserver.com/init-upload dengan titik akhir inisialisasi aktual Anda di server.

    curl -X POST https://yourserver.com/init-upload \
      -H "Content-Type: application/json" \
      -d '{
        "fileName": "exampleobject.jpg",
        "fileSize": 104857600,
        "partSize": 5242880
    }'
  2. Server memulai unggah multi-bagian dan menghasilkan URL yang ditandatangani sebelumnya.

    1. Memanggil InitiateMultipartUpload untuk memulai unggahan dan mendapatkan ID unggahan.

    2. Menghitung jumlah bagian berdasarkan ukuran objek dan ukuran bagian.

    3. Menghasilkan URL yang ditandatangani sebelumnya untuk setiap bagian.

    4. Mengembalikan daftar URL yang ditandatangani sebelumnya dalam bentuk JSON.

    Catatan

    Server mencatat ID unggahan, yang secara unik mengidentifikasi unggahan multi-bagian dan digunakan untuk memverifikasi serta menggabungkan bagian-bagian tersebut. Anda dapat menyimpan ID unggahan dengan menggunakan cache, database, atau metode lainnya.

    Kode contoh untuk memulai unggahan multi-bagian dan menghasilkan URL yang ditandatangani sebelumnya

    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 {
            // Simulasikan ukuran file dan ukuran bagian yang diberikan klien dalam byte.
            long fileSize = 15 * 1024 * 1024L;   // Simulasikan ukuran file 15 MB.
            long partSize = 5 * 1024 * 1024L;    // Simulasikan ukuran bagian 5 MB.
    
            // Hitung jumlah bagian.
            int totalParts = (int) ((fileSize + partSize - 1) / partSize);
    
            // Tentukan titik akhir untuk mengakses OSS. 
            // Pada contoh ini, titik akhir untuk wilayah China (Hangzhou) digunakan.
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            String region = "cn-hangzhou";
            String bucketName = "exampleBucket";
            String objectName = "exampleObject.jpeg";
            long expireTime = 3600 * 1000L; // Tentukan bahwa URL berlaku selama 1 jam.
    
            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();
    
            // Mulai unggahan multi-bagian.
            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);
    
            // Hasilkan URL yang ditandatangani sebelumnya untuk setiap bagian.
            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"`
    }
    
    // Definisikan variabel global.
    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()
    
    	// Asumsikan ukuran file 15 MB dan ukuran bagian 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)
    
    	// Mulai unggahan multi-bagian.
    	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)
    
    	// Hasilkan URL yang ditandatangani sebelumnya untuk setiap bagian.
    	expire := time.Hour // Atur periode validitas URL yang ditandatangani sebelumnya menjadi 1 jam.
    	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",
    		})
    	}
    
    	// Tampilkan respons dalam 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
    
    # Simulasikan parameter.
    FILE_SIZE = 15 * 1024 * 1024  # Simulasikan ukuran file 15 MB.
    PART_SIZE = 5 * 1024 * 1024   # Simulasikan ukuran bagian 5 MB.
    EXPIRE_TIME = 3600            # Simulasikan periode validitas URL 3.600 detik.
    
    def main():
        # Buat parser argumen baris perintah dan jelaskan tujuan skrip.
        parser = argparse.ArgumentParser(description="presign multipart upload sample")
    
        # Tambahkan argumen --region untuk menentukan wilayah bucket. Argumen ini wajib diisi.
        parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
        # Tambahkan argumen --bucket untuk menentukan bucket. Argumen ini wajib diisi.
        parser.add_argument('--bucket', help='The name of the bucket.', required=True)
        # Tambahkan argumen --endpoint untuk menentukan titik akhir untuk mengakses OSS. Argumen ini opsional.
        parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
        # Tambahkan argumen --key untuk menentukan nama objek. Argumen ini wajib diisi.
        parser.add_argument('--key', help='The name of the object.', required=True)
    
        args = parser.parse_args()
    
        # Buat konfigurasi untuk menginisialisasi klien OSS.
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
        cfg.region = args.region
        # Jika titik akhir disediakan, gunakan titik akhir tersebut.
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
        
        # Inisialisasi klien OSS.
        client = oss.Client(cfg)
    
        # Kirim permintaan inisiasi unggahan multi-bagian dan dapatkan ID unggahan.
        init_result = client.presign(oss.InitiateMultipartUploadRequest(
            bucket=args.bucket,
            key=args.key,
        ))
    
        # Gunakan URL yang telah ditandatangani sebelumnya untuk memulai unggahan dan mendapatkan ID unggahan.
        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
    
        # Hitung jumlah bagian dan hasilkan URL yang ditandatangani sebelumnya untuk setiap bagian.
        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"
            })
    
        # Tampilkan ID unggahan dan URL yang ditandatangani sebelumnya dari semua bagian dalam format JSON.
        output = {
            "upload_id": upload_id,
            "parts": parts
        }
    
        print(json.dumps(output, indent=2))
    
    if __name__ == "__main__":
        main()
  3. Klien mengunggah objek secara bertahap.

    Klien mengirim permintaan PUT untuk mengunggah bagian individual menggunakan URL yang ditandatangani sebelumnya yang dikembalikan oleh server. Setiap bagian diunggah dengan cara yang sama seperti mengunggah objek dengan URL yang ditandatangani sebelumnya. Setelah semua bagian diunggah, klien memberi tahu server untuk menggabungkan bagian-bagian tersebut.

    Catatan

    Unggahan multi-bagian melalui URL yang ditandatangani sebelumnya mendukung pemrosesan konkuren, tetapi sangat penting untuk memastikan bahwa setiap URL digunakan secara eksklusif untuk bagian yang ditentukan. Misalnya, nomor bagian N dalam URL harus digunakan untuk mengunggah bagian ke-N dari file, dan tidak diperbolehkan penggunaan ulang atau melewatkan nomor bagian.

    Perintah contoh berikut mengunggah bagian pertama (/path/to/local/file adalah jalur file lokal):

    curl -X PUT -T /path/to/local/file "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.jpg?partNumber=1\u0026uploadId=BE2D0BC931BE4DE1B23F339AABFA49EE\u0026x-oss-credential=LTAI********************%2F20250520%2Fcn-hangzhou%2Foss%2Faliyun_v4_request\u0026x-oss-date=20250520T082728Z\u0026x-oss-expires=3600\u0026x-oss-signature=81f3d2e5eaa67c432291577ed20af3b3f60df05ab3cddedcdce168ef707f7ad0\u0026x-oss-signature-version=OSS4-HMAC-SHA256"
  4. (Opsional) Server memverifikasi bagian yang diunggah.

    Anda dapat mengonfigurasi server untuk memverifikasi bagian yang diunggah saat diberi tahu tentang penyelesaian unggahan bagian.

    • Verifikasi integritas bagian yang diunggah.

    • Verifikasi bahwa setiap bagian memiliki ukuran yang diharapkan.

    Catatan

    Verifikasi bagian memerlukan ID unggahan yang dicatat sebelumnya.

    Kode contoh untuk memverifikasi bagian

    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 {
            // Tentukan titik akhir untuk wilayah bucket Anda. Pada contoh ini, titik akhir untuk wilayah China (Hangzhou) digunakan.
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // Tentukan nama bucket.
            String bucketName = "examplebucket";
            // Tentukan jalur lengkap objek. Jangan sertakan nama bucket dalam jalur lengkap.
            String objectName = "exampleobject.jpeg";
            // Tentukan ID unggahan.
            String uploadId = "4B78****************************";
            // Tentukan ID wilayah bucket.
            String region = "cn-hangzhou";
    
            // Gunakan kredensial yang disimpan dalam variabel lingkungan untuk membuat klien OSS.
            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 {
                // Daftar semua bagian yang telah diunggah dalam unggahan multi-bagian.
                ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, objectName, uploadId);
                PartListing partListing = ossClient.listParts(listPartsRequest);
    
                // Kumpulkan ETag bagian. ETag akan digunakan untuk penggabungan bagian nanti.
                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. The operation stopped.");
                    return;
                }
    
                // Gabungkan bagian.
                System.out.println("Combining parts...");
                CompleteMultipartUploadRequest completeRequest =
                        new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
    
                CompleteMultipartUploadResult result = ossClient.completeMultipartUpload(completeRequest);
                System.out.println("Parts combined.");
                System.out.println("ETag: " + result.getETag());
    
            } catch (Exception e) {
                System.err.println("Part combination failed: " + 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)
    
    	// Daftar bagian yang diunggah.
    	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,
    		})
    	}
    
    	// Gabungkan bagian yang diunggah.
    	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)
    	}
    
    	// Tampilkan hasil penggabungan.
    	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():
        # Buat parser argumen baris perintah dan jelaskan tujuan skrip.
        parser = argparse.ArgumentParser(description="presign multipart upload sample")
        # Tambahkan argumen --region untuk menentukan wilayah bucket. Argumen ini wajib diisi.
        parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
        # Tambahkan argumen --bucket untuk menentukan bucket. Argumen ini wajib diisi.
        parser.add_argument('--bucket', help='The name of the bucket.', required=True)
        # Tambahkan argumen --endpoint untuk menentukan titik akhir untuk mengakses OSS. Argumen ini opsional.
        parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
        # Tambahkan argumen --key untuk menentukan nama objek. Argumen ini wajib diisi.
        parser.add_argument('--key', help='The name of the object.', required=True)
        # Tambahkan argumen --upload_id untuk menentukan ID unggahan. Argumen ini wajib diisi.
        parser.add_argument('--upload_id', help='The upload ID to list parts for.',required=True)
    
        args = parser.parse_args()
    
        # Tentukan konfigurasi dan kredensial untuk menginisialisasi klien OSS.
        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:
            # Daftar semua bagian yang diunggah.
            list_parts_result = client.list_parts(oss.ListPartsRequest(
                bucket=args.bucket,
                key=args.key,
                upload_id=args.upload_id
            ))
    
            # Bangun daftar bagian yang diunggah.
            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. The operation stopped.")
                return
    
            # Bangun dan eksekusi permintaan penggabungan bagian.
            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("Parts combined.")
            print(f"ETag: {result.etag}")
            print(f"Bucket: {result.bucket}")
            print(f"Key: {result.key}")
    
        except Exception as e:
            print("Part combination failed:", e)
    
    if __name__ == "__main__":
        main()
  5. Server menggabungkan bagian dan mengembalikan hasil unggahan.

    Setelah verifikasi bagian berhasil, server memanggil operasi CompleteMultipartUpload untuk menggabungkan bagian menjadi satu objek utuh dan mengembalikan hasil unggahan ke klien.

    Kode contoh untuk menggabungkan bagian

    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 {
            // Tentukan titik akhir untuk wilayah bucket Anda. Pada contoh ini, titik akhir untuk wilayah China (Hangzhou) digunakan.
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // Tentukan nama bucket Anda.
            String bucketName = "examplebucket";
            // Tentukan jalur lengkap objek Anda. Jangan sertakan nama bucket dalam jalur lengkap.
            String objectName = "exampleobject.jpeg";
            // Tentukan ID unggahan.
            String uploadId = "4B78****************************";
            // Tentukan ID wilayah bucket Anda.
            String region = "cn-hangzhou";
    
            // Gunakan kredensial yang disimpan dalam variabel lingkungan untuk membuat klien OSS.
            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 {
                // Daftar semua bagian yang telah diunggah dalam unggahan multi-bagian.
                ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, objectName, uploadId);
                PartListing partListing = ossClient.listParts(listPartsRequest);
    
                // Kumpulkan ETag bagian. ETag akan digunakan untuk penggabungan bagian nanti.
                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. The operation stopped.");
                    return;
                }
    
                // Gabungkan bagian.
                System.out.println("Combining parts...");
                CompleteMultipartUploadRequest completeRequest =
                        new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
    
                CompleteMultipartUploadResult result = ossClient.completeMultipartUpload(completeRequest);
                System.out.println("Parts combined.");
                System.out.println("ETag: " + result.getETag());
    
            } catch (Exception e) {
                System.err.println("Part combination failed: " + 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)
    
    	// Daftar bagian yang diunggah.
    	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,
    		})
    	}
    
    	// Gabungkan bagian yang diunggah.
    	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)
    	}
    
    	// Tampilkan hasil penggabungan.
    	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():
        # Buat parser argumen baris perintah dan jelaskan tujuan skrip.
        parser = argparse.ArgumentParser(description="presign multipart upload sample")
        # Tambahkan argumen --region untuk menentukan wilayah bucket. Argumen ini wajib diisi.
        parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
        # Tambahkan argumen --bucket untuk menentukan bucket. Argumen ini wajib diisi.
        parser.add_argument('--bucket', help='The name of the bucket.', required=True)
        # Tambahkan argumen --endpoint untuk menentukan titik akhir untuk mengakses OSS. Argumen ini opsional.
        parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
        # Tambahkan argumen --key untuk menentukan nama objek. Argumen ini wajib diisi.
        parser.add_argument('--key', help='The name of the object.', required=True)
        # Tambahkan argumen --upload_id untuk menentukan ID unggahan. Argumen ini wajib diisi.
        parser.add_argument('--upload_id', help='The upload ID to list parts for.',required=True)
    
        args = parser.parse_args()
    
        # Tentukan konfigurasi dan kredensial untuk menginisialisasi klien OSS.
        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:
            # Daftar semua bagian yang diunggah.
            list_parts_result = client.list_parts(oss.ListPartsRequest(
                bucket=args.bucket,
                key=args.key,
                upload_id=args.upload_id
            ))
    
            # Bangun daftar bagian yang diunggah.
            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. The operation stopped.")
                return
    
            # Bangun dan eksekusi permintaan penggabungan bagian.
            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("Parts combined.")
            print(f"ETag: {result.etag}")
            print(f"Bucket: {result.bucket}")
            print(f"Key: {result.key}")
    
        except Exception as e:
            print("Part combination failed:", e)
    
    if __name__ == "__main__":
        main()

Konfigurasikan header untuk menentukan kebijakan unggahan

Saat menghasilkan URL yang ditandatangani sebelumnya, Anda dapat menentukan kebijakan unggahan dengan menetapkan parameter Header. Misalnya, Anda dapat mengatur header seperti x-oss-storage-class (untuk menentukan kelas penyimpanan) dan Content-Type (untuk menentukan tipe objek).

Penting

Jika Anda menyertakan header saat menghasilkan URL yang ditandatangani sebelumnya, Anda harus memberikan header yang sama saat mengunggah objek. Jika header tidak cocok, OSS akan mengembalikan kesalahan 403 karena kegagalan verifikasi tanda tangan.

Untuk informasi tentang header yang didukung, lihat PutObject. Anda juga dapat mengonfigurasi header kustom untuk mengelola metadata objek. Untuk informasi lebih lanjut, lihat Kelola metadata objek.

  1. Pemilik objek menghasilkan URL yang ditandatangani sebelumnya dengan parameter Header.

    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 {
            // Contoh ini menggunakan titik akhir publik Wilayah China (Hangzhou). Tentukan titik akhir yang sebenarnya.
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // Dapatkan kredensial akses dari variabel lingkungan. Sebelum Anda menjalankan kode sampel, pastikan variabel lingkungan OSS_ACCESS_KEY_ID dan OSS_ACCESS_KEY_SECRET telah dikonfigurasi.
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            // Tentukan nama bucket, misalnya, examplebucket.
            String bucketName = "examplebucket";
            // Tentukan jalur lengkap objek, misalnya, exampleobject.txt. Jalur lengkap objek tidak boleh berisi nama bucket.
            String objectName = "exampleobject.txt";
            // Tentukan wilayah tempat bucket berada. Misalnya, jika bucket berada di Wilayah China (Hangzhou), atur wilayah ke cn-hangzhou.
            String region = "cn-hangzhou";
    
            // Buat instans OSSClient.
            // Saat instans OSSClient tidak lagi digunakan, panggil metode shutdown untuk melepaskan sumber daya.
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region(region)
                    .build();
    
            // Atur header permintaan.
            Map<String, String> headers = new HashMap<String, String>();
            // Tentukan StorageClass.
            headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
            // Tentukan ContentType.
            headers.put(OSSHeaders.CONTENT_TYPE, "text/plain; charset=utf8");
    
            // Atur metadata pengguna kustom.
            Map<String, String> userMetadata = new HashMap<String, String>();
            userMetadata.put("key1","value1");
            userMetadata.put("key2","value2");
    
            URL signedUrl = null;
            try {
                // Tentukan waktu kedaluwarsa URL yang telah ditandatangani yang dihasilkan dalam milidetik. Contoh ini mengatur waktu kedaluwarsa menjadi 1 jam.
                Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
    
                // Hasilkan URL yang telah ditandatangani.
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
                // Atur waktu kedaluwarsa.
                request.setExpiration(expiration);
    
                // Tambahkan header permintaan ke permintaan.
                request.setHeaders(headers);
                // Tambahkan metadata pengguna kustom.
                request.setUserMetadata(userMetadata);
    
                // Hasilkan URL yang telah ditandatangani untuk permintaan HTTP PUT.
                signedUrl = ossClient.generatePresignedUrl(request);
                // Cetak URL yang telah ditandatangani.
                System.out.println("signed url for putObject: " + signedUrl);
    
            } catch (OSSException oe) {
                System.out.println("Menangkap OSSException, yang berarti permintaan Anda berhasil sampai ke OSS, "
                        + "tetapi ditolak dengan tanggapan kesalahan karena alasan tertentu.");
                System.out.println("Pesan Kesalahan:" + oe.getErrorMessage());
                System.out.println("Kode Kesalahan:" + oe.getErrorCode());
                System.out.println("ID Permintaan:" + oe.getRequestId());
                System.out.println("ID Host:" + oe.getHostId());
            } catch (ClientException ce) {
                System.out.println("Menangkap ClientException, yang berarti klien mengalami "
                        + "masalah internal yang serius saat mencoba berkomunikasi dengan OSS, "
                        + "seperti tidak dapat mengakses jaringan.");
                System.out.println("Pesan Kesalahan:" + 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()
    
    	// 检查bucket名称是否为空
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, bucket name required")
    	}
    
    	// 检查region是否为空
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, region required")
    	}
    
    	// 检查object名称是否为空
    	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与在使用URL时设置的ContentType一致
    		StorageClass: oss.StorageClassStandard,                              // 请确保在服务端生成该签名URL时设置的StorageClass与在使用URL时设置的StorageClass一致
    		Metadata:    map[string]string{"key1": "value1", "key2": "value2"}, // 请确保在服务端生成该签名URL时设置的Metadata与在使用URL时设置的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 {
    		//当返回结果包含签名头时,使用预签名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
    
    # Buat parser argumen baris perintah dan jelaskan tujuan skrip: untuk membuat URL permintaan PUT presigned untuk sebuah objek (Presign Put Object).
    parser = argparse.ArgumentParser(description="presign put object sample")
    
    # Tentukan argumen baris perintah, termasuk wilayah, nama bucket, titik akhir, dan kunci objek yang diperlukan.
    parser.add_argument('--region', help='Wilayah tempat bucket berada.', required=True)
    parser.add_argument('--bucket', help='Nama bucket.', required=True)
    parser.add_argument('--endpoint', help='Nama domain yang dapat digunakan layanan lain untuk mengakses OSS')
    parser.add_argument('--key', help='Nama objek.', required=True)
    
    def main():
        # Urai argumen baris perintah untuk mendapatkan nilai yang diberikan pengguna.
        args = parser.parse_args()
    
        # Muat informasi kredensial akses dari variabel lingkungan untuk verifikasi identitas.
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    
        # Buat objek konfigurasi menggunakan pengaturan default SDK dan atur penyedia otentikasi.
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
    
        # Atur properti wilayah dari objek konfigurasi berdasarkan argumen baris perintah.
        cfg.region = args.region
    
        # Jika titik akhir kustom disediakan, perbarui properti titik akhir di objek konfigurasi.
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
    
        # Inisialisasi klien OSS dengan konfigurasi di atas untuk berinteraksi dengan OSS.
        client = oss.Client(cfg)
    
        # Kirim permintaan untuk membuat permintaan PUT presigned untuk objek yang ditentukan.
        pre_result = client.presign(oss.PutObjectRequest(
            bucket=args.bucket,  # Nama bucket
            key=args.key,  # Kunci objek
            content_type='text/plain;charset=utf8',  # Tentukan tipe konten.
            storage_class='Standard',  # Tentukan kelas penyimpanan.
            metadata={
                'key1': 'value1',   # Tentukan metadata.
                'key2': 'value2'    # Tentukan metadata.
            }
        ),expires=timedelta(seconds=3600)) # Atur waktu kedaluwarsa dalam detik. Di sini, diatur ke 3600 detik.
    
    
        # Cetak metode, waktu kedaluwarsa, dan URL permintaan presigned untuk mengonfirmasi validitas tautan.
        print(f'method: {pre_result.method},'
              f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
              f' url: {pre_result.url}'
              )
    
        # Cetak informasi header yang ditandatangani dari permintaan presigned. Informasi ini akan disertakan dalam header HTTP saat permintaan yang sebenarnya dikirim.
        for key, value in pre_result.signed_headers.items():
            print(f'signed headers key: {key}, signed headers value: {value}')
    
    # Saat skrip ini dieksekusi secara langsung, panggil fungsi utama untuk memulai logika pemrosesan.
    if __name__ == "__main__":
        main()  # Titik masuk skrip. Alur program dimulai di sini.
  2. Pihak lain mengunggah objek dengan menggunakan URL yang ditandatangani sebelumnya sambil memberikan header yang sama.

    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;
    
            // Ganti  dengan URL yang ditandatangani.
            URL signedUrl = new URL("");
    
            // Tentukan jalur lengkap file lokal. Secara default, jika Anda tidak menentukan jalur lengkap file lokal, file lokal diunggah dari jalur proyek tempat program contoh berada.
            String pathName = "C:\\Users\\demo.txt";
    
            // Tentukan header permintaan. Pastikan bahwa nilai header permintaan sama dengan header permintaan saat URL yang ditandatangani dihasilkan.
            Map headers = new HashMap();
            //Tentukan kelas penyimpanan.
            headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
            //Tentukan tipe konten.
            headers.put(OSSHeaders.CONTENT_TYPE, "text/plain;charset=utf8");
    
            // Tentukan metadata pengguna. Pastikan bahwa metadata pengguna sama dengan metadata pengguna saat URL yang ditandatangani dihasilkan.
            Map userMetadata = new HashMap();
            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);
                // Jika Anda mengonfigurasi header seperti metadata pengguna dan kelas penyimpanan saat URL yang ditandatangani sebelumnya dihasilkan, header ini harus dikirim ke server saat URL tersebut digunakan untuk mengunggah file. Jika header yang dikirim ke server untuk perhitungan tanda tangan tidak konsisten dengan yang ditentukan saat URL yang ditandatangani sebelumnya dihasilkan, kesalahan tanda tangan akan dilaporkan.
                for(Map.Entry header: headers.entrySet()){
                    put.addHeader(header.getKey().toString(),header.getValue().toString());
                }
                for(Map.Entry meta: userMetadata.entrySet()){
                    // Jika metadata pengguna ditentukan, SDK menambahkan awalan "x-oss-meta-" ke kunci metadata pengguna. Jika Anda menggunakan metode lain untuk mengunggah file, pastikan awalan "x-oss-meta-" juga ditambahkan ke kunci metadata pengguna.
                    put.addHeader("x-oss-meta-"+meta.getKey().toString(), meta.getValue().toString());
                }
    
                httpClient = HttpClients.createDefault();
    
                response = httpClient.execute(put);
    
                System.out.println("Status code of the upload: "+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("The object is uploaded by using the 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 {
    	// Buka file lokal
    	file, err := os.Open(filePath)
    	if err != nil {
    		return err
    	}
    	defer file.Close()
    
    	// Baca konten objek
    	fileBytes, err := ioutil.ReadAll(file)
    	if err != nil {
    		return err
    	}
    
    	// Buat permintaan
    	req, err := http.NewRequest("PUT", signedUrl, bytes.NewBuffer(fileBytes))
    	if err != nil {
    		return err
    	}
    
    	// Tentukan header permintaan
    	for key, value := range headers {
    		req.Header.Set(key, value)
    	}
    
    	// Tentukan metadata pengguna
    	for key, value := range metadata {
    		req.Header.Set(fmt.Sprintf("x-oss-meta-%s", key), value)
    	}
    
    	// Kirim permintaan
    	client := &http.Client{}
    	resp, err := client.Do(req)
    	if err != nil {
    		return err
    	}
    	defer resp.Body.Close()
    
    	// Proses tanggapan
    	fmt.Printf("Status code: %d\n", resp.StatusCode)
    	if resp.StatusCode == 200 {
    		fmt.Println("The object is uploaded by using the network library")
    	} else {
    		fmt.Println("Upload failed")
    	}
    	body, _ := ioutil.ReadAll(resp.Body)
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// Ganti dengan URL yang ditandatangani.
    	signedUrl := ""
    
    	// Tentukan jalur lengkap file lokal. Secara default, jika Anda tidak menentukan jalur lengkap file lokal, file lokal akan diunggah dari jalur proyek tempat program sampel berada.
    	filePath := "C:\\Users\\demo.txt"
    
    	// Tentukan header permintaan. Pastikan nilai header permintaan sama dengan yang ditentukan saat URL yang telah ditandatangani dibuat.
    	headers := map[string]string{
    		"Content-Type": "text/plain;charset=utf8",
    		"x-oss-storage-class": "Standard",
    	}
    
    	// Tentukan metadata pengguna. Pastikan metadata pengguna sama dengan metadata pengguna saat URL yang ditandatangani dibuat.
    	metadata := map[string]string{
    		"key1": "value1",
    		"key2": "value2",
    	}, 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):
        """
        Gunakan URL yang telah ditandatangani sebelumnya untuk mengunggah objek ke OSS.
    
        :param signed_url: URL yang telah ditandatangani sebelumnya.
        :param file_path: jalur lengkap file lokal yang ingin Anda unggah.
        :param headers: Opsional. Tentukan header permintaan.
        :param metadata: metadata pengguna. Parameter ini opsional.
        :return: None
        """
        if not headers:
            headers = {}
        if not metadata:
            metadata = {}
    
        # Tambahkan awalan x-oss-meta- ke kunci 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: {response.status_code}")
                if response.status_code == 200:
                    print("The object is uploaded by using the network library")
                else:
                    print("Upload failed")
                print(response.text)
        except Exception as e:
            print(f"An error occurred: {e}")
    
    if __name__ == "__main__":
        // Ganti  dengan URL yang ditandatangani.
        signed_url = ""
       
        // Tentukan jalur lengkap file lokal. Secara default, jika Anda tidak menentukan jalur lengkap file lokal, file lokal diunggah dari direktori tempat skrip disimpan.
        file_path = "C:\\Users\\demo.txt"
    
        // Tentukan header permintaan. Pastikan bahwa nilai header permintaan sama dengan yang ditentukan saat URL yang ditandatangani sebelumnya dihasilkan.
        headers = {
             "Content-Type": "text/plain;charset=utf8",
             "x-oss-storage-class": "Standard"
        }
    
        // Tentukan metadata pengguna. Pastikan bahwa metadata pengguna sama dengan metadata pengguna saat URL yang ditandatangani sebelumnya dihasilkan.
        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 {
            // Perbarui header permintaan dan tentukan awalan metadata.
            for (const [key, value] of Object.entries(metadata)) {
                headers[`x-oss-meta-${key}`] = value;
            }
    
            // Baca aliran file
            const fileStream = fs.createReadStream(filePath);
    
            // Kirim permintaan PUT
            const response = await axios.put(signedUrl, fileStream, {
                headers: headers
            });
    
            console.log(`Status code: ${response.status}`);
            if (response.status === 200) {
                console.log("The object is uploaded by using the network library");
            } else {
                console.log("Upload failed");
            }
            console.log(response.data);
        } catch (error) {
            console.error(`An error occurred: ${error.message}`);
        }
    }
    
    // Tentukan fungsi utama
    (async () => {
        // Ganti  dengan URL yang ditandatangani.
        const signedUrl = "";
    
        // Tentukan jalur lengkap file lokal. Secara default, jika Anda tidak menentukan jalur lengkap file lokal, file lokal diunggah dari direktori tempat skrip disimpan.
        const filePath = "C:\\Users\\demo.txt";
    
        // Tentukan header permintaan. Pastikan bahwa nilai header permintaan sama dengan yang ditentukan saat URL yang ditandatangani sebelumnya dihasilkan.
        const headers = {
             "Content-Type": "text/plain;charset=utf8",
             "x-oss-storage-class": "Standard"
        };
    
        // Tentukan metadata pengguna. Pastikan bahwa metadata pengguna sama dengan metadata pengguna saat URL yang ditandatangani sebelumnya dihasilkan.
        const metadata = {
             "key1": "value1",
             "key2": "value2"
        };
    
        await uploadFile(signedUrl, filePath, headers, metadata);
    })();
    

    Browser.js

    Penting

    Saat Anda menggunakan kode Browser.js untuk mengunggah objek berdasarkan URL yang ditandatangani sebelumnya, Anda mungkin mengalami kesalahan 403 yang menunjukkan ketidaksesuaian tanda tangan. Kesalahan ini muncul dari kegagalan verifikasi tanda tangan, yang terjadi karena browser secara otomatis menambahkan header permintaan Content-Type, elemen yang tidak ditentukan saat URL yang ditandatangani sebelumnya dihasilkan. Untuk mengatasi kesalahan ini, Anda harus menentukan header Content-Type saat menghasilkan URL yang ditandatangani sebelumnya yang diharapkan akan digunakan dalam kode Browser.js untuk mengunggah data ke OSS.

    C++

    #include <iostream>
    #include <fstream>
    #include <curl/curl.h>
    #include <map>
    #include <string>
    
    // Fungsi callback yang digunakan untuk memproses respons 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) {
            // Tentukan URL yang ditandatangani
            curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str());
    
            // Atur metode permintaan ke PUT
            curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    
            // Buka file lokal
            FILE* file = fopen(filePath.c_str(), "rb");
            if (!file) {
                std::cerr << "Unable to open the local file: " << filePath << std::endl;
                return;
            }
    
            // Tentukan ukuran file lokal
            fseek(file, 0, SEEK_END);
            long fileSize = ftell(file);
            rewind(file);
    
            // Konfigurasikan callback pembacaan file
            curl_easy_setopt(curl, CURLOPT_READDATA, file);
            curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
    
            // Tentukan header permintaan
            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);
    
            // Tentukan fungsi callback yang digunakan untuk memproses respons
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
    
            // Eksekusi permintaan
            res = curl_easy_perform(curl);
    
            // Periksa respons
            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 << "Upload status code: " << responseCode << std::endl;
                if (responseCode == 200) {
                    std::cout << "The object is uploaded by using the network library." << std::endl;
                } else {
                    std::cout << "Upload failed." << std::endl;
                }
                std::cout << readBuffer << std::endl;
            }
    
            // Bersihkan
            fclose(file);
            curl_slist_free_all(chunk);
            curl_easy_cleanup(curl);
        }
    
        curl_global_cleanup();
    }
    
    int main() {
        // Ganti <signedUrl> dengan URL yang ditandatangani sebelumnya.
        std::string signedUrl = "<signedUrl>";
    
        // Tentukan jalur lengkap file lokal. Secara default, jika Anda tidak menentukan jalur lengkap file lokal, file lokal diunggah dari jalur proyek tempat program contoh berada.
        std::string filePath = "C:\\Users\\demo.txt";
    
        // Tentukan header permintaan. Pastikan bahwa nilai header permintaan sama dengan yang ditentukan saat URL yang ditandatangani sebelumnya dihasilkan.
        std::map<std::string, std::string> headers = {
             {"Content-Type", "text/plain;charset=utf8"},
             {"x-oss-storage-class", "Standard"}
        };
    
        // Tentukan metadata pengguna. Pastikan bahwa metadata pengguna di sini sama dengan metadata pengguna yang ditentukan saat URL yang ditandatangani sebelumnya dihasilkan.
        std::map<std::string, std::string> metadata = {
             {"key1", "value1"},
             {"key2", "value2"}
        };
    
        uploadFile(signedUrl, filePath, headers, metadata);
    
        return 0;
    }
    

    C

    Berbeda dengan contoh C++, yang bergantung pada std::string dan <iostream>/<fstream> dan karena itu memerlukan kompiler C++, contoh berikut ditulis dalam C standar (menggunakan char*, <stdio.h>, dan libcurl) sehingga dapat dikompilasi dan dijalankan dengan rantai alat C biasa.

    #include <stdio.h>
    #include <stdlib.h>
    #include <curl/curl.h>
    
    void upload_file(const char *signed_url, const char *file_path) {
        CURL *curl;
        CURLcode res;
        curl_global_init(CURL_GLOBAL_DEFAULT);
        curl = curl_easy_init();
        if (curl) {
            curl_easy_setopt(curl, CURLOPT_URL, signed_url);
            curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
            FILE *file = fopen(file_path, "rb");
            if (!file) {
                fprintf(stderr, "Failed to open file: %s\n", file_path);
                return;
            }
            fseek(file, 0, SEEK_END);
            long file_size = ftell(file);
            fseek(file, 0, SEEK_SET);
            curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_size);
            curl_easy_setopt(curl, CURLOPT_READDATA, file);
            res = curl_easy_perform(curl);
            if (res != CURLE_OK)
                fprintf(stderr, "Upload failed: %s\n", curl_easy_strerror(res));
            else {
                long http_code = 0;
                curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
                printf("HTTP status code: %ld\n", http_code);
            }
            fclose(file);
            curl_easy_cleanup(curl);
        }
        curl_global_cleanup();
    }
    
    int main(int argc, char *argv[]) {
        if (argc != 3) {
            printf("Usage: %s <presigned URL> <file path>\n", argv[0]);
            return 1;
        }
        upload_file(argv[1], argv[2]);
        return 0;
    }
    

    Kompilasi dan jalankan:

    gcc -o oss_upload oss_upload.c -lcurl
    ./oss_upload <presigned URL> <file path>
    

    Program membaca ukuran file lokal di file_path, mengirimkannya sebagai isi permintaan PUT ke signed_url menggunakan libcurl, dan mencetak kode status HTTP yang dikembalikan (200 menunjukkan unggahan berhasil).

    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);
    
            // Ganti <signedUrl> dengan URL yang ditandatangani sebelumnya.
            String signedUrl = "<signedUrl>";
    
            // Tentukan jalur lengkap file lokal. Secara default, jika Anda tidak menentukan jalur lengkap, file lokal diunggah dari jalur proyek tempat program contoh berada.
            String pathName = "/storage/emulated/0/demo.txt";
    
            // Tentukan header permintaan. Pastikan bahwa nilai header permintaan sama dengan yang ditentukan saat URL yang ditandatangani sebelumnya dihasilkan.
            Map<String, String> headers = new HashMap<>();
            headers.put("Content-Type", "text/plain;charset=utf8");
            headers.put("x-oss-storage-class", "Standard");
    
            // Tentukan metadata pengguna. Pastikan bahwa metadata pengguna di sini sama dengan metadata pengguna yang ditentukan saat URL yang ditandatangani sebelumnya dihasilkan.
            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);
    
                    // Tentukan header permintaan
                    for (Entry<String, String> header : headers.entrySet()) {
                        connection.setRequestProperty(header.getKey(), header.getValue());
                    }
    
                    // Tentukan metadata pengguna
                    for (Entry<String, String> meta : userMetadata.entrySet()) {
                        connection.setRequestProperty("x-oss-meta-" + meta.getKey(), meta.getValue());
                    }
    
                    // Baca file lokal
                    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();
    
                    // Dapatkan respons
                    int responseCode = connection.getResponseCode();
                    Log.d(TAG, "Upload status code: " + responseCode);
                    if (responseCode == 200) {
                        Log.d(TAG, "The object is uploaded by using the 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, "Object uploaded", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(SignUrlUploadActivity.this, "Upload failed", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }
    

Konfigurasikan callback unggahan

Saat mengunggah objek, Anda dapat menyertakan parameter callback ke dalam URL yang ditandatangani sebelumnya untuk secara otomatis memberi tahu server aplikasi Anda setelah unggahan selesai. Untuk informasi lebih lanjut tentang cara kerja callback, lihat Callback.

  1. Pemilik objek menghasilkan URL yang ditandatangani sebelumnya (dengan metode PUT) yang berisi parameter callback unggahan yang diperlukan.

    Python

    import argparse
    import requests
    import alibabacloud_oss_v2 as oss
    
    from datetime import datetime, timedelta
    
    # Buat parser argumen baris perintah dan jelaskan tujuan skrip: untuk membuat URL permintaan PUT pra-tanda tangan untuk sebuah objek (Presign Put Object).
    parser = argparse.ArgumentParser(description="presign put object sample")
    
    # Tentukan argumen baris perintah, termasuk wilayah, nama bucket, titik akhir, dan kunci objek yang diperlukan.
    parser.add_argument('--region', help='Wilayah tempat bucket berada.', required=True)
    parser.add_argument('--bucket', help='Nama bucket.', required=True)
    parser.add_argument('--endpoint', help='Nama domain yang dapat digunakan layanan lain untuk mengakses OSS')
    parser.add_argument('--key', help='Nama objek.', required=True)
    
    def main():
        # Urai argumen baris perintah untuk mendapatkan nilai yang diberikan pengguna.
        args = parser.parse_args()
    
        # Muat informasi kredensial akses dari variabel lingkungan untuk verifikasi identitas.
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    
        # Buat objek konfigurasi menggunakan pengaturan default SDK dan atur penyedia otentikasi.
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
    
        # Atur properti wilayah dari objek konfigurasi berdasarkan argumen baris perintah.
        cfg.region = args.region
    
        # Jika titik akhir kustom disediakan, perbarui properti titik akhir di objek konfigurasi.
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
    
        # Inisialisasi klien OSS dengan konfigurasi di atas untuk berinteraksi dengan OSS.
        client = oss.Client(cfg)
    
        # Kirim permintaan untuk membuat permintaan PUT pra-tanda tangan untuk objek yang ditentukan.
        pre_result = client.presign(oss.PutObjectRequest(
            bucket=args.bucket,  # Nama bucket
            key=args.key,  # Kunci objek
            content_type='text/plain;charset=utf8',  # Tentukan tipe konten.
            storage_class='Standard',  # Tentukan kelas penyimpanan.
            metadata={
                'key1': 'value1',   # Tentukan metadata.
                'key2': 'value2'    # Tentukan metadata.
            }
        ),expires=timedelta(seconds=3600)) # Atur waktu kedaluwarsa dalam detik. Di sini, diatur ke 3600 detik.
    
    
        # Cetak metode, waktu kedaluwarsa, dan URL permintaan pra-tanda tangan untuk mengonfirmasi validitas tautan.
        print(f'method: {pre_result.method},'
              f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
              f' url: {pre_result.url}'
              )
    
        # Cetak informasi header yang ditandatangani dari permintaan pra-tanda tangan. Informasi ini akan disertakan dalam header HTTP saat permintaan aktual dikirim.
        for key, value in pre_result.signed_headers.items():
            print(f'signed headers key: {key}, signed headers value: {value}')
    
    # Saat skrip ini dieksekusi secara langsung, panggil fungsi utama untuk memulai logika pemrosesan.
    if __name__ == "__main__":
        main()  # Titik masuk skrip. Alur program dimulai di sini.

    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"
    )
    
    // Definisikan variabel global.
    var (
    	region     string // Wilayah tempat bucket berada.
    	bucketName string // Nama bucket.
    	objectName string // Nama objek.
    )
    
    // Definisikan fungsi init untuk menginisialisasi parameter baris perintah.
    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() {
    	// Uraikan parameter baris perintah.
    	flag.Parse()
    
    	// Periksa apakah nama bucket kosong.
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, bucket name required")
    	}
    
    	// Periksa apakah wilayah kosong.
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, region required")
    	}
    
    	// Periksa apakah nama objek kosong.
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, object name required")
    	}
    
    	// Muat konfigurasi default dan tentukan penyedia kredensial serta wilayah.
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	// Buat klien OSS.
    	client := oss.NewClient(cfg)
    
    	// Tentukan parameter callback.
    	callbackMap := map[string]string{
    		"callbackUrl":      "http://example.com:23450",                                                                   // Tentukan URL server callback, seperti https://example.com:23450.
    		"callbackBody":     "bucket=${bucket}&object=${object}&size=${size}&my_var_1=${x:my_var1}&my_var_2=${x:my_var2}", // Tentukan isi permintaan callback.
    		"callbackBodyType": "application/x-www-form-urlencoded",                                                          // Tentukan Content-Type isi permintaan callback.
    	}
    
    	// Konversi konfigurasi parameter callback menjadi string JSON, lalu encode Base64 untuk meneruskan pengaturan callback.
    	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)
    
    	// Hasilkan URL yang ditandatangani sebelumnya dengan metode PutObject.
    	result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
    		Bucket:      oss.Ptr(bucketName),
    		Key:         oss.Ptr(objectName),
    		Callback:    oss.Ptr(callbackBase64),    // Tentukan parameter callback, yang merupakan string JSON yang diencode Base64.
    		CallbackVar: oss.Ptr(callbackVarBase64), // Tentukan parameter callback kustom, yang merupakan string JSON yang diencode Base64.
    	},
    		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 {
    // Saat respons menyertakan header tanda tangan, Anda harus mengatur header permintaan yang sesuai saat mengirim permintaan PUT dengan URL yang ditandatangani sebelumnya.
    		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 {
            // Contoh ini menggunakan titik akhir publik dari wilayah China (Hangzhou). Tentukan titik akhir yang sebenarnya.
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // Dapatkan kredensial akses dari variabel lingkungan. Sebelum Anda menjalankan kode sampel, pastikan variabel lingkungan OSS_ACCESS_KEY_ID dan OSS_ACCESS_KEY_SECRET telah dikonfigurasi.
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            // Tentukan nama bucket, misalnya, examplebucket.
            String bucketName = "examplebucket";
            // Tentukan jalur lengkap objek, misalnya, exampleobject.txt. Jalur lengkap objek tidak boleh berisi nama bucket.
            String objectName = "exampleobject.txt";
            // Tentukan wilayah tempat bucket berada. Misalnya, jika bucket berada di wilayah China (Hangzhou), atur wilayah ke cn-hangzhou.
            String region = "cn-hangzhou";
    
            // Buat instans OSSClient.
            // Saat instans OSSClient tidak lagi digunakan, panggil metode shutdown untuk melepaskan sumber daya.
            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 {
                // Buat parameter callback.
                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());
                // Atur header permintaan.
                Map<String, String> headers = new HashMap<String, String>();
                // Tentukan CALLBACK.
                headers.put(OSSHeaders.OSS_HEADER_CALLBACK, callbackBase64);
                // Tentukan CALLBACK-VAR.
                headers.put(OSSHeaders.OSS_HEADER_CALLBACK_VAR, callbackVarBase64);
    
                // Atur waktu kedaluwarsa (3600 detik kemudian).
                Date expiration = new Date(new Date().getTime() + 3600 * 1000);
    
                // Format waktu kedaluwarsa.
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
                dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
                String expirationStr = dateFormat.format(expiration);
    
                // Buat permintaan.
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName);
                request.setMethod(HttpMethod.PUT);
                request.setExpiration(expiration);
                // Tambahkan header permintaan ke permintaan.
                request.setHeaders(headers);
    
                // Cetak parameter callback dan callback-var.
                System.out.println("callback:"+callbackBase64);
                System.out.println("callback-var:"+callbackVarBase64);
    
                // Buat URL yang ditandatangani.
                URL url = ossClient.generatePresignedUrl(request);
    
                // Cetak hasilnya.
                System.out.println("method: PUT,");
                System.out.println(" expiration: " + expirationStr + ",");
                System.out.println(" url: " + url);
    
            } catch (OSSException oe) {
                System.out.println("Menangkap OSSException, yang berarti permintaan Anda berhasil sampai ke OSS, "
                        + "tetapi ditolak dengan tanggapan error karena alasan tertentu.");
                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("Menangkap ClientException, yang berarti klien mengalami masalah internal yang serius "
                        + "saat mencoba berkomunikasi dengan OSS, "
                        + "seperti tidak dapat mengakses jaringan.");
                System.out.println("Error Message:" + ce.getMessage());
            }
        }
    }

    PHP

    <?php
    
    // Sertakan file autoload sehingga dependensi yang diperlukan dapat dimuat.
    require_once __DIR__ . '/../vendor/autoload.php';
    
    use AlibabaCloud\Oss\V2 as Oss;
    
    // Tentukan deskripsi untuk argumen baris perintah.
    $optsdesc = [
        "region" => ['help' => 'The region in which the bucket is located.', 'required' => True], // (Wajib) Tentukan wilayah tempat bucket berada.
        "endpoint" => ['help' => 'The domain names that other services can use to access OSS.', 'required' => False], // (Opsional) Tentukan titik akhir yang dapat digunakan oleh layanan lain untuk mengakses OSS.
        "bucket" => ['help' => 'The name of the bucket', 'required' => True], // (Wajib) Tentukan nama bucket.
        "key" => ['help' => 'The name of the object', 'required' => True], // (Wajib) Tentukan nama objek.
    ];
    
    // Konversi deskripsi menjadi daftar opsi panjang yang diperlukan oleh getopt.
    // Tambahkan titik dua (:) di akhir setiap parameter untuk menunjukkan bahwa nilai diperlukan.
    $longopts = \array_map(function ($key) {
        return "$key:";
    }, array_keys($optsdesc));
    
    // Uraikan parameter baris perintah.
    $options = getopt("", $longopts);
    
    // Periksa apakah parameter yang diperlukan telah disediakan.
    foreach ($optsdesc as $key => $value) {
        if ($value['required'] === True && empty($options[$key])) {
            $help = $value['help']; // Dapatkan informasi bantuan untuk parameter
            echo "Error: the following arguments are required: --$key, $help" . PHP_EOL;
            exit(1); // Keluar dari program jika parameter yang diperlukan tidak dikonfigurasi.
        }
    }
    
    // Dapatkan nilai dari parameter yang diuraikan.
    $region = $options["region"]; // Wilayah tempat bucket berada
    $bucket = $options["bucket"]; // Nama bucket
    $key = $options["key"];       // Nama objek
    
    // Muat kredensial akses dari variabel lingkungan.
    // Gunakan EnvironmentVariableCredentialsProvider untuk mengambil ID AccessKey dan rahasia AccessKey dari variabel lingkungan.
    $credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();
    
    // Gunakan konfigurasi default SDK.
    $cfg = Oss\Config::loadDefault();
    $cfg->setCredentialsProvider($credentialsProvider); // Atur penyedia kredensial
    $cfg->setRegion($region); // Atur wilayah tempat bucket berada
    if (isset($options["endpoint"])) {
        $cfg->setEndpoint($options["endpoint"]); // Atur titik akhir jika disediakan
    }
    
    // Buat instance klien OSS.
    $client = new Oss\Client($cfg);
    
    // Tambahkan header x-oss-callback dan x-oss-callback-var
    // Definisikan alamat webhook
    $call_back_url = "http://www.example.com/callback";
    
    // Bangun parameter callback: tentukan alamat webhook dan isi permintaan, dan gunakan encoding Base64
    // Gunakan placeholder {var1} dan {var2} untuk mengganti ${x:var1} dan ${x: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
    ]));
    
    // Bangun variabel kustom (callback-var) menggunakan encoding Base64
    $callback_var = base64_encode(json_encode([
        "x:var1" => "value1",
        "x:var2" => "value2"
    ]));
    
    // Buat objek PutObjectRequest untuk mengunggah objek.
    // Catatan: parameter contentType, metadata, dan headers ditambahkan di sini untuk perhitungan tanda tangan.
    $request = new Oss\Models\PutObjectRequest(
        bucket: $bucket,
        key: $key,
        callback:$callback,
        callbackVar:$callback_var,
    );
    
    // Panggil metode presign untuk menghasilkan URL yang ditandatangani sebelumnya.
    $result = $client->presign($request);
    
    // Cetak hasil yang ditandatangani sebelumnya, keluarkan URL yang ditandatangani sebelumnya yang dapat langsung digunakan pengguna untuk mengunggah objek
    print(
        'put object presign result:' . var_export($result, true) . PHP_EOL .
        'put object url:' . $result->url . PHP_EOL
    );
    
  2. Pihak lain mengunggah objek dengan menggunakan URL yang ditandatangani sebelumnya dengan metode PUT.

    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;
    
            // Ganti <signedUrl> dengan URL yang diotorisasi.
            URL signedUrl = new URL("<signedUrl>");
    
            // Tentukan jalur lengkap file lokal. Jika Anda tidak menentukan jalur lokal, file diunggah dari jalur proyek program contoh secara default.
            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 {
                 if (response != null) {
                     response.close();
                 }
                 if (httpClient != null) {
                     httpClient.close();
                 }
             }
        }
    }       

    Go

    package main
    
    import (
    	"fmt"
    	"io"
    	"net/http"
    	"os"
    )
    
    func uploadFile(signedUrl, filePath string) error {
    	// Buka file.
    	file, err := os.Open(filePath)
    	if err != nil {
    		return fmt.Errorf("Unable to open file: %w", err)
    	}
    	defer file.Close()
    
    	// Buat klien HTTP baru.
    	client := &http.Client{}
    
    	// Buat permintaan PUT.
    	req, err := http.NewRequest("PUT", signedUrl, file)
    	if err != nil {
    		return fmt.Errorf("Failed to create request: %w", err)
    	}
    
    	// Kirim permintaan.
    	resp, err := client.Do(req)
    	if err != nil {
    		return fmt.Errorf("Failed to send request: %w", err)
    	}
    	defer resp.Body.Close()
    
    	// Baca respons.
    	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() {
    	// Ganti <signedUrl> dengan URL yang diotorisasi.
    	signedUrl := "<signedUrl>"
    
    	// Tentukan jalur lengkap file lokal. Jika Anda tidak menentukan jalur lokal, file diunggah dari jalur proyek program contoh secara default.
    	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:
            # Buka file.
            with open(file_path, 'rb') as file:
                # Kirim permintaan PUT untuk mengunggah file.
                response = requests.put(signed_url, data=file)
         
            print(f"Kode status yang dikembalikan untuk unggahan: {response.status_code}")
            if response.status_code == 200:
                print("Unggahan berhasil menggunakan pustaka jaringan.")
            print(response.text)
     
        except Exception as e:
            print(f"Terjadi kesalahan: {e}")
    
    if __name__ == "__main__":
        # Ganti <signedUrl> dengan URL yang diotorisasi.
        signed_url = "<signedUrl>"
        
        # Tentukan jalur lengkap file lokal. Jika Anda tidak menentukan jalur lokal, file akan diunggah dari jalur proyek contoh program secara default.
        file_path = "C:\\Users\\demo.txt"
    
        upload_file(signed_url, file_path)
    

    Node.js

    Penting

    Jika Anda mengalami kesalahan ketidaksesuaian tanda tangan 403 saat menggunakan Node.js untuk mengunggah file, biasanya karena header permintaan Content-Type secara eksplisit diatur dalam permintaan unggahan, tetapi header ini tidak disertakan saat URL yang ditandatangani sebelumnya dihasilkan. Ketidaksesuaian ini menyebabkan kegagalan verifikasi tanda tangan. Untuk mengatasi masalah ini, jangan mengatur header permintaan Content-Type dalam permintaan unggahan, atau tentukan header permintaan Content-Type saat menghasilkan URL yang ditandatangani sebelumnya.

    const fs = require('fs');
    const axios = require('axios');
    
    async function uploadFile(signedUrl, filePath) {
        try {
            // Buat aliran baca.
            const fileStream = fs.createReadStream(filePath);
    
            // Kirim permintaan PUT untuk mengunggah file.
            const response = await axios.put(signedUrl, fileStream);
    
            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}`);
        }
    }
    
    // Fungsi utama.
    (async () => {
        // Ganti <signedUrl> dengan URL yang diotorisasi.
        const signedUrl = '<signedUrl>';
        
        // Tentukan jalur lengkap file lokal. Jika Anda tidak menentukan jalur lokal, file diunggah dari jalur proyek program contoh secara default.
        const filePath = 'C:\\Users\\demo.txt';
    
        await uploadFile(signedUrl, filePath);
    })();

    browser.js

    Penting

    Jika Anda mengalami kesalahan ketidaksesuaian tanda tangan 403 saat menggunakan Browser.js untuk mengunggah file, biasanya karena browser secara otomatis menambahkan header permintaan Content-Type, dan header ini tidak disertakan saat URL yang ditandatangani sebelumnya dihasilkan. Ketidaksesuaian ini menyebabkan kegagalan verifikasi tanda tangan. Untuk mengatasi masalah ini, Anda harus menentukan header permintaan Content-Type saat menghasilkan URL yang ditandatangani sebelumnya.

    <!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>
    
        <!-- Pilih file -->
        <input type="file" id="fileInput" />
        <button id="uploadButton">Upload File</button>
    
        <script>
            // Ganti ini dengan URL yang ditandatangani sebelumnya yang dihasilkan pada Langkah 1.
            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);
                }
            });
    
            /**
             * Unggah file ke OSS.
             * @param {File} file - File yang akan diunggah.
             * @param {string} presignedUrl - URL yang ditandatangani sebelumnya.
             */
            const upload = async (file, presignedUrl) => {
                const response = await fetch(presignedUrl, {
                    method: 'PUT',
                    body: file,  // Unggah seluruh file secara langsung.
                });
    
                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;
    
    // Tentukan jalur lengkap file lokal. Jika Anda tidak menentukan jalur lokal, file diunggah dari jalur proyek program contoh secara default.
    var filePath = "C:\\Users\\demo.txt";
    // Ganti <signedUrl> dengan URL yang diotorisasi.
    var presignedUrl = "<signedUrl>";
    
    // Buat klien HTTP dan buka aliran file lokal.
    using var httpClient = new HttpClient(); 
    using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
    using var content = new StreamContent(fileStream);
                
    // Buat permintaan PUT.
    var request = new HttpRequestMessage(HttpMethod.Put, presignedUrl);
    request.Content = content;
    
    // Kirim permintaan.
    var response = await httpClient.SendAsync(request);
    
    // Proses respons.
    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) {
            // Atur URL.
            curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str());
    
            // Atur metode permintaan ke PUT.
            curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    
            // Buka file.
            FILE *file = fopen(filePath.c_str(), "rb");
            if (!file) {
                std::cerr << "Unable to open file: " << filePath << std::endl;
                return;
            }
    
            // Dapatkan ukuran file.
            fseek(file, 0, SEEK_END);
            long fileSize = ftell(file);
            fseek(file, 0, SEEK_SET);
    
            // Atur ukuran file.
            curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
    
            // Atur handle input file.
            curl_easy_setopt(curl, CURLOPT_READDATA, file);
    
            // Eksekusi permintaan.
            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;
                }
            }
    
            // Tutup file.
            fclose(file);
    
            // Bersihkan.
            curl_easy_cleanup(curl);
        }
    
        curl_global_cleanup();
    }
    
    int main() {
        // Ganti <signedUrl> dengan URL yang diotorisasi.
        std::string signedUrl = "<signedUrl>";
    
        // Tentukan jalur lengkap file lokal. Jika Anda tidak menentukan jalur lokal, file diunggah dari jalur proyek program contoh secara default.
        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();
            // Ganti <signedUrl> dengan URL yang diotorisasi.
            String signedUrl = "<signedUrl>";
            // Tentukan jalur lengkap file lokal. Jika Anda tidak menentukan jalur lokal, file diunggah dari jalur proyek program contoh secara default.
            String filePath = "C:\\Users\\demo.txt";
            activity.uploadFile(signedUrl, filePath);
        }
    }
    

Informasi tambahan

Apa itu URL yang ditandatangani sebelumnya?

URL yang ditandatangani sebelumnya adalah tautan aman berbatas waktu yang memberikan akses sementara ke objek tertentu di OSS. Untuk menghasilkan URL yang ditandatangani sebelumnya, klien membuat tanda tangan kriptografi dengan menggunakan pasangan AccessKey, jalur resource, dan waktu kedaluwarsa. Tanda tangan ini ditambahkan ke URL asli, menghasilkan tautan akses berbatas waktu. Format umum URL yang ditandatangani sebelumnya adalah https://BucketName.Endpoint/Object?Signature.

Saat pihak ketiga mengakses URL yang ditandatangani sebelumnya, OSS memvalidasi tanda tangan tersebut. Jika tanda tangan tidak valid atau telah kedaluwarsa, akses akan ditolak.

Contoh URL yang ditandatangani sebelumnya:

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********************************

Dengan metode ini, pemilik objek dapat memberikan akses terbatas kepada pihak ketiga secara aman, tanpa pernah mengungkapkan kunci privat mereka.

Skenario yang berlaku

  • Berbagi objek sementara: Untuk mengizinkan pihak ketiga mengunggah atau mengunduh objek tertentu secara aman, backend menghasilkan URL yang ditandatangani sebelumnya berbatas waktu dan memberikannya ke frontend. Pihak ketiga kemudian dapat menggunakan URL ini untuk melakukan operasi dalam jangka waktu yang diizinkan, memastikan akses data yang terkendali dan aman.

  • Berbagi objek fleksibel: Pemilik objek dapat berbagi objek mereka secara aman dengan menghasilkan URL yang ditandatangani sebelumnya dan mendistribusikannya melalui email atau aplikasi pesan kepada pihak ketiga. Penerima cukup menempelkan URL tersebut ke browser mereka untuk mengunduh objek yang diinginkan.

FAQ

Bagaimana cara memberikan izin yang lebih luas kepada orang lain untuk resource OSS saya daripada hanya membatasi mereka pada akses unggah saja?

Selain URL yang ditandatangani sebelumnya, OSS menawarkan metode otorisasi sementara yang lebih fleksibel: kredensial akses sementara STS. Berbeda dengan URL yang ditandatangani sebelumnya, yang terbatas pada tindakan tertentu, kredensial STS memungkinkan pihak ketiga melakukan berbagai operasi yang lebih luas, seperti mencantumkan, menyalin, atau menghapus objek, dengan izin berbatas waktu. Untuk informasi lebih lanjut, lihat Akses OSS menggunakan kredensial sementara STS.

Bisakah saya membatasi akses ke objek saya sehingga hanya permintaan dari situs web tertentu yang diizinkan sementara memblokir semua yang lain?

Ya, Anda dapat mengaktifkan perlindungan hotlink berbasis Referer dengan menyetel daftar putih untuk membatasi akses ke resource OSS Anda. Ini memastikan hanya situs web yang diotorisasi (seperti milik Anda) yang dapat mengakses resource tersebut, memblokir permintaan langsung dari sumber yang tidak sah atau berbahaya. Untuk informasi lebih lanjut, lihat Konfigurasikan daftar putih atau blacklist Referer untuk mencegah situs web lain menautkan ke objek OSS Anda.

Mengapa browser saya menampilkan kesalahan CORS dan bagaimana cara mengatasinya?

Bucket Anda tidak memiliki kebijakan Berbagi sumber daya lintas asal (CORS) yang dikonfigurasi atau pengaturan CORS saat ini salah. Verifikasi dan sesuaikan pengaturan dengan mengacu pada CORS.

Mengapa browser saya menampilkan kesalahan "405 Method Not Allowed" dan bagaimana cara mengatasinya?

Metode permintaan salah. Saat mengunggah objek menggunakan URL yang ditandatangani sebelumnya, pastikan Anda menggunakan metode PUT, bukan metode POST.

Mengapa OSS menyimpan string URL alih-alih file aktual saat saya meneruskan URL sebagai isi unggahan?

Saat Anda menggunakan URL yang ditandatangani sebelumnya untuk mengunggah data, mekanisme URL yang ditandatangani sebelumnya adalah saluran unggahan langsung dari klien ke OSS. OSS menyimpan byte mentah dari isi permintaan PUT sebagai objek — tidak menafsirkan isi tersebut sebagai URL atau mengambil konten dari alamat eksternal atas nama Anda.

Jika Anda meneruskan string URL (misalnya, https://example.com/video.mp4) sebagai isi permintaan PUT, OSS menyimpan string URL tersebut sebagai konten objek. Misalnya, string URL sepanjang 66 byte menghasilkan objek berukuran 66 byte yang hanya berisi teks URL, bukan file di alamat tersebut.

Untuk menyimpan file yang dihosting di URL eksternal ke bucket OSS, server Anda harus terlebih dahulu mengunduh data file tersebut dan kemudian mengunggah byte aktualnya ke OSS menggunakan PutObject atau unggah multi-bagian. Contoh Python berikut menunjukkan cara mengimplementasikan relay sisi server ini:

import requests
import alibabacloud_oss_v2 as oss

def relay_url_to_oss(external_url, bucket_name, object_key, region):
    # Langkah 1: Unduh file dari URL eksternal.
    response = requests.get(external_url, stream=True)
    response.raise_for_status()
    file_content = response.content

    # Langkah 2: Konfigurasikan Klien OSS.
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider
    cfg.region = region
    client = oss.Client(cfg)

    # Langkah 3: Unggah data yang diunduh ke OSS.
    result = client.put_object(oss.PutObjectRequest(
        bucket=bucket_name,
        key=object_key,
        body=file_content,
    ))
    print(f"Upload successful. ETag: {result.etag}")

# Contoh penggunaan
relay_url_to_oss(
    external_url="https://example.com/sample.jpg",
    bucket_name="examplebucket",
    object_key="sample.jpg",
    region="cn-hangzhou"
)