Secara default, objek OSS bersifat privat—hanya pemilik bucket yang dapat mengunggah ke dalamnya. Hasilkan URL yang ditandatangani sebelumnya dengan OSS SDK untuk Python 2.0 guna memberikan akses unggah sementara kepada klien HTTP mana pun tanpa membagikan kredensial Anda.
URL yang ditandatangani sebelumnya menyematkan signature V4 dan dapat digunakan beberapa kali hingga kedaluwarsa. Jika kunci objek yang sama diunggah lebih dari sekali, setiap unggahan akan menimpa objek sebelumnya. Setelah URL kedaluwarsa, Anda harus membuat URL yang ditandatangani sebelumnya baru untuk melanjutkan unggahan.
Catatan penggunaan
Kode contoh dalam topik ini menggunakan wilayah
cn-hangzhou. Untuk mengakses bucket dari layanan Alibaba Cloud lain di wilayah yang sama, gunakan titik akhir internal alih-alih titik akhir publik default. Untuk detailnya, lihat Wilayah dan titik akhir.Pemanggil yang menghasilkan URL harus memiliki izin
oss:PutObject. Tidak diperlukan izin khusus untuk menghasilkan URL tersebut. Untuk detailnya, lihat Otorisasi Pengguna RAM untuk mengakses beberapa direktori dalam bucket.Topik ini menggunakan signature V4, yang mendukung periode validitas maksimum 7 hari. Untuk detailnya, lihat (Disarankan) Signature V4 dalam URL yang ditandatangani sebelumnya.
Cara kerja
Diagram berikut menunjukkan alur unggah menggunakan URL yang ditandatangani sebelumnya yang memungkinkan permintaan HTTP PUT.
Referensi metode presign()
Gunakan metode presign() untuk menghasilkan URL yang ditandatangani sebelumnya untuk PutObjectRequest:
presign(request: PutObjectRequest, **kwargs) → PresignResultNilai kembalian
presign() mengembalikan objek PresignResult:
| Field | Tipe | Deskripsi |
|---|---|---|
method | str | Metode HTTP untuk permintaan unggah (PUT untuk PutObject). |
url | str | URL yang ditandatangani sebelumnya untuk dibagikan kepada pengunggah. |
expiration | datetime | Waktu kedaluwarsa absolut URL tersebut. |
signed_headers | MutableMapping | Header permintaan yang disertakan dalam tanda tangan. Pengunggah harus mengirim header ini pada setiap permintaan PUT — jika tidak, akan terjadi error SignatureNotMatch. |
Untuk detail API lengkap, lihat presign.
Buat URL yang ditandatangani sebelumnya
Contoh berikut menghasilkan URL yang ditandatangani sebelumnya dengan masa berlaku 3.600 detik (1 jam).
Jika Anda menyertakan header permintaan (seperti Content-Type, kelas penyimpanan, atau metadata pengguna) saat membuat URL, header yang sama tersebut harus dikirim pada setiap permintaan PUT yang menggunakan URL tersebut. Header merupakan bagian dari tanda tangan—ketidaksesuaian akan menyebabkan error SignatureNotMatch.
import argparse
import alibabacloud_oss_v2 as oss
from datetime import timedelta
parser = argparse.ArgumentParser(description="presign put object sample")
parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
parser.add_argument('--bucket', help='The name of the bucket.', required=True)
parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
parser.add_argument('--key', help='The name of the object.', required=True)
def main():
args = parser.parse_args()
# Muat kredensial dari variabel lingkungan — jangan pernah menyematkan kunci secara langsung di kode sumber.
credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials_provider
cfg.region = args.region
if args.endpoint is not None:
cfg.endpoint = args.endpoint
client = oss.Client(cfg)
# Hasilkan URL yang ditandatangani sebelumnya untuk permintaan PUT, berlaku selama 1 jam.
pre_result = client.presign(
oss.PutObjectRequest(
bucket=args.bucket,
key=args.key,
),
expires=timedelta(seconds=3600)
)
print(f'method: {pre_result.method}')
print(f'expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")}')
print(f'url: {pre_result.url}')
# signed_headers mencantumkan semua header yang disertakan dalam tanda tangan.
# Pengunggah harus mengirim header ini bersama permintaan PUT.
for key, value in pre_result.signed_headers.items():
print(f'signed header: {key}: {value}')
if __name__ == "__main__":
main()Untuk contoh lengkap, lihat presigner_put_object.py.
Unggah objek menggunakan URL yang ditandatangani sebelumnya
Berikan URL yang ditandatangani sebelumnya ke klien HTTP mana pun untuk mengunggah. Semua contoh di bawah menggunakan HTTP PUT biasa—tidak diperlukan OSS SDK di sisi klien.
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******************************************************"Python
import requests
def upload_file(signed_url, file_path):
try:
with open(file_path, 'rb') as file:
response = requests.put(signed_url, data=file)
print(f"Status code: {response.status_code}")
if response.status_code == 200:
print("Upload successful.")
print(response.text)
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == "__main__":
# Ganti <signedUrl> dengan URL yang ditandatangani sebelumnya yang dihasilkan di atas.
signed_url = "<signedUrl>"
file_path = "C:\\Users\\demo.txt"
upload_file(signed_url, file_path)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;
URL signedUrl = new URL("<signedUrl>");
String pathName = "C:\\Users\\demo.txt";
Map<String, String> headers = new HashMap<>();
headers.put("x-oss-callback", "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9");
headers.put("x-oss-callback-var", "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==");
try {
HttpPut put = new HttpPut(signedUrl.toString());
HttpEntity entity = new FileEntity(new File(pathName));
put.setEntity(entity);
for (Map.Entry header : headers.entrySet()) {
put.addHeader(header.getKey().toString(), header.getValue().toString());
}
httpClient = HttpClients.createDefault();
response = httpClient.execute(put);
System.out.println("Kode status: " + response.getStatusLine().getStatusCode());
if (response.getStatusLine().getStatusCode() == 200) {
System.out.println("Unggahan berhasil.");
}
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 {
file, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("unable to open file: %w", err)
}
defer file.Close()
client := &http.Client{}
req, err := http.NewRequest("PUT", signedUrl, file)
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read response: %w", err)
}
fmt.Printf("Status code: %d\n", resp.StatusCode)
if resp.StatusCode == 200 {
fmt.Println("Upload successful.")
}
fmt.Println(string(body))
return nil
}
func main() {
// Ganti <signedUrl> dengan URL yang ditandatangani sebelumnya.
signedUrl := "<signedUrl>"
filePath := "C:\\Users\\demo.txt"
if err := uploadFile(signedUrl, filePath); err != nil {
fmt.Println("An error occurred:", err)
}
}Node.js
const fs = require('fs');
const axios = require('axios');
async function uploadFile(signedUrl, filePath) {
try {
const fileStream = fs.createReadStream(filePath);
const response = await axios.put(signedUrl, fileStream, {
headers: {
'Content-Type': 'application/octet-stream'
}
});
console.log(`Status code: ${response.status}`);
if (response.status === 200) {
console.log("Upload successful.");
}
console.log(response.data);
} catch (error) {
console.error(`An error occurred: ${error.message}`);
}
}
(async () => {
// Ganti <signedUrl> dengan URL yang ditandatangani sebelumnya.
const signedUrl = '<signedUrl>';
const filePath = 'C:\\Users\\demo.txt';
await uploadFile(signedUrl, filePath);
})();Browser.js
Browser secara otomatis menambahkan header Content-Type ke permintaan PUT. Jika Content-Type tidak disertakan saat membuat URL yang ditandatangani sebelumnya, tanda tangan tidak akan sesuai dan OSS mengembalikan 403 SignatureNotMatch. Selalu tentukan Content-Type saat membuat URL yang ditandatangani sebelumnya untuk penggunaan browser.
<!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>
<input type="file" id="fileInput" />
<button id="uploadButton">Upload File</button>
<script>
// Ganti <signedUrl> 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);
}
});
const upload = async (file, presignedUrl) => {
const response = await fetch(presignedUrl, {
method: 'PUT',
body: file,
});
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 yang akan diunggah.
var filePath = "C:\\Users\\demo.txt";
// Ganti <signedUrl> dengan URL yang ditandatangani sebelumnya.
var presignedUrl = "<signedUrl>";
using var httpClient = new HttpClient();
using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
using var content = new StreamContent(fileStream);
var request = new HttpRequestMessage(HttpMethod.Put, presignedUrl);
request.Content = content;
var response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
Console.WriteLine($"Uploaded! 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) {
curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str());
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
FILE *file = fopen(filePath.c_str(), "rb");
if (!file) {
std::cerr << "Unable to open file: " << filePath << std::endl;
return;
}
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
fseek(file, 0, SEEK_SET);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
curl_easy_setopt(curl, CURLOPT_READDATA, file);
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: " << httpCode << std::endl;
if (httpCode == 200) {
std::cout << "Upload successful." << std::endl;
}
}
fclose(file);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
}
int main() {
// Ganti <signedUrl> dengan URL yang ditandatangani sebelumnya.
std::string signedUrl = "<signedUrl>";
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: " + responseCode);
if (responseCode == 200) {
Log.d(TAG, "Upload successful.");
}
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 ditandatangani sebelumnya.
String signedUrl = "<signedUrl>";
String filePath = "C:\\Users\\demo.txt";
activity.uploadFile(signedUrl, filePath);
}
}Skenario umum
FAQ
Langkah selanjutnya
presigner_put_object.py — contoh lengkap untuk unggahan satu bagian
presigner_complete_multipart_upload.py — contoh lengkap untuk unggahan multi-bagian