By default, files in an OSS bucket are private and only the bucket owner can upload them. To let a third party upload a file without exposing your credentials, generate a presigned URL for that file. Anyone with the URL can upload within its validity period—useful for scenarios like letting partners submit contracts or letting users upload profile pictures.
Due to a policy change to improve compliance and security, starting March 20, 2025, new OSS users must use a custom domain name (CNAME) to call data API operations on buckets in Chinese mainland regions. Default public endpoints are restricted for these operations. Refer to the official announcement for a complete list of the affected operations. If you access OSS via HTTPS, bind a valid SSL certificate to your custom domain—this is mandatory for OSS Console access.
Prerequisites
Before you begin, make sure that you have:
An OSS bucket in the target region
The
oss:PutObjectpermission for the bucket owner's account. No permission is required to generate a presigned URL itself, but the upload succeeds only if the URL generator has this permission. See Grant custom permissions to a RAM user
Usage notes
Examples in this topic use the public endpoint of the China (Hangzhou) region. When accessing OSS from other Alibaba Cloud services in the same region, use an internal endpoint. For details, see Regions and endpoints.
Presigned URLs do not support FormData uploads. For FormData, use OSS form upload instead.
How presigned URLs work
A presigned URL is a signed link that grants temporary access to a specific OSS object. The signature is computed locally from the AccessKey pair, resource path, expiration time, and other parameters, then embedded in the URL as query parameters.
The typical URL format is:
https://BucketName.Endpoint/Object?signature_parametersFor example:
https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-process=image%2Fresize%2Cp_10&x-oss-date=20241115T095058Z&x-oss-expires=3600&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI****************%2F20241115%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-signature=6e7a********************************The URL contains the following parameters:
| Parameter | Description |
|---|---|
x-oss-date | The date and time (ISO 8601) when the signature was created |
x-oss-expires | How long the URL remains valid, in seconds, from x-oss-date |
x-oss-signature-version | The signing algorithm used |
x-oss-credential | Credential scope identifying the AccessKey ID, date, region, service, and request type |
x-oss-signature | The computed signature |
When OSS receives a request with a presigned URL, it verifies the signature. If any parameter has been modified or the URL has expired, OSS denies the request.
Validity periods:
| Credential type | Maximum validity |
|---|---|
| SDK (AccessKey pair) | 7 days |
| Security Token Service (STS) temporary credentials | 43,200 seconds (12 hours) |
When you generate a presigned URL using STS temporary credentials, the URL expires when either the configured validity period ends or the STS token expires—whichever occurs first. If the STS token has only 30 minutes remaining, the URL becomes invalid after 30 minutes regardless of the configured expiry. Set the URL validity period to be shorter than the STS token's remaining lifetime.
Security: Treat presigned URLs as bearer tokens—anyone with the URL can perform the authorized operation until it expires. Share presigned URLs only with intended recipients, and use short validity periods for sensitive operations.
Common use cases:
Temporary uploads: A backend generates a short-lived presigned URL and returns it to a frontend. The user uploads directly to OSS without needing OSS credentials.
Flexible sharing: Share a presigned URL via email or messaging so a recipient can upload without accessing the OSS console.
Upload a single file
The upload flow involves two parties: the server (bucket owner) that generates the presigned URL, and the client (third party) that uses it to upload.
Step 1: Generate a presigned URL (server side)
The bucket owner generates a presigned URL for a PUT request. The third party uses this URL to upload the file.
The maximum validity period for a presigned URL generated with an SDK is 7 days. When using an STS token, the maximum is 43,200 seconds (12 hours). If the STS token expires before the configured validity period ends, the URL becomes invalid at token expiry—not at the configured time.
The following examples use Java, Go, and Python. For other SDKs, see the links below each example.
Java
For more SDK details, see Upload a file using a presigned URL in Java.
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 {
// This example uses the public endpoint of the China (Hangzhou) region. Specify the actual endpoint.
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured.
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// Specify the bucket name, for example, examplebucket.
String bucketName = "examplebucket";
// Specify the full path of the object, for example, exampleobject.txt. Do not include the bucket name.
String objectName = "exampleobject.txt";
// Specify the region where the bucket is located, for example, cn-hangzhou.
String region = "cn-hangzhou";
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 {
// Set the expiration time to 1 hour.
Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
request.setExpiration(expiration);
signedUrl = ossClient.generatePresignedUrl(request);
System.out.println("signed url for putObject: " + signedUrl);
} catch (OSSException oe) {
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Error Message:" + ce.getMessage());
}
}
}Go
For more SDK details, see Upload a file using a presigned URL in Go.
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"
)
var (
region string
bucketName string
objectName string
)
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
flag.StringVar(&objectName, "object", "", "The name of the object.")
}
func main() {
flag.Parse()
if len(bucketName) == 0 || len(region) == 0 || len(objectName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, bucket name, region, and object name are required")
}
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
client := oss.NewClient(cfg)
// Generate a presigned URL for the PutObject request.
result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
Bucket: oss.Ptr(bucketName),
Key: oss.Ptr(objectName),
// ContentType: oss.Ptr("text/txt"), // If you set ContentType here, include the same value in the PUT request.
// Metadata: map[string]string{"key1": "value1"}, // Same applies to Metadata.
},
oss.PresignExpires(10*time.Minute),
)
if err != nil {
log.Fatalf("failed to presign PutObject: %v", err)
}
log.Printf("method: %v\n", result.Method)
log.Printf("expiration: %v\n", result.Expiration)
log.Printf("url: %v\n", result.URL)
if len(result.SignedHeaders) > 0 {
// If headers are signed into the URL, the PUT request must include the same headers.
log.Printf("signed headers:\n")
for k, v := range result.SignedHeaders {
log.Printf("%v: %v\n", k, v)
}
}
}Python
For more SDK details, see Upload a file using a presigned URL in Python.
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 name used to access OSS.')
parser.add_argument('--key', help='The name of the object.', required=True)
def main():
args = parser.parse_args()
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 valid for 3,600 seconds.
pre_result = client.presign(oss.PutObjectRequest(
bucket=args.bucket,
key=args.key,
), expires=timedelta(seconds=3600))
print(f'method: {pre_result.method},'
f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
f' url: {pre_result.url}')
for key, value in pre_result.signed_headers.items():
print(f'signed header - {key}: {value}')
if __name__ == "__main__":
main()Other SDKs
Step 2: Upload the file (client side)
The third party uses the presigned URL to upload the file with a PUT request.
A presigned URL can be used multiple times until it expires. Multiple uploads to the same URL overwrite the object. After the URL expires, regenerate it.
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;
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>");
String pathName = "C:\\Users\\demo.txt";
try {
HttpPut put = new HttpPut(signedUrl.toString());
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.");
}
} 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 {
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, _ := io.ReadAll(resp.Body)
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)
}
}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.
signed_url = "<signedUrl>"
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 {
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.');
}
} 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 request header. If Content-Type was not included when generating the presigned URL, OSS returns a 403 signature mismatch error. To avoid this, always specify Content-Type when generating the URL for browser-based uploads. Specifying Content-Type also prevents clients from uploading files of unexpected types—if the upload request uses a different Content-Type, OSS rejects it with a 403 error.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>File Upload Example</title>
</head>
<body>
<input type="file" id="fileInput" />
<button id="uploadButton">Upload file</button>
<script>
// Replace this 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>Perform a multipart upload
For files that cannot use an OSS SDK directly but are too large for a single PUT (over 5 GB), use presigned URLs to perform a multipart upload. The server initiates the upload task, generates a presigned URL for each part, and the client uploads each part independently. After all parts are uploaded, the server completes the upload by merging the parts.
Multipart upload with presigned URLs requires careful coordination. Each presigned URL is tied to a specific partNumber, and uploading the wrong data to a URL results in a corrupted file. If your client can integrate an OSS SDK, use STS credentials for direct client-side uploads instead—it's simpler and more reliable.
Step 1: The client requests an upload
The client sends the file name, file size, and desired part size to the server. The part size cannot exceed 5 GB. A part size of 5 MB is recommended.
curl -X POST https://yourserver.com/init-upload \
-H "Content-Type: application/json" \
-d '{
"fileName": "exampleobject.jpg",
"fileSize": 104857600,
"partSize": 5242880
}'Replace https://yourserver.com/init-upload with your server's initialization endpoint.
Step 2: The server initializes the upload and generates presigned URLs
The server:
Calls InitiateMultipartUpload to get an upload ID.
Calculates the number of parts based on the file size and part size.
Generates a presigned URL for each part.
Returns the upload ID and URL list to the client.
Record the upload ID on the server and map it to the file. You'll need it to verify and merge parts later. Store it in a cache or database.
Step 3: The client uploads parts
The client uses each presigned URL to upload the corresponding part via a PUT request. Concurrent uploads are supported.
Each URL is bound to a specific partNumber. Upload only the correct part data to each URL—parts cannot be mixed or skipped.The following example uploads part 1:
curl -X PUT -T /path/to/local/file "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.jpg?partNumber=1&uploadId=BE2D0BC931BE4DE1B23F339AABFA49EE&x-oss-credential=LTAI********************%2F20250520%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-date=20250520T082728Z&x-oss-expires=3600&x-oss-signature=81f3d2e5eaa67c432291577ed20af3b3f60df05ab3cddedcdce168ef707f7ad0&x-oss-signature-version=OSS4-HMAC-SHA256"Step 4: (Optional) The server verifies parts and completes the upload
After the client signals upload completion, the server can optionally call ListParts to verify that all parts are present and have the expected sizes, then calls CompleteMultipartUpload to merge the parts.
Use the upload ID recorded in step 2.
Java
import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.*;
import java.util.ArrayList;
import java.util.List;
public class VerifyAndComplete {
public static void main(String[] args) throws Exception {
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
String bucketName = "examplebucket";
String objectName = "exampleobject.jpeg";
String uploadId = "4B78****************************";
String region = "cn-hangzhou";
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
ClientBuilderConfiguration config = new ClientBuilderConfiguration();
config.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.region(region)
.credentialsProvider(credentialsProvider)
.clientConfiguration(config)
.build();
try {
ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, objectName, uploadId);
PartListing partListing = ossClient.listParts(listPartsRequest);
List<PartETag> partETags = new ArrayList<>();
for (PartSummary part : partListing.getParts()) {
partETags.add(new PartETag(part.getPartNumber(), part.getETag()));
}
if (partETags.isEmpty()) {
System.out.println("No uploaded parts found. Aborting merge.");
return;
}
CompleteMultipartUploadRequest completeRequest =
new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
CompleteMultipartUploadResult result = ossClient.completeMultipartUpload(completeRequest);
System.out.println("Parts merged successfully!");
System.out.println("ETag: " + result.getETag());
} catch (Exception e) {
System.err.println("Failed to merge parts: " + e.getMessage());
} finally {
if (ossClient != null) ossClient.shutdown();
}
}
}Go
package main
import (
"context"
"flag"
"log"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)
var (
region string
bucketName string
objectName string
uploadId string
)
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
flag.StringVar(&objectName, "object", "", "The name of the object.")
flag.StringVar(&uploadId, "uploadId", "", "The upload ID.")
}
func main() {
flag.Parse()
if region == "" || bucketName == "" || objectName == "" || uploadId == "" {
flag.PrintDefaults()
log.Fatal("Missing required parameters.")
}
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
client := oss.NewClient(cfg)
partListResp, err := client.ListParts(context.TODO(), &oss.ListPartsRequest{
Bucket: oss.Ptr(bucketName),
Key: oss.Ptr(objectName),
UploadId: oss.Ptr(uploadId),
})
if err != nil {
log.Fatalf("failed to list parts: %v", err)
}
var parts []oss.UploadPart
for _, p := range partListResp.Parts {
parts = append(parts, oss.UploadPart{PartNumber: p.PartNumber, ETag: p.ETag})
}
completeResult, err := client.CompleteMultipartUpload(context.TODO(), &oss.CompleteMultipartUploadRequest{
Bucket: oss.Ptr(bucketName),
Key: oss.Ptr(objectName),
UploadId: oss.Ptr(uploadId),
CompleteMultipartUpload: &oss.CompleteMultipartUpload{Parts: parts},
})
if err != nil {
log.Fatalf("failed to complete multipart upload: %v", err)
}
log.Println("Upload completed successfully.")
log.Printf("Bucket: %s\n", oss.ToString(completeResult.Bucket))
log.Printf("Key: %s\n", oss.ToString(completeResult.Key))
log.Printf("ETag: %s\n", oss.ToString(completeResult.ETag))
}Python
# -*- coding: utf-8 -*-
import argparse
import alibabacloud_oss_v2 as oss
def main():
parser = argparse.ArgumentParser(description="complete multipart upload sample")
parser.add_argument('--region', required=True)
parser.add_argument('--bucket', required=True)
parser.add_argument('--endpoint')
parser.add_argument('--key', required=True)
parser.add_argument('--upload_id', required=True)
args = parser.parse_args()
credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials_provider
cfg.region = args.region
if args.endpoint:
cfg.endpoint = args.endpoint
client = oss.Client(cfg)
try:
list_parts_result = client.list_parts(oss.ListPartsRequest(
bucket=args.bucket,
key=args.key,
upload_id=args.upload_id
))
upload_parts = [
oss.UploadPart(part_number=part.part_number, etag=part.etag)
for part in list_parts_result.parts
]
if not upload_parts:
print("No uploaded parts found. Aborting operation.")
return
result = client.complete_multipart_upload(oss.CompleteMultipartUploadRequest(
bucket=args.bucket,
key=args.key,
upload_id=args.upload_id,
complete_multipart_upload=oss.CompleteMultipartUpload(parts=upload_parts)
))
print("Merge successful!")
print(f"ETag: {result.etag}")
print(f"Bucket: {result.bucket}")
print(f"Key: {result.key}")
except Exception as e:
print("Failed to merge parts:", e)
if __name__ == "__main__":
main()Set headers to control upload behavior
When generating a presigned URL, specify header parameters to enforce storage policies. For example, set x-oss-storage-class to control the storage class, or set Content-Type to restrict file types.
Any headers included when generating the presigned URL must also be included in the PUT request. Omitting them causes a 403 error due to signature mismatch. For a full list of supported system headers, see PutObject. For custom headers, see Manage file metadata.
Security tip: SpecifyingContent-Typein the presigned URL prevents clients from uploading files of unexpected types. If the upload request uses a differentContent-Type, OSS rejects it with a 403 error.
Step 1: Generate a presigned URL with headers (server side)
Java
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.internal.OSSHeaders;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.aliyun.oss.model.StorageClass;
import java.net.URL;
import java.util.*;
public class GetSignUrl {
public static void main(String[] args) throws Throwable {
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
String bucketName = "examplebucket";
String objectName = "exampleobject.txt";
String region = "cn-hangzhou";
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
Map<String, String> headers = new HashMap<>();
headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
headers.put(OSSHeaders.CONTENT_TYPE, "text/plain; charset=utf8");
Map<String, String> userMetadata = new HashMap<>();
userMetadata.put("key1", "value1");
userMetadata.put("key2", "value2");
URL signedUrl = null;
try {
Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
request.setExpiration(expiration);
request.setHeaders(headers);
request.setUserMetadata(userMetadata);
signedUrl = ossClient.generatePresignedUrl(request);
System.out.println("signed url for putObject: " + signedUrl);
} catch (OSSException oe) {
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Error Message:" + oe.getErrorMessage());
} catch (ClientException ce) {
System.out.println("Error Message:" + ce.getMessage());
}
}
}Go
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"
)
var (
region string
bucketName string
objectName string
)
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
flag.StringVar(&objectName, "object", "", "The name of the object.")
}
func main() {
flag.Parse()
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
client := oss.NewClient(cfg)
result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
Bucket: oss.Ptr(bucketName),
Key: oss.Ptr(objectName),
ContentType: oss.Ptr("text/plain;charset=utf8"), // Must match the Content-Type in the PUT request.
StorageClass: oss.StorageClassStandard, // Must match the storage class in the PUT request.
Metadata: map[string]string{"key1": "value1", "key2": "value2"}, // Must match metadata in the PUT request.
},
oss.PresignExpires(10*time.Minute),
)
if err != nil {
log.Fatalf("failed to presign PutObject: %v", err)
}
log.Printf("method: %v\n", result.Method)
log.Printf("expiration: %v\n", result.Expiration)
log.Printf("url: %v\n", result.URL)
if len(result.SignedHeaders) > 0 {
log.Printf("signed headers:\n")
for k, v := range result.SignedHeaders {
log.Printf("%v: %v\n", k, v)
}
}
}Python
import argparse
import alibabacloud_oss_v2 as oss
from datetime import timedelta
parser = argparse.ArgumentParser(description="presign put object with headers sample")
parser.add_argument('--region', required=True)
parser.add_argument('--bucket', required=True)
parser.add_argument('--endpoint')
parser.add_argument('--key', required=True)
def main():
args = parser.parse_args()
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)
pre_result = client.presign(oss.PutObjectRequest(
bucket=args.bucket,
key=args.key,
content_type='text/plain;charset=utf8',
storage_class='Standard',
metadata={'key1': 'value1', 'key2': 'value2'}
), expires=timedelta(seconds=3600))
print(f'method: {pre_result.method},'
f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
f' url: {pre_result.url}')
for key, value in pre_result.signed_headers.items():
print(f'signed header - {key}: {value}')
if __name__ == "__main__":
main()Step 2: Upload with the matching headers (client side)
Pass the same headers that were included when generating the presigned URL.
curl
curl -X PUT \
-H "Content-Type: text/plain;charset=utf8" \
-H "x-oss-storage-class: Standard" \
-H "x-oss-meta-key1: value1" \
-H "x-oss-meta-key2: value2" \
-T "C:\\Users\\demo.txt" \
"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******************************************************"Set an upload callback
Add callback parameters to the presigned URL so OSS notifies your application server after a successful upload. For details on how callbacks work, see Callback.
Step 1: Generate a presigned URL with callback parameters (server side)
Python
import argparse
import base64
import alibabacloud_oss_v2 as oss
from datetime import timedelta
parser = argparse.ArgumentParser(description="presign put object with callback sample")
parser.add_argument('--region', required=True)
parser.add_argument('--bucket', required=True)
parser.add_argument('--endpoint')
parser.add_argument('--key', required=True)
def main():
args = parser.parse_args()
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)
call_back_url = "http://www.example.com/callback"
callback = base64.b64encode(str(
'{"callbackUrl":"' + call_back_url + '",'
'"callbackBody":"bucket=${bucket}&object=${object}&my_var_1=${x:var1}&my_var_2=${x:var2}"}'
).encode()).decode()
callback_var = base64.b64encode('{"x:var1":"value1","x:var2":"value2"}'.encode()).decode()
pre_result = client.presign(oss.PutObjectRequest(
bucket=args.bucket,
key=args.key,
callback=callback,
callback_var=callback_var,
), expires=timedelta(seconds=3600))
print(f'method: {pre_result.method},'
f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
f' url: {pre_result.url}')
for key, value in pre_result.signed_headers.items():
print(f'signed header - {key}: {value}')
if __name__ == "__main__":
main()Go
package main
import (
"context"
"encoding/base64"
"encoding/json"
"flag"
"log"
"time"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)
var (
region string
bucketName string
objectName string
)
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
flag.StringVar(&objectName, "object", "", "The name of the object.")
}
func main() {
flag.Parse()
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
client := oss.NewClient(cfg)
callbackMap := map[string]string{
"callbackUrl": "http://example.com:23450",
"callbackBody": "bucket=${bucket}&object=${object}&size=${size}&my_var_1=${x:my_var1}&my_var_2=${x:my_var2}",
"callbackBodyType": "application/x-www-form-urlencoded",
}
callbackStr, err := json.Marshal(callbackMap)
if err != nil {
log.Fatalf("failed to marshal callback map: %v", err)
}
callbackBase64 := base64.StdEncoding.EncodeToString(callbackStr)
callbackVarMap := map[string]string{
"x:my_var1": "this is var 1",
"x:my_var2": "this is var 2",
}
callbackVarStr, err := json.Marshal(callbackVarMap)
if err != nil {
log.Fatalf("failed to marshal callback var: %v", err)
}
callbackVarBase64 := base64.StdEncoding.EncodeToString(callbackVarStr)
result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
Bucket: oss.Ptr(bucketName),
Key: oss.Ptr(objectName),
Callback: oss.Ptr(callbackBase64),
CallbackVar: oss.Ptr(callbackVarBase64),
},
oss.PresignExpires(10*time.Minute),
)
if err != nil {
log.Fatalf("failed to presign PutObject: %v", err)
}
log.Printf("method: %v\n", result.Method)
log.Printf("expiration: %v\n", result.Expiration)
log.Printf("url: %v\n", result.URL)
}Step 2: Upload with the callback headers (client side)
curl
curl -X PUT \
-H "x-oss-callback: eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9" \
-H "x-oss-callback-var: eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==" \
-T "C:\\Users\\demo.txt" \
"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******************************************************"FAQ
Why did my presigned URL expire earlier than expected?
Presigned URLs remain valid only while the credentials used to generate them are valid. If you generate a URL with STS temporary credentials, the URL expires when the STS token expires—regardless of the configured validity period. For example, if the STS token has 30 minutes remaining and you configure a 7-day validity period, the URL expires after 30 minutes. To avoid unexpected expiry, set the URL validity period to be shorter than the STS token's remaining lifetime.
Why am I getting a 403 Forbidden error?
A 403 error means signature verification failed. Check the following:
The request headers don't match the headers signed into the URL. If you set
Content-Typeor metadata when generating the URL, include the same values in the PUT request.The URL has already expired—generate a new one.
The URL was modified after generation—use the URL exactly as returned by the SDK.
For Browser.js uploads: the browser automatically adds a
Content-Typeheader. Always specifyContent-Typewhen generating the presigned URL to prevent mismatches.When using curl, enclose the URL in quotes to prevent the shell from misinterpreting special characters.