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-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 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.
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 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
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 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(®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 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) } } }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
PentingJika 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
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.