Secara default, objek dalam bucket Object Storage Service (OSS) bersifat privat. Hanya pemilik objek yang memiliki izin untuk mengunggah objek. Anda dapat menggunakan OSS Software Development Kit (SDK) untuk Node.js guna menghasilkan URL yang ditandatangani sebelumnya (presigned URL) yang memungkinkan pengguna lain mengunggah objek. Saat menghasilkan URL tersebut, Anda dapat menetapkan waktu kedaluwarsa kustom. URL ini dapat digunakan beberapa kali selama periode validitasnya. Jika Anda melakukan beberapa unggahan menggunakan URL yang sama, objek tersebut mungkin akan ditimpa. Setelah URL kedaluwarsa, URL tersebut tidak dapat digunakan lagi. Anda harus menghasilkan URL yang ditandatangani sebelumnya yang baru untuk melakukan unggahan.
Catatan
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 Regions and endpoints.
Topik ini menggunakan kredensial akses yang dibaca dari variabel lingkungan sebagai contoh. Untuk informasi selengkapnya tentang cara mengonfigurasi kredensial akses, lihat Configure access credentials (Node.js SDK).
Untuk menghasilkan URL yang ditandatangani sebelumnya guna unggahan, Anda harus memiliki izin
oss:PutObject. Untuk informasi selengkapnya, lihat Grant custom policies to a RAM user.CatatanSaat Anda menghasilkan URL yang ditandatangani sebelumnya, SDK menggunakan kunci yang disimpan secara lokal untuk menghitung signature berdasarkan algoritma tertentu. SDK kemudian menambahkan signature tersebut ke URL guna memastikan validitas dan keamanannya. Proses perhitungan dan pembuatan URL ini terjadi di sisi client dan tidak melibatkan permintaan jaringan ke server. Oleh karena itu, pemanggil tidak memerlukan izin khusus untuk menghasilkan URL yang ditandatangani sebelumnya. Namun, identitas yang menghasilkan URL tersebut harus memiliki izin yang diperlukan untuk resource tersebut. Hal ini memastikan bahwa pengguna pihak ketiga dapat melakukan operasi yang diizinkan pada resource tersebut.
Topik ini menggunakan URL yang ditandatangani sebelumnya versi V4 sebagai contoh. Periode validitas maksimum adalah tujuh hari. Untuk informasi selengkapnya, lihat Signature V4 (Recommended).
Proses
Proses berikut menjelaskan cara mengunggah file menggunakan URL yang ditandatangani sebelumnya untuk permintaan PUT:
Kode contoh
Pemilik objek menghasilkan URL yang ditandatangani sebelumnya untuk metode PUT.
const OSS = require("ali-oss"); // Definisikan fungsi untuk menghasilkan URL yang ditandatangani. async function generateSignatureUrl(fileName) { // Dapatkan URL yang ditandatangani sebelumnya. const client = await new OSS({ accessKeyId: 'yourAccessKeyId', accessKeySecret: 'yourAccessKeySecret', bucket: 'examplebucket', region: 'oss-cn-hangzhou', authorizationV4: true }); return await client.signatureUrlV4('PUT', 3600, { headers: {} // Tetapkan header permintaan sesuai dengan header permintaan aktual. }, fileName); } // Panggil fungsi dan berikan nama file. generateSignatureUrl('yourFileName').then(url => { console.log('Generated Signature URL:', url); }).catch(err => { console.error('Error generating signature URL:', err); });Pengguna lain mengunggah file menggunakan URL yang ditandatangani sebelumnya 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 diizinkan. 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("Status code returned for the upload:"+response.getStatusLine().getStatusCode()); if(response.getStatusLine().getStatusCode() == 200){ System.out.println("Upload successful using network library."); } 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("Unable to open 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("Failed to create request: %w", err) } // Kirim permintaan. resp, err := client.Do(req) if err != nil { return fmt.Errorf("Failed to send request: %w", err) } defer resp.Body.Close() // Baca respons. body, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("Failed to read response: %w", err) } fmt.Printf("Status code returned for the upload: %d\n", resp.StatusCode) if resp.StatusCode == 200 { fmt.Println("Upload successful using network library.") } 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 akan diunggah dari path proyek program contoh secara default. filePath := "C:\\Users\\demo.txt" err := uploadFile(signedUrl, filePath) if err != nil { fmt.Println("An error occurred:", 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"Status code returned for the upload: {response.status_code}") if response.status_code == 200: print("Upload successful using network library.") print(response.text) except Exception as e: print(f"An error occurred: {e}") if __name__ == "__main__": // Ganti <signedUrl> dengan URL yang diizinkan. 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
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(`Status code returned for the upload: ${response.status}`); if (response.status === 200) { console.log('Upload successful using network library.'); } console.log(response.data); } catch (error) { console.error(`An error occurred: ${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 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 403 signature mismatch saat menggunakan Browser.js untuk mengunggah file, biasanya hal ini terjadi karena browser secara otomatis menambahkan header permintaan Content-Type, tetapi header ini tidak ditentukan saat URL yang ditandatangani sebelumnya dihasilkan. Hal 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>File Upload Example</title> </head> <body> <h1>File Upload Example</h1> <!-- Pilih file --> <input type="file" id="fileInput" /> <button id="uploadButton">Upload 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('Please select a file to upload.'); return; } try { await upload(file, signedUrl); alert('File uploaded successfully!'); } catch (error) { console.error('Error during upload:', error); alert('Upload failed: ' + 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(`Upload failed, status: ${response.status}`); } console.log('File uploaded successfully'); }; </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 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 respons. if (response.IsSuccessStatusCode) { Console.WriteLine($"Upload successful! Status code: {response.StatusCode}"); Console.WriteLine("Response headers:"); foreach (var header in response.Headers) { Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}"); } } else { string responseContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Upload failed! Status code: {response.StatusCode}"); Console.WriteLine("Response content: " + 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) { // Tetapkan URL. curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str()); // Tetapkan metode permintaan ke PUT. curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); // Buka file. FILE *file = fopen(filePath.c_str(), "rb"); if (!file) { std::cerr << "Unable to open file: " << filePath << std::endl; return; } // Dapatkan ukuran file. fseek(file, 0, SEEK_END); long fileSize = ftell(file); fseek(file, 0, SEEK_SET); // Tetapkan ukuran file. curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize); // Tetapkan 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() failed: " << curl_easy_strerror(res) << std::endl; } else { long httpCode = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode); std::cout << "Status code returned for the upload: " << httpCode << std::endl; if (httpCode == 200) { std::cout << "Upload successful using network library." << 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 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, "Status code returned for the upload: " + responseCode); if (responseCode == 200) { Log.d(TAG, "Upload successful using network library."); } return "Upload complete. Status code: " + responseCode; } catch (IOException e) { e.printStackTrace(); return "Upload failed: " + 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 akan diunggah dari path proyek program contoh secara default. String filePath = "C:\\Users\\demo.txt"; activity.uploadFile(signedUrl, filePath); } }
Skenario lainnya
Hasilkan URL yang ditandatangani sebelumnya yang mencakup parameter pemrosesan citra
Hasilkan URL yang ditandatangani sebelumnya dengan ID versi
FAQ
Jika saya menggunakan signature temporary untuk mengunggah file dan signature tersebut kedaluwarsa selama proses unggahan, apakah unggahan tersebut gagal?
Apakah metode POST didukung saat Anda menghasilkan URL yang ditandatangani sebelumnya?
Referensi
Untuk informasi selengkapnya tentang operasi API yang digunakan untuk menghasilkan URL yang ditandatangani sebelumnya, lihat GeneratePresignedUrlRequest.