Secara default, objek dalam bucket Object Storage Service (OSS) bersifat privat. Hanya pemilik objek yang memiliki izin untuk mengunggah objek. Anda dapat menggunakan OSS SDK for Java untuk menghasilkan URL yang ditandatangani sebelumnya guna memungkinkan pengguna lain mengunggah objek. Saat menghasilkan URL tersebut, Anda dapat menentukan waktu kedaluwarsa untuk membatasi periode validitasnya. URL ini dapat digunakan beberapa kali sebelum kedaluwarsa; jika dilakukan beberapa operasi unggah, objek mungkin akan ditimpa. Setelah URL kedaluwarsa, URL tersebut tidak dapat lagi digunakan untuk unggah, dan Anda harus menghasilkan URL yang ditandatangani sebelumnya yang baru.
Catatan penggunaan
-
Pada topik ini, titik akhir publik wilayah China (Hangzhou) digunakan. Untuk mengakses OSS dari layanan Alibaba Cloud lainnya di wilayah yang sama, gunakan titik akhir internal. Untuk detail tentang wilayah dan titik akhir yang didukung, lihat Wilayah dan titik akhir.
-
Pada topik ini, kredensial akses diperoleh dari variabel lingkungan. Untuk informasi selengkapnya, lihat 配置访问凭证.
-
Topik ini menunjukkan cara membuat instans OSSClient dengan titik akhir OSS. Untuk konfigurasi alternatif, seperti menggunakan domain kustom atau melakukan autentikasi dengan kredensial dari Security Token Service (STS), lihat Konfigurasi klien.
-
Izin tidak diperlukan untuk menghasilkan URL yang ditandatangani sebelumnya. Namun, pihak ketiga hanya dapat berhasil mengunggah file menggunakan URL tersebut jika Anda memiliki izin
oss:PutObject. Untuk informasi selengkapnya, lihat Berikan kebijakan akses kustom kepada RAM user. -
Topik ini menggunakan URL yang ditandatangani sebelumnya versi V4 sebagai contoh. Periode validitas maksimum adalah 7 hari. Untuk informasi selengkapnya, lihat Signature Version 4 (direkomendasikan).
Proses
Untuk mengunggah file menggunakan URL yang ditandatangani dan metode PUT, ikuti prosedur berikut:
Contoh kode
-
Pemilik objek menghasilkan URL yang ditandatangani sebelumnya untuk permintaan PUT.
import com.aliyun.oss.*; import com.aliyun.oss.common.auth.*; import com.aliyun.oss.common.comm.SignVersion; import com.aliyun.oss.model.GeneratePresignedUrlRequest; import java.net.URL; import java.util.*; import java.util.Date; public class GetSignUrl { public static void main(String[] args) throws Throwable { // Contoh ini menggunakan titik akhir publik wilayah China (Hangzhou). Tentukan titik akhir yang sebenarnya. String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // Dapatkan kredensial akses dari variabel lingkungan. Sebelum menjalankan kode contoh, pastikan variabel lingkungan OSS_ACCESS_KEY_ID dan OSS_ACCESS_KEY_SECRET telah dikonfigurasi. EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider(); // Tentukan nama bucket, misalnya examplebucket. String bucketName = "examplebucket"; // Tentukan path lengkap objek, misalnya exampleobject.txt. Path lengkap objek tidak boleh mengandung nama bucket. String objectName = "exampleobject.txt"; // Tentukan wilayah tempat bucket berada. Misalnya, jika bucket berada di wilayah China (Hangzhou), atur wilayah menjadi cn-hangzhou. String region = "cn-hangzhou"; // Buat instans OSSClient. // Setelah instans OSSClient tidak digunakan lagi, panggil metode shutdown untuk melepaskan sumber daya. ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration(); clientBuilderConfiguration.setSignatureVersion(SignVersion.V4); OSS ossClient = OSSClientBuilder.create() .endpoint(endpoint) .credentialsProvider(credentialsProvider) .clientConfiguration(clientBuilderConfiguration) .region(region) .build(); URL signedUrl = null; try { // Tentukan waktu kedaluwarsa URL yang ditandatangani sebelumnya yang dihasilkan dalam milidetik. Contoh ini mengatur waktu kedaluwarsa menjadi 1 jam. Date expiration = new Date(new Date().getTime() + 3600 * 1000L); // Hasilkan URL yang ditandatangani sebelumnya. GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT); // Atur waktu kedaluwarsa. request.setExpiration(expiration); // Hasilkan URL yang ditandatangani sebelumnya untuk permintaan HTTP PUT. signedUrl = ossClient.generatePresignedUrl(request); // Cetak URL yang ditandatangani sebelumnya. System.out.println("signed url for putObject: " + signedUrl); } catch (OSSException oe) { System.out.println("Terjadi OSSException, yang berarti permintaan Anda sampai ke OSS, " + "tetapi ditolak dengan tanggapan error karena suatu alasan."); System.out.println("Pesan Error:" + oe.getErrorMessage()); System.out.println("Kode Error:" + oe.getErrorCode()); System.out.println("ID Permintaan:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); } catch (ClientException ce) { System.out.println("Terjadi ClientException, yang berarti klien mengalami " + "masalah internal serius saat mencoba berkomunikasi dengan OSS, " + "seperti tidak dapat mengakses jaringan."); System.out.println("Pesan Error:" + ce.getMessage()); } } } -
Pengguna lain mengunggah file menggunakan URL yang ditandatangani sebelumnya untuk permintaan 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 diizinkan. 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 unggahan:"+response.getStatusLine().getStatusCode()); if(response.getStatusLine().getStatusCode() == 200){ System.out.println("Unggah berhasil menggunakan pustaka 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 tanggapan. body, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("Gagal membaca tanggapan: %w", err) } fmt.Printf("Kode status yang dikembalikan untuk unggahan: %d\n", resp.StatusCode) if resp.StatusCode == 200 { fmt.Println("Unggah berhasil menggunakan pustaka jaringan.") } fmt.Println(string(body)) return nil } func main() { // Ganti <signedUrl> dengan URL yang diizinkan. 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 error:", 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 pustaka 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 jalur lengkap file lokal. Jika Anda tidak menentukan jalur lokal, file akan diunggah dari jalur proyek contoh program secara default. file_path = "C:\\Users\\demo.txt" upload_file(signed_url, file_path)Node.js
PentingJika Anda mengalami error ketidakcocokan signature 403 saat menggunakan Node.js untuk mengunggah file, biasanya hal ini disebabkan oleh header permintaan Content-Type yang secara eksplisit diatur dalam permintaan unggah, tetapi header tersebut tidak disertakan saat URL yang ditandatangani sebelumnya dihasilkan. Ketidakcocokan ini menyebabkan kegagalan verifikasi signature. Untuk mengatasi masalah ini, jangan atur header permintaan Content-Type dalam permintaan unggah, atau tentukan header permintaan Content-Type saat menghasilkan URL yang ditandatangani sebelumnya.
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); console.log(`Kode status yang dikembalikan untuk unggahan: ${response.status}`); if (response.status === 200) { console.log('Unggah berhasil menggunakan pustaka jaringan.'); } console.log(response.data); } catch (error) { console.error(`Terjadi error: ${error.message}`); } } // Fungsi utama. (async () => { // Ganti <signedUrl> dengan URL yang diizinkan. 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 error signature mismatch 403 saat menggunakan Browser.js untuk mengunggah file, hal ini biasanya disebabkan oleh browser yang secara otomatis menambahkan header permintaan Content-Type, sedangkan header tersebut tidak disertakan saat menghasilkan URL yang ditandatangani sebelumnya. Ketidakcocokan ini menyebabkan kegagalan verifikasi signature. Untuk mengatasi masalah ini, Anda harus menentukan header permintaan Content-Type saat menghasilkan URL yang ditandatangani sebelumnya.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>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 ditandatangani sebelumnya 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('Error saat unggah:', error); alert('Unggah gagal: ' + error.message); } }); /** * Unggah file ke OSS. * @param {File} file - File yang akan diunggah. * @param {string} presignedUrl - URL yang ditandatangani sebelumnya. */ const upload = async (file, presignedUrl) => { const response = await fetch(presignedUrl, { method: 'PUT', body: file, // Unggah seluruh file secara langsung. }); if (!response.ok) { throw new Error(`Unggah 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 diizinkan. 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 tanggapan. if (response.IsSuccessStatusCode) { Console.WriteLine($"Unggah berhasil! Kode status: {response.StatusCode}"); Console.WriteLine("Header tanggapan:"); foreach (var header in response.Headers) { Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}"); } } else { string responseContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Unggah gagal! Kode status: {response.StatusCode}"); Console.WriteLine("Konten tanggapan: " + 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 file input. 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 << "Unggah berhasil menggunakan pustaka jaringan." << std::endl; } } // Tutup file. fclose(file); // Bersihkan. curl_easy_cleanup(curl); } curl_global_cleanup(); } int main() { // Ganti <signedUrl> dengan URL yang diizinkan. 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 unggahan: " + responseCode); if (responseCode == 200) { Log.d(TAG, "Unggah berhasil menggunakan pustaka jaringan."); } return "Unggah selesai. Kode status: " + responseCode; } catch (IOException e) { e.printStackTrace(); return "Unggah 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 diizinkan. 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 lainnya
Gunakan URL yang ditandatangani sebelumnya untuk mengunggah file dengan header permintaan dan metadata kustom tertentu
Gunakan URL yang ditandatangani sebelumnya untuk melakukan unggah multi-bagian
Gunakan URL yang ditandatangani sebelumnya untuk mengunggah file dan atur parameter callback unggahan
FAQ
Jika signature sementara kedaluwarsa selama unggah file, apakah unggah gagal?
Jika saya tidak mengatur header permintaan dan metadata kustom saat menghasilkan URL, apakah saya perlu mengonfigurasinya saat menggunakan URL untuk unggah?
Referensi
-
Untuk kode contoh lengkap untuk menghasilkan URL yang ditandatangani sebelumnya versi V4, lihat contoh GitHub.
-
Untuk informasi selengkapnya tentang operasi API untuk mengunggah file menggunakan URL yang ditandatangani sebelumnya, lihat GeneratePresignedUrlRequest.