All Products
Search
Document Center

Object Storage Service:Unggah file menggunakan URL yang ditandatangani (Go SDK V2)

Last Updated:Dec 25, 2025

Secara default, objek dalam bucket Object Storage Service (OSS) bersifat privat dan hanya dapat diakses oleh pemiliknya. Topik ini menjelaskan cara menggunakan OSS Go Software Development Kit (SDK) V2 untuk menghasilkan URL yang ditandatangani untuk metode PUT dengan time-to-live (TTL), sehingga pengguna lain dapat sementara waktu mengunggah objek. URL tersebut dapat digunakan beberapa kali sebelum kedaluwarsa; setelah kedaluwarsa, Anda harus menghasilkan URL baru.

Perhatian

  • Kode contoh dalam topik ini menggunakan ID wilayah cn-hangzhou dan endpoint publik untuk wilayah China (Hangzhou). Jika Anda ingin mengakses OSS dari produk Alibaba Cloud lainnya di wilayah yang sama, gunakan endpoint jaringan internal. Untuk informasi lebih lanjut mengenai pemetaan antara wilayah OSS dan endpoint, lihat Wilayah dan Endpoint.

  • Contoh dalam topik ini menunjukkan cara memperoleh kredensial akses dari variabel lingkungan. Untuk informasi lebih lanjut tentang cara mengonfigurasi kredensial akses, lihat Konfigurasikan kredensial akses.

  • Izin tidak diperlukan untuk menghasilkan URL presigned. Namun, pihak ketiga hanya dapat berhasil mengunggah file menggunakan URL presigned tersebut jika pengguna yang menghasilkan URL memiliki izin oss:PutObject. Untuk informasi lebih lanjut tentang cara memberikan izin, lihat Berikan izin kustom kepada RAM user.

  • Kode contoh dalam topik ini menggunakan URL signed V4, yang memiliki TTL maksimum 7 hari. Untuk informasi lebih lanjut, lihat Signature V4 (direkomendasikan).

Proses

Gambar berikut menunjukkan proses pengunggahan file menggunakan URL presigned untuk permintaan PUT.

image

Definisi metode

Anda dapat memanggil operasi presign untuk menghasilkan URL yang ditandatangani, yang memberikan akses sementara ke objek dalam bucket. URL tersebut dapat digunakan beberapa kali sebelum kedaluwarsa.

Operasi presign didefinisikan sebagai berikut:

func (c *Client) Presign(ctx context.Context, request any, optFns ...func(*PresignOptions)) (result *PresignResult, err error)

Parameter permintaan

Parameter

Tipe

Deskripsi

ctx

context.Context

Konteks permintaan.

request

*PutObjectRequest

Nama operasi untuk menghasilkan URL yang ditandatangani.

optFns

...func(*PresignOptions)

(Opsional) TTL. Jika Anda tidak menentukan parameter ini, TTL default adalah 15 menit.

Opsi PresignOptions tercantum di bawah ini:

Opsi

Tipe

Deskripsi

Expires

time.Duration

Durasi dari waktu saat ini hingga URL kedaluwarsa. Misalnya, untuk mengatur TTL selama 30 menit, gunakan 30 * time.Minute.

Expiration

time.Time

Waktu kedaluwarsa absolut.

Penting

Untuk signature V4, TTL maksimum adalah 7 hari. Jika Anda menetapkan Expiration dan Expires secara bersamaan, Expiration akan diutamakan.

Nilai kembali

Nilai kembali

Tipe

Deskripsi

result

*PresignResult

Hasil. Termasuk URL yang ditandatangani, metode HTTP, waktu kedaluwarsa, dan header permintaan yang ditandatangani.

err

error

Status permintaan. Jika permintaan gagal, err tidak bernilai nil.

Nilai kembali PresignResult tercantum di bawah ini:

Parameter

Tipe

Deskripsi

Method

string

Metode HTTP yang sesuai dengan operasi. Misalnya, untuk operasi PutObject, nilai yang dikembalikan adalah PUT.

URL

string

URL presigned

Expiration

time.Time

Waktu kedaluwarsa URL yang ditandatangani.

SignedHeaders

map[string]string

Header permintaan yang ditandatangani. Misalnya, jika Anda menetapkan Content-Type untuk operasi PutObject, informasi mengenai Content-Type akan dikembalikan.

Kode contoh

  1. Pemilik objek menghasilkan URL yang ditandatangani untuk metode PUT.

    Penting

    Saat menghasilkan URL yang ditandatangani untuk metode PUT, jika Anda menentukan header permintaan, Anda harus menyertakan header yang sama saat menggunakan URL tersebut untuk mengirim permintaan PUT. Jika header tidak sesuai, permintaan akan gagal dan kesalahan signature dikembalikan.

    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 penyimpanan.
    	bucketName string // Nama bucket.
    	objectName string // Nama objek.
    )
    
    // Fungsi init digunakan untuk menginisialisasi parameter command-line.
    func init() {
    	flag.StringVar(&region, "region", "", "Wilayah tempat bucket berada.")
    	flag.StringVar(&bucketName, "bucket", "", "Nama bucket.")
    	flag.StringVar(&objectName, "object", "", "Nama objek.")
    }
    
    func main() {
    	// Parsing parameter command-line.
    	flag.Parse()
    
    	// Periksa apakah nama bucket kosong.
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, nama bucket wajib diisi")
    	}
    
    	// Periksa apakah wilayah kosong.
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, wilayah wajib diisi")
    	}
    
    	// Periksa apakah nama objek kosong.
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, nama objek wajib diisi")
    	}
    
    	// Muat konfigurasi default dan atur penyedia kredensial serta wilayah.
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	// Buat klien OSS.
    	client := oss.NewClient(cfg)
    
    	// Hasilkan URL yang ditandatangani untuk PutObject.
    	result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
    		Bucket: oss.Ptr(bucketName),
    		Key:    oss.Ptr(objectName),
    	},
    		oss.PresignExpires(10*time.Minute),
    	)
    	if err != nil {
    		log.Fatalf("gagal menghasilkan presign put object %v", err)
    	}
    
    	log.Printf("metode permintaan:%v\n", result.Method)
    	log.Printf("waktu kedaluwarsa permintaan:%v\n", result.Expiration)
    	log.Printf("URL permintaan:%v\n", result.URL)
    	if len(result.SignedHeaders) > 0 {
    		// Jika hasil mencakup header yang ditandatangani, Anda harus menyetel header permintaan yang sesuai saat mengirim permintaan PUT menggunakan URL yang ditandatangani.
    		log.Printf("header yang ditandatangani:\n")
    		for k, v := range result.SignedHeaders {
    			log.Printf("%v: %v\n", k, v)
    		}
    	}
    }
    
  2. Pengguna lain mengunggah objek menggunakan URL yang ditandatangani untuk metode PUT.

    curl

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

    Java

    import org.apache.http.HttpEntity;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPut;
    import org.apache.http.entity.FileEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import java.io.*;
    import java.net.URL;
    import java.util.*;
    
    public class SignUrlUpload {
        public static void main(String[] args) throws Throwable {
            CloseableHttpClient httpClient = null;
            CloseableHttpResponse response = null;
    
            // Ganti <signedUrl> dengan URL yang diotorisasi.
            URL signedUrl = new URL("<signedUrl>");
    
            // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file diunggah dari path 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("Kode status yang dikembalikan untuk pengunggahan:"+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("Pengunggahan berhasil menggunakan library jaringan.");
                }
                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("Tidak dapat membuka 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("Gagal membuat permintaan: %w", err)
    	}
    
    	// Kirim permintaan.
    	resp, err := client.Do(req)
    	if err != nil {
    		return fmt.Errorf("Gagal mengirim permintaan: %w", err)
    	}
    	defer resp.Body.Close()
    
    	// Baca respons.
    	body, err := io.ReadAll(resp.Body)
    	if err != nil {
    		return fmt.Errorf("Gagal membaca respons: %w", err)
    	}
    
    	fmt.Printf("Kode status yang dikembalikan untuk pengunggahan: %d\n", resp.StatusCode)
    	if resp.StatusCode == 200 {
    		fmt.Println("Pengunggahan berhasil menggunakan library jaringan.")
    	}
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// Ganti <signedUrl> dengan URL yang diotorisasi.
    	signedUrl := "<signedUrl>"
    
    	// Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file diunggah dari path proyek program contoh secara default.
    	filePath := "C:\\Users\\demo.txt"
    
    	err := uploadFile(signedUrl, filePath)
    	if err != nil {
    		fmt.Println("Terjadi kesalahan:", err)
    	}
    }
    

    python

    import requests
    
    def upload_file(signed_url, file_path):
        try:
            # Buka file.
            with open(file_path, 'rb') as file:
                # Kirim permintaan PUT untuk mengunggah file.
                response = requests.put(signed_url, data=file)
         
            print(f"Kode status yang dikembalikan untuk pengunggahan: {response.status_code}")
            if response.status_code == 200:
                print("Pengunggahan berhasil menggunakan library jaringan.")
            print(response.text)
     
        except Exception as e:
            print(f"Terjadi kesalahan: {e}")
    
    if __name__ == "__main__":
        # Ganti <signedUrl> dengan URL yang diotorisasi.
        signed_url = "<signedUrl>"
        
        // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file diunggah dari path 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 stream 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(`Kode status yang dikembalikan untuk pengunggahan: ${response.status}`);
            if (response.status === 200) {
                console.log('Pengunggahan berhasil menggunakan library jaringan.');
            }
            console.log(response.data);
        } catch (error) {
            console.error(`Terjadi kesalahan: ${error.message}`);
        }
    }
    
    // Fungsi utama.
    (async () => {
        // Ganti <signedUrl> dengan URL yang diotorisasi.
        const signedUrl = '<signedUrl>';
        
        // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file diunggah dari path proyek program contoh secara default.
        const filePath = 'C:\\Users\\demo.txt';
    
        await uploadFile(signedUrl, filePath);
    })();

    browser.js

    Penting

    Jika Anda mengalami kesalahan ketidaksesuaian signature 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 presigned dihasilkan. Hal ini menyebabkan kegagalan verifikasi signature. Untuk mengatasi masalah ini, Anda harus menentukan header permintaan Content-Type saat menghasilkan URL presigned.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Contoh Pengunggahan File</title>
    </head>
    <body>
        <h1>Contoh Pengunggahan File</h1>
    
        <!-- Pilih file -->
        <input type="file" id="fileInput" />
        <button id="uploadButton">Unggah File</button>
    
        <script>
            // Ganti ini dengan URL presigned yang dihasilkan pada Langkah 1.
            const signedUrl = "<signedUrl>"; 
    
    
            document.getElementById('uploadButton').addEventListener('click', async () => {
                const fileInput = document.getElementById('fileInput');
                const file = fileInput.files[0];
    
                if (!file) {
                    alert('Silakan pilih file untuk diunggah.');
                    return;
                }
    
                try {
                    await upload(file, signedUrl);
                    alert('File berhasil diunggah!');
                } catch (error) {
                    console.error('Kesalahan saat pengunggahan:', error);
                    alert('Pengunggahan gagal: ' + error.message);
                }
            });
    
            /**
             * Unggah file ke OSS.
             * @param {File} file - File yang akan diunggah.
             * @param {string} presignedUrl - URL presigned.
             */
            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(`Pengunggahan gagal, status: ${response.status}`);
                }
    
                console.log('File berhasil diunggah');
            };
        </script>
    </body>
    </html>

    C#

    using System.Net.Http.Headers;
    
    // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file diunggah dari path 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 stream 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($"Pengunggahan berhasil! Kode status: {response.StatusCode}");
        Console.WriteLine("Header respons:");
        foreach (var header in response.Headers)
        {
            Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
        }
    }
    else
    {
        string responseContent = await response.Content.ReadAsStringAsync();
        Console.WriteLine($"Pengunggahan gagal! Kode status: {response.StatusCode}");
        Console.WriteLine("Konten respons: " + 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 << "Tidak dapat membuka file: " << filePath << std::endl;
                return;
            }
    
            // Dapatkan ukuran file.
            fseek(file, 0, SEEK_END);
            long fileSize = ftell(file);
            fseek(file, 0, SEEK_SET);
    
            // Atur ukuran file.
            curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
    
            // Atur handle input file.
            curl_easy_setopt(curl, CURLOPT_READDATA, file);
    
            // Jalankan permintaan.
            res = curl_easy_perform(curl);
    
            if (res != CURLE_OK) {
                std::cerr << "curl_easy_perform() gagal: " << curl_easy_strerror(res) << std::endl;
            } else {
                long httpCode = 0;
                curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
                std::cout << "Kode status yang dikembalikan untuk pengunggahan: " << httpCode << std::endl;
    
                if (httpCode == 200) {
                    std::cout << "Pengunggahan berhasil menggunakan library jaringan." << 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 path lengkap file lokal. Jika Anda tidak menentukan path lokal, file diunggah dari path 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, "Kode status yang dikembalikan untuk pengunggahan: " + responseCode);
    
                    if (responseCode == 200) {
                        Log.d(TAG, "Pengunggahan berhasil menggunakan library jaringan.");
                    }
    
                    return "Pengunggahan selesai. Kode status: " + responseCode;
    
                } catch (IOException e) {
                    e.printStackTrace();
                    return "Pengunggahan gagal: " + 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 path lengkap file lokal. Jika Anda tidak menentukan path lokal, file diunggah dari path proyek program contoh secara default.
            String filePath = "C:\\Users\\demo.txt";
            activity.uploadFile(signedUrl, filePath);
        }
    }
    

Skenario umum

Unggah file dengan header permintaan tertentu dan metadata kustom menggunakan URL yang ditandatangani

  1. Pemilik objek menghasilkan URL yang ditandatangani untuk metode PUT.

    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 penyimpanan.
    	bucketName string // Nama bucket.
    	objectName string // Nama objek.
    )
    
    // Fungsi init digunakan untuk menginisialisasi parameter command-line.
    func init() {
    	flag.StringVar(&region, "region", "", "Wilayah tempat bucket berada.")
    	flag.StringVar(&bucketName, "bucket", "", "Nama bucket.")
    	flag.StringVar(&objectName, "object", "", "Nama objek.")
    }
    
    func main() {
    	// Parsing parameter command-line.
    	flag.Parse()
    
    	// Periksa apakah nama bucket kosong.
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, nama bucket wajib diisi")
    	}
    
    	// Periksa apakah wilayah kosong.
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, wilayah wajib diisi")
    	}
    
    	// Periksa apakah nama objek kosong.
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, nama objek wajib diisi")
    	}
    
    	// Muat konfigurasi default dan atur penyedia kredensial serta wilayah.
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	// Buat klien OSS.
    	client := oss.NewClient(cfg)
    
    	// Hasilkan URL yang ditandatangani untuk 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 ContentType yang ditetapkan saat menghasilkan URL yang ditandatangani di server sama dengan ContentType yang ditetapkan saat menggunakan URL tersebut.
    		StorageClass: oss.StorageClassStandard,                              // Pastikan StorageClass yang ditetapkan saat menghasilkan URL yang ditandatangani di server sama dengan StorageClass yang ditetapkan saat menggunakan URL tersebut.
    		Metadata:    map[string]string{"key1": "value1", "key2": "value2"}, // Pastikan metadata yang ditetapkan saat menghasilkan URL yang ditandatangani di server sama dengan metadata yang ditetapkan saat menggunakan URL tersebut.
    	},
    		oss.PresignExpires(10*time.Minute),
    	)
    	if err != nil {
    		log.Fatalf("gagal menghasilkan presign put object %v", err)
    	}
    
    	log.Printf("metode permintaan:%v\n", result.Method)
    	log.Printf("waktu kedaluwarsa permintaan:%v\n", result.Expiration)
    	log.Printf("URL permintaan:%v\n", result.URL)
    	if len(result.SignedHeaders) > 0 {
    		// Jika hasil mencakup header yang ditandatangani, Anda harus menyetel header permintaan yang sesuai saat mengirim permintaan PUT menggunakan URL yang ditandatangani.
    		log.Printf("header yang ditandatangani:\n")
    		for k, v := range result.SignedHeaders {
    			log.Printf("%v: %v\n", k, v)
    		}
    	}
    }
    
  2. Pengguna lain mengunggah objek menggunakan URL yang ditandatangani untuk metode PUT.

    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 path lengkap file lokal. Jika Anda tidak menentukan path lokal, file diunggah dari path 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 menyetel parameter header, seperti metadata pengguna dan kelas penyimpanan, saat menghasilkan URL presigned, Anda juga harus mengirim parameter ini ke server saat menggunakan URL presigned untuk mengunggah file. Kesalahan signature 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 presigned untuk pengunggahan, 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("Kode status yang dikembalikan untuk pengunggahan:"+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("Pengunggahan berhasil menggunakan library jaringan.");
                }
                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("Kode status yang dikembalikan untuk pengunggahan: %d\n", resp.StatusCode)
    	if resp.StatusCode == 200 {
    		fmt.Println("Pengunggahan berhasil menggunakan library jaringan.")
    	} else {
    		fmt.Println("Pengunggahan gagal.")
    	}
    	body, _ := ioutil.ReadAll(resp.Body)
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// Ganti <signedUrl> dengan URL yang diotorisasi.
    	signedUrl := "<signedUrl>"
    
    	// Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file diunggah dari path 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("Terjadi kesalahan: %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 presigned untuk mengunggah file ke OSS.
    
        :param signed_url: URL presigned.
        :param file_path: Path 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"Kode status yang dikembalikan untuk pengunggahan: {response.status_code}")
                if response.status_code == 200:
                    print("Pengunggahan berhasil menggunakan library jaringan.")
                else:
                    print("Pengunggahan gagal.")
                print(response.text)
        except Exception as e:
            print(f"Terjadi kesalahan: {e}")
    
    if __name__ == "__main__":
        // Ganti <signedUrl> dengan URL yang diotorisasi.
        signed_url = "<signedUrl>"
       
        // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path 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 stream file.
            const fileStream = fs.createReadStream(filePath);
    
            // Kirim permintaan PUT.
            const response = await axios.put(signedUrl, fileStream, {
                headers: headers
            });
    
            console.log(`Kode status yang dikembalikan untuk pengunggahan: ${response.status}`);
            if (response.status === 200) {
                console.log("Pengunggahan berhasil menggunakan library jaringan.");
            } else {
                console.log("Pengunggahan gagal.");
            }
            console.log(response.data);
        } catch (error) {
            console.error(`Terjadi kesalahan: ${error.message}`);
        }
    }
    
    // Fungsi utama.
    (async () => {
        // Ganti <signedUrl> dengan URL yang diotorisasi.
        const signedUrl = "<signedUrl>";
    
        // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path 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 ketidaksesuaian signature 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 presigned dihasilkan. Hal ini menyebabkan kegagalan verifikasi signature. Untuk mengatasi masalah ini, Anda harus menentukan header permintaan Content-Type saat menghasilkan URL presigned.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Contoh Pengunggahan File</title>
    </head>
    <body>
        <h1>Contoh Pengunggahan File (Java SDK)</h1>
    
        <input type="file" id="fileInput" />
        <button id="uploadButton">Unggah File</button>
    
        <script>
            // Ganti ini dengan URL presigned 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('Kesalahan saat pengunggahan:', error);
                        alert('Pengunggahan gagal: ' + error.message);
                    }
                } else {
                    alert('Silakan pilih file untuk diunggah.');
                }
            });
    
            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(`Pengunggahan gagal, status: ${response.status}`);
                }
    
                alert('File berhasil diunggah');
                console.log('File berhasil diunggah');
            };
        </script>
    </body>
    </html>

    C#

    using System.Net.Http.Headers;
    
    // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file diunggah dari path 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 stream 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 menyetel parameter header, seperti metadata pengguna dan kelas penyimpanan, saat menghasilkan URL presigned, Anda juga harus mengirim parameter ini ke server saat menggunakan URL presigned untuk mengunggah file. Kesalahan signature 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("Header permintaan:");
    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($"Pengunggahan berhasil! Kode status: {response.StatusCode}");
        Console.WriteLine("Header respons:");
        foreach (var header in response.Headers)
        {
            Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
        }
    }
    else
    {
        string responseContent = await response.Content.ReadAsStringAsync();
        Console.WriteLine($"Pengunggahan gagal! Kode status: {response.StatusCode}");
        Console.WriteLine("Konten respons: " + 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 << "Tidak dapat membuka file: " << filePath << std::endl;
                return;
            }
    
            // Atur ukuran file.
            fseek(file, 0, SEEK_END);
            long fileSize = ftell(file);
            rewind(file);
    
            // Atur callback pembacaan 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);
    
            // Jalankan permintaan.
            res = curl_easy_perform(curl);
    
            // Periksa respons.
            if (res != CURLE_OK) {
                std::cerr << "curl_easy_perform() gagal: " << curl_easy_strerror(res) << std::endl;
            } else {
                long responseCode;
                curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
                std::cout << "Kode status yang dikembalikan untuk pengunggahan: " << responseCode << std::endl;
                if (responseCode == 200) {
                    std::cout << "Pengunggahan berhasil menggunakan library jaringan." << std::endl;
                } else {
                    std::cout << "Pengunggahan gagal." << 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 path lengkap file lokal. Jika Anda tidak menentukan path lokal, file diunggah dari path 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 path lengkap file lokal. Jika Anda tidak menentukan path lokal, file diunggah dari path 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, "Kode status yang dikembalikan untuk pengunggahan: " + responseCode);
                    if (responseCode == 200) {
                        Log.d(TAG, "Pengunggahan berhasil menggunakan library jaringan.");
                    } else {
                        Log.d(TAG, "Pengunggahan gagal.");
                    }
    
                    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, "Pengunggahan berhasil.", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(SignUrlUploadActivity.this, "Pengunggahan gagal.", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }
    

Lakukan unggah multi-bagian menggunakan URL yang ditandatangani

Untuk melakukan unggah multi-bagian menggunakan URL yang ditandatangani, Anda harus mengonfigurasi ukuran bagian dan menghasilkan URL yang ditandatangani untuk setiap bagian. Kode contoh berikut memberikan ilustrasi:

package main

import (
	"bytes"
	"context"
	"flag"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)

var (
	region     string                     // Wilayah.
	bucketName string                     // Nama bucket.
	objectName string                     // Nama objek.
	length     = int64(5000 * 1024)       // Ukuran total file dalam byte.
	partSize   = int64(200 * 1024)        // Ukuran setiap bagian dalam byte.
	partsNum   = int(length/partSize + 1) // Jumlah bagian.
	data       = make([]byte, length)     // Data simulasi untuk pengunggahan.
)

// Inisialisasi parameter command-line.
func init() {
	flag.StringVar(&region, "region", "", "Wilayah tempat bucket berada.")
	flag.StringVar(&bucketName, "bucket", "", "Nama bucket.")
	flag.StringVar(&objectName, "object", "", "Nama objek.")
}

func main() {
	// Parsing parameter command-line.
	flag.Parse()

	// Periksa apakah parameter yang diperlukan telah disetel.
	if len(bucketName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("parameter tidak valid, nama bucket wajib diisi")
	}

	if len(region) == 0 {
		flag.PrintDefaults()
		log.Fatalf("parameter tidak valid, wilayah wajib diisi")
	}

	if len(objectName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("parameter tidak valid, nama objek wajib diisi")
	}

	// Konfigurasikan klien OSS.
	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	// Buat klien OSS.
	client := oss.NewClient(cfg)

	// Inisialisasi unggah multi-bagian.
	initResult, err := client.InitiateMultipartUpload(context.TODO(), &oss.InitiateMultipartUploadRequest{
		Bucket: oss.Ptr(bucketName),
		Key:    oss.Ptr(objectName),
	})
	if err != nil {
		log.Fatalf("gagal InitiateMultipartUpload %v", err)
	}

	// Telusuri setiap bagian, hasilkan URL yang ditandatangani, dan unggah bagian tersebut.
	for i := 0; i < partsNum; i++ {
		start := int64(i) * partSize
		end := start + partSize
		if end > length {
			end = length
		}
		signedResult, err := client.Presign(context.TODO(), &oss.UploadPartRequest{
			Bucket:     oss.Ptr(bucketName),
			Key:        oss.Ptr(objectName),
			PartNumber: int32(i + 1),
			Body:       bytes.NewReader(data[start:end]),
			UploadId:   initResult.UploadId,
		}, oss.PresignExpiration(time.Now().Add(1*time.Hour))) // Hasilkan URL yang ditandatangani dengan TTL 1 jam.
		if err != nil {
			log.Fatalf("gagal menghasilkan URL presigned %v", err)
		}
		fmt.Printf("url yang ditandatangani:%#v\n", signedResult.URL) // Cetak URL yang ditandatangani yang dihasilkan.

		// Buat permintaan HTTP dan unggah bagian tersebut.
		req, err := http.NewRequest(signedResult.Method, signedResult.URL, bytes.NewReader(data[start:end]))
		if err != nil {
			log.Fatalf("gagal membuat permintaan HTTP %v", err)
		}

		c := &http.Client{} // Buat klien HTTP.
		_, err = c.Do(req)
		if err != nil {
			log.Fatalf("gagal mengunggah bagian melalui URL yang ditandatangani %v", err)
		}
	}

	// Daftar bagian yang diunggah.
	partsResult, err := client.ListParts(context.TODO(), &oss.ListPartsRequest{
		Bucket:   oss.Ptr(bucketName),
		Key:      oss.Ptr(objectName),
		UploadId: initResult.UploadId,
	})
	if err != nil {
		log.Fatalf("gagal mendaftar bagian %v", err)
	}

	// Kumpulkan informasi tentang bagian yang diunggah.
	var parts []oss.UploadPart
	for _, p := range partsResult.Parts {
		parts = append(parts, oss.UploadPart{PartNumber: p.PartNumber, ETag: p.ETag})
	}

	// Selesaikan unggah multi-bagian.
	result, err := client.CompleteMultipartUpload(context.TODO(), &oss.CompleteMultipartUploadRequest{
		Bucket:   oss.Ptr(bucketName),
		Key:      oss.Ptr(objectName),
		UploadId: initResult.UploadId,
		CompleteMultipartUpload: &oss.CompleteMultipartUpload{
			Parts: parts,
		},
	})
	if err != nil {
		log.Fatalf("gagal menyelesaikan unggah multi-bagian %v", err)
	}

	// Cetak hasil penyelesaian unggah multi-bagian.
	log.Printf("hasil penyelesaian unggah multi-bagian:%#v\n", result)
}

Unggah file dan atur parameter callback unggahan menggunakan URL yang ditandatangani

  1. Pemilik objek menghasilkan URL yang ditandatangani untuk metode PUT dengan parameter callback unggahan tertentu.

    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 penyimpanan.
    	bucketName string // Nama bucket.
    	objectName string // Nama objek.
    )
    
    // Fungsi init digunakan untuk menginisialisasi parameter command-line.
    func init() {
    	flag.StringVar(&region, "region", "", "Wilayah tempat bucket berada.")
    	flag.StringVar(&bucketName, "bucket", "", "Nama bucket.")
    	flag.StringVar(&objectName, "object", "", "Nama objek.")
    }
    
    func main() {
    	// Parsing parameter command-line.
    	flag.Parse()
    
    	// Periksa apakah nama bucket kosong.
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, nama bucket wajib diisi")
    	}
    
    	// Periksa apakah wilayah kosong.
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, wilayah wajib diisi")
    	}
    
    	// Periksa apakah nama objek kosong.
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, nama objek wajib diisi")
    	}
    
    	// Muat konfigurasi default dan atur penyedia kredensial serta wilayah.
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	// Buat klien OSS.
    	client := oss.NewClient(cfg)
    
    	// Definisikan parameter callback.
    	callbackMap := map[string]string{
    		"callbackUrl":      "http://example.com:23450",                                                                   // Atur URL server callback, misalnya, https://example.com:23450.
    		"callbackBody":     "bucket=${bucket}&object=${object}&size=${size}&my_var_1=${x:my_var1}&my_var_2=${x:my_var2}", // Atur isi permintaan callback.
    		"callbackBodyType": "application/x-www-form-urlencoded",                                                          // Atur tipe isi permintaan callback.
    	}
    
    	// Konversi parameter callback ke JSON lalu encode Base64 untuk diteruskan sebagai parameter callback.
    	callbackStr, err := json.Marshal(callbackMap)
    	if err != nil {
    		log.Fatalf("gagal marshal callback map: %v", err)
    	}
    	callbackBase64 := base64.StdEncoding.EncodeToString(callbackStr)
    
    	callbackVarMap := map[string]string{}
    	callbackVarMap["x:my_var1"] = "this is var 1"
    	callbackVarMap["x:my_var2"] = "this is var 2"
    	callbackVarStr, err := json.Marshal(callbackVarMap)
    	if err != nil {
    		log.Fatalf("gagal marshal callback var: %v", err)
    	}
    	callbackVarBase64 := base64.StdEncoding.EncodeToString(callbackVarStr)
    
    	// Hasilkan URL yang ditandatangani untuk PutObject.
    	result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
    		Bucket:      oss.Ptr(bucketName),
    		Key:         oss.Ptr(objectName),
    		Callback:    oss.Ptr(callbackBase64),    // Atur parameter callback. Ini adalah string JSON yang diencode Base64.
    		CallbackVar: oss.Ptr(callbackVarBase64), // Atur parameter callback kustom. Ini adalah string JSON yang diencode Base64.
    	},
    		oss.PresignExpires(10*time.Minute),
    	)
    	if err != nil {
    		log.Fatalf("gagal menghasilkan presign put object %v", err)
    	}
    
    	log.Printf("metode permintaan:%v\n", result.Method)
    	log.Printf("waktu kedaluwarsa permintaan:%v\n", result.Expiration)
    	log.Printf("URL permintaan:%v\n", result.URL)
    	if len(result.SignedHeaders) > 0 {
    		// Jika hasil mencakup header yang ditandatangani, Anda harus menyetel header permintaan yang sesuai saat mengirim permintaan PUT menggunakan URL yang ditandatangani.
    		log.Printf("header yang ditandatangani:\n")
    		for k, v := range result.SignedHeaders {
    			log.Printf("%v: %v\n", k, v)
    		}
    	}
    }
    
  2. Gunakan URL presigned untuk mengunggah objek.

    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 presigned untuk mengunggah objek ke OSS.
    
        :param signed_url: URL presigned.
        :param file_path: Path 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"Kode status: {response.status_code}")
                if response.status_code == 200:
                    print("Objek diunggah menggunakan library.")
                else:
                    print("Pengunggahan gagal.")
                print(response.text)
        except Exception as e:
            print(f"Terjadi kesalahan: {e}")
    
    if __name__ == "__main__":
        // Ganti <signedUrl> dengan URL presigned.
        signed_url = "<signedUrl>"
    
        // Tentukan path lengkap file lokal. Secara default, jika Anda tidak menentukan path 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("Kode status: %d\n", resp.StatusCode)
    	if resp.StatusCode == 200 {
    		fmt.Println("Objek diunggah menggunakan library.")
    	} else {
    		fmt.Println("Pengunggahan gagal.")
    	}
    	body, _ := io.ReadAll(resp.Body)
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// Ganti <signedUrl> dengan URL presigned.
    	signedUrl := "<signedUrl>"
    	// Tentukan path lengkap file lokal. Jika path file lokal tidak ditentukan, file lokal diunggah dari path proyek tempat program contoh berada.
    	filePath := "C:\\Users\\demo.txt"
    
    	// Tentukan header permintaan. Pastikan nilai header permintaan sama dengan yang ditentukan saat URL presigned dihasilkan.
    	headers := map[string]string{
    		"x-oss-callback":     "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9",
    		"x-oss-callback-var": "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==",
    	}
    
    	err := uploadFile(signedUrl, filePath, headers)
    	if err != nil {
    		fmt.Printf("Terjadi kesalahan: %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 presigned.
            URL signedUrl = new URL("<signedUrl>");
    
            // Tentukan path lengkap file lokal. Jika path file lokal tidak ditentukan, file lokal diunggah dari path 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 presigned dihasilkan, header ini harus dikirim ke server saat URL presigned digunakan untuk mengunggah file. Jika header yang dikirim ke server untuk perhitungan signature berbeda dengan header yang ditentukan saat URL presigned dihasilkan, kesalahan signature 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("Kode status: "+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("Objek diunggah menggunakan 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 "File tidak ada: $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));
    
        // Jalankan permintaan cURL.
        $response = curl_exec($ch);
    
        // Periksa kode status HTTP.
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
        // Tutup sesi cURL.
        curl_close($ch);
    
        // Keluarkan hasil
        echo "Kode status: $httpCode\n";
        if ($httpCode == 200) {
            echo "Objek diunggah menggunakan library.\n";
        } else {
            echo "Pengunggahan gagal.\n";
        }
        echo $response . "\n";
    }
    
    // Ganti <signedUrl> dengan URL presigned.
    $signedUrl = "<signedUrl>";
    
    // Tentukan path lengkap file lokal. Secara default, jika Anda tidak menentukan path 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);
    
    ?>

Referensi

  • Untuk kode contoh lengkap mengenai URL yang ditandatangani, lihat contoh GitHub.

  • Untuk informasi lebih lanjut mengenai operasi API untuk URL yang ditandatangani, lihat Presign.