Download an object by using a signed URL (Swift SDK)

Updated at:

OSS objects are private by default — only the object owner can access them. To grant temporary download access without exposing credentials, generate a signed URL for a GET request using the OSS Swift SDK. The URL embeds an expiration time and can be reused until it expires. After it expires, generate a new one.

How it works

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

  2. Share the signed URL with the intended recipient.

  3. The recipient makes a GET request to that URL — using curl, an HTTP client, or any language's HTTP library — to download the object.

  4. After the URL expires, generate a new one.

image

Prerequisites

Before you begin, make sure that you have:

  • An OSS bucket with the object you want to share

  • The oss:GetObject permission on that object (required to allow others to download via the signed URL — generating the URL itself requires no specific permissions)

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

For more information about permissions, see Common examples of RAM policies.

Usage notes

  • The code examples use the China (Hangzhou) region (cn-hangzhou) with a public endpoint. To access OSS from another Alibaba Cloud service in the same region, use an internal endpoint instead. For endpoint mappings by region, see Regions and endpoints.

  • Treat signed URLs as bearer tokens: anyone with the URL can download the object until it expires. Use short expiration times for sensitive content and share URLs only with intended recipients.

Step 1: Generate a signed URL (Swift SDK)

Call client.presign() to generate a signed URL for a GET request. The result contains the URL and any required signed headers.

import AlibabaCloudOSS
import Foundation

@main
struct Main {
    static func main() async {

        do {
            // Specify the region where the bucket is located. Example: China (Hangzhou) is cn-hangzhou.
            let region = "cn-hangzhou"
            // Optional. Specify the domain name used to access OSS. For the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com.
            let endpoint: String? = nil
            // Specify the bucket name.
            let bucket = "yourBucketName"
            // Specify the name of the object to download.
            let key = "yourObjectName"

            // Obtain access credentials from environment variables. Before running the sample code, make sure the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are set.
            let credentialsProvider = EnvironmentCredentialsProvider()

            // Configure the OSS client.
            let config = Configuration.default()
                .withRegion(region)
                .withCredentialsProvider(credentialsProvider)

            // Set the endpoint.
            if let endpoint = endpoint {
                config.withEndpoint(endpoint)
            }

            // Create an OSS client instance.
            let client = Client(config)

            // Generate the presigned URL.
            let result = try await client.presign(
                GetObjectRequest(
                    bucket: bucket,
                    key: key
                )
            )
            // Print the result.
            print("result:\n\(result)")

        } catch {
            print("error: \(error)")
        }
    }
}

Replace the following placeholders with your actual values:

PlaceholderDescriptionExample
yourBucketNameName of the bucket containing the objectmy-bucket
yourObjectNameFull path of the object to downloaddocs/report.pdf

The generated signed URL follows this structure:

https://<bucket>.oss-<region>.aliyuncs.com/<object>?x-oss-date=<timestamp>&x-oss-expires=<seconds>&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=<credential>&x-oss-signature=<signature>
ParameterDescription
x-oss-dateThe date and time when the signature was created (ISO 8601 format, UTC)
x-oss-expiresHow long the URL remains valid, in seconds, starting from x-oss-date
x-oss-signature-versionThe signing algorithm used to generate the URL
x-oss-credentialThe AccessKey ID and scope information used to calculate the signature
x-oss-signatureThe signature that verifies the URL was signed with the correct secret key

Step 2: Download the object using the signed URL

Pass the signed URL to any HTTP client to download the object. All examples below use the same URL structure — replace the example URL with the one generated in Step 1.

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******************************************************"

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) {
        // Specify the presigned URL that allows HTTP GET requests.
        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******************************************************";
        // Specify the local path to save the downloaded object.
        String savePath = "C:/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 = "C:/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. Server responded with code: ${response.statusCode}`);
    }
}).on('error', (err) => {
    console.error("Error during download:", err.message);
});

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 = "C:/downloads/myfile.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"No file to download. Server replied HTTP code: {response.status_code}")
except Exception as e:
    print("Error during download:", e)

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 := "C:/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

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 = "C:/downloads/myfile.txt"; // Specify the name of the downloaded object.

fetch(fileURL)
    .then(response => {
        if (!response.ok) {
            throw new Error(`Server replied HTTP code: ${response.status}`);
        }
        return response.blob(); // Convert the response to a blob.
    })
    .then(blob => {
        const link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = savePath;
        document.body.appendChild(link); // Ensure the link exists in the document.
        link.click(); // Trigger the download.
        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 {
        // Specify the presigned URL and the local path to save the object.
        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"; // Replace your_username with your username.

        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];

        // Keep the main thread running to complete the asynchronous request.
        [[NSRunLoop currentRunLoop] run];
    }
    return 0;
}

Background download (Swift)

For long-running downloads on iOS or macOS, use a background URLSession so the download continues even if the app moves to the background.

The presign() result includes both the signed URL and any required headers (result.signedHeaders). Apply both to the URLRequest before starting the background download task.

import AlibabaCloudOSS
import Foundation

@main
struct Main {
    static func main() async {

        do {
            // Specify the region where the bucket is located. Example: China (Hangzhou) is cn-hangzhou.
            let region = "cn-hangzhou"
            // Optional. Specify the domain name used to access OSS. For the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com.
            let endpoint: String? = nil
            // Specify the bucket name.
            let bucket = "yourBucketName"
            // Specify the name of the object to download.
            let key = "yourObjectName"

            // Obtain access credentials from environment variables.
            let credentialsProvider = EnvironmentCredentialsProvider()

            // Configure the OSS client.
            let config = Configuration.default()
                .withRegion(region)
                .withCredentialsProvider(credentialsProvider)

            if let endpoint = endpoint {
                config.withEndpoint(endpoint)
            }

            let client = Client(config)

            // Generate the presigned URL.
            let result = try await client.presign(
                GetObjectRequest(
                    bucket: bucket,
                    key: key
                )
            )

            // Build the URLRequest from the presigned result.
            var urlRequest = URLRequest(url: URL(string: result.url)!)
            urlRequest.httpMethod = result.method
            for (key, value) in result.signedHeaders ?? [:] {
                urlRequest.addValue(value, forHTTPHeaderField: key)
            }

            // Use a background URLSession so the download continues in the background.
            let sessionConfig = URLSessionConfiguration.background(withIdentifier: "background.session.id")
            let session = URLSession(configuration: sessionConfig,
                                     delegate: URLSessionDelegateImp(),
                                     delegateQueue: OperationQueue())
            session.downloadTask(with: urlRequest).resume()

        } catch {
            print("error: \(error)")
        }
    }
}

class URLSessionDelegateImp: NSObject, URLSessionTaskDelegate {
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: (any Error)?) {
        print("task complete")
        if let e = error {
            print("error: \(e)")
        }
    }
}

extension URLSessionDelegateImp: URLSessionDownloadDelegate {
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        print("url: \(location)")
    }
}

Troubleshooting

The URL expires before the configured expiration time.

A signed URL is valid only while the underlying credentials are valid. If the AccessKey credentials or Security Token Service (STS) token used to generate the URL expire before the configured URL expiration, the URL stops working at the credential expiration time. To avoid this, use long-lived AccessKey credentials to generate presigned URLs, or regenerate the URL before the credentials expire.

403 Forbidden error when accessing the URL.

The caller's account lacks the oss:GetObject permission on the target object. Verify that the RAM policy attached to the AccessKey used for generation includes oss:GetObject for the target bucket and object path. See Common examples of RAM policies.

SignatureDoesNotMatch error.

Common causes:

  • The system clock used to generate the URL drifted significantly from UTC. Synchronize your system clock with a Network Time Protocol (NTP) server.

  • The URL was modified after generation (for example, a query parameter was altered). Use the URL exactly as returned by client.presign().

  • The signed headers returned in result.signedHeaders were not included in the request. Apply all signed headers to the HTTP request before sending it, as shown in the background download example above.

What's next