All Products
Search
Document Center

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

Last Updated:Sep 03, 2026

Secara default, objek dalam bucket Object Storage Service (OSS) bersifat privat dan hanya dapat diakses oleh pemiliknya. Topik ini menjelaskan cara menggunakan kit pengembangan perangkat lunak (SDK) OSS Go V2 untuk menghasilkan URL yang ditandatangani untuk metode PUT dengan waktu hidup (TTL) tertentu, sehingga memungkinkan pengguna lain mengunggah objek secara sementara. URL tersebut dapat digunakan beberapa kali sebelum kedaluwarsa; setelah itu, Anda harus membuat 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 yang telah ditandatangani sebelumnya. Namun, pihak ketiga hanya dapat berhasil mengunggah file menggunakan URL tersebut jika Anda memiliki izin oss:PutObject. Untuk informasi lebih lanjut, lihat Berikan kebijakan akses kustom kepada Pengguna RAM.

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

Proses

Untuk mengunggah file menggunakan URL yang ditandatangani dan metode PUT, ikuti prosedur berikut:

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 didahulukan.

Nilai kembali

Nilai kembali

Tipe

Deskripsi

result

*PresignResult

Hasilnya. 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, dikembalikan nilai PUT.

URL

string

URL yang telah ditandatangani sebelumnya

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 cocok, permintaan akan gagal dan mengembalikan error signature.

    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 diperlukan")
    	}
    
    	// Periksa apakah wilayah kosong.
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, wilayah diperlukan")
    	}
    
    	// Periksa apakah nama objek kosong.
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, nama objek diperlukan")
    	}
    
    	// 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 untuk 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 mengatur 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 telah diotorisasi.
            URL signedUrl = new URL("<signedUrl>");
    
            // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan 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 unggahan:"+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("Unggahan berhasil menggunakan library jaringan.");
                }
                System.out.println(response.toString());
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                 if (response != null) {
                     response.close();
                 }
                 if (httpClient != null) {
                     httpClient.close();
                 }
             }
        }
    }       

    Go

    package main
    
    import (
    	"fmt"
    	"io"
    	"net/http"
    	"os"
    )
    
    func uploadFile(signedUrl, filePath string) error {
    	// Buka file.
    	file, err := os.Open(filePath)
    	if err != nil {
    		return fmt.Errorf("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 unggahan: %d\n", resp.StatusCode)
    	if resp.StatusCode == 200 {
    		fmt.Println("Unggahan berhasil menggunakan library jaringan.")
    	}
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// Ganti <signedUrl> dengan URL yang telah diotorisasi.
    	signedUrl := "<signedUrl>"
    
    	// Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan 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 unggahan: {response.status_code}")
            if response.status_code == 200:
                print("Unggahan berhasil menggunakan library jaringan.")
            print(response.text)
     
        except Exception as e:
            print(f"Terjadi kesalahan: {e}")
    
    if __name__ == "__main__":
        // Ganti <signedUrl> dengan URL yang telah diotorisasi.
        signed_url = "<signedUrl>"
        
        // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan diunggah dari path proyek program contoh secara default.
        file_path = "C:\\Users\\demo.txt"
    
        upload_file(signed_url, file_path)
    

    Node.js

    Penting

    Jika Anda mengalami error signature mismatch 403 saat menggunakan Node.js untuk mengunggah file, biasanya karena header permintaan Content-Type secara eksplisit ditetapkan dalam permintaan unggahan, tetapi header ini tidak disertakan saat URL yang telah ditandatangani dihasilkan. Ketidaksesuaian ini menyebabkan kegagalan verifikasi signature. Untuk mengatasi masalah ini, jangan menetapkan header permintaan Content-Type dalam permintaan unggahan, atau tentukan header permintaan Content-Type saat menghasilkan URL yang telah ditandatangani.

    const fs = require('fs');
    const axios = require('axios');
    
    async function uploadFile(signedUrl, filePath) {
        try {
            // Buat aliran baca.
            const fileStream = fs.createReadStream(filePath);
    
            // Kirim permintaan PUT untuk mengunggah file.
            const response = await axios.put(signedUrl, fileStream);
    
            console.log(`Kode status yang dikembalikan untuk unggahan: ${response.status}`);
            if (response.status === 200) {
                console.log('Unggahan berhasil menggunakan library jaringan.');
            }
            console.log(response.data);
        } catch (error) {
            console.error(`Terjadi kesalahan: ${error.message}`);
        }
    }
    
    // Fungsi utama.
    (async () => {
        // Ganti <signedUrl> dengan URL yang telah diotorisasi.
        const signedUrl = '<signedUrl>';
        
        // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan diunggah dari path proyek program contoh secara default.
        const filePath = 'C:\\Users\\demo.txt';
    
        await uploadFile(signedUrl, filePath);
    })();

    browser.js

    Penting

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

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Contoh Unggah File</title>
    </head>
    <body>
        <h1>Contoh Unggah File</h1>
    
        <!-- Pilih file -->
        <input type="file" id="fileInput" />
        <button id="uploadButton">Unggah File</button>
    
        <script>
            // Ganti ini dengan URL yang telah ditandatangani 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 unggah:', error);
                    alert('Unggahan gagal: ' + error.message);
                }
            });
    
            /**
             * Unggah file ke OSS.
             * @param {File} file - File yang akan diunggah.
             * @param {string} presignedUrl - URL yang telah ditandatangani.
             */
            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(`Unggahan 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 akan diunggah dari path proyek program contoh secara default.
    var filePath = "C:\\Users\\demo.txt";
    // Ganti <signedUrl> dengan URL yang telah 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($"Unggahan 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($"Unggahan 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 unggahan: " << httpCode << std::endl;
    
                if (httpCode == 200) {
                    std::cout << "Unggahan 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 telah diotorisasi.
        std::string signedUrl = "<signedUrl>";
    
        // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan 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 unggahan: " + responseCode);
    
                    if (responseCode == 200) {
                        Log.d(TAG, "Unggahan berhasil menggunakan library jaringan.");
                    }
    
                    return "Unggahan selesai. Kode status: " + responseCode;
    
                } catch (IOException e) {
                    e.printStackTrace();
                    return "Unggahan 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 telah diotorisasi.
            String signedUrl = "<signedUrl>";
            // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan 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 diperlukan")
    	}
    
    	// Periksa apakah wilayah kosong.
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, wilayah diperlukan")
    	}
    
    	// Periksa apakah nama objek kosong.
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, nama objek diperlukan")
    	}
    
    	// 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 untuk 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 mengatur 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

    package com.aliyun.sample;
    
    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 telah diotorisasi.
            URL signedUrl = new URL("<signedUrl>");
    
            // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan 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 menetapkan parameter header, seperti metadata pengguna dan kelas penyimpanan, saat menghasilkan URL yang telah ditandatangani, Anda juga harus mengirim parameter ini ke server saat menggunakan URL tersebut untuk mengunggah file. Error signature akan dilaporkan jika parameter yang dikirim ke server tidak konsisten dengan parameter yang ditandatangani.
                for(Map.Entry<String, String> header: headers.entrySet()){
                    put.addHeader(header.getKey(), header.getValue());
                }
                for(Map.Entry<String, String> meta: userMetadata.entrySet()){
                    // Jika Anda menggunakan userMeta, SDK secara internal menambahkan awalan 'x-oss-meta-'. Saat Anda menggunakan metode lain untuk menghasilkan URL yang telah ditandatangani untuk unggahan, Anda juga harus menambahkan awalan 'x-oss-meta-' ke userMeta.
                    put.addHeader("x-oss-meta-"+meta.getKey(), meta.getValue());
                }
    
                httpClient = HttpClients.createDefault();
    
                response = httpClient.execute(put);
    
                System.out.println("Kode status yang dikembalikan untuk unggahan:"+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("Unggahan berhasil menggunakan library jaringan.");
                }
                System.out.println(response.toString());
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                if (response != null) {
                    response.close();
                }
                if (httpClient != null) {
                    httpClient.close();
                }
            }
        }
    }

    Go

    package main
    
    import (
    	"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 unggahan: %d\n", resp.StatusCode)
    	if resp.StatusCode == 200 {
    		fmt.Println("Unggahan berhasil menggunakan library jaringan.")
    	} else {
    		fmt.Println("Unggahan gagal.")
    	}
    	body, _ := ioutil.ReadAll(resp.Body)
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// Ganti <signedUrl> dengan URL yang telah diotorisasi.
    	signedUrl := "<signedUrl>"
    
    	// Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan 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 yang telah ditandatangani untuk mengunggah file ke OSS.
    
        :param signed_url: URL yang telah ditandatangani.
        :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 unggahan: {response.status_code}")
                if response.status_code == 200:
                    print("Unggahan berhasil menggunakan library jaringan.")
                else:
                    print("Unggahan gagal.")
                print(response.text)
        except Exception as e:
            print(f"Terjadi kesalahan: {e}")
    
    if __name__ == "__main__":
        // Ganti <signedUrl> dengan URL yang telah diotorisasi.
        signed_url = "<signedUrl>"
       
        // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan 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(`Kode status yang dikembalikan untuk unggahan: ${response.status}`);
            if (response.status === 200) {
                console.log("Unggahan berhasil menggunakan library jaringan.");
            } else {
                console.log("Unggahan gagal.");
            }
            console.log(response.data);
        } catch (error) {
            console.error(`Terjadi kesalahan: ${error.message}`);
        }
    }
    
    // Fungsi utama.
    (async () => {
        // Ganti <signedUrl> dengan URL yang telah diotorisasi.
        const signedUrl = "<signedUrl>";
    
        // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan 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 error signature mismatch 403 saat menggunakan Browser.js untuk mengunggah file, biasanya karena browser secara otomatis menambahkan header permintaan Content-Type, dan header ini tidak disertakan saat URL yang telah ditandatangani dihasilkan. Ketidaksesuaian ini menyebabkan kegagalan verifikasi signature. Untuk mengatasi masalah ini, Anda harus menentukan header permintaan Content-Type saat menghasilkan URL yang telah ditandatangani.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Contoh Unggah File</title>
    </head>
    <body>
        <h1>Contoh Unggah File (Java SDK)</h1>
    
        <input type="file" id="fileInput" />
        <button id="uploadButton">Unggah File</button>
    
        <script>
            // Ganti ini dengan URL yang telah ditandatangani 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 unggah:', error);
                        alert('Unggahan 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(`Unggahan 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 akan diunggah dari path proyek program contoh secara default.
    var filePath = "C:\\Users\\demo.txt";
    // Ganti <signedUrl> dengan URL yang telah 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 menetapkan parameter header, seperti metadata pengguna dan kelas penyimpanan, saat menghasilkan URL yang telah ditandatangani, Anda juga harus mengirim parameter ini ke server saat menggunakan URL tersebut untuk mengunggah file. Error signature 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("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($"Unggahan 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($"Unggahan 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 unggahan: " << responseCode << std::endl;
                if (responseCode == 200) {
                    std::cout << "Unggahan berhasil menggunakan library jaringan." << std::endl;
                } else {
                    std::cout << "Unggahan 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 telah diotorisasi.
        std::string signedUrl = "<signedUrl>";
    
        // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan 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 telah diotorisasi.
            String signedUrl = "<signedUrl>";
    
            // Tentukan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan 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 unggahan: " + responseCode);
                    if (responseCode == 200) {
                        Log.d(TAG, "Unggahan berhasil menggunakan library jaringan.");
                    } else {
                        Log.d(TAG, "Unggahan 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, "Unggahan berhasil.", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(SignUrlUploadActivity.this, "Unggahan gagal.", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }
    

Pemecahan masalah: HTTP 403 SignatureDoesNotMatch akibat ketidaksesuaian Content-Type

Jika Anda menentukan Content-Type saat menghasilkan URL yang ditandatangani, tetapi permintaan unggahan tidak menyertakan header Content-Type atau menyertakan nilai yang berbeda, OSS akan mengembalikan HTTP 403 SignatureDoesNotMatch. Header Content-Type dalam permintaan unggahan harus persis sama dengan Content-Type yang digunakan saat URL yang ditandatangani dihasilkan.

Anda dapat mengatasi masalah ini dengan salah satu cara berikut:

  • Tambahkan header Content-Type yang sama ke permintaan unggahan seperti yang digunakan saat Anda menghasilkan URL yang ditandatangani.

  • Jangan menentukan Content-Type saat menghasilkan URL yang ditandatangani, dan jangan sertakan header Content-Type dalam permintaan unggahan.

Catatan

Pengaturan CORS yang salah dapat menyebabkan kegagalan unggahan lintas-origin di browser, tetapi masalah CORS tidak terkait dengan error HTTP 403 SignatureDoesNotMatch. Jika Anda mengalami error ini, periksa konsistensi Content-Type terlebih dahulu sebelum menyelidiki konfigurasi CORS.

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 unggahan.
)

// 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 diatur.
	if len(bucketName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("parameter tidak valid, nama bucket diperlukan")
	}

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

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

	// 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)
	}

	// Iterasi 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 yang telah ditandatangani %v", err)
		}
		fmt.Printf("URL yang ditandatangani:%#v\n", signedResult.URL) // Cetak URL 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 telah 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 telah 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 unggah multi-bagian yang telah diselesaikan.
	log.Printf("hasil unggah multi-bagian selesai:%#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 diperlukan")
    	}
    
    	// Periksa apakah wilayah kosong.
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, wilayah diperlukan")
    	}
    
    	// Periksa apakah nama objek kosong.
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("parameter tidak valid, nama objek diperlukan")
    	}
    
    	// 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 peta callback: %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 var callback: %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 telah diencode Base64.
    		CallbackVar: oss.Ptr(callbackVarBase64), // Atur parameter callback kustom. Ini adalah string JSON yang telah diencode Base64.
    	},
    		oss.PresignExpires(10*time.Minute),
    	)
    	if err != nil {
    		log.Fatalf("gagal menghasilkan presign untuk 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 mengatur 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 file menggunakan URL yang telah ditandatangani untuk metode PUT.

    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):
        """
        Mengunggah file ke OSS menggunakan URL yang ditandatangani.
    
        :param signed_url: URL yang ditandatangani.
        :param file_path: Path lengkap dari file yang akan diunggah.
        :param headers: Opsional. Header HTTP kustom.
        :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 unggahan yang dikembalikan: {response.status_code}")
                if response.status_code == 200:
                    print("Unggahan berhasil menggunakan pustaka jaringan.")
                else:
                    print("Unggahan 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>"
    
        # Masukkan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan diunggah dari direktori tempat skrip berada secara default.
        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 file.
        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
        }
    
        // Atur 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 unggahan yang dikembalikan: %d\n", resp.StatusCode)
        if resp.StatusCode == 200 {
            fmt.Println("Unggahan berhasil menggunakan library jaringan.")
        } else {
            fmt.Println("Unggahan gagal.")
        }
        body, _ := io.ReadAll(resp.Body)
        fmt.Println(string(body))
    
        return nil
    }
    
    func main() {
        // Ganti <signedUrl> dengan URL yang telah diotorisasi.
        signedUrl := "<signedUrl>"
        // Masukkan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan diunggah dari path lokal proyek secara default.
        filePath := "C:\\Users\\demo.txt"
    
        // Atur header permintaan. Informasi header di sini harus konsisten dengan informasi yang digunakan untuk menghasilkan URL.
        headers := map[string]string{
            "x-oss-callback":     "eyJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9XHUwMDI2b2JqZWN0PSR7b2JqZWN0fVx1MDAyNnNpemU9JHtzaXplfVx1MDAyNm15X3Zhcl8xPSR7eDpteV92YXIxfVx1MDAyNm15X3Zhcl8yPSR7eDpteV92YXIyfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQiLCJjYWxsYmFja1VybCI6Imh0dHA6Ly9leGFtcGxlLmNvbToyMzQ1MCJ9",
            "x-oss-callback-var": "eyJ4Om15X3ZhcjEiOiJ0aGkgaXMgdmFyIDEiLCJ4Om15X3ZhcjIiOiJ0aGkgaXMgdmFyIDIifQ==",
        }
    
        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 IOException {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;
    
        // Ganti <signedUrl> dengan URL yang telah diotorisasi.
        URL signedUrl = new URL("<signedUrl>");
    
        // Masukkan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan diunggah dari path lokal proyek secara default.
        String pathName = "C:\\Users\\demo.txt";
    
        // Atur 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 parameter header ditetapkan saat menghasilkan URL yang telah ditandatangani, parameter ini juga harus dikirim ke server saat menggunakan URL tersebut untuk mengunggah file. Jika signature dan parameter yang dikirim ke server tidak cocok, akan terjadi error signature.
          for(Map.Entry<String, String> header: headers.entrySet()){
            put.addHeader(header.getKey(),header.getValue());
          }
    
          httpClient = HttpClients.createDefault();
    
          response = httpClient.execute(put);
    
          System.out.println("Kode status unggahan yang dikembalikan:"+response.getStatusLine().getStatusCode());
          if(response.getStatusLine().getStatusCode() == 200){
            System.out.println("Unggahan berhasil menggunakan library jaringan.");
          }
          System.out.println(response.toString());
        } catch (Exception e){
          e.printStackTrace();
        } finally {
          if (response != null) {
            response.close();
          }
          if (httpClient != null) {
            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);
    
        // Dapatkan kode status HTTP.
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
        // Tutup sesi cURL.
        curl_close($ch);
    
        // Keluarkan hasilnya.
        echo "Kode status unggahan yang dikembalikan: $httpCode\n";
        if ($httpCode == 200) {
            echo "Unggahan berhasil menggunakan library jaringan.\n";
        } else {
            echo "Unggahan gagal.\n";
        }
        echo $response . "\n";
    }
    
    // Ganti <signedUrl> dengan URL yang telah diotorisasi.
    $signedUrl = "<signedUrl>";
    
    // Masukkan path lengkap file lokal. Jika Anda tidak menentukan path lokal, file akan diunggah dari direktori tempat skrip berada secara default.
    $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.