全部产品
Search
文档中心

Object Storage Service:Unggah file menggunakan URL yang ditandatangani sebelumnya

更新时间:Dec 17, 2025

Secara default, file dalam bucket OSS bersifat privat dan hanya dapat diunggah oleh pemilik bucket. Namun, pemilik bucket dapat menghasilkan URL yang ditandatangani sebelumnya (pre-signed URL) untuk file tertentu guna memberikan izin unggah sementara. Siapa pun yang memiliki URL tersebut dapat mengunggah file selama periode validitasnya. Fitur ini berguna untuk skenario seperti memberi otorisasi kepada mitra untuk mengunggah kontrak atau memungkinkan pengguna mengunggah foto profil.

Perhatian

  • Pada topik ini, titik akhir publik wilayah China (Hangzhou) digunakan. Untuk mengakses OSS dari layanan Alibaba Cloud lainnya dalam wilayah yang sama, gunakan titik akhir internal. Untuk detail 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 pengguna yang menghasilkan URL memiliki izin oss:PutObject. Untuk informasi lebih lanjut tentang cara memberikan izin, lihat Berikan izin kustom kepada Pengguna RAM.

  • URL yang ditandatangani sebelumnya tidak mendukung unggahan dalam format FormData. Untuk mengunggah data menggunakan FormData, gunakan unggahan form OSS.

Penting

Karena adanya perubahan kebijakan untuk meningkatkan kepatuhan dan keamanan, mulai 20 Maret 2025, pengguna OSS baru harus menggunakan nama domain kustom (CNAME) untuk melakukan operasi API data pada bucket OSS yang berlokasi di wilayah daratan Tiongkok. Titik akhir publik default dibatasi untuk operasi ini. Lihat pengumuman resmi untuk daftar lengkap operasi yang terdampak. Jika Anda mengakses data melalui HTTPS, Anda harus mengikat Sertifikat SSL yang valid ke domain kustom Anda. Hal ini wajib untuk akses Konsol OSS, karena konsol menerapkan HTTPS.

Tinjauan proses

Proses mengunggah file menggunakan URL yang ditandatangani sebelumnya adalah sebagai berikut:

image

Unggah satu file menggunakan URL yang ditandatangani sebelumnya

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

    Catatan

    Periode validitas maksimum URL yang ditandatangani sebelumnya yang dihasilkan menggunakan SDK adalah 7 hari. Jika Anda menggunakan token Security Token Service (STS) untuk menghasilkan URL tersebut, periode validitas maksimumnya adalah 43.200 detik (12 jam).

    Java

    Untuk informasi lebih lanjut tentang SDK, lihat Unggah file menggunakan URL yang ditandatangani sebelumnya di 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 {
            // 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 menjalankan kode contoh, 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 mengandung nama bucket.
            String objectName = "exampleobject.txt";
            // Tentukan wilayah tempat bucket berada. Misalnya, jika bucket berada di wilayah China (Hangzhou), atur wilayah menjadi cn-hangzhou.
            String region = "cn-hangzhou";
    
            // Buat instance OSSClient.
            // Saat instance 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 {
                // Tentukan waktu kedaluwarsa URL yang ditandatangani sebelumnya dalam milidetik. Contoh ini menetapkan waktu kedaluwarsa menjadi 1 jam.
                Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
    
                // Hasilkan URL yang ditandatangani sebelumnya.
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
                // Atur waktu kedaluwarsa.
                request.setExpiration(expiration);
                // Hasilkan URL yang ditandatangani sebelumnya untuk permintaan HTTP PUT.
                signedUrl = ossClient.generatePresignedUrl(request);
                // Cetak 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 lebih lanjut tentang SDK, lihat Unggah file menggunakan URL yang ditandatangani sebelumnya di 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"
    )
    
    // Tentukan variabel global.
    var (
    	region     string // Wilayah tempat bucket berada.
    	bucketName string // Nama bucket.
    	objectName string // Nama objek.
    )
    
    // Tentukan fungsi init yang digunakan 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)
    
    	// Hasilkan URL yang ditandatangani sebelumnya untuk permintaan PutObject.
    	result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
    		Bucket: oss.Ptr(bucketName),
    		Key:    oss.Ptr(objectName),
    		//ContentType: oss.Ptr("text/txt"),                                 // Pastikan nilai parameter ContentType yang terdapat dalam URL sama dengan nilai ContentType yang ditentukan dalam permintaan.
    		//Metadata:    map[string]string{"key1": "value1", "key2": "value2"}, // Pastikan nilai parameter Metadata yang terdapat dalam URL sama dengan nilai Metadata yang ditentukan dalam permintaan.
    	},
    		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 Anda menentukan header permintaan saat menghasilkan URL yang ditandatangani sebelumnya yang memungkinkan permintaan HTTP PUT, pastikan header permintaan tersebut disertakan dalam permintaan PUT yang diprakarsai menggunakan URL tersebut.
    		log.Printf("signed headers:\n")
    		for k, v := range result.SignedHeaders {
    			log.Printf("%v: %v\n", k, v)
    		}
    	}
    }
    

    Python

    Untuk informasi lebih lanjut tentang SDK, lihat Unggah file menggunakan URL yang ditandatangani sebelumnya di Python.

    import argparse
    import requests
    import alibabacloud_oss_v2 as oss
    
    from datetime import datetime, timedelta
    
    # Buat parser parameter baris perintah dan jelaskan tujuan skrip. Contoh ini menjelaskan cara menghasilkan URL yang ditandatangani sebelumnya yang memungkinkan permintaan HTTP PUT.
    parser = argparse.ArgumentParser(description="presign put object sample")
    
    # Tentukan parameter baris perintah, termasuk wilayah, nama bucket, titik akhir, dan nama objek yang wajib diisi.
    parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
    parser.add_argument('--bucket', help='The name of the bucket.', required=True)
    parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
    parser.add_argument('--key', help='The name of the object.', required=True)
    
    def main():
        # Uraikan parameter baris perintah untuk mendapatkan nilai yang ditentukan pengguna.
        args = parser.parse_args()
    
        # Dapatkan kredensial akses dari variabel lingkungan untuk autentikasi.
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    
        # Gunakan konfigurasi default SDK untuk membuat objek konfigurasi dan tentukan penyedia kredensial.
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
    
        # Tentukan atribut wilayah objek konfigurasi berdasarkan parameter baris perintah yang ditentukan pengguna.
        cfg.region = args.region
    
        # Jika titik akhir kustom disediakan, ubah parameter titik akhir dalam objek konfigurasi.
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
    
        # Gunakan konfigurasi di atas untuk menginisialisasi instance OSSClient dan memungkinkan instance berinteraksi dengan OSS.
        client = oss.Client(cfg)
    
        # Kirim permintaan untuk memprakarsai permintaan PUT dan menghasilkan URL yang ditandatangani sebelumnya untuk objek yang ditentukan.
        pre_result = client.presign(oss.PutObjectRequest(
            bucket=args.bucket, # Nama bucket.
            key=args.key, # Nama objek.
        ),expires=timedelta(seconds=3600)) # Tentukan periode validitas permintaan. Pada contoh ini, periode validitas diatur menjadi 3.600 detik.
    
        # Tampilkan metode, waktu kedaluwarsa, dan URL yang ditandatangani sebelumnya yang ditentukan dalam permintaan untuk memeriksa validitas URL tersebut.
        print(f'method: {pre_result.method},'
              f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
              f' url: {pre_result.url}'
              )
    
        # Tampilkan header yang ditandatangani dalam permintaan, yang disertakan dalam header HTTP saat permintaan dikirim.
        for key, value in pre_result.signed_headers.items():
            print(f'signed headers key: {key}, signed headers value: {value}')
    
    # Panggil fungsi utama untuk memulai logika pemrosesan saat skrip dijalankan secara langsung.
    if __name__ == "__main__":
        main() # Tentukan titik masuk dalam fungsi skrip. Alur kontrol program dimulai di sini.

    Node.js

    Bagian ini memberikan contoh skenario umum. Untuk informasi lebih lanjut tentang cara menghasilkan URL yang ditandatangani sebelumnya dengan parameter pemrosesan citra atau ID versi, lihat Unggah file menggunakan URL yang ditandatangani sebelumnya di Node.js.

    const OSS = require("ali-oss");
    
    // Definisikan fungsi untuk menghasilkan URL yang ditandatangani.
    async function generateSignatureUrl(fileName) {
      // Dapatkan URL yang ditandatangani sebelumnya.
      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 aktual.
      }, fileName);
    }
    // Panggil fungsi dan berikan nama file.
    generateSignatureUrl('yourFileName').then(url => {
      console.log('Generated Signature URL:', url);
    }).catch(err => {
      console.error('Error generating signature URL:', err);
    });

    PHP

    Bagian ini memberikan contoh skenario umum. Untuk informasi lebih lanjut tentang cara menghasilkan URL yang ditandatangani sebelumnya dengan ID versi, lihat Unggah file menggunakan URL yang ditandatangani sebelumnya di 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 menjalankan kode contoh, 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 menjadi 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 ditandatangani sebelumnya menjadi 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);
        // Hasilkan URL yang ditandatangani sebelumnya. 
        $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 lebih lanjut tentang SDK, lihat Unggah file menggunakan URL yang ditandatangani sebelumnya di Android.

    // Tentukan nama bucket, misalnya examplebucket.
    String bucketName = "examplebucket";
    // Tentukan jalur lengkap objek sumber, tanpa nama bucket, misalnya exampleobject.txt.
    String objectKey = "exampleobject.txt";
    // Atur tipe konten.
    String contentType = "application/octet-stream";
    String url = null;
    try {
        // Hasilkan URL yang ditandatangani sebelumnya untuk mengunggah file.
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectKey);
        // Atur waktu kedaluwarsa URL yang ditandatangani sebelumnya 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 lebih lanjut tentang SDK, lihat Unggah file menggunakan URL yang ditandatangani sebelumnya di C++.

    #include <alibabacloud/oss/OssClient.h>
    using namespace AlibabaCloud::OSS;
    
    int main(void)
    {
        /* Inisialisasi informasi akun yang digunakan untuk mengakses OSS. */
                
        /* Tentukan titik akhir wilayah tempat bucket berada. Misalnya, jika bucket berada di wilayah China (Hangzhou), atur titik akhir menjadi 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 menjadi cn-hangzhou.   * /
        std::string Region = "yourRegion";
        /* Tentukan nama bucket. Contoh: examplebucket. */
        std::string BucketName = "examplebucket";
        /* Tentukan jalur lengkap objek. Jangan sertakan nama bucket dalam 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 menjalankan kode contoh, pastikan 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 yang ditandatangani sebelumnya. Periode validitas maksimum adalah 32.400. Satuan: detik. */
        std::time_t t = std::time(nullptr) + 1200;
        /* Hasilkan URL yang ditandatangani sebelumnya. */
        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 lebih lanjut tentang SDK, lihat Unggah file menggunakan URL yang ditandatangani sebelumnya di iOS.

    // 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;
    // Hasilkan URL yang ditandatangani sebelumnya dengan periode validitas untuk mengunggah objek. Pada 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 lebih lanjut tentang SDK, lihat Unggah file menggunakan URL yang ditandatangani sebelumnya di .NET.

    using Aliyun.OSS;
    using Aliyun.OSS.Common;
    // Tentukan Titik Akhir untuk wilayah tempat bucket berada. Misalnya, jika bucket berada di wilayah China (Hangzhou), atur Titik Akhir menjadi https://oss-cn-hangzhou.aliyuncs.com.
    var endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
    // Dapatkan kredensial akses dari variabel lingkungan. Sebelum menjalankan kode ini, pastikan 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 mencakup 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 China (Hangzhou), atur wilayah menjadi cn-hangzhou.
    const string region = "cn-hangzhou";
    
    // Buat instance ClientConfiguration dan ubah parameter default sesuai kebutuhan.
    var conf = new ClientConfiguration();
    
    // Atur versi tanda tangan menjadi V4.
    conf.SignatureVersion = SignatureVersion.V4;
    
    // Buat instance OssClient.
    var client = new OssClient(endpoint, accessKeyId, accessKeySecret, conf);
    client.SetRegion(region);
    try
    {
        // Hasilkan URL yang ditandatangani.
        var generatePresignedUriRequest = new GeneratePresignedUriRequest(bucketName, objectName, SignHttpMethod.Put)
        {
            // Atur waktu kedaluwarsa URL yang ditandatangani. Nilai default 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 lebih lanjut tentang SDK, lihat Unduh file menggunakan URL yang ditandatangani sebelumnya di C.

    #include "oss_api.h"
    #include "aos_http_io.h"
    /* Atur yourEndpoint ke titik akhir wilayah tempat bucket berada. Misalnya, jika bucket berada di wilayah China (Hangzhou), atur titik akhir menjadi https://oss-cn-hangzhou.aliyuncs.com. */
    const char *endpoint = "yourEndpoint";
    
    /* Tentukan nama bucket. Misalnya, examplebucket. */
    const char *bucket_name = "examplebucket";
    /* Tentukan jalur lengkap objek. Jalur lengkap tidak boleh mengandung nama bucket. Misalnya, exampledir/exampleobject.txt. */
    const char *object_name = "exampledir/exampleobject.txt";
    /* Tentukan jalur 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 diatur. */
        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;
        /* Atur 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 di entri program untuk menginisialisasi sumber daya global, seperti jaringan dan memori. */
        if (aos_http_io_initialize(NULL, 0) != AOSE_OK) {
            exit(1);
        }
        /* Pool memori (pool) untuk manajemen memori, yang setara dengan apr_pool_t. Kode implementasinya ada di pustaka APR. */
        aos_pool_t *pool;
        /* Buat pool memori baru. Parameter kedua adalah NULL, yang menunjukkan bahwa pool tidak mewarisi dari pool memori 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 pool memori. */
        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 sebelumnya. */
        url_str = oss_gen_signed_url(oss_client_options, &bucket, &object, expire_time, req);
        aos_str_set(&url, url_str);
        printf("Temporary upload URL: %s\n", url_str);    
        /* Lepaskan pool memori. 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 ketiga menggunakan URL yang ditandatangani sebelumnya yang dihasilkan untuk mengunggah file dengan permintaan PUT.

    Penting

    URL yang ditandatangani sebelumnya dapat diakses beberapa kali sebelum kedaluwarsa. Namun, melakukan beberapa operasi unggahan menimbulkan risiko menimpa file. Setelah URL kedaluwarsa, Anda harus mengikuti Langkah 1 untuk menghasilkan ulang URL yang ditandatangani sebelumnya agar dapat terus mengakses file.

    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 {
                response.close();
                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"Status code returned for the upload: {response.status_code}")
            if response.status_code == 200:
                print("Upload successful using network library.")
            print(response.text)
     
        except Exception as e:
            print(f"An error occurred: {e}")
    
    if __name__ == "__main__":
        # Ganti <signedUrl> dengan URL yang diotorisasi.
        signed_url = "<signedUrl>"
        
        // Tentukan jalur lengkap file lokal. Jika Anda tidak menentukan jalur lokal, file diunggah dari jalur proyek program contoh secara default.
        file_path = "C:\\Users\\demo.txt"
    
        upload_file(signed_url, file_path)
    

    Node.js

    const fs = require('fs');
    const axios = require('axios');
    
    async function uploadFile(signedUrl, filePath) {
        try {
            // Buat aliran baca.
            const fileStream = fs.createReadStream(filePath);
            
            // Kirim permintaan PUT untuk mengunggah file.
            const response = await axios.put(signedUrl, fileStream, {
                headers: {
                    'Content-Type': 'application/octet-stream' // Sesuaikan Content-Type sesuai kebutuhan.
                }
            });
    
            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 ketidakcocokan tanda tangan 403 saat menggunakan Browser.js untuk mengunggah file, biasanya karena browser secara otomatis menambahkan header permintaan Content-Type, tetapi header ini tidak ditentukan saat URL yang ditandatangani sebelumnya dihasilkan. Hal 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 di 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 menjadi 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 file input.
            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);
        }
    }
    

Lakukan unggah multi-bagian menggunakan URL yang ditandatangani sebelumnya

Jika klien tidak dapat diintegrasikan dengan SDK OSS tetapi perlu mengunggah file besar, Anda dapat menggunakan URL yang ditandatangani sebelumnya untuk melakukan unggah multi-bagian. Dalam metode ini, server memprakarsai tugas unggahan dan menghasilkan URL yang ditandatangani sebelumnya dengan izin untuk setiap bagian. Klien atau aplikasi pihak ketiga kemudian menggunakan URL tersebut untuk mengunggah bagian-bagian tersebut. Setelah semua bagian diunggah, server memprakarsai permintaan untuk menggabungkan bagian-bagian tersebut menjadi satu file lengkap.

Penting

Unggah multi-bagian yang menggunakan URL yang ditandatangani sebelumnya relatif kompleks. Klien harus mengunggah bagian yang benar untuk `partNumber` yang ditentukan dalam URL. Bagian yang hilang atau tidak cocok menghasilkan file akhir yang salah. Oleh karena itu, jika klien Anda dapat diintegrasikan dengan SDK OSS, kami merekomendasikan agar Anda menggunakan otorisasi STS untuk mengimplementasikan unggahan langsung dari sisi klien. Metode ini lebih sederhana untuk dikembangkan dan menyediakan unggahan yang lebih stabil.

image

  1. Klien memprakarsai permintaan unggahan

    Klien mengirim permintaan unggahan ke server yang berisi nama file, ukuran file, dan ukuran bagian yang diinginkan. Ukuran bagian tidak boleh melebihi 5 GB. Ukuran bagian 5 MB direkomendasikan.

    Contoh berikut menunjukkan permintaan. Dalam contoh ini, https://yourserver.com/init-upload adalah titik akhir API inisialisasi yang disediakan oleh server Anda. Gantilah dengan titik akhir yang sebenarnya.

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

    1. Panggil operasi InitiateMultipartUpload untuk menginisialisasi tugas unggahan dan mendapatkan UploadId.

    2. Hitung jumlah bagian berdasarkan ukuran file dan ukuran bagian yang diinginkan.

    3. Hasilkan URL yang ditandatangani sebelumnya yang sesuai untuk setiap bagian.

    4. Kembalikan daftar URL dalam format JSON.

    Catatan

    Server harus mencatat UploadId dan memastikan bahwa UploadId tersebut memiliki pemetaan unik ke file yang diunggah. Hal ini diperlukan untuk verifikasi dan penggabungan bagian selanjutnya. Anda dapat menggunakan cache atau database untuk menyimpan UploadId sesuai kebutuhan.

    Kode contoh: Inisialisasi permintaan unggahan dan hasilkan 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 parameter yang diteruskan dari klien (satuan: byte).
            long fileSize = 15 * 1024 * 1024L;   // Simulasikan file 15 MB.
            long partSize = 5 * 1024 * 1024L;    // Simulasikan ukuran bagian yang diinginkan 5 MB.
    
            // Hitung jumlah bagian.
            int totalParts = (int) ((fileSize + partSize - 1) / partSize);
    
            // Contoh ini menggunakan titik akhir publik wilayah China (Hangzhou).
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            String region = "cn-hangzhou";
            String bucketName = "exampleBucket";
            String objectName = "exampleObject.jpeg";
            long expireTime = 3600 * 1000L; // URL kedaluwarsa dalam 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();
    
            // Inisialisasi tugas unggahan.
            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"`
    }
    
    // Variabel parameter 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)
    
    	// Prakarsai 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",
    		})
    	}
    
    	// Keluarkan 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.
    FILE_SIZE = 15 * 1024 * 1024  # Simulasikan ukuran file yang akan diunggah (15 MB).
    PART_SIZE = 5 * 1024 * 1024   # Ukuran setiap bagian: 5 MB.
    EXPIRE_TIME = 3600            # Periode validitas URL yang ditandatangani sebelumnya (dalam detik).
    
    def main():
        # Buat parser argumen baris perintah dan jelaskan tujuan skrip.
        parser = argparse.ArgumentParser(description="presign multipart upload sample")
    
        # Tambahkan argumen baris perintah --region, yang menentukan wilayah tempat bucket berada. Ini adalah parameter wajib.
        parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
        # Tambahkan argumen baris perintah --bucket, yang menentukan nama bucket tempat objek akan diunggah. Ini adalah parameter wajib.
        parser.add_argument('--bucket', help='The name of the bucket.', required=True)
        # Tambahkan argumen baris perintah --endpoint, yang menentukan nama domain yang dapat digunakan layanan lain untuk mengakses OSS. Ini adalah parameter opsional.
        parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
        # Tambahkan argumen baris perintah --key, yang menentukan kunci objek (file) di OSS. Ini adalah parameter wajib.
        parser.add_argument('--key', help='The name of the object.', required=True)
    
        args = parser.parse_args()
    
        # Konfigurasikan klien OSS.
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
        cfg.region = args.region
        # Jika titik akhir kustom disediakan, perbarui properti titik akhir dalam objek konfigurasi.
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
        
        # Inisialisasi klien OSS.
        client = oss.Client(cfg)
    
        # Kirim permintaan untuk memprakarsai unggahan multi-bagian dan dapatkan upload_id.
        init_result = client.presign(oss.InitiateMultipartUploadRequest(
            bucket=args.bucket,
            key=args.key,
        ))
    
        # Gunakan URL yang ditandatangani sebelumnya untuk memprakarsai permintaan dan dapatkan upload_id.
        import requests
        with requests.post(init_result.url, headers=init_result.signed_headers) as resp:
            obj = oss.InitiateMultipartUploadResult()
            oss.serde.deserialize_xml(xml_data=resp.content, obj=obj)
            upload_id = obj.upload_id
    
        # Hitung jumlah bagian berdasarkan ukuran file 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"
            })
    
        # Keluarkan upload_id dan URL yang ditandatangani sebelumnya untuk 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 data bagian

    Klien menggunakan daftar URL yang ditandatangani sebelumnya yang dikembalikan oleh server untuk mengunggah setiap bagian menggunakan permintaan HTTP PUT. Metodenya sama dengan mengunggah satu file. Setelah semua bagian diunggah, klien harus memberi tahu server untuk memprakarsai operasi penggabungan.

    Catatan

    Unggahan konkuren didukung. Namun, Anda harus memastikan bahwa konten yang diunggah menggunakan setiap URL sesuai dengan bagian yang benar. Artinya, untuk URL dengan `partNumber=N`, Anda hanya dapat mengunggah bagian ke-N dari file. Anda tidak boleh mencampur atau melewatkan bagian.

    Contoh berikut menunjukkan cara mengunggah bagian pertama. /path/to/local/file adalah jalur bagian lokal.

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

    Setelah server menerima notifikasi "unggahan selesai", server dapat memanggil operasi ListParts untuk memverifikasi hal berikut:

    • Apakah jumlah bagian lengkap.

    • Apakah ukuran setiap bagian sesuai harapan.

    Catatan

    Operasi ini menggunakan UploadId yang sebelumnya dicatat.

    Kode contoh: Verifikasi sisi server

    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 {
            // Titik akhir layanan OSS. Gantilah dengan wilayah yang sebenarnya. Contoh ini menggunakan China (Hangzhou).
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // Gantilah dengan nama bucket Anda. Contoh ini menggunakan examplebucket.
            String bucketName = "examplebucket";
            // Gantilah dengan jalur lengkap objek Anda. Jalur lengkap tidak boleh mengandung nama bucket, misalnya, exampledir/exampleobject.jpeg.
            String objectName = "exampleobject.jpeg";
            // ID tugas unggahan multi-bagian.
            String uploadId = "4B78****************************";
            // Gantilah dengan wilayah Anda. Contoh ini menggunakan China (Hangzhou).
            String region = "cn-hangzhou";
    
            // Dapatkan kredensial dari variabel lingkungan dan bangun OSSClient.
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            ClientBuilderConfiguration config = new ClientBuilderConfiguration();
            config.setSignatureVersion(SignVersion.V4);
    
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .region(region)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(config)
                    .build();
    
            try {
                // Cantumkan semua bagian yang diunggah dalam tugas unggahan saat ini.
                ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, objectName, uploadId);
                PartListing partListing = ossClient.listParts(listPartsRequest);
    
                // Kumpulkan informasi ETag bagian untuk penggabungan selanjutnya.
                List<PartETag> partETags = new ArrayList<>();
                for (PartSummary part : partListing.getParts()) {
                    partETags.add(new PartETag(part.getPartNumber(), part.getETag()));
                }
    
                if (partETags.isEmpty()) {
                    System.out.println("No uploaded parts found. Aborting merge.");
                    return;
                }
    
                // Kirim permintaan penggabungan untuk menyelesaikan unggahan multi-bagian.
                System.out.println("Starting to merge parts...");
                CompleteMultipartUploadRequest completeRequest =
                        new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
    
                CompleteMultipartUploadResult result = ossClient.completeMultipartUpload(completeRequest);
                System.out.println("Parts merged successfully!");
                System.out.println("ETag: " + result.getETag());
    
            } catch (Exception e) {
                System.err.println("Failed to merge parts: " + e.getMessage());
                e.printStackTrace();
            } finally {
                if (ossClient != null) {
                    ossClient.shutdown();
                }
            }
        }
    }
    

    Go

    package main
    
    import (
    	"context"
    	"flag"
    	"log"
    
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
    )
    
    var (
    	region     string
    	bucketName string
    	objectName string
    	uploadId   string
    )
    
    func init() {
    	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
    	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
    	flag.StringVar(&objectName, "object", "", "The name of the object.")
    	flag.StringVar(&uploadId, "uploadId", "", "The upload ID.")
    }
    
    func main() {
    	flag.Parse()
    
    	if region == "" || bucketName == "" || objectName == "" || uploadId == "" {
    		flag.PrintDefaults()
    		log.Fatal("Missing required parameters.")
    	}
    
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	client := oss.NewClient(cfg)
    
    	// Cantumkan bagian yang diunggah dan bangun daftar lengkap bagian.
    	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)
    	}
    
    	// Cetak 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 baris perintah --region, yang menentukan wilayah tempat bucket berada. Ini adalah parameter wajib.
        parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
        # Tambahkan argumen baris perintah --bucket, yang menentukan nama bucket tempat objek akan diunggah. Ini adalah parameter wajib.
        parser.add_argument('--bucket', help='The name of the bucket.', required=True)
        # Tambahkan argumen baris perintah --endpoint, yang menentukan nama domain yang dapat digunakan layanan lain untuk mengakses OSS. Ini adalah parameter opsional.
        parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
        # Tambahkan argumen baris perintah --key, yang menentukan kunci objek (file) di OSS. Ini adalah parameter wajib.
        parser.add_argument('--key', help='The name of the object.', required=True)
        # Tambahkan argumen baris perintah --upload_id, yang menentukan ID Unggah tugas unggahan multi-bagian. Ini adalah parameter wajib.
        parser.add_argument('--upload_id', help='The upload ID to list parts for.',required=True)
    
        args = parser.parse_args()
    
        # Inisialisasi konfigurasi klien dan kredensial.
        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:
            # Dapatkan 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.
            upload_parts = [
                oss.UploadPart(part_number=part.part_number, etag=part.etag)
                for part in list_parts_result.parts
            ]
    
            if not upload_parts:
                print("No uploaded parts found. Aborting operation.")
                return
    
            # Bangun dan kirim permintaan penggabungan.
            request = oss.CompleteMultipartUploadRequest(
                bucket=args.bucket,
                key=args.key,
                upload_id=args.upload_id,
                complete_multipart_upload=oss.CompleteMultipartUpload(parts=upload_parts)
            )
    
            result = client.complete_multipart_upload(request)
    
            print("Merge successful!")
            print(f"ETag: {result.etag}")
            print(f"Bucket: {result.bucket}")
            print(f"Key: {result.key}")
    
        except Exception as e:
            print("Failed to merge parts:", e)
    
    if __name__ == "__main__":
        main()
  5. Server menggabungkan bagian dan mengembalikan hasil

    Setelah verifikasi berhasil, server memanggil operasi CompleteMultipartUpload untuk menggabungkan bagian dan mengembalikan hasil unggahan kepada klien.

    Kode contoh: Server 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 {
            // Titik akhir layanan OSS. Gantilah dengan wilayah yang sebenarnya. Contoh ini menggunakan China (Hangzhou).
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // Gantilah dengan nama bucket Anda. Contoh ini menggunakan examplebucket.
            String bucketName = "examplebucket";
            // Gantilah dengan jalur lengkap objek Anda. Jalur lengkap tidak boleh mengandung nama bucket, misalnya, exampledir/exampleobject.jpeg.
            String objectName = "exampleobject.jpeg";
            // ID tugas unggahan multi-bagian.
            String uploadId = "4B78****************************";
            // Gantilah dengan wilayah Anda. Contoh ini menggunakan China (Hangzhou).
            String region = "cn-hangzhou";
    
            // Dapatkan kredensial dari variabel lingkungan dan bangun OSSClient.
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            ClientBuilderConfiguration config = new ClientBuilderConfiguration();
            config.setSignatureVersion(SignVersion.V4);
    
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .region(region)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(config)
                    .build();
    
            try {
                // Cantumkan semua bagian yang diunggah dalam tugas unggahan saat ini.
                ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, objectName, uploadId);
                PartListing partListing = ossClient.listParts(listPartsRequest);
    
                // Kumpulkan informasi ETag bagian untuk penggabungan selanjutnya.
                List<PartETag> partETags = new ArrayList<>();
                for (PartSummary part : partListing.getParts()) {
                    partETags.add(new PartETag(part.getPartNumber(), part.getETag()));
                }
    
                if (partETags.isEmpty()) {
                    System.out.println("No uploaded parts found. Aborting merge.");
                    return;
                }
    
                // Kirim permintaan penggabungan untuk menyelesaikan unggahan multi-bagian.
                System.out.println("Starting to merge parts...");
                CompleteMultipartUploadRequest completeRequest =
                        new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
    
                CompleteMultipartUploadResult result = ossClient.completeMultipartUpload(completeRequest);
                System.out.println("Parts merged successfully!");
                System.out.println("ETag: " + result.getETag());
    
            } catch (Exception e) {
                System.err.println("Failed to merge parts: " + e.getMessage());
                e.printStackTrace();
            } finally {
                if (ossClient != null) {
                    ossClient.shutdown();
                }
            }
        }
    }
    

    Go

    package main
    
    import (
    	"context"
    	"flag"
    	"log"
    
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
    )
    
    var (
    	region     string
    	bucketName string
    	objectName string
    	uploadId   string
    )
    
    func init() {
    	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
    	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
    	flag.StringVar(&objectName, "object", "", "The name of the object.")
    	flag.StringVar(&uploadId, "uploadId", "", "The upload ID.")
    }
    
    func main() {
    	flag.Parse()
    
    	if region == "" || bucketName == "" || objectName == "" || uploadId == "" {
    		flag.PrintDefaults()
    		log.Fatal("Missing required parameters.")
    	}
    
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	client := oss.NewClient(cfg)
    
    	// Cantumkan bagian yang diunggah dan bangun daftar lengkap bagian.
    	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)
    	}
    
    	// Cetak 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 baris perintah --region, yang menentukan wilayah tempat bucket berada. Ini adalah parameter wajib.
        parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
        # Tambahkan argumen baris perintah --bucket, yang menentukan nama bucket tempat objek akan diunggah. Ini adalah parameter wajib.
        parser.add_argument('--bucket', help='The name of the bucket.', required=True)
        # Tambahkan argumen baris perintah --endpoint, yang menentukan nama domain yang dapat digunakan layanan lain untuk mengakses OSS. Ini adalah parameter opsional.
        parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
        # Tambahkan argumen baris perintah --key, yang menentukan kunci objek (file) di OSS. Ini adalah parameter wajib.
        parser.add_argument('--key', help='The name of the object.', required=True)
        # Tambahkan argumen baris perintah --upload_id, yang menentukan ID Unggah tugas unggahan multi-bagian. Ini adalah parameter wajib.
        parser.add_argument('--upload_id', help='The upload ID to list parts for.',required=True)
    
        args = parser.parse_args()
    
        # Inisialisasi konfigurasi klien dan kredensial.
        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:
            # Dapatkan 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.
            upload_parts = [
                oss.UploadPart(part_number=part.part_number, etag=part.etag)
                for part in list_parts_result.parts
            ]
    
            if not upload_parts:
                print("No uploaded parts found. Aborting operation.")
                return
    
            # Bangun dan kirim permintaan penggabungan.
            request = oss.CompleteMultipartUploadRequest(
                bucket=args.bucket,
                key=args.key,
                upload_id=args.upload_id,
                complete_multipart_upload=oss.CompleteMultipartUpload(parts=upload_parts)
            )
    
            result = client.complete_multipart_upload(request)
    
            print("Merge successful!")
            print(f"ETag: {result.etag}")
            print(f"Bucket: {result.bucket}")
            print(f"Key: {result.key}")
    
        except Exception as e:
            print("Failed to merge parts:", e)
    
    if __name__ == "__main__":
        main()

Atur header untuk menentukan kebijakan unggahan

Saat Anda menghasilkan URL yang ditandatangani sebelumnya, Anda dapat menentukan parameter header untuk menentukan kebijakan unggahan. Misalnya, Anda dapat mengatur kelas penyimpanan file x-oss-storage-class dan tipe konten Content-Type, seperti yang ditunjukkan dalam contoh kode berikut.

Penting

Jika Anda menentukan parameter header saat menghasilkan URL yang ditandatangani sebelumnya, Anda juga harus meneruskan header yang sama saat menggunakan URL tersebut untuk mengunggah file. Jika tidak, OSS akan mengembalikan kesalahan 403 karena verifikasi tanda tangan gagal.

Untuk informasi lebih lanjut tentang header sistem OSS yang dapat Anda atur, lihat PutObject. Anda juga dapat mengatur header kustom untuk mengelola file. Untuk informasi lebih lanjut, lihat Kelola metadata file.

  1. Pemilik bucket 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 menjalankan kode contoh, 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 mengandung nama bucket.
            String objectName = "exampleobject.txt";
            // Tentukan wilayah tempat bucket berada. Misalnya, jika bucket berada di wilayah China (Hangzhou), atur wilayah menjadi cn-hangzhou.
            String region = "cn-hangzhou";
    
            // Buat instance OSSClient.
            // Saat instance 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 ditandatangani sebelumnya dalam milidetik. Contoh ini menetapkan waktu kedaluwarsa menjadi 1 jam.
                Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
    
                // Hasilkan URL yang ditandatangani sebelumnya.
                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 ditandatangani sebelumnya untuk permintaan HTTP PUT.
                signedUrl = ossClient.generatePresignedUrl(request);
                // Cetak 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

    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 tempat bucket berada.
    	bucketName string // Nama bucket.
    	objectName string // Nama objek.
    )
    
    // Tentukan fungsi init yang digunakan 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 ditentukan.
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, bucket name required")
    	}
    
    	// Periksa apakah wilayah ditentukan.
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, region required")
    	}
    
    	// Periksa apakah nama objek ditentukan.
    	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)
    
    	// Hasilkan URL yang ditandatangani sebelumnya untuk permintaan PutObject.
    	result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
    		Bucket:      oss.Ptr(bucketName),
    		Key:         oss.Ptr(objectName),
    		ContentType:  oss.Ptr("text/plain;charset=utf8"),                    // Pastikan nilai parameter ContentType yang terdapat dalam URL sama dengan nilai ContentType yang ditentukan dalam permintaan.
    		StorageClass: oss.StorageClassStandard,                              // Pastikan nilai parameter StorageClass yang terdapat dalam URL sama dengan nilai StorageClass yang ditentukan dalam permintaan.
    		Metadata:    map[string]string{"key1": "value1", "key2": "value2"}, // Pastikan nilai Metadata yang terdapat dalam URL sama dengan nilai Metadata yang ditentukan dalam permintaan.
    	},
    		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 Anda menentukan header permintaan saat menghasilkan URL yang ditandatangani sebelumnya yang memungkinkan permintaan HTTP PUT, pastikan header permintaan tersebut disertakan dalam permintaan PUT yang diprakarsai menggunakan URL tersebut.
    		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 parameter baris perintah dan jelaskan tujuan skrip. Contoh ini menjelaskan cara menghasilkan URL yang ditandatangani sebelumnya yang memungkinkan permintaan HTTP PUT.
    parser = argparse.ArgumentParser(description="presign put object sample")
    
    # Tentukan parameter baris perintah, termasuk wilayah, nama bucket, titik akhir, dan nama objek yang wajib diisi.
    parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
    parser.add_argument('--bucket', help='The name of the bucket.', required=True)
    parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
    parser.add_argument('--key', help='The name of the object.', required=True)
    
    def main():
        # Uraikan parameter baris perintah untuk mendapatkan nilai yang ditentukan pengguna.
        args = parser.parse_args()
    
        # Dapatkan kredensial akses dari variabel lingkungan untuk autentikasi.
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    
        # Gunakan konfigurasi default SDK untuk membuat objek konfigurasi dan tentukan penyedia kredensial.
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
    
        # Tentukan atribut wilayah objek konfigurasi berdasarkan parameter baris perintah yang ditentukan pengguna.
        cfg.region = args.region
    
        # Jika titik akhir kustom disediakan, ubah parameter titik akhir dalam objek konfigurasi.
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
    
        # Gunakan konfigurasi di atas untuk menginisialisasi instance OSSClient dan memungkinkan instance berinteraksi dengan OSS.
        client = oss.Client(cfg)
    
        # Kirim permintaan untuk memprakarsai permintaan PUT dan menghasilkan URL yang ditandatangani sebelumnya untuk objek yang ditentukan.
        pre_result = client.presign(oss.PutObjectRequest(
            bucket=args.bucket, # Nama bucket.
            key=args.key, # Nama objek.
            content_type='text/plain;charset=utf8', # Tentukan tipe objek.
            storage_class='Standard', # Tentukan kelas penyimpanan objek.
            metadata={
                'key1': 'value1', # Tentukan metadata objek.
                'key2': 'value2' # Tentukan metadata objek.
            }
        ),expires=timedelta(seconds=3600)) # Tentukan periode validitas permintaan. Pada contoh ini, periode validitas diatur menjadi 3.600 detik.
    
    
        # Tampilkan metode, waktu kedaluwarsa, dan URL yang ditandatangani sebelumnya yang ditentukan dalam permintaan untuk memeriksa validitas URL tersebut.
        print(f'method: {pre_result.method},'
              f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
              f' url: {pre_result.url}'
              )
    
        # Tampilkan header yang ditandatangani dalam permintaan, yang disertakan dalam header HTTP saat permintaan dikirim.
        for key, value in pre_result.signed_headers.items():
            print(f'signed headers key: {key}, signed headers value: {value}')
    
    # Panggil fungsi utama untuk memulai logika pemrosesan saat skrip dijalankan secara langsung.
    if __name__ == "__main__":
        main() # Tentukan titik masuk dalam fungsi skrip. Alur kontrol program dimulai di sini.
  2. Pihak ketiga menggunakan URL yang ditandatangani sebelumnya untuk mengunggah file dan meneruskan 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 <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";
    
            // Atur header permintaan. Informasi header permintaan harus sama dengan informasi yang digunakan saat URL dihasilkan.
            Map<String, String> headers = new HashMap<String, String>();
            //Tentukan kelas penyimpanan objek.
            headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
            //Tentukan ContentType.
            headers.put(OSSHeaders.CONTENT_TYPE, "text/plain;charset=utf8");
    
            // Atur metadata pengguna kustom. Metadata pengguna kustom harus sama dengan informasi yang digunakan saat URL dihasilkan.
            Map<String, String> userMetadata = new HashMap<String, String>();
            userMetadata.put("key1","value1");
            userMetadata.put("key2","value2");
    
            try {
                HttpPut put = new HttpPut(signedUrl.toString());
                System.out.println(put);
                HttpEntity entity = new FileEntity(new File(pathName));
                put.setEntity(entity);
                // Jika Anda mengatur parameter header, seperti metadata pengguna dan kelas penyimpanan, saat menghasilkan URL yang ditandatangani sebelumnya, Anda juga harus mengirim parameter ini ke server saat menggunakan URL tersebut untuk mengunggah file. Kesalahan tanda tangan akan dilaporkan jika parameter yang dikirim ke server tidak konsisten dengan parameter yang ditandatangani.
                for(Map.Entry header: headers.entrySet()){
                    put.addHeader(header.getKey().toString(),header.getValue().toString());
                }
                for(Map.Entry meta: userMetadata.entrySet()){
                    // Jika Anda menggunakan userMeta, SDK secara internal menambahkan awalan 'x-oss-meta-'. Saat Anda menggunakan metode lain untuk menghasilkan URL yang ditandatangani sebelumnya untuk unggahan, Anda juga harus menambahkan awalan 'x-oss-meta-' ke userMeta.
                    put.addHeader("x-oss-meta-"+meta.getKey().toString(), meta.getValue().toString());
                }
    
                httpClient = HttpClients.createDefault();
    
                response = httpClient.execute(put);
    
                System.out.println("Status code returned for the upload:"+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("Upload successful using network library.");
                }
                System.out.println(response.toString());
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                response.close();
                httpClient.close();
            }
        }
    }       

    Go

    package main
    
    import (
    	"bytes"
    	"fmt"
    	"io/ioutil"
    	"net/http"
    	"os"
    )
    
    func uploadFile(signedUrl string, filePath string, headers map[string]string, metadata map[string]string) error {
    	// Buka file.
    	file, err := os.Open(filePath)
    	if err != nil {
    		return err
    	}
    	defer file.Close()
    
    	// Baca konten file.
    	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
    	}
    
    	// Atur header permintaan.
    	for key, value := range headers {
    		req.Header.Set(key, value)
    	}
    
    	// Atur metadata pengguna kustom.
    	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 respons.
    	fmt.Printf("Status code returned for the upload: %d\n", resp.StatusCode)
    	if resp.StatusCode == 200 {
    		fmt.Println("Upload successful using network library.")
    	} else {
    		fmt.Println("Upload failed.")
    	}
    	body, _ := ioutil.ReadAll(resp.Body)
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// 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"
    
    	// Atur header permintaan. Informasi header permintaan harus sama dengan informasi yang digunakan saat URL dihasilkan.
    	headers := map[string]string{
    		"Content-Type": "text/plain;charset=utf8",
    		"x-oss-storage-class": "Standard",
    	}
    
    	// Atur metadata pengguna kustom. Metadata pengguna kustom harus sama dengan informasi yang digunakan saat URL dihasilkan.
    	metadata := map[string]string{
    		"key1": "value1",
    		"key2": "value2",
    	}
    
    	err := uploadFile(signedUrl, filePath, headers, metadata)
    	if err != nil {
    		fmt.Printf("An error occurred: %v\n", err)
    	}
    }
    

    Python

    import requests
    from requests.auth import HTTPBasicAuth
    import os
    
    def upload_file(signed_url, file_path, headers=None, metadata=None):
        """
        Gunakan URL yang ditandatangani sebelumnya untuk mengunggah file ke OSS.
    
        :param signed_url: URL yang ditandatangani sebelumnya.
        :param file_path: Jalur lengkap file yang akan diunggah.
        :param headers: Opsional. Header HTTP kustom.
        :param metadata: Opsional. Metadata kustom.
        :return: None
        """
        if not headers:
            headers = {}
        if not metadata:
            metadata = {}
    
        # Perbarui header dan tambahkan awalan metadata.
        for key, value in metadata.items():
            headers[f'x-oss-meta-{key}'] = value
    
        try:
            with open(file_path, 'rb') as file:
                response = requests.put(signed_url, data=file, headers=headers)
                print(f"Status code returned for the upload: {response.status_code}")
                if response.status_code == 200:
                    print("Upload successful using network library.")
                else:
                    print("Upload failed.")
                print(response.text)
        except Exception as e:
            print(f"An error occurred: {e}")
    
    if __name__ == "__main__":
        // Ganti <signedUrl> dengan URL yang diotorisasi.
        signed_url = "<signedUrl>"
       
        // Tentukan jalur lengkap file lokal. Jika Anda tidak menentukan jalur lokal, file diunggah dari direktori tempat skrip berada secara default.
        file_path = "C:\\Users\\demo.txt"
    
        // Atur header permintaan. Informasi header permintaan harus sama dengan informasi yang digunakan saat URL dihasilkan.
        headers = {
             "Content-Type": "text/plain;charset=utf8",
             "x-oss-storage-class": "Standard"
        }
    
        // Atur metadata pengguna kustom. Metadata pengguna kustom harus sama dengan informasi yang digunakan saat URL 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 dan tambahkan 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 returned for the upload: ${response.status}`);
            if (response.status === 200) {
                console.log("Upload successful using network library.");
            } else {
                console.log("Upload failed.");
            }
            console.log(response.data);
        } catch (error) {
            console.error(`An error occurred: ${error.message}`);
        }
    }
    
    // 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 direktori tempat skrip berada secara default.
        const filePath = "C:\\Users\\demo.txt";
    
        // Atur header permintaan. Informasi header permintaan harus sama dengan informasi yang digunakan saat URL dihasilkan.
        const headers = {
             "Content-Type": "text/plain;charset=utf8",
             "x-oss-storage-class": "Standard"
        };
    
        // Atur metadata pengguna kustom. Metadata pengguna kustom harus sama dengan informasi yang digunakan saat URL dihasilkan.
        const metadata = {
             "key1": "value1",
             "key2": "value2"
        };
    
        await uploadFile(signedUrl, filePath, headers, metadata);
    })();
    

    Browser.js

    Penting

    Jika Anda mengalami kesalahan ketidakcocokan tanda tangan 403 saat menggunakan Browser.js untuk mengunggah file, biasanya karena browser secara otomatis menambahkan header permintaan Content-Type, tetapi header ini tidak ditentukan saat URL yang ditandatangani sebelumnya dihasilkan. Hal 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 (Java SDK)</h1>
    
        <input type="file" id="fileInput" />
        <button id="uploadButton">Upload File</button>
    
        <script>
            // Ganti ini dengan URL yang ditandatangani sebelumnya yang sebenarnya.
            const signedUrl = "<signedUrl>"; 
    
            document.getElementById('uploadButton').addEventListener('click', async () => {
                const fileInput = document.getElementById('fileInput');
                const file = fileInput.files[0];
    
                if (file) {
                    try {
                        await upload(file, signedUrl);
                    } catch (error) {
                        console.error('Error during upload:', error);
                        alert('Upload failed: ' + error.message);
                    }
                } else {
                    alert('Please select a file to upload.');
                }
            });
    
            const upload = async (file, presignedUrl) => {
                const headers = {
                    "Content-Type": "text/plain;charset=utf8",
                    'x-oss-storage-class': 'Standard',
                    'x-oss-meta-key1': 'value1',
                    'x-oss-meta-key2': 'value2'
                };
    
                const response = await fetch(presignedUrl, {
                    method: 'PUT',
                    headers: headers,
                    body: file
                });
    
                if (!response.ok) {
                    throw new Error(`Upload failed, status: ${response.status}`);
                }
    
                alert('File uploaded successfully');
                console.log('File uploaded successfully');
            };
        </script>
    </body>
    </html>

    C#

    using System.Net.Http.Headers;
    
    // 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;
    
    // Jika Anda mengatur parameter header, seperti metadata pengguna dan kelas penyimpanan, saat menghasilkan URL yang ditandatangani sebelumnya, Anda juga harus mengirim parameter ini ke server saat menggunakan URL tersebut untuk mengunggah file. Kesalahan tanda tangan akan dilaporkan jika parameter yang dikirim ke server tidak konsisten dengan parameter yang ditandatangani.
    // Atur header permintaan. Informasi header permintaan harus sama dengan informasi yang digunakan saat URL dihasilkan.
    request.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain") { CharSet = "utf8" };  // Tentukan ContentType.       
    // Atur metadata pengguna kustom. Metadata pengguna kustom harus sama dengan informasi yang digunakan saat URL dihasilkan.
    request.Content.Headers.Add("x-oss-meta-key1", "value1");
    request.Content.Headers.Add("x-oss-meta-key2", "value2");
    // Tentukan kelas penyimpanan objek.
    request.Content.Headers.Add("x-oss-storage-class", "Standard");
    
    // Cetak header permintaan.
    Console.WriteLine("Request headers:");
    foreach (var header in request.Content.Headers)
    {
        Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
    }
    
    // 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>
    #include <map>
    #include <string>
    
    // Fungsi callback untuk menangani 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) {
            // Atur URL.
            curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str());
    
            // Atur metode permintaan menjadi 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;
            }
    
            // Atur ukuran file.
            fseek(file, 0, SEEK_END);
            long fileSize = ftell(file);
            rewind(file);
    
            // Atur callback baca file.
            curl_easy_setopt(curl, CURLOPT_READDATA, file);
            curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
    
            // Atur 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);
    
            // Atur callback penanganan 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 << "Status code returned for the upload: " << responseCode << std::endl;
                if (responseCode == 200) {
                    std::cout << "Upload successful using network library." << std::endl;
                } else {
                    std::cout << "Upload failed." << std::endl;
                }
                std::cout << readBuffer << std::endl;
            }
    
            // Bersihkan.
            fclose(file);
            curl_slist_free_all(chunk);
            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";
    
        // Atur header permintaan. Informasi header permintaan harus sama dengan informasi yang digunakan saat URL dihasilkan.
        std::map<std::string, std::string> headers = {
             {"Content-Type", "text/plain;charset=utf8"},
             {"x-oss-storage-class", "Standard"}
        };
    
        // Atur metadata pengguna kustom. Metadata pengguna kustom harus sama dengan informasi yang digunakan saat URL dihasilkan.
        std::map<std::string, std::string> metadata = {
             {"key1", "value1"},
             {"key2", "value2"}
        };
    
        uploadFile(signedUrl, filePath, headers, metadata);
    
        return 0;
    }
    

    Android

    import android.os.AsyncTask;
    import android.util.Log;
    
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Map.Entry;
    
    public class SignUrlUploadActivity extends AppCompatActivity {
    
        private static final String TAG = "SignUrlUploadActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // 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 pathName = "/storage/emulated/0/demo.txt";
    
            // Atur header permintaan. Informasi header permintaan harus sama dengan informasi yang digunakan saat URL dihasilkan.
            Map<String, String> headers = new HashMap<>();
            headers.put("Content-Type", "text/plain;charset=utf8");
            headers.put("x-oss-storage-class", "Standard");
    
            // Atur metadata pengguna kustom. Metadata pengguna kustom harus sama dengan informasi yang digunakan saat URL 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);
    
                    // Atur header permintaan.
                    for (Entry<String, String> header : headers.entrySet()) {
                        connection.setRequestProperty(header.getKey(), header.getValue());
                    }
    
                    // Atur metadata pengguna kustom.
                    for (Entry<String, String> meta : userMetadata.entrySet()) {
                        connection.setRequestProperty("x-oss-meta-" + meta.getKey(), meta.getValue());
                    }
    
                    // Baca file.
                    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, "Status code returned for the upload: " + responseCode);
                    if (responseCode == 200) {
                        Log.d(TAG, "Upload successful using network library.");
                    } else {
                        Log.d(TAG, "Upload failed.");
                    }
    
                    InputStream is = connection.getInputStream();
                    byte[] responseBuffer = new byte[1024];
                    StringBuilder responseStringBuilder = new StringBuilder();
                    while ((count = is.read(responseBuffer)) != -1) {
                        responseStringBuilder.append(new String(responseBuffer, 0, count));
                    }
                    Log.d(TAG, responseStringBuilder.toString());
    
                    return responseCode;
                } catch (IOException e) {
                    e.printStackTrace();
                    return -1;
                }
            }
    
            @Override
            protected void onPostExecute(Integer result) {
                super.onPostExecute(result);
                if (result == 200) {
                    Toast.makeText(SignUrlUploadActivity.this, "Upload successful.", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(SignUrlUploadActivity.this, "Upload failed.", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }
    

Atur callback unggahan

Saat Anda mengunggah file, Anda dapat menambahkan parameter callback untuk secara otomatis memberi tahu server aplikasi Anda setelah unggahan berhasil. Untuk informasi lebih lanjut tentang prinsip callback, lihat Callback.

  1. Pemilik bucket menghasilkan URL yang ditandatangani sebelumnya untuk permintaan PUT yang mencakup parameter callback unggahan.

    Python

    import argparse
    import base64
    import requests
    import alibabacloud_oss_v2 as oss
    
    from datetime import datetime, timedelta
    
    # Buat parser parameter baris perintah dan jelaskan tujuan skrip. Contoh ini menjelaskan cara membuat URL yang ditandatangani yang mengizinkan permintaan HTTP PUT.
    parser = argparse.ArgumentParser(description="presign put object sample")
    
    # Tentukan parameter baris perintah, termasuk Wilayah, nama bucket, titik akhir, dan nama 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():
        # Mengurai parameter baris perintah.
        args = parser.parse_args()
    
        # Dapatkan kredensial akses dari variabel lingkungan untuk autentikasi.
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    
        # Gunakan konfigurasi default untuk membuat objek cfg dan tentukan penyedia kredensial.
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
    
        # Atur atribut Wilayah dari objek cfg ke Wilayah yang disediakan di baris perintah.
        cfg.region = args.region
    
        # Jika titik akhir kustom disediakan, perbarui atribut titik akhir dari objek cfg dengan titik akhir yang disediakan.
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
    
        # Gunakan konfigurasi sebelumnya untuk menginisialisasi instans OSSClient.
        client = oss.Client(cfg)
    
        # Tentukan URL callback kustom.
        call_back_url = "http://www.example.com/callback"
        # Buat parameter callback: tentukan URL callback dan badan permintaan, dan enkode parameter dalam Base64.
        callback=base64.b64encode(str('{\"callbackUrl\":\"' + call_back_url + '\",\"callbackBody\":\"bucket=${bucket}&object=${object}&my_var_1=${x:var1}&my_var_2=${x:var2}\"}').encode()).decode()
        # Tentukan variabel callback kustom dan enkode ke Base64.
        callback_var=base64.b64encode('{\"x:var1\":\"value1\",\"x:var2\":\"value2\"}'.encode()).decode()
    
        # Kirim permintaan untuk memulai permintaan PUT dan buat URL yang ditandatangani untuk objek yang ditentukan.
        pre_result = client.presign(oss.PutObjectRequest(
            bucket=args.bucket,  # Nama bucket.
            key=args.key,  # Nama objek.
            callback=callback,
            callback_var=callback_var,
        ),expires=timedelta(seconds=3600)) # Tentukan periode validitas permintaan. Dalam contoh ini, periode validitas diatur ke 3.600 detik.
    
        # Tampilkan metode, waktu kedaluwarsa, dan URL yang ditandatangani yang ditentukan dalam permintaan untuk memeriksa validitas URL yang ditandatangani.
        print(f'method: {pre_result.method},'
              f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
              f' url: {pre_result.url}'
              )
    
        # Tampilkan header yang ditandatangani dalam permintaan, yang disertakan dalam header HTTP saat permintaan dikirim.
        for key, value in pre_result.signed_headers.items():
            print(f'signed headers key: {key}, signed headers value: {value}')
    
    # Panggil fungsi utama untuk memulai logika pemrosesan saat skrip dijalankan secara langsung.
    if __name__ == "__main__":
        main()  # Tentukan titik masuk skrip. Alur kontrol 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.
    )
    
    // Tentukan fungsi init yang digunakan 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 ditentukan.
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, bucket name required")
    	}
    
    	// Periksa apakah wilayah ditentukan.
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, region required")
    	}
    
    	// Periksa apakah nama objek ditentukan.
    	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. Contoh: 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 tipe isi permintaan callback.
    	}
    
    	// Konversi konfigurasi parameter callback ke string JSON dan enkode string tersebut dalam Base64 untuk meneruskan konfigurasi 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 untuk permintaan PutObject.
    	result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
    		Bucket:      oss.Ptr(bucketName),
    		Key:         oss.Ptr(objectName),
    		Callback:    oss.Ptr(callbackBase64),    // Atur parameter callback, yang merupakan string JSON yang dienkode Base64 dalam kasus ini.
    		CallbackVar: oss.Ptr(callbackVarBase64), // Tentukan parameter kustom untuk permintaan callback, yang merupakan string JSON yang dienkode Base64 dalam contoh ini. 
    	},
    		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 Anda menentukan header permintaan saat menghasilkan URL yang ditandatangani sebelumnya yang memungkinkan permintaan HTTP PUT, pastikan header permintaan tersebut disertakan dalam permintaan PUT yang diprakarsai menggunakan URL tersebut.
    		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 wilayah China (Hangzhou). Tentukan titik akhir yang sebenarnya.
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // Dapatkan kredensial akses dari variabel lingkungan. Sebelum menjalankan kode contoh, 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 mengandung nama bucket.
            String objectName = "exampleobject.txt";
            // Tentukan wilayah tempat bucket berada. Misalnya, jika bucket berada di wilayah China (Hangzhou), atur wilayah menjadi cn-hangzhou.
            String region = "cn-hangzhou";
    
            // Buat instance OSSClient.
            // Saat instance 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 {
                // Bangun 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);
    
                // Bangun 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);
    
                // Hasilkan URL yang ditandatangani sebelumnya.
                URL url = ossClient.generatePresignedUrl(request);
    
                // Cetak hasil.
                System.out.println("method: PUT,");
                System.out.println(" expiration: " + expirationStr + ",");
                System.out.println(" url: " + url);
    
            } catch (OSSException oe) {
                System.out.println("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                System.out.println("Error Message:" + oe.getErrorMessage());
                System.out.println("Error Code:" + oe.getErrorCode());
                System.out.println("Request ID:" + oe.getRequestId());
                System.out.println("Host ID:" + oe.getHostId());
            } catch (ClientException ce) {
                System.out.println("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                System.out.println("Error Message:" + ce.getMessage());
            }
        }
    }

    PHP

    <?php
    
    // Sertakan file autoload untuk memuat dependensi.
    require_once __DIR__ . '/../vendor/autoload.php';
    
    use AlibabaCloud\Oss\V2 as Oss;
    
    // Definisikan dan jelaskan opsi 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 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 ke daftar opsi panjang yang diperlukan oleh getopt.
    // Tambahkan titik dua (:) di akhir setiap opsi untuk menunjukkan bahwa nilai diperlukan.
    $longopts = \array_map(function ($key) {
        return "$key:";
    }, array_keys($optsdesc));
    
    // Uraikan opsi baris perintah.
    $options = getopt("", $longopts);
    
    // Periksa apakah opsi wajib telah dikonfigurasi.
    foreach ($optsdesc as $key => $value) {
        if ($value['required'] === True && empty($options[$key])) {
            $help = $value['help']; // Dapatkan informasi bantuan.
            echo "Error: the following arguments are required: --$key, $help" . PHP_EOL;
            exit(1); // Keluar dari program jika opsi wajib hilang.
        }
    }
    
    // Tetapkan nilai yang diuraikan dari opsi baris perintah ke variabel yang sesuai.
    $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); // Tentukan penyedia kredensial.
    $cfg->setRegion($region); // Tentukan wilayah tempat bucket berada.
    if (isset($options["endpoint"])) {
        $cfg->setEndpoint($options["endpoint"]); // Tentukan titik akhir jika disediakan.
    }
    
    // Buat instance OSSClient.
    $client = new Oss\Client($cfg);
    
    // Sertakan header x-oss-callback dan x-oss-callback-var.
    // Tentukan URL callback.
    $call_back_url = "http://www.example.com/callback";
    
    // Bangun parameter callback: Tentukan URL callback dan isi permintaan, keduanya harus dienkode Base64.
    // Ganti ${x:var1} dan ${x:var2} dengan placeholder {var1} dan {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
    ]));
    
    // Definisikan dan enkode Base64 variabel callback kustom.
    $callback_var = base64_encode(json_encode([
        "x:var1" => "value1",
        "x:var2" => "value2"
    ]));
    
    // Buat objek PutObjectRequest untuk mengunggah data. 
    // Perhatikan bahwa Content-Type, metadata, dan header disertakan untuk perhitungan tanda tangan. 
    $request = new Oss\Models\PutObjectRequest(
        bucket: $bucket,
        key: $key,
        callback:$callback,
        callbackVar:$callback_var,
    );
    
    // Panggil metode presign untuk menghasilkan permintaan yang ditandatangani sebelumnya.
    $result = $client->presign($request);
    
    // Tampilkan URL yang ditandatangani sebelumnya, yang dapat digunakan untuk mengunggah objek yang ditentukan.
    print(
        'put object presign result:' . var_export($result, true) . PHP_EOL .
        'put object url:' . $result->url . PHP_EOL
    );
    
  2. Pihak ketiga menggunakan URL yang ditandatangani sebelumnya untuk permintaan PUT guna mengunggah file.

    curl

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

    Python

    import requests
    
    def upload_file(signed_url, file_path, headers=None):
        """
        Gunakan URL yang ditandatangani sebelumnya untuk mengunggah objek ke OSS.
    
        :param signed_url: URL yang ditandatangani sebelumnya.
        :param file_path: Jalur lengkap file lokal yang akan diunggah.
        :param headers: Header permintaan. Parameter ini opsional.
        :return: None
        """
        if not headers:
            headers = {}
    
        try:
            with open(file_path, 'rb') as file:
                response = requests.put(signed_url, data=file, headers=headers)
                print(f"Status code: {response.status_code}")
                if response.status_code == 200:
                    print("The object is uploaded by using the library.")
                else:
                    print("Upload failed.")
                print(response.text)
        except Exception as e:
            print(f"An error occurred: {e}")
    
    if __name__ == "__main__":
        // Ganti <signedUrl> dengan URL yang ditandatangani sebelumnya.
        signed_url = "<signedUrl>"
    
        // 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"
    
        headers = {
            "x-oss-callback": "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9",
            "x-oss-callback-var": "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==",
        }
    
        upload_file(signed_url,  file_path, headers)

    Go

    package main
    
    import (
    	"bytes"
    	"fmt"
    	"io"
    
    	"net/http"
    	"os"
    )
    
    func uploadFile(signedUrl string, filePath string, headers map[string]string) error {
    	// Buka file.
    	file, err := os.Open(filePath)
    	if err != nil {
    		return err
    	}
    	defer file.Close()
    
    	// Baca konten.
    	fileBytes, err := io.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.Add(key, value)
    	}
    
    	// Kirim permintaan.
    	client := &http.Client{}
    	resp, err := client.Do(req)
    	if err != nil {
    		return err
    	}
    	defer resp.Body.Close()
    
    	// Proses respons.
    	fmt.Printf("Status code: %d\n", resp.StatusCode)
    	if resp.StatusCode == 200 {
    		fmt.Println("The object is uploaded by using the library.")
    	} else {
    		fmt.Println("Upload failed.")
    	}
    	body, _ := io.ReadAll(resp.Body)
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// Ganti <signedUrl> dengan URL yang ditandatangani sebelumnya.
    	signedUrl := "<signedUrl>"
    	// Tentukan jalur lengkap file lokal. Jika jalur file lokal tidak ditentukan, file lokal diunggah dari jalur proyek tempat program contoh berada.
    	filePath := "C:\\Users\\demo.txt"
    
    	// Tentukan header permintaan. Pastikan nilai header permintaan sama dengan yang ditentukan saat URL yang ditandatangani sebelumnya dihasilkan.
    	headers := map[string]string{
    		"x-oss-callback":     "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9",
    		"x-oss-callback-var": "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==",
    	}
    
    	err := uploadFile(signedUrl, filePath, headers)
    	if err != nil {
    		fmt.Printf("An error occurred: %v\n", err)
    	}
    }
    

    Java

    import org.apache.http.HttpEntity;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPut;
    import org.apache.http.entity.FileEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import java.io.*;
    import java.net.URL;
    import java.util.*;
    
    public class SignUrlUpload {
        public static void main(String[] args) throws Throwable {
            CloseableHttpClient httpClient = null;
            CloseableHttpResponse response = null;
    
            // Ganti <signedUrl> dengan URL yang ditandatangani sebelumnya.
            URL signedUrl = new URL("<signedUrl>");
    
            // Tentukan jalur lengkap file lokal. Jika jalur file lokal tidak ditentukan, file lokal diunggah dari jalur proyek tempat program contoh berada.
            String pathName = "C:\\Users\\demo.txt";
    
            // Tentukan header permintaan, termasuk x-oss-callback dan x-oss-callback-var.
            Map<String, String> headers = new HashMap<String, String>();
            headers.put("x-oss-callback", "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9");
            headers.put("x-oss-callback-var", "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==");
    
            try {
                HttpPut put = new HttpPut(signedUrl.toString());
                System.out.println(put);
                HttpEntity entity = new FileEntity(new File(pathName));
                put.setEntity(entity);
                // Jika Anda mengonfigurasi header 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 berbeda dengan header 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());
                }
    
                httpClient = HttpClients.createDefault();
    
                response = httpClient.execute(put);
    
                System.out.println("Status code: "+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("The object is uploaded by using the library.");
                }
                System.out.println(response.toString());
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                response.close();
                httpClient.close();
            }
        }
    }       

    PHP

    <?php
    
    function uploadFile($signedUrl, $filePath, $headers = []) {
        // Periksa apakah file ada.
        if (!file_exists($filePath)) {
            echo "The file does not exist: $filePath\n";
            return;
        }
    
        // Inisialisasi sesi cURL.
        $ch = curl_init();
    
        // Atur opsi cURL.
        curl_setopt($ch, CURLOPT_URL, $signedUrl);
        curl_setopt($ch, CURLOPT_PUT, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_INFILE, fopen($filePath, 'rb'));
        curl_setopt($ch, CURLOPT_INFILESIZE, filesize($filePath));
        curl_setopt($ch, CURLOPT_HTTPHEADER, array_map(function($key, $value) {
            return "$key: $value";
        }, array_keys($headers), $headers));
    
        // Lakukan permintaan cURL.
        $response = curl_exec($ch);
    
        // Kueri kode status HTTP.
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
        // Tutup sesi cURL.
        curl_close($ch);
    
        // Keluarkan hasil
        echo "Status code: $httpCode\n";
        if ($httpCode == 200) {
            echo "The object is uploaded by using the library.\n";
        } else {
            echo "Upload failed.\n";
        }
        echo $response . "\n";
    }
    
    // Ganti <signedUrl> dengan URL yang ditandatangani sebelumnya.
    $signedUrl = "<signedUrl>";
    
    // Tentukan jalur lengkap file lokal. Secara default, jika Anda tidak menentukan jalur lengkap file lokal, file lokal diunggah dari direktori tempat skrip disimpan.
    $filePath = "C:\\Users\\demo.txt";
    
    $headers = [
        "x-oss-callback" => "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9",
        "x-oss-callback-var" => "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==",
    ];
    
    uploadFile($signedUrl, $filePath, $headers);
    
    ?>

Pelajari lebih lanjut

Apa itu URL yang ditandatangani sebelumnya

URL yang ditandatangani sebelumnya adalah tautan aman yang memberikan akses sementara ke file OSS tertentu menggunakan tanda tangan terenkripsi dan verifikasi periode validitas. Saat URL yang ditandatangani sebelumnya dihasilkan, tanda tangan terenkripsi dihitung secara lokal berdasarkan pasangan AccessKey, jalur sumber daya, waktu kedaluwarsa, dan parameter lainnya. Parameter tanda tangan kemudian ditambahkan ke URL untuk membentuk URL yang ditandatangani sebelumnya lengkap. Format tipikalnya adalah: https://BucketName.Endpoint/Object?signature_parameters.

Saat pihak ketiga mengakses URL ini, OSS memverifikasi parameter tanda tangan. Jika parameter telah dimanipulasi atau URL telah kedaluwarsa, akses ditolak.

Berikut adalah 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 cara ini, pemilik bucket dapat memberi otorisasi akses pihak ketiga ke file secara aman tanpa mengekspos pasangan AccessKey.

Skenario

  • Berbagi file jangka pendek: Pihak ketiga meminta untuk mengunggah atau mengunduh file tertentu. Backend menghasilkan URL yang ditandatangani sebelumnya dengan periode validitas dan mengembalikannya ke frontend. Pihak ketiga dapat menggunakan URL ini untuk mengunggah atau mengunduh file dalam periode validitas, yang menjamin keamanan data.

  • Akses fleksibel: Pemilik bucket dapat membagikan URL yang ditandatangani sebelumnya melalui email atau obrolan. Pihak ketiga kemudian dapat menempelkan URL tersebut ke bilah alamat browser untuk mengunduh file.

FAQ

Bagaimana cara saya memberi otorisasi kepada pihak ketiga untuk melakukan lebih banyak operasi pada sumber daya OSS, bukan hanya unggahan?

Selain URL yang ditandatangani sebelumnya, Alibaba Cloud menyediakan metode otorisasi sementara yang lebih fleksibel: kredensial akses sementara STS. Jika Anda ingin pihak ketiga melakukan lebih banyak operasi manajemen pada sumber daya OSS, seperti mencantumkan atau menyalin file, selain mengunggah file, kami merekomendasikan agar Anda menggunakan kredensial akses sementara STS. Untuk informasi lebih lanjut, lihat Akses OSS menggunakan kredensial STS.

Dapatkah saya hanya mengizinkan situs web tertentu untuk mengakses sumber daya OSS dan menolak permintaan dari sumber lain?

Ya, Anda bisa. Anda dapat mengonfigurasi perlindungan hotlink berbasis Referer dan menyiapkan daftar putih. Konfigurasi ini membatasi akses ke sumber daya OSS Anda ke situs web tertentu, seperti milik Anda sendiri, dan mencegah sumber lain merujuk langsung ke file OSS Anda. Untuk langkah-langkah konfigurasi terperinci, lihat Konfigurasikan perlindungan hotlink berbasis Referer untuk mencegah situs web lain merujuk file OSS.

Kesalahan CORS

Kesalahan ini terjadi jika kebijakan lintas domain untuk bucket tidak dikonfigurasi atau dikonfigurasi salah. Untuk informasi tentang cara memeriksa konfigurasi, lihat Konfigurasikan pengaturan lintas domain.

Kesalahan 405 Method Not Allowed

Kesalahan ini terjadi jika metode permintaan salah. Saat Anda menggunakan URL yang ditandatangani untuk mengunggah file, pastikan untuk menggunakan permintaan PUT, bukan permintaan POST.