All Products
Search
Document Center

Object Storage Service:Download an object using a presigned URL

Last Updated:Mar 20, 2026

By default, objects in an OSS bucket are private. A presigned URL grants temporary, credential-free access to a specific object. Anyone with the URL can download the object until it expires — no OSS credentials required on the recipient's end.

Use the OSS SDK for Python to generate a presigned URL, then share it with the intended recipient. The recipient can download the object using any HTTP client.

Prerequisites

Before you begin, ensure that you have:

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

  • The OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables set with your AccessKey ID and AccessKey secret

Note: Generating a presigned URL itself requires no specific permissions. The oss:GetObject permission is checked when the recipient uses the URL to download.

How it works

Presigned URL download flow
  1. The object owner generates a presigned URL for a GET request using the OSS SDK for Python.

  2. The owner shares the URL with the recipient.

  3. The recipient sends a GET request to the URL using any HTTP client (curl, browser, or code). No credentials needed.

  4. OSS validates the signature embedded in the URL and returns the object if the URL is still valid.

Generate a presigned URL

All examples in this document use V4 presigned URLs. The URL embeds your signature, the object path, and an expiration time. To generate one, specify:

  • Object path: The full path of the object, excluding the bucket name (e.g., exampledir/exampleobject.txt)

  • HTTP method: GET for downloads

  • Expiration: Validity in seconds, up to 604,800 seconds (7 days)

# -*- coding: utf-8 -*-
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider

# Load credentials from environment variables.
# Set OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET before running this code.
auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())

# Set the endpoint for the region where your bucket is located.
# Example: China (Hangzhou) public endpoint
endpoint = "https://oss-cn-hangzhou.aliyuncs.com"

# The region parameter is required for V4 signatures.
region = "cn-hangzhou"

bucket = oss2.Bucket(auth, endpoint, "yourBucketName", region=region)

# Full path of the object, excluding the bucket name.
object_name = 'exampledir/exampleobject.txt'

# Generate a presigned URL valid for 600 seconds.
# slash_safe=True prevents OSS from percent-encoding the forward slashes in the
# object path, which would otherwise make the URL unusable.
url = bucket.sign_url('GET', object_name, 600, slash_safe=True)
print('Presigned URL:', url)

The generated URL looks like this:

https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampledir/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******

Key parameters in the URL:

ParameterDescription
x-oss-dateTimestamp when the signature was created (ISO 8601 format)
x-oss-expiresValidity duration in seconds, starting from x-oss-date
x-oss-signature-versionSigning algorithm (OSS4-HMAC-SHA256)
x-oss-credentialCredential scope: AccessKey ID, date, region, service, and request type
x-oss-signatureThe computed signature that authorizes the request

Download using the presigned URL

The presigned URL works with any HTTP client. Pass it as a plain GET request — no authentication headers required.

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

file_url = "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******************************************************"
save_path = "/downloads/exampleobject.txt"

try:
    response = requests.get(file_url, stream=True)
    if response.status_code == 200:
        with open(save_path, 'wb') as f:
            for chunk in response.iter_content(4096):
                f.write(chunk)
        print("Download completed!")
    else:
        print(f"Download failed. Status code: {response.status_code}")
except Exception as e:
    print("Error during download:", e)

Java

import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class Demo {
    public static void main(String[] args) {
        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 = "/downloads/myfile.txt";

        try {
            downloadFile(fileURL, savePath);
            System.out.println("Download completed!");
        } catch (IOException e) {
            System.err.println("Error during download: " + e.getMessage());
        }
    }

    private static void downloadFile(String fileURL, String savePath) throws IOException {
        URL url = new URL(fileURL);
        HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
        httpConn.setRequestMethod("GET");

        int responseCode = httpConn.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK) {
            InputStream inputStream = new BufferedInputStream(httpConn.getInputStream());
            FileOutputStream outputStream = new FileOutputStream(savePath);

            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }

            outputStream.close();
            inputStream.close();
        } else {
            System.out.println("No file to download. Server replied HTTP code: " + responseCode);
        }
        httpConn.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 = "/downloads/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. Status code: ${response.statusCode}`);
    }
}).on('error', (err) => {
    console.error("Error during download:", 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 := "/downloads/myfile.txt"

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

    if response.StatusCode == http.StatusOK {
        outFile, err := os.Create(savePath)
        if err != nil {
            panic(err)
        }
        defer outFile.Close()

        _, err = io.Copy(outFile, response.Body)
        if err != nil {
            panic(err)
        }
        println("Download completed!")
    } else {
        println("No file to download. Server replied HTTP code:", response.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******************************************************";
const fileName = "exampleobject.txt";

fetch(fileURL)
    .then(response => {
        if (!response.ok) {
            throw new Error(`Server replied HTTP code: ${response.status}`);
        }
        return response.blob();
    })
    .then(blob => {
        const link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = fileName;
        document.body.appendChild(link);
        link.click();
        link.remove();
        console.log("Download completed!");
    })
    .catch(error => {
        console.error("Error during download:", error);
    });

Android (Java)

import android.os.AsyncTask;
import android.os.Environment;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class DownloadTask extends AsyncTask<String, String, 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 httpConn = (HttpURLConnection) url.openConnection();
            httpConn.setRequestMethod("GET");
            int responseCode = httpConn.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                InputStream inputStream = new BufferedInputStream(httpConn.getInputStream());
                FileOutputStream outputStream = new FileOutputStream(savePath);
                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
                outputStream.close();
                inputStream.close();
                return "Download completed!";
            } else {
                return "No file to download. Server replied HTTP code: " + responseCode;
            }
        } catch (Exception e) {
            return "Error during download: " + 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/Desktop/myfile.txt";

        NSURL *url = [NSURL URLWithString:fileURL];

        NSURLSessionDataTask *task = [[NSURLSession sharedSession]
            dataTaskWithURL:url
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                if (error) {
                    NSLog(@"Error during download: %@", error.localizedDescription);
                    return;
                }
                if (!data) {
                    NSLog(@"No data received.");
                    return;
                }
                NSError *writeError = nil;
                BOOL success = [data writeToURL:[NSURL fileURLWithPath:savePath]
                                        options:NSDataWritingAtomic
                                          error:&writeError];
                if (success) {
                    NSLog(@"Download completed!");
                } else {
                    NSLog(@"Error saving file: %@", writeError.localizedDescription);
                }
            }];

        [task resume];
        [[NSRunLoop currentRunLoop] run];
    }
    return 0;
}

Other scenarios

Download a specific version of an object

To restrict access to a specific object version, pass the versionId in the headers when generating the presigned URL.

# -*- coding: utf-8 -*-
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider

auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
region = "cn-hangzhou"
bucket = oss2.Bucket(auth, endpoint, "yourBucketName", region=region)

object_name = 'exampledir/exampleobject.txt'

headers = dict()
headers["versionId"] = "CAEQARiBgID8rumR2hYiIGUyOTAyZGY2MzU5MjQ5ZjlhYzQzZjNlYTAyZDE3****"

url = bucket.sign_url('GET', object_name, 600, slash_safe=True, headers=headers)
print('Presigned URL:', url)

Use request headers in the presigned URL

If you include request headers (such as Content-Type) when generating the URL, the recipient must send the same headers in the GET request. Otherwise, OSS rejects the request with a signature error.

Step 1: Generate the URL with request headers.

# -*- coding: utf-8 -*-
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider

auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
region = "cn-hangzhou"
bucket = oss2.Bucket(auth, endpoint, "yourBucketName", region=region)

object_name = 'exampledir/exampleobject.txt'

headers = dict()
headers['Content-Type'] = 'text/plain; charset=utf8'

# Optional query parameters (uncomment as needed):
params = dict()
# Bandwidth throttling in bit/s (example: 100 KB/s = 100 * 1024 * 8)
# params['x-oss-traffic-limit'] = str(100 * 1024 * 8)
# Restrict access to a specific IP address or CIDR block
# params['x-oss-ac-source-ip'] = "127.0.0.1"
# params['x-oss-ac-subnet-mask'] = "32"
# Restrict access to a specific VPC
# params['x-oss-ac-vpc-id'] = "vpc-t4nlw426y44rd3iq4xxxx"
# Allow or disallow request forwarding
# params['x-oss-ac-forward-allow'] = "true"

url = bucket.sign_url('GET', object_name, 600, slash_safe=True, headers=headers, params=params)
print('Presigned URL:', url)

Step 2: Download using the URL, passing the same headers.

curl:

curl -X GET "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241113T093321Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI****************&x-oss-signature=ed5a******************************************************" \
  -H "Content-Type: text/plain; charset=utf8" \
  -o "myfile.txt"

Python:

import requests

def download_file(signed_url, file_path, headers=None):
    """
    Downloads an object using a presigned URL.

    :param signed_url: The presigned URL.
    :param file_path: Local path to save the downloaded object.
    :param headers: HTTP headers that were included when generating the URL.
    """
    if not headers:
        headers = {}

    try:
        response = requests.get(signed_url, headers=headers, stream=True)
        print(f"Status code: {response.status_code}")

        if response.status_code == 200:
            with open(file_path, 'wb') as file:
                for chunk in response.iter_content(chunk_size=8192):
                    if chunk:
                        file.write(chunk)
            print("Download completed!")
        else:
            print("Download failed.")
            print(response.text)
    except Exception as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    signed_url = "<signedUrl>"
    file_path = "/downloads/exampleobject.txt"

    headers = {
        "Content-Type": "text/plain; charset=utf-8",
    }

    download_file(signed_url, file_path, headers=headers)

Generate a presigned URL using a custom domain name

To generate the URL using a custom domain name bound to your bucket, set is_cname=True and pass the custom domain as the endpoint.

# -*- coding: utf-8 -*-
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider

auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
region = "cn-hangzhou"

# Set the custom domain name as the endpoint.
endpoint = 'http://static.example.com'

bucket = oss2.Bucket(auth, endpoint, "yourBucketName", region=region, is_cname=True)

object_name = 'exampledir/exampleobject.txt'

url = bucket.sign_url('GET', object_name, 600, slash_safe=True)
print('Presigned URL:', url)

Usage notes

  • Examples use the public endpoint for the China (Hangzhou) region (https://oss-cn-hangzhou.aliyuncs.com). If you access OSS from another Alibaba Cloud service in the same region, use the internal endpoint instead. For a full list of regions and endpoints, see Regions and endpoints.

  • The region parameter is required when using V4 signatures. It must match the region of your bucket's endpoint.

  • The maximum validity period for a V4 presigned URL is 7 days (604,800 seconds).

  • A presigned URL can be used multiple times until it expires. After expiry, generate a new URL.

  • To create an OSSClient instance using Security Token Service (STS) temporary credentials or a custom domain name, see Initialization (Python SDK V1).

  • For more information on V4 signatures, see Signature V4 (recommended).

Security considerations

Treat a presigned URL as a bearer token — anyone who has the URL can download the object until it expires. To reduce exposure:

  • Set the shortest expiration time that meets your use case.

  • Share URLs only with the intended recipient.

  • If a URL is compromised before it expires, there is no built-in way to revoke it. Generate a new URL with a different expiration if needed.

  • For permission setup, see Common examples of RAM policies.

What's next