All Products
Search
Document Center

Object Storage Service:Use a presigned URL to download an object (Python SDK V2)

Last Updated:Mar 20, 2026

A presigned URL grants time-limited, unauthenticated access to a private OSS object. Anyone with the URL can download the object via HTTP GET — no OSS credentials required — until the URL expires. Use the presign method in OSS SDK for Python V2 to generate these URLs.

Prerequisites

Before you begin, ensure that you have:

  • An OSS bucket with at least one object to share

  • The oss:GetObject permission on the object (required to allow recipients to download via the presigned URL)

  • OSS SDK for Python V2 installed and credentials configured via environment variables

Note

Generating a presigned URL requires no specific permissions. The oss:GetObject permission is what allows the URL recipient to download the object. For more information, see Common examples of RAM policies.

How it works

image
  1. The object owner calls client.presign() with a GetObjectRequest to generate a presigned URL.

  2. OSS signs the URL using the V4 signature algorithm and embeds the expiration time as a query parameter.

  3. The URL recipient makes a standard HTTP GET request to download the object — no authentication headers needed.

  4. OSS validates the signature and expiration at request time. If valid, it returns the object.

Generate a presigned URL

The following example wraps the presign call in a reusable function. Call it with your bucket name, object key, and desired validity period to get a presigned download URL.

import datetime
import alibabacloud_oss_v2 as oss


def generate_presigned_download_url(
    region: str,
    bucket: str,
    key: str,
    expires_in: datetime.timedelta = datetime.timedelta(hours=1),
) -> str:
    """Generate a presigned URL for downloading an OSS object via HTTP GET.

    Args:
        region: The region where the bucket is located, e.g., "cn-hangzhou".
        bucket: The bucket name.
        key: The object key (path) within the bucket.
        expires_in: How long the URL remains valid. Defaults to 1 hour.
                    Maximum validity with V4 signatures is 7 days.

    Returns:
        A presigned URL string. Anyone with this URL can download the object
        until it expires.
    """
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider
    cfg.region = region

    client = oss.Client(cfg)

    result = client.presign(
        oss.GetObjectRequest(bucket=bucket, key=key),
        expires=expires_in,
    )

    return result.url


if __name__ == "__main__":
    url = generate_presigned_download_url(
        region="cn-hangzhou",
        bucket="examplebucket",
        key="exampleobject.txt",
        expires_in=datetime.timedelta(hours=1),
    )
    print(f"Presigned URL: {url}")
Important

With the V4 signature algorithm, the maximum validity period is 7 days. If you specify both expiration (absolute datetime) and expires (relative timedelta), expiration takes precedence.

presign method reference

Syntax

presign(request: GetObjectRequest, **kwargs) -> PresignResult

Parameters

ParameterTypeRequiredDescription
requestGetObjectRequestYesSpecifies the operation the presigned URL supports. See Client.presign.
expiresdatetime.timedeltaNoValidity period relative to the current time. Default: 15 minutes.
expirationdatetime.datetimeNoAbsolute expiration datetime. Takes precedence over expires if both are set.

Return value: `PresignResult`

FieldTypeDescription
methodstrHTTP method corresponding to the request operation (GET for GetObject).
urlstrThe presigned URL.
expirationdatetimeWhen the URL expires.
signed_headersMutableMappingRequest headers that were signed. Include these headers in the download request if present.

For full API details, see PresignResult.

Use the presigned URL

The presigned URL is a standard HTTP GET endpoint — no additional authentication headers are needed. Use it with any HTTP client, in any language, or directly in a browser.

The following examples all use the same presigned URL structure:

https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt
  ?x-oss-date=20241112T092756Z
  &x-oss-expires=3599
  &x-oss-signature-version=OSS4-HMAC-SHA256
  &x-oss-credential=LTAI****************/20241112/cn-hangzhou/oss/aliyun_v4_request
  &x-oss-signature=ed5a******************************************************

curl

curl -SO "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T092756Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI****************/20241112/cn-hangzhou/oss/aliyun_v4_request&x-oss-signature=ed5a******************************************************"

Python

import requests

def download_with_presigned_url(presigned_url: str, save_path: str) -> None:
    response = requests.get(presigned_url, stream=True)
    if response.status_code == 200:
        with open(save_path, "wb") as f:
            for chunk in response.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)
        print("Download completed.")
    else:
        print(f"Download failed. HTTP {response.status_code}")
        print(response.text)

download_with_presigned_url(
    presigned_url="<presigned-url>",
    save_path="/path/to/save/exampleobject.txt",
)

Java

import java.io.*;
import java.net.*;

public class PresignedDownload {
    public static void main(String[] args) throws IOException {
        String fileURL = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T092756Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI****************/20241112/cn-hangzhou/oss/aliyun_v4_request&x-oss-signature=ed5a******************************************************";
        String savePath = "C:/downloads/myfile.txt";

        URL url = new URL(fileURL);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");

        if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
            try (InputStream in = new BufferedInputStream(conn.getInputStream());
                 FileOutputStream out = new FileOutputStream(savePath)) {
                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesRead);
                }
                System.out.println("Download completed.");
            }
        } else {
            System.out.println("Download failed. HTTP " + conn.getResponseCode());
        }
        conn.disconnect();
    }
}

Node.js

const https = require("https");
const fs = require("fs");

const fileURL = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T092756Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI****************/20241112/cn-hangzhou/oss/aliyun_v4_request&x-oss-signature=ed5a******************************************************";
const savePath = "myfile.txt";

https.get(fileURL, (response) => {
    if (response.statusCode === 200) {
        const fileStream = fs.createWriteStream(savePath);
        response.pipe(fileStream);
        fileStream.on("finish", () => {
            fileStream.close();
            console.log("Download completed.");
        });
    } else {
        console.error(`Download failed. HTTP ${response.statusCode}`);
    }
}).on("error", (err) => {
    console.error("Error:", err.message);
});

Go

package main

import (
    "io"
    "net/http"
    "os"
)

func main() {
    fileURL := "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T092756Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI****************/20241112/cn-hangzhou/oss/aliyun_v4_request&x-oss-signature=ed5a******************************************************"
    savePath := "myfile.txt"

    resp, err := http.Get(fileURL)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    if resp.StatusCode == http.StatusOK {
        out, err := os.Create(savePath)
        if err != nil {
            panic(err)
        }
        defer out.Close()
        io.Copy(out, resp.Body)
        println("Download completed.")
    } else {
        println("Download failed. HTTP", resp.StatusCode)
    }
}

JavaScript (browser)

const fileURL = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T092756Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI****************/20241112/cn-hangzhou/oss/aliyun_v4_request&x-oss-signature=ed5a******************************************************";

fetch(fileURL)
    .then((response) => {
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        return response.blob();
    })
    .then((blob) => {
        const link = document.createElement("a");
        link.href = window.URL.createObjectURL(blob);
        link.download = "exampleobject.txt";
        document.body.appendChild(link);
        link.click();
        link.remove();
    })
    .catch((err) => console.error("Error:", err));

Android (Java)

import android.os.*;
import java.io.*;
import java.net.*;

public class DownloadTask extends AsyncTask<String, Void, String> {
    @Override
    protected String doInBackground(String... params) {
        String fileURL = params[0];
        String savePath = Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_DOWNLOADS) + "/myfile.txt";
        try {
            URL url = new URL(fileURL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                try (InputStream in = new BufferedInputStream(conn.getInputStream());
                     FileOutputStream out = new FileOutputStream(savePath)) {
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) != -1) {
                        out.write(buffer, 0, bytesRead);
                    }
                }
                return "Download completed.";
            }
            return "HTTP " + conn.getResponseCode();
        } catch (Exception e) {
            return "Error: " + e.getMessage();
        }
    }
}

Objective-C

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString *fileURL = @"https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T092756Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI****************/20241112/cn-hangzhou/oss/aliyun_v4_request&x-oss-signature=ed5a******************************************************";
        NSString *savePath = @"/Users/<your-username>/Downloads/myfile.txt";

        NSURL *url = [NSURL URLWithString:fileURL];
        NSURLSessionDataTask *task = [[NSURLSession sharedSession]
            dataTaskWithURL:url
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                if (error) {
                    NSLog(@"Error: %@", error.localizedDescription);
                    return;
                }
                if (!data) {
                    NSLog(@"No data received.");
                    return;
                }
                NSError *writeError = nil;
                BOOL success = [data writeToURL:[NSURL fileURLWithPath:savePath]
                                       options:NSDataWritingAtomic
                                         error:&writeError];
                NSLog(success ? @"Download completed." : @"Save error: %@", writeError.localizedDescription);
            }];
        [task resume];
        [[NSRunLoop currentRunLoop] run];
    }
    return 0;
}
Important

Treat presigned URLs as bearer tokens. Anyone with the URL can download the object until it expires. Share URLs only with intended recipients and use short expiration times for sensitive objects.

Common scenarios

Download a specific version of an object

When versioning is enabled on your bucket, include version_id in the request to generate a URL for a specific object version.

result = client.presign(
    oss.GetObjectRequest(
        bucket="examplebucket",
        key="exampleobject.txt",
        version_id="<version-id>",  # Replace with the actual version ID
    )
)
print(result.url)

Force a file download instead of browser preview

Set response_content_disposition to override how the browser handles the response. This forces a download dialog instead of rendering the object inline.

result = client.presign(
    oss.GetObjectRequest(
        bucket="examplebucket",
        key="exampleobject.txt",
        response_content_disposition="attachment;filename=test.txt",
    )
)
print(result.url)

Then download using the URL:

curl -X GET "<presigned-url-with-response-content-disposition>" -o "myfile.txt"
import requests

response = requests.get("<presigned-url-with-response-content-disposition>", stream=True)
if response.status_code == 200:
    with open("/path/to/save/myfile.txt", "wb") as f:
        for chunk in response.iter_content(chunk_size=8192):
            if chunk:
                f.write(chunk)

Include custom request headers

If you sign request headers into the URL, the client must include the same headers in the download request — otherwise signature validation fails.

Step 1: Generate the URL with signed headers.

result = client.presign(
    oss.GetObjectRequest(
        bucket="examplebucket",
        key="exampleobject.txt",
        range_behavior="standard",   # Signed into the URL
        request_payer="requester",   # Signed into the URL
    )
)
print(result.url)
# result.signed_headers shows which headers must be included in the download request

Step 2: Download the object, including the same headers.

curl -X GET "<presigned-url>" \
  -H "x-oss-range-behavior: standard" \
  -H "x-oss-request-payer: requester" \
  -o "myfile.txt"
import requests

headers = {
    "X-Oss-Range-Behavior": "standard",
    "X-Oss-Request-Payer": "requester",
}
response = requests.get("<presigned-url>", headers=headers, stream=True)
if response.status_code == 200:
    with open("/path/to/save/myfile.txt", "wb") as f:
        for chunk in response.iter_content(chunk_size=8192):
            if chunk:
                f.write(chunk)

Use a custom domain name

To generate a presigned URL under your own domain (mapped to OSS via CNAME), set cfg.endpoint to your custom domain and enable cfg.use_cname.

credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials_provider
cfg.region = "cn-hangzhou"
cfg.endpoint = "http://static.example.com"  # Your custom domain
cfg.use_cname = True

client = oss.Client(cfg)

result = client.presign(
    oss.GetObjectRequest(
        bucket="examplebucket",
        key="exampleobject.txt",
    )
)
print(result.url)
# The URL will use your custom domain: http://static.example.com/exampleobject.txt?...

What's next