OSS objects are private by default — only the bucket owner can upload to them. Generate a presigned URL with OSS SDK for Python 2.0 to grant temporary upload access to any HTTP client without sharing your credentials.
A presigned URL embeds a V4 signature and can be used multiple times until it expires. If the same object key is uploaded more than once, each upload overwrites the previous object. After the URL expires, you must generate a new presigned URL to continue uploading.
Usage notes
Sample code in this topic uses region
cn-hangzhou. To access a bucket from another Alibaba Cloud service in the same region, use an internal endpoint instead of the default public one. For details, see Regions and endpoints.The caller generating the URL must have the
oss:PutObjectpermission. No special permission is needed to generate the URL itself. For details, see Authorize a RAM user to access multiple directories in a bucket.This topic uses V4 signatures, which support a maximum validity period of 7 days. For details, see (Recommended) V4 signatures in presigned URLs.
How it works
The following diagram shows the upload flow using a presigned URL that allows HTTP PUT requests.
presign() method reference
Use the presign() method to generate a presigned URL for a PutObjectRequest:
presign(request: PutObjectRequest, **kwargs) → PresignResultReturn value
presign() returns a PresignResult object:
| Field | Type | Description |
|---|---|---|
method | str | HTTP method for the upload request (PUT for PutObject). |
url | str | The presigned URL to share with the uploader. |
expiration | datetime | Absolute expiration time of the URL. |
signed_headers | MutableMapping | Request headers included in the signature. The uploader must send these headers with every PUT request — omitting them causes a SignatureNotMatch error. |
For full API details, see presign.
Generate a presigned URL
The following example generates a presigned URL valid for 3,600 seconds (1 hour).
If you include request headers (such as Content-Type, storage class, or user metadata) when generating the URL, those same headers must be sent with every PUT request that uses the URL. Headers are part of the signature — a mismatch causes a SignatureNotMatch error.
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()
# Load credentials from environment variables — never hardcode keys in source code.
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)
# Generate a presigned URL for a PUT request, valid for 1 hour.
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 lists all headers included in the signature.
# The uploader must send these headers with the PUT request.
for key, value in pre_result.signed_headers.items():
print(f'signed header: {key}: {value}')
if __name__ == "__main__":
main()For the complete sample, see presigner_put_object.py.
Upload an object using the presigned URL
Pass the presigned URL to any HTTP client to upload. All examples below use a plain HTTP PUT — no OSS SDK is required on the client side.
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__":
# Replace <signedUrl> with the presigned URL generated above.
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;
// Replace <signedUrl> with the presigned URL.
URL signedUrl = new URL("<signedUrl>");
// Specify the full path of the local file to upload.
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: " + response.getStatusLine().getStatusCode());
if (response.getStatusLine().getStatusCode() == 200) {
System.out.println("Upload successful.");
}
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() {
// Replace <signedUrl> with the presigned URL.
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 () => {
// Replace <signedUrl> with the presigned URL.
const signedUrl = '<signedUrl>';
const filePath = 'C:\\Users\\demo.txt';
await uploadFile(signedUrl, filePath);
})();Browser.js
Browsers automatically add a Content-Type header to PUT requests. If Content-Type was not included when generating the presigned URL, the signature will not match and OSS returns 403 SignatureNotMatch. Always specify Content-Type when generating a presigned URL for browser use.
<!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>
// Replace <signedUrl> with the presigned URL generated in step 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;
// Specify the full path of the local file to upload.
var filePath = "C:\\Users\\demo.txt";
// Replace <signedUrl> with the presigned URL.
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() {
// Replace <signedUrl> with the presigned URL.
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();
// Replace <signedUrl> with the presigned URL.
String signedUrl = "<signedUrl>";
String filePath = "C:\\Users\\demo.txt";
activity.uploadFile(signedUrl, filePath);
}
}Common scenarios
FAQ
What's next
presigner_put_object.py — complete sample for single-part upload
presigner_complete_multipart_upload.py — complete sample for multipart upload