All Products
Search
Document Center

Object Storage Service:Upload an object using a presigned URL (Java SDK V1)

Last Updated:Mar 20, 2026

OSS buckets are private by default — only the bucket owner can upload objects. A presigned URL lets you delegate upload access to a third party without sharing your credentials. Generate the URL with a PUT method and an expiration time, then share it. The third party can use it to upload an object directly to OSS until the URL expires.

Key behaviors:

  • The URL can be used multiple times before it expires. Each upload overwrites the previous object.

  • After the URL expires, uploads fail. Generate a new URL if needed.

  • The maximum validity period for a Signature Version 4 (V4) presigned URL is 7 days. If you generate the URL using Security Token Service (STS) temporary credentials, the URL expires when the STS token expires — even if you set a longer expiration time in your code.

Prerequisites

Before you begin, make sure you have:

No permission is required to generate the presigned URL itself. However, the third party's upload will fail unless the account that generated the URL has oss:PutObject permission.

How it works

Uploading an object with a presigned URL involves two parties:

  1. The object owner generates a presigned URL for a PUT request using the Java SDK and shares it with the uploader.

  2. The uploader sends an HTTP PUT request to the URL — no OSS credentials needed.

The following diagram shows the flow.

image

Usage notes

  • All examples use the public endpoint for the China (Hangzhou) region (https://oss-cn-hangzhou.aliyuncs.com). If you access OSS from other Alibaba Cloud services in the same region, use the internal endpoint instead. For supported regions and endpoints, see Regions and endpoints.

  • All examples create an OSSClient instance using an OSS endpoint. To create one using a custom domain or STS, see Configuration examples for common scenarios.

  • All examples use V4 presigned URLs. For background, see Signature Version 4 (recommended).

Upload an object using a presigned URL

Step 1: Generate a presigned URL (object owner)

Use the Java SDK to generate a presigned URL for an HTTP PUT request.

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 {
        // Endpoint for the China (Hangzhou) region. Replace with your actual endpoint.
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // Read access credentials from environment variables.
        EnvironmentVariableCredentialsProvider credentialsProvider =
            CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
        // Replace with your bucket name, for example, examplebucket.
        String bucketName = "examplebucket";
        // Replace with the full object path, for example, exampleobject.txt.
        // Do not include the bucket name in the object path.
        String objectName = "exampleobject.txt";
        // Replace with the region where your bucket is located, for example, cn-hangzhou.
        String region = "cn-hangzhou";

        // Build the OSSClient instance with Signature Version 4.
        // Call shutdown() when the client is no longer needed to release resources.
        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. This example sets it to 1 hour from now.
            Date expiration = new Date(new Date().getTime() + 3600 * 1000L);

            // Build the presigned URL request for an HTTP PUT.
            GeneratePresignedUrlRequest request =
                new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
            request.setExpiration(expiration);

            // Generate the presigned URL and print it.
            signedUrl = ossClient.generatePresignedUrl(request);
            System.out.println("Presigned URL for PUT: " + signedUrl);

        } catch (OSSException oe) {
            System.out.println("OSS error — the request reached OSS but was rejected.");
            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("Client error — could not communicate with OSS.");
            System.out.println("Error message: " + ce.getMessage());
        }
    }
}

Step 2: Upload a file using the presigned URL (uploader)

Share the presigned URL with the uploader. All examples below send an HTTP PUT request to the URL — no OSS credentials needed.

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;
import java.util.*;

public class SignUrlUpload {
    public static void main(String[] args) throws Throwable {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;

        // Replace <signedUrl> with the presigned URL from Step 1.
        URL signedUrl = new URL("<signedUrl>");

        // Replace with the full path to the local file to upload.
        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("HTTP status: " + response.getStatusLine().getStatusCode());
            if (response.getStatusLine().getStatusCode() == 200) {
                System.out.println("Upload successful.");
            }
            System.out.println(response.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.close();
            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, err := io.ReadAll(resp.Body)
    if err != nil {
        return fmt.Errorf("failed to read response: %w", err)
    }

    fmt.Printf("HTTP status: %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 from Step 1.
    signedUrl := "<signedUrl>"

    // Replace with the full path to the local file to upload.
    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"HTTP status: {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 from Step 1.
    signed_url = "<signedUrl>"

    # Replace with the full path to the local file to upload.
    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(`HTTP status: ${response.status}`);
        if (response.status === 200) {
            console.log('Upload successful.');
        }
        console.log(response.data);
    } catch (error) {
        console.error(`An error occurred: ${error.message}`);
    }
}

(async () => {
    // Replace <signedUrl> with the presigned URL from Step 1.
    const signedUrl = '<signedUrl>';

    // Replace with the full path to the local file to upload.
    const filePath = 'C:\\Users\\demo.txt';

    await uploadFile(signedUrl, filePath);
})();

Browser.js

Important

If you get a 403 signature mismatch error when uploading from a browser, it is usually because the browser automatically adds a Content-Type request header that was not included when you generated the presigned URL. To fix this, specify Content-Type when generating the URL (see Upload with request headers and user metadata).

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File upload example</title>
</head>
<body>
    <h1>File upload example</h1>

    <input type="file" id="fileInput" />
    <button id="uploadButton">Upload file</button>

    <script>
        // Replace with the presigned URL from 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>

C#

using System.Net.Http.Headers;

// Replace with the full path to the local file to upload.
var filePath = "C:\\Users\\demo.txt";
// Replace <signedUrl> with the presigned URL from Step 1.
var presignedUrl = "<signedUrl>";

using var httpClient = new HttpClient();
using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
using var content = new StreamContent(fileStream);

var request = new HttpRequestMessage(HttpMethod.Put, presignedUrl);
request.Content = content;

var response = await httpClient.SendAsync(request);

if (response.IsSuccessStatusCode)
{
    Console.WriteLine($"Upload successful. Status: {response.StatusCode}");
    Console.WriteLine("Response headers:");
    foreach (var header in response.Headers)
    {
        Console.WriteLine($"  {header.Key}: {string.Join(", ", header.Value)}");
    }
}
else
{
    string responseContent = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"Upload failed. Status: {response.StatusCode}");
    Console.WriteLine("Response: " + responseContent);
}

C++

#include <iostream>
#include <fstream>
#include <curl/curl.h>

void uploadFile(const std::string& signedUrl, const std::string& filePath) {
    CURL *curl;
    CURLcode res;

    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();

    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str());
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

        FILE *file = fopen(filePath.c_str(), "rb");
        if (!file) {
            std::cerr << "Unable to open file: " << filePath << std::endl;
            return;
        }

        fseek(file, 0, SEEK_END);
        long fileSize = ftell(file);
        fseek(file, 0, SEEK_SET);

        curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
        curl_easy_setopt(curl, CURLOPT_READDATA, file);

        res = curl_easy_perform(curl);

        if (res != CURLE_OK) {
            std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
        } else {
            long httpCode = 0;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
            std::cout << "HTTP status: " << httpCode << std::endl;
            if (httpCode == 200) {
                std::cout << "Upload successful." << std::endl;
            }
        }

        fclose(file);
        curl_easy_cleanup(curl);
    }

    curl_global_cleanup();
}

int main() {
    // Replace <signedUrl> with the presigned URL from Step 1.
    std::string signedUrl = "<signedUrl>";

    // Replace with the full path to the local file to upload.
    std::string filePath = "C:\\Users\\demo.txt";

    uploadFile(signedUrl, filePath);

    return 0;
}

Android

package com.example.signurlupload;

import android.os.AsyncTask;
import android.util.Log;

import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;

public class SignUrlUploadActivity {

    private static final String TAG = "SignUrlUploadActivity";

    public void uploadFile(String signedUrl, String filePath) {
        new UploadTask().execute(signedUrl, filePath);
    }

    private class UploadTask extends AsyncTask<String, Void, String> {

        @Override
        protected String doInBackground(String... params) {
            String signedUrl = params[0];
            String filePath = params[1];

            HttpURLConnection connection = null;
            DataOutputStream dos = null;
            FileInputStream fis = null;

            try {
                URL url = new URL(signedUrl);
                connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("PUT");
                connection.setDoOutput(true);
                connection.setRequestProperty("Content-Type", "application/octet-stream");

                fis = new FileInputStream(filePath);
                dos = new DataOutputStream(connection.getOutputStream());

                byte[] buffer = new byte[1024];
                int length;
                while ((length = fis.read(buffer)) != -1) {
                    dos.write(buffer, 0, length);
                }

                dos.flush();
                dos.close();
                fis.close();

                int responseCode = connection.getResponseCode();
                Log.d(TAG, "HTTP status: " + responseCode);
                if (responseCode == 200) {
                    Log.d(TAG, "Upload successful.");
                }

                return "Upload complete. Status: " + responseCode;

            } catch (IOException e) {
                e.printStackTrace();
                return "Upload failed: " + e.getMessage();
            } finally {
                if (connection != null) {
                    connection.disconnect();
                }
            }
        }

        @Override
        protected void onPostExecute(String result) {
            Log.d(TAG, result);
        }
    }

    public static void main(String[] args) {
        SignUrlUploadActivity activity = new SignUrlUploadActivity();
        // Replace <signedUrl> with the presigned URL from Step 1.
        String signedUrl = "<signedUrl>";
        // Replace with the full path to the local file to upload.
        String filePath = "C:\\Users\\demo.txt";
        activity.uploadFile(signedUrl, filePath);
    }
}

Other scenarios

Use a presigned URL to upload with request headers and user metadata

Include request headers (such as storage class and Content-Type) and user metadata in the presigned URL. The uploader must send the same headers when uploading — OSS validates them against the signed parameters and returns a signature error if they differ.

Step 1: Generate the presigned URL

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.*;
import java.util.Date;

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();

        // Define the request headers to sign.
        Map<String, String> headers = new HashMap<String, String>();
        headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
        headers.put(OSSHeaders.CONTENT_TYPE, "text/plain; charset=utf8");

        // Define the user metadata to sign.
        // The SDK automatically adds the x-oss-meta- prefix.
        Map<String, String> userMetadata = new HashMap<String, String>();
        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("Presigned URL for PUT: " + signedUrl);

        } catch (OSSException oe) {
            System.out.println("OSS error: " + oe.getErrorMessage());
            System.out.println("Error code: " + oe.getErrorCode());
            System.out.println("Request ID: " + oe.getRequestId());
        } catch (ClientException ce) {
            System.out.println("Client error: " + ce.getMessage());
        }
    }
}

Step 2: Upload the file with the matching headers

All examples below set the same headers that were signed in Step 1. Omitting or changing any signed header causes a signature error.

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******************************************************"
Java
import com.aliyun.oss.internal.OSSHeaders;
import com.aliyun.oss.model.StorageClass;
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;
import java.util.*;

public class SignUrlUpload {
    public static void main(String[] args) throws Throwable {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;

        URL signedUrl = new URL("<signedUrl>");
        String pathName = "C:\\Users\\demo.txt";

        // These headers must match exactly what was signed in Step 1.
        Map<String, String> headers = new HashMap<String, String>();
        headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
        headers.put(OSSHeaders.CONTENT_TYPE, "text/plain;charset=utf8");

        // These metadata entries must match exactly what was signed in Step 1.
        Map<String, String> userMetadata = new HashMap<String, String>();
        userMetadata.put("key1", "value1");
        userMetadata.put("key2", "value2");

        try {
            HttpPut put = new HttpPut(signedUrl.toString());
            HttpEntity entity = new FileEntity(new File(pathName));
            put.setEntity(entity);

            for (Map.Entry header : headers.entrySet()) {
                put.addHeader(header.getKey().toString(), header.getValue().toString());
            }
            for (Map.Entry meta : userMetadata.entrySet()) {
                // Add the x-oss-meta- prefix when setting user metadata directly via HTTP.
                put.addHeader("x-oss-meta-" + meta.getKey().toString(), meta.getValue().toString());
            }

            httpClient = HttpClients.createDefault();
            response = httpClient.execute(put);

            System.out.println("HTTP status: " + response.getStatusLine().getStatusCode());
            if (response.getStatusLine().getStatusCode() == 200) {
                System.out.println("Upload successful.");
            }
            System.out.println(response.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.close();
            httpClient.close();
        }
    }
}
Go
package main

import (
    "bytes"
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
)

func uploadFile(signedUrl string, filePath string, headers map[string]string, metadata map[string]string) error {
    file, err := os.Open(filePath)
    if err != nil {
        return err
    }
    defer file.Close()

    fileBytes, err := ioutil.ReadAll(file)
    if err != nil {
        return err
    }

    req, err := http.NewRequest("PUT", signedUrl, bytes.NewBuffer(fileBytes))
    if err != nil {
        return err
    }

    // Set request headers — must match what was signed in Step 1.
    for key, value := range headers {
        req.Header.Set(key, value)
    }
    // Set user metadata — must match what was signed in Step 1.
    for key, value := range metadata {
        req.Header.Set(fmt.Sprintf("x-oss-meta-%s", key), value)
    }

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    fmt.Printf("HTTP status: %d\n", resp.StatusCode)
    if resp.StatusCode == 200 {
        fmt.Println("Upload successful.")
    } else {
        fmt.Println("Upload failed.")
    }
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))

    return nil
}

func main() {
    signedUrl := "<signedUrl>"
    filePath := "C:\\Users\\demo.txt"

    headers := map[string]string{
        "Content-Type":        "text/plain;charset=utf8",
        "x-oss-storage-class": "Standard",
    }

    metadata := map[string]string{
        "key1": "value1",
        "key2": "value2",
    }

    if err := uploadFile(signedUrl, filePath, headers, metadata); err != nil {
        fmt.Printf("An error occurred: %v\n", err)
    }
}
Python
import requests

def upload_file(signed_url, file_path, headers=None, metadata=None):
    if not headers:
        headers = {}
    if not metadata:
        metadata = {}

    # Merge user metadata into headers with the x-oss-meta- prefix.
    for key, value in metadata.items():
        headers[f'x-oss-meta-{key}'] = value

    try:
        with open(file_path, 'rb') as file:
            response = requests.put(signed_url, data=file, headers=headers)
            print(f"HTTP status: {response.status_code}")
            if response.status_code == 200:
                print("Upload successful.")
            else:
                print("Upload failed.")
            print(response.text)
    except Exception as e:
        print(f"An error occurred: {e}")

if __name__ == "__main__":
    signed_url = "<signedUrl>"
    file_path = "C:\\Users\\demo.txt"

    # Must match what was signed in Step 1.
    headers = {
        "Content-Type": "text/plain;charset=utf8",
        "x-oss-storage-class": "Standard"
    }
    metadata = {
        "key1": "value1",
        "key2": "value2"
    }

    upload_file(signed_url, file_path, headers, metadata)
Node.js
const fs = require('fs');
const axios = require('axios');

async function uploadFile(signedUrl, filePath, headers = {}, metadata = {}) {
    try {
        // Merge user metadata into headers with the x-oss-meta- prefix.
        for (const [key, value] of Object.entries(metadata)) {
            headers[`x-oss-meta-${key}`] = value;
        }

        const fileStream = fs.createReadStream(filePath);

        const response = await axios.put(signedUrl, fileStream, { headers });

        console.log(`HTTP status: ${response.status}`);
        if (response.status === 200) {
            console.log('Upload successful.');
        } else {
            console.log('Upload failed.');
        }
        console.log(response.data);
    } catch (error) {
        console.error(`An error occurred: ${error.message}`);
    }
}

(async () => {
    const signedUrl = '<signedUrl>';
    const filePath = 'C:\\Users\\demo.txt';

    // Must match what was signed in Step 1.
    const headers = {
        'Content-Type': 'text/plain;charset=utf8',
        'x-oss-storage-class': 'Standard'
    };
    const metadata = {
        'key1': 'value1',
        'key2': 'value2'
    };

    await uploadFile(signedUrl, filePath, headers, metadata);
})();
Browser.js
Important

If you get a 403 signature mismatch error when uploading from a browser, it is usually because the browser automatically adds a Content-Type request header that was not included when you generated the presigned URL. To fix this, specify Content-Type when generating the URL (as shown in Step 1 above).

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File upload example (Java SDK)</title>
</head>
<body>
    <h1>File upload example (Java SDK)</h1>

    <input type="file" id="fileInput" />
    <button id="uploadButton">Upload file</button>

    <script>
        // Replace with the presigned URL from Step 1.
        const signedUrl = "<signedUrl>";

        document.getElementById('uploadButton').addEventListener('click', async () => {
            const fileInput = document.getElementById('fileInput');
            const file = fileInput.files[0];

            if (file) {
                try {
                    await upload(file, signedUrl);
                } catch (error) {
                    console.error('Error during upload:', error);
                    alert('Upload failed: ' + error.message);
                }
            } else {
                alert('Please select a file to upload.');
            }
        });

        const upload = async (file, presignedUrl) => {
            // These headers must match what was signed in Step 1.
            const headers = {
                'Content-Type': 'text/plain;charset=utf8',
                'x-oss-storage-class': 'Standard',
                'x-oss-meta-key1': 'value1',
                'x-oss-meta-key2': 'value2'
            };

            const response = await fetch(presignedUrl, {
                method: 'PUT',
                headers: headers,
                body: file
            });

            if (!response.ok) {
                throw new Error(`Upload failed, status: ${response.status}`);
            }

            alert('File uploaded successfully.');
            console.log('File uploaded successfully.');
        };
    </script>
</body>
</html>
C#
using System.Net.Http.Headers;

var filePath = "C:\\Users\\demo.txt";
var presignedUrl = "<signedUrl>";

using var httpClient = new HttpClient();
using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
using var content = new StreamContent(fileStream);

var request = new HttpRequestMessage(HttpMethod.Put, presignedUrl);
request.Content = content;

// These headers must match what was signed in Step 1.
request.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain") { CharSet = "utf8" };
request.Content.Headers.Add("x-oss-meta-key1", "value1");
request.Content.Headers.Add("x-oss-meta-key2", "value2");
request.Content.Headers.Add("x-oss-storage-class", "Standard");

Console.WriteLine("Request headers:");
foreach (var header in request.Content.Headers)
{
    Console.WriteLine($"  {header.Key}: {string.Join(", ", header.Value)}");
}

var response = await httpClient.SendAsync(request);

if (response.IsSuccessStatusCode)
{
    Console.WriteLine($"Upload successful. Status: {response.StatusCode}");
}
else
{
    string responseContent = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"Upload failed. Status: {response.StatusCode}");
    Console.WriteLine("Response: " + responseContent);
}
C++
#include <iostream>
#include <fstream>
#include <curl/curl.h>
#include <map>
#include <string>

size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) {
    size_t totalSize = size * nmemb;
    output->append((char*)contents, totalSize);
    return totalSize;
}

void uploadFile(const std::string& signedUrl, const std::string& filePath,
                const std::map<std::string, std::string>& headers,
                const std::map<std::string, std::string>& metadata) {
    CURL* curl;
    CURLcode res;
    std::string readBuffer;

    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();

    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str());
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

        FILE* file = fopen(filePath.c_str(), "rb");
        if (!file) {
            std::cerr << "Unable to open file: " << filePath << std::endl;
            return;
        }

        fseek(file, 0, SEEK_END);
        long fileSize = ftell(file);
        rewind(file);

        curl_easy_setopt(curl, CURLOPT_READDATA, file);
        curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);

        // Build the header list — must match what was signed in Step 1.
        struct curl_slist* chunk = nullptr;
        for (const auto& header : headers) {
            std::string headerStr = header.first + ": " + header.second;
            chunk = curl_slist_append(chunk, headerStr.c_str());
        }
        for (const auto& meta : metadata) {
            std::string metaStr = "x-oss-meta-" + meta.first + ": " + meta.second;
            chunk = curl_slist_append(chunk, metaStr.c_str());
        }
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);

        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);

        res = curl_easy_perform(curl);

        if (res != CURLE_OK) {
            std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
        } else {
            long responseCode;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
            std::cout << "HTTP status: " << responseCode << std::endl;
            if (responseCode == 200) {
                std::cout << "Upload successful." << std::endl;
            } else {
                std::cout << "Upload failed." << std::endl;
            }
            std::cout << readBuffer << std::endl;
        }

        fclose(file);
        curl_slist_free_all(chunk);
        curl_easy_cleanup(curl);
    }

    curl_global_cleanup();
}

int main() {
    std::string signedUrl = "<signedUrl>";
    std::string filePath = "C:\\Users\\demo.txt";

    // Must match what was signed in Step 1.
    std::map<std::string, std::string> headers = {
        {"Content-Type", "text/plain;charset=utf8"},
        {"x-oss-storage-class", "Standard"}
    };
    std::map<std::string, std::string> metadata = {
        {"key1", "value1"},
        {"key2", "value2"}
    };

    uploadFile(signedUrl, filePath, headers, metadata);

    return 0;
}
Android
import android.os.AsyncTask;
import android.util.Log;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

public class SignUrlUploadActivity extends AppCompatActivity {

    private static final String TAG = "SignUrlUploadActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String signedUrl = "<signedUrl>";
        String pathName = "/storage/emulated/0/demo.txt";

        // Must match what was signed in Step 1.
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "text/plain;charset=utf8");
        headers.put("x-oss-storage-class", "Standard");

        Map<String, String> userMetadata = new HashMap<>();
        userMetadata.put("key1", "value1");
        userMetadata.put("key2", "value2");

        new UploadTask().execute(signedUrl, pathName, headers, userMetadata);
    }

    private class UploadTask extends AsyncTask<Object, Void, Integer> {
        @Override
        protected Integer doInBackground(Object... params) {
            String signedUrl = (String) params[0];
            String pathName = (String) params[1];
            Map<String, String> headers = (Map<String, String>) params[2];
            Map<String, String> userMetadata = (Map<String, String>) params[3];

            try {
                URL url = new URL(signedUrl);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("PUT");
                connection.setDoOutput(true);
                connection.setUseCaches(false);

                for (Entry<String, String> header : headers.entrySet()) {
                    connection.setRequestProperty(header.getKey(), header.getValue());
                }
                for (Entry<String, String> meta : userMetadata.entrySet()) {
                    connection.setRequestProperty("x-oss-meta-" + meta.getKey(), meta.getValue());
                }

                File file = new File(pathName);
                FileInputStream fileInputStream = new FileInputStream(file);
                DataOutputStream dos = new DataOutputStream(connection.getOutputStream());

                byte[] buffer = new byte[1024];
                int count;
                while ((count = fileInputStream.read(buffer)) != -1) {
                    dos.write(buffer, 0, count);
                }

                fileInputStream.close();
                dos.flush();
                dos.close();

                int responseCode = connection.getResponseCode();
                Log.d(TAG, "HTTP status: " + responseCode);
                if (responseCode == 200) {
                    Log.d(TAG, "Upload successful.");
                } else {
                    Log.d(TAG, "Upload failed.");
                }

                InputStream is = connection.getInputStream();
                byte[] responseBuffer = new byte[1024];
                StringBuilder sb = new StringBuilder();
                while ((count = is.read(responseBuffer)) != -1) {
                    sb.append(new String(responseBuffer, 0, count));
                }
                Log.d(TAG, sb.toString());

                return responseCode;
            } catch (IOException e) {
                e.printStackTrace();
                return -1;
            }
        }

        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);
            if (result == 200) {
                Toast.makeText(SignUrlUploadActivity.this, "Upload successful.", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(SignUrlUploadActivity.this, "Upload failed.", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

Use a presigned URL for multipart upload

For large files, generate a separate presigned URL for each part. Each part URL includes the partNumber and uploadId query parameters. After all parts are uploaded, merge them using completeMultipartUpload.

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.common.utils.CRC64;
import com.aliyun.oss.internal.Mimetypes;
import com.aliyun.oss.model.*;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.Date;

public class MultipartUrl {
    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";
        // Replace with the full path to the local file to upload.
        String pathName = "D:\\localpath\\examplefile.txt";
        // Expiration time for each part's presigned URL, in milliseconds.
        long expireTime = 3600 * 1000L;
        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();

        // Initialize the multipart upload.
        InitiateMultipartUploadRequest initRequest =
            new InitiateMultipartUploadRequest(bucketName, objectName);
        ObjectMetadata metadata = new ObjectMetadata();
        // Set Content-Type based on the file extension. Defaults to application/octet-stream.
        if (metadata.getContentType() == null) {
            metadata.setContentType(
                Mimetypes.getInstance().getMimetype(new File(pathName), objectName));
        }
        initRequest.setObjectMetadata(metadata);

        InitiateMultipartUploadResult upResult = ossClient.initiateMultipartUpload(initRequest);
        // The upload ID identifies this multipart upload session.
        // Use it to cancel, query, or complete the upload.
        String uploadId = upResult.getUploadId();

        List<PartETag> partETags = new ArrayList<PartETag>();
        long partSize = 1 * 100 * 1024L;   // 100 KB per part

        File sampleFile = new File(pathName);
        long fileLength = sampleFile.length();
        int partCount = (int) (fileLength / partSize);
        if (fileLength % partSize != 0) {
            partCount++;
        }

        Map<String, String> headers = new HashMap<String, String>();
        // Uncomment to set Content-Type for each part:
        // headers.put(OSSHeaders.CONTENT_TYPE, "text/plain");

        // Generate a presigned URL for each part and upload it sequentially.
        for (int i = 0; i < partCount; i++) {
            long startPos = i * partSize;
            long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;

            String signUrl = getSignUrl(ossClient, bucketName, objectName,
                HttpMethod.PUT, expireTime, i + 1, uploadId, headers);

            putObjectWithHttp(signUrl, pathName, startPos, curPartSize, headers);
        }

        // List the uploaded parts (needed if merging from a different process).
        ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, objectName, uploadId);
        PartListing partListing = ossClient.listParts(listPartsRequest);

        for (PartSummary part : partListing.getParts()) {
            partETags.add(new PartETag(part.getPartNumber(), part.getETag()));
        }

        // Merge the parts to complete the upload.
        CompleteMultipartUploadRequest completeRequest =
            new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
        CompleteMultipartUploadResult completeResult =
            ossClient.completeMultipartUpload(completeRequest);
        System.out.println("Multipart upload complete.");

        // Verify file integrity using CRC-64.
        CRC64 crc = new CRC64();
        InputStream inStream = new FileInputStream(pathName);
        byte[] bytes = new byte[1024];
        int cnt;
        while ((cnt = inStream.read(bytes)) != -1) {
            crc.update(bytes, 0, cnt);
        }

        if (crc.getValue() == completeResult.getServerCRC()) {
            System.out.println("File integrity verified.");
        } else {
            System.out.println("File integrity check failed. Handle the exception.");
        }
    }

    public static void putObjectWithHttp(String signedUrl, String pathName,
                                          long startPos, long partSize,
                                          Map<String, String> headers) throws IOException {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;
        try {
            HttpPut put = new HttpPut(signedUrl);

            FileInputStream inStream = new FileInputStream(pathName);
            inStream.skip(startPos);
            InputStreamEntity entity = new InputStreamEntity(inStream, partSize);
            BufferedHttpEntity byteArrayEntity = new BufferedHttpEntity(entity);
            put.setEntity(byteArrayEntity);

            for (Map.Entry header : headers.entrySet()) {
                put.addHeader(header.getKey().toString(), header.getValue().toString());
            }

            // Retry up to 3 times on transient failures.
            httpClient = HttpClients.custom()
                .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true))
                .build();

            response = httpClient.execute(put);

            System.out.println("Part upload status: " + response.getStatusLine().getStatusCode());
            if (response.getStatusLine().getStatusCode() == 200) {
                System.out.println("Part uploaded successfully.");
            }
            System.out.println(response.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (response != null) response.close();
            if (httpClient != null) httpClient.close();
        }
    }

    public static String getSignUrl(OSS ossClient, String bucketName, String objectName,
                                     HttpMethod method, long expireTime, int partNum,
                                     String uploadId, Map<String, String> headers) {
        Date expiration = new Date(new Date().getTime() + expireTime);

        GeneratePresignedUrlRequest request =
            new GeneratePresignedUrlRequest(bucketName, objectName, method);
        request.setExpiration(expiration);
        request.setHeaders(headers);
        // Embed the part number and upload ID in the URL as query parameters.
        request.addQueryParameter("partNumber", String.valueOf(partNum));
        request.addQueryParameter("uploadId", uploadId);

        URL signedUrl = ossClient.generatePresignedUrl(request);
        System.out.println("Part presigned URL: " + signedUrl);
        return signedUrl.toString();
    }
}

Use a presigned URL with upload callback

To trigger a server-side callback after a successful upload, include the callback parameters in the presigned URL. The callback body and variables are Base64-encoded and passed as request headers.

Step 1: Generate the presigned URL with callback parameters

import com.aliyun.oss.*;
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.internal.OSSHeaders;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;

import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;

public class OssPresignExample {
    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();

        URL signedUrl = null;
        try {
            // Build the callback body. Replace the URL and variables with your values.
            String callbackUrl = "http://www.example.com/callback";
            String callbackBody = "{\"callbackUrl\":\"" + callbackUrl
                + "\",\"callbackBody\":\"bucket=${bucket}&object=${object}"
                + "&my_var_1=${x:var1}&my_var_2=${x:var2}\"}";
            String callbackBase64 = Base64.getEncoder().encodeToString(callbackBody.getBytes());

            String callbackVarJson = "{\"x:var1\":\"value1\",\"x:var2\":\"value2\"}";
            String callbackVarBase64 = Base64.getEncoder().encodeToString(callbackVarJson.getBytes());

            // Add the callback headers to the signed request.
            Map<String, String> headers = new HashMap<String, String>();
            headers.put(OSSHeaders.OSS_HEADER_CALLBACK, callbackBase64);
            headers.put(OSSHeaders.OSS_HEADER_CALLBACK_VAR, callbackVarBase64);

            Date expiration = new Date(new Date().getTime() + 3600 * 1000);

            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
            dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
            String expirationStr = dateFormat.format(expiration);

            GeneratePresignedUrlRequest request =
                new GeneratePresignedUrlRequest(bucketName, objectName);
            request.setMethod(HttpMethod.PUT);
            request.setExpiration(expiration);
            request.setHeaders(headers);

            System.out.println("callback:     " + callbackBase64);
            System.out.println("callback-var: " + callbackVarBase64);

            URL url = ossClient.generatePresignedUrl(request);
            System.out.println("method:     PUT");
            System.out.println("expiration: " + expirationStr);
            System.out.println("url:        " + url);

        } catch (OSSException oe) {
            System.out.println("OSS error: " + oe.getErrorMessage());
            System.out.println("Error code: " + oe.getErrorCode());
            System.out.println("Request ID: " + oe.getRequestId());
        } catch (ClientException ce) {
            System.out.println("Client error: " + ce.getMessage());
        }
    }
}

Step 2: Upload the file with the callback headers

All examples below pass the x-oss-callback and x-oss-callback-var headers, which must match the values signed in Step 1.

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******************************************************"
Python
import requests

def upload_file(signed_url, file_path, headers=None):
    if not headers:
        headers = {}

    try:
        with open(file_path, 'rb') as file:
            response = requests.put(signed_url, data=file, headers=headers)
            print(f"HTTP status: {response.status_code}")
            if response.status_code == 200:
                print("Upload successful.")
            else:
                print("Upload failed.")
            print(response.text)
    except Exception as e:
        print(f"An error occurred: {e}")

if __name__ == "__main__":
    signed_url = "<signedUrl>"
    file_path = "C:\\Users\\demo.txt"

    headers = {
        "x-oss-callback": "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9",
        "x-oss-callback-var": "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==",
    }

    upload_file(signed_url, file_path, headers)
Go
package main

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

func uploadFile(signedUrl string, filePath string, headers map[string]string) error {
    file, err := os.Open(filePath)
    if err != nil {
        return err
    }
    defer file.Close()

    fileBytes, err := io.ReadAll(file)
    if err != nil {
        return err
    }

    req, err := http.NewRequest("PUT", signedUrl, bytes.NewBuffer(fileBytes))
    if err != nil {
        return err
    }

    for key, value := range headers {
        req.Header.Add(key, value)
    }

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    fmt.Printf("HTTP status: %d\n", resp.StatusCode)
    if resp.StatusCode == 200 {
        fmt.Println("Upload successful.")
    } else {
        fmt.Println("Upload failed.")
    }
    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))

    return nil
}

func main() {
    signedUrl := "<signedUrl>"
    filePath := "C:\\Users\\demo.txt"

    headers := map[string]string{
        "x-oss-callback":     "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9",
        "x-oss-callback-var": "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==",
    }

    if err := uploadFile(signedUrl, filePath, headers); err != nil {
        fmt.Printf("An error occurred: %v\n", err)
    }
}
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;
import java.util.*;

public class SignUrlUpload {
    public static void main(String[] args) throws Throwable {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;

        URL signedUrl = new URL("<signedUrl>");
        String pathName = "C:\\Users\\demo.txt";

        // These headers must match what was signed in Step 1.
        Map<String, String> headers = new HashMap<String, String>();
        headers.put("x-oss-callback",
            "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9");
        headers.put("x-oss-callback-var",
            "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==");

        try {
            HttpPut put = new HttpPut(signedUrl.toString());
            HttpEntity entity = new FileEntity(new File(pathName));
            put.setEntity(entity);

            for (Map.Entry header : headers.entrySet()) {
                put.addHeader(header.getKey().toString(), header.getValue().toString());
            }

            httpClient = HttpClients.createDefault();
            response = httpClient.execute(put);

            System.out.println("HTTP status: " + response.getStatusLine().getStatusCode());
            if (response.getStatusLine().getStatusCode() == 200) {
                System.out.println("Upload successful.");
            }
            System.out.println(response.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.close();
            httpClient.close();
        }
    }
}
PHP
<?php

function uploadFile($signedUrl, $filePath, $headers = []) {
    if (!file_exists($filePath)) {
        echo "File not found: $filePath\n";
        return;
    }

    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $signedUrl);
    curl_setopt($ch, CURLOPT_PUT, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_INFILE, fopen($filePath, 'rb'));
    curl_setopt($ch, CURLOPT_INFILESIZE, filesize($filePath));
    curl_setopt($ch, CURLOPT_HTTPHEADER, array_map(function($key, $value) {
        return "$key: $value";
    }, array_keys($headers), $headers));

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    echo "HTTP status: $httpCode\n";
    if ($httpCode == 200) {
        echo "Upload successful.\n";
    } else {
        echo "Upload failed.\n";
    }
    echo $response . "\n";
}

$signedUrl = "<signedUrl>";
$filePath = "C:\\Users\\demo.txt";

$headers = [
    "x-oss-callback" => "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9",
    "x-oss-callback-var" => "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==",
];

uploadFile($signedUrl, $filePath, $headers);

?>

FAQ

If the presigned URL expires mid-upload, does the upload fail?

No. The upload is successful as long as it is within the URL's validity period. The effective validity period is the shorter of the presigned URL expiration time and the STS token validity period (if the URL was generated using STS temporary credentials).

Do I need to set request headers and user metadata when uploading if I didn't set them when generating the URL?

No. Request headers and user metadata are optional. If you didn't include them when generating the URL, omit the related code from your upload request.

What's next

  • For the complete sample code for generating a V4 presigned URL, see the GitHub example.

  • For the GeneratePresignedUrlRequest API reference, see the Javadoc.