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-hangzhoudan 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. |
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
-
Pemilik objek menghasilkan URL yang ditandatangani untuk metode PUT.
PentingSaat 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(®ion, "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) } } } -
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
PentingJika 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
PentingJika 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
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.