All Products
Search
Document Center

Object Storage Service:Conditional download

Last Updated:Mar 20, 2026

OSS lets you attach conditions to a GetObject request so that the object is returned only when those conditions are met. Set conditions using HTTP headers based on the object's last modified time or ETag — if the condition fails, OSS returns an error instead of the object body. This prevents redundant downloads of unchanged objects and reduces bandwidth usage.

Conditional download is only available through OSS SDKs and the OSS API. The OSS console and ossutil do not support it.

Prerequisites

Before you begin, ensure that you have:

  • Objects uploaded to your OSS bucket

  • The oss:GetObject permission granted to the Resource Access Management (RAM) user performing the download

  • (Archive objects) The object restored, or real-time access of Archive objects enabled for the bucket

  • (Cold Archive or Deep Cold Archive objects) The object restored

Conditional request headers

Time values must be in GMT format — for example, Fri, 13 Nov 2015 14:47:53 GMT. All headers default to blank.

HeaderDownload proceeds (200 OK) when...Download blocked when...
If-Modified-SinceSpecified time is earlier than the object's last modified time (or the value is invalid)Specified time is the same as or later → 304 Not Modified
If-Unmodified-SinceSpecified time is the same as or later than the object's last modified timeSpecified time is earlier → 412 Precondition Failed
If-MatchSpecified ETag matches the object's ETagETags do not match → 412 Precondition Failed
If-None-MatchSpecified ETag does not match the object's ETagETags match → 304 Not Modified

Combining headers: If-Modified-Since and If-Unmodified-Since can be combined in the same request, as can If-Match and If-None-Match.

Use cases

  • App resource updates: Download an object only when its ETag changes or its last modified time is updated, minimizing data usage.

  • Data synchronization and incremental backup: Sync or back up only objects whose ETags or last modified times have changed, reducing bandwidth costs.

  • Collaborative workflows: Use ETags or last modified times to detect changes before syncing, reducing unnecessary data transfer.

Download objects with conditions

Use OSS SDKs

The following examples show how to set conditional headers in a GetObject call using OSS SDKs. For SDKs not listed here, see the SDK overview.

Java

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.GetObjectRequest;
import java.io.File;
import java.util.Date;

public class Demo {

    public static void main(String[] args) throws Exception {
        // In this example, the endpoint of the China (Hangzhou) region is used. Specify your actual endpoint.
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // Load credentials from environment variables OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET.
        EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
        String bucketName = "examplebucket";
        String objectName = "testfolder/exampleobject.txt";
        String pathName = "D:\\localpath\\examplefile.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();

        try {
            GetObjectRequest request = new GetObjectRequest(bucketName, objectName);
            // Object last modified at 13:27:04, September 26, 2023.
            // The condition is met because the specified time (Sep 25) is earlier than the last modified time (Sep 26).
            request.setModifiedSinceConstraint(new Date("Tue Sep 25 13:27:04 CST 2023"));

            ossClient.getObject(request, new File(pathName));
        } 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());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}

PHP

<?php

require_once __DIR__ . '/../vendor/autoload.php';

use AlibabaCloud\Oss\V2 as Oss;

$optsdesc = [
    "region" => ['help' => 'The region in which the bucket is located.', 'required' => True],
    "endpoint" => ['help' => 'The domain names that other services can use to access OSS.', 'required' => False],
    "bucket" => ['help' => 'The name of the bucket', 'required' => True],
    "key" => ['help' => 'The name of the object', 'required' => True],
];

$longopts = \array_map(function ($key) {
    return "$key:";
}, array_keys($optsdesc));

$options = getopt("", $longopts);

foreach ($optsdesc as $key => $value) {
    if ($value['required'] === True && empty($options[$key])) {
        $help = $value['help'];
        echo "Error: the following arguments are required: --$key, $help" . PHP_EOL;
        exit(1);
    }
}

$region = $options["region"];
$bucket = $options["bucket"];
$key = $options["key"];

// Load credentials from environment variables OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET.
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();

$cfg = Oss\Config::loadDefault();
$cfg->setCredentialsProvider($credentialsProvider);
$cfg->setRegion($region);
if (isset($options["endpoint"])) {
    $cfg->setEndpoint($options["endpoint"]);
}

$client = new Oss\Client($cfg);

// The If-Modified-Since condition is met when the specified UTC time is earlier than the object's last modified time.
$ifModifiedSince = "Sun, 21 Oct 2024 18:43:02 GMT";

// The If-Match condition is met when the specified ETag equals the object's ETag.
$etag = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";

$request = new Oss\Models\GetObjectRequest(
    bucket: $bucket,
    key: $key,
    ifModifiedSince: $ifModifiedSince,
    ifMatch: $etag
);

$result = $client->getObject($request);

printf(
    'status code: ' . $result->statusCode . PHP_EOL .
    'request id: ' . $result->requestId . PHP_EOL .
    'object content: ' . $result->body->getContents() . PHP_EOL
);

Node.js

const OSS = require('ali-oss');

const client = new OSS({
    region: 'yourRegion',
    // Load credentials from environment variables OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET.
    accessKeyId: process.env.OSS_ACCESS_KEY_ID,
    accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
    bucket: 'examplebucket'
});

async function main() {
    try {
        await client.put("exampleobject.txt", Buffer.from("contenttest"));

        // If-Modified-Since is earlier than the object's last modified time — download proceeds, returns 200.
        let result = await client.get("exampleobject.txt", {
            headers: {
                "If-Modified-Since": new Date("1970-01-01").toGMTString(),
            },
        });
        console.log(result.content.toString() === "contenttest"); // true
        console.log(result.res.status === 200);                   // true

        // If-Modified-Since is the same as or later than the last modified time — returns 304 Not Modified.
        result = await client.get("exampleobject.txt", {
            headers: {
                "If-Modified-Since": new Date().toGMTString(),
            },
        });
        console.log(result.content.toString() === "");  // true
        console.log(result.res.status === 304);         // true
    } catch (e) {
        console.log(e.code === "Not Modified");
    }
}

main();

Python

import argparse
import alibabacloud_oss_v2 as oss
from datetime import datetime, timezone

parser = argparse.ArgumentParser(description="get object to file 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)
parser.add_argument('--file_path', help='The path of the file to save the downloaded content.', required=True)

def main():
    args = parser.parse_args()

    # Load credentials from environment variables OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET.
    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)

    # Download the object only if it was modified after October 1, 2024, 12:00 UTC.
    if_modified_since = datetime(2024, 10, 1, 12, 0, 0, tzinfo=timezone.utc)

    # Download the object only if its ETag matches.
    etag = "\"DA5223EFCD7E0353BE08866700000000\""

    result = client.get_object_to_file(
        oss.GetObjectRequest(
            bucket=args.bucket,
            key=args.key,
            if_modified_since=if_modified_since,
            if_match=etag,
        ),
        args.file_path
    )

    print(f'status code: {result.status_code},'
          f' request id: {result.request_id},'
          f' etag: {result.etag},'
          f' last modified: {result.last_modified}')

if __name__ == "__main__":
    main()

Browser.js

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Document</title>
</head>
<body>
    <button id='upload'>Upload</button>
    <button id='download'>Download</button>
    <script type="text/javascript" src="https://gosspublic.alicdn.com/aliyun-oss-sdk-6.16.0.min.js"></script>
    <script type="text/javascript">
        const client = new OSS({
            region: 'yourRegion',
            authorizationV4: true,
            // Use temporary credentials obtained from STS.
            accessKeyId: 'yourAccessKeyId',
            accessKeySecret: 'yourAccessKeySecret',
            stsToken: 'yourSecurityToken',
            bucket: "examplebucket",
        });

        const download = document.getElementById('download');
        const upload = document.getElementById('upload');

        upload.addEventListener('click', () => {
            const file = new Blob(['examplecontent']);
            const fileName = 'exampledir/exampleobject.txt';
            client.put(fileName, file).then(r => console.log(r));
        });

        download.addEventListener('click', () => {
            client.get('exampledir/exampleobject.txt', {
                headers: {
                    // If-Modified-Since: download proceeds when the specified time is earlier than the last modified time.
                    // Returns 304 Not Modified when the specified time is the same as or later.
                    "If-Modified-Since": new Date("1970-01-01").toGMTString()

                    // If-Unmodified-Since: download proceeds when the specified time is the same as or later than the last modified time.
                    // Returns 412 Precondition Failed when the specified time is earlier.
                    //"If-Unmodified-Since": new Date(1970-01-01).toGMTString()

                    // If-Match: download proceeds when the specified ETag matches the object's ETag.
                    // Returns 412 Precondition Failed when the ETags do not match.
                    //"If-Match": '5B3C1A2E0563E1B002CC607C****'

                    // If-None-Match: download proceeds when the specified ETag does not match the object's ETag.
                    // Returns 304 Not Modified when the ETags match.
                    //"If-None-Match": '5B3C1A2E0563E1B002CC607C****'
                },
            }).then(r => {
                if (r.content.length > 0) {
                    const newBlob = new Blob([r.content], { type: r.res.headers['content-type'] });
                    const link = document.createElement('a');
                    link.href = window.URL.createObjectURL(newBlob);
                    link.download = 'foo.txt';
                    link.click();
                    window.URL.revokeObjectURL(link.href);
                } else {
                    console.log('Error code', r.res.status);
                    console.log('No objects matched the download conditions');
                }
            });
        });
    </script>
</body>
</html>

Android

String bucketName = "examplebucket";
String objectKey = "exampledir/exampleobject.txt";

Map<String, String> headers = new HashMap<>();
// If-Modified-Since: download proceeds when the specified time is earlier than the last modified time.
// Returns 304 Not Modified when the specified time is the same as or later.
headers.put(OSSHeaders.GET_OBJECT_IF_MODIFIED_SINCE, "Fri, 13 Nov 2015 14:47:53 GMT");
// If-Unmodified-Since: download proceeds when the specified time is the same as or later.
// Returns 412 Precondition Failed when the specified time is earlier.
// headers.put(OSSHeaders.GET_OBJECT_IF_UNMODIFIED_SINCE, "Fri, 13 Nov 2015 14:47:53 GMT");
// If-Match: download proceeds when the specified ETag matches.
// Returns 412 Precondition Failed when the ETags do not match.
// headers.put(OSSHeaders.GET_OBJECT_IF_MATCH, "5B3C1A2E0563E1B002CC607C*****");
// If-None-Match: download proceeds when the specified ETag does not match.
// Returns 304 Not Modified when the ETags match.
// headers.put(OSSHeaders.GET_OBJECT_IF_NONE_MATCH, "5B3C1A2E0563E1B002CC607C*****");

GetObjectRequest get = new GetObjectRequest(bucketName, objectKey);
get.setRequestHeaders(headers);

OSSAsyncTask task = oss.asyncGetObject(get, new OSSCompletedCallback<GetObjectRequest, GetObjectResult>() {
    @Override
    public void onSuccess(GetObjectRequest request, GetObjectResult result) {
        InputStream inputStream = result.getObjectContent();
        byte[] buffer = new byte[2048];
        int len;
        try {
            while ((len = inputStream.read(buffer)) != -1) {
                // Process the downloaded data.
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onFailure(GetObjectRequest request, ClientException clientException, ServiceException serviceException) {
        if (clientException != null) {
            clientException.printStackTrace();
        }
        if (serviceException != null) {
            Log.e("ErrorCode", serviceException.getErrorCode());
            Log.e("RequestId", serviceException.getRequestId());
            Log.e("HostId", serviceException.getHostId());
            Log.e("RawMessage", serviceException.getRawMessage());
        }
    }
});

Go

package main

import (
    "context"
    "flag"
    "log"
    "net/http"
    "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(&region, "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 {
        flag.PrintDefaults()
        log.Fatalf("invalid parameters, bucket name required")
    }
    if len(region) == 0 {
        flag.PrintDefaults()
        log.Fatalf("invalid parameters, region required")
    }
    if len(objectName) == 0 {
        flag.PrintDefaults()
        log.Fatalf("invalid parameters, object name required")
    }

    // Load credentials from environment variables OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET.
    cfg := oss.LoadDefaultConfig().
        WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
        WithRegion(region)

    client := oss.NewClient(cfg)

    localFile := "download.file"

    // Set the If-Modified-Since condition. The object is downloaded only if the specified time
    // is earlier than the object's last modified time.
    date := time.Date(2024, time.October, 21, 18, 43, 2, 0, time.UTC)

    // The If-Match condition is met when the specified ETag matches the object's ETag.
    etag := "\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\""

    getRequest := &oss.GetObjectRequest{
        Bucket:          oss.Ptr(bucketName),
        Key:             oss.Ptr(objectName),
        IfModifiedSince: oss.Ptr(date.Format(http.TimeFormat)),
        IfMatch:         oss.Ptr(etag),
    }

    result, err := client.GetObjectToFile(context.TODO(), getRequest, localFile)
    if err != nil {
        log.Fatalf("failed to get object to file %v", err)
    }

    log.Printf("get object to file result:%#v\n", result)
}

iOS

OSSGetObjectRequest *get = [OSSGetObjectRequest new];
// Specify the bucket name.
get.bucketName = @"examplebucket";
// Specify the object key. Do not include the bucket name.
get.objectKey = @"exampledir/exampleobject.txt";

NSMutableDictionary *headerFields = [NSMutableDictionary dictionary];
// If-Modified-Since: download proceeds when the specified time is earlier than the last modified time.
[headerFields setValue:@"Fri, 13 Oct 2021 14:47:53 GMT" forKey:@"If-Modified-Since"];
// If-Unmodified-Since: download proceeds when the specified time is the same as or later than the last modified time.
[headerFields setValue:[[NSDate new] oss_asStringValue] forKey:@"If-Unmodified-Since"];
// If-None-Match: download proceeds when the specified ETag does not match the object's ETag.
[headerFields setValue:@"5B3C1A2E0563E1B002CC607C****" forKey:@"If-None-Match"];
// If-Match: download proceeds when the specified ETag matches the object's ETag.
[headerFields setValue:@"fba9dede5f27731c9771645a3986****" forKey:@"If-Match"];
get.headerFields = headerFields;

[[[client getObject:get] continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
    if (!task.error) {
        NSLog(@"get object success!");
    } else {
        NSLog(@"get object error: %@", task.error);
    }
    return nil;
}] waitUntilFinished];

Use the OSS API directly

To call GetObject with conditional headers directly via the RESTful API, include the signature calculation in your code. For details, see GetObject.

What's next