All Products
Search
Document Center

Object Storage Service:Upload an object using a presigned URL (C# SDK V2)

Last Updated:Aug 06, 2025

By default, objects in an OSS bucket are private. Only the object owner has the permission to upload objects. You can use the OSS C# SDK to generate a presigned URL that allows other users to upload objects. When you generate a presigned URL, you can specify its expiration time to limit the access duration. The presigned URL can be used multiple times before it expires. If multiple upload operations are performed, the object may be overwritten. After the URL expires, you cannot use it for uploads. In this case, you must generate a new presigned URL.

Precautions

  • The sample code in this topic uses the China (Hangzhou) region, whose region ID is cn-hangzhou, as an example. The public endpoint is used by default. If you want to access OSS from other Alibaba Cloud services in the same region as OSS, use an internal endpoint. For more information about the regions and endpoints that OSS supports, see OSS regions and endpoints.

  • You do not need specific permissions to generate a presigned URL. However, to allow others to use the presigned URL for uploading an object, you must have the oss:PutObject permission. For more information, see Authorize a RAM user to access multiple directories in a bucket.

Process

The following figure shows how to use a presigned URL that allows HTTP PUT requests to upload an object to OSS.

image

Sample code

  1. The object owner generates a presigned URL for a PUT request.

    Important

    When you generate a presigned URL for a PUT request, if you specify request headers, you must include the same request headers when you use the presigned URL to initiate the PUT request. Otherwise, the request fails and reports a signature mismatch error.

    using System.Text;
    using OSS = AlibabaCloud.OSS.V2;  // Create an alias for the Alibaba Cloud OSS SDK to simplify subsequent use.
    
    var region = "cn-hangzhou";  // Required. Specify the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the region to cn-hangzhou.
    var endpoint = null as string;  // Optional. Specify the endpoint that is used to access OSS. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com.
    var bucket = "your bucket name";  // Required. The bucket name.
    var key = "your object key";  // Required. The name of the object after it is uploaded to OSS.
    
    // Load the default configurations of the OSS SDK. The configurations automatically read credential information (such as AccessKey) from environment variables.
    var cfg = OSS.Configuration.LoadDefault();
    // Explicitly configure the use of environment variables to obtain credentials for identity verification. Format: OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET.
    cfg.CredentialsProvider = new OSS.Credentials.EnvironmentVariableCredentialsProvider();
    // Set the region of the bucket in the configuration.
    cfg.Region = region;   
    // If an endpoint is specified, it overwrites the default endpoint. 
    if(endpoint != null) 
    {
        cfg.Endpoint = endpoint;
    } 
    
    // Create an OSS client instance using the configuration information.
    using var client = new OSS.Client(cfg); 
    // Call the Presign method to generate a presigned URL.
    var result = client.Presign(new OSS.Models.PutObjectRequest()
    {
        Bucket = bucket,
        Key = key,
    });
    
    // The content to upload.
    const string content = "hello oss!";
    // Use HttpClient to upload content using the presigned URL.
    using var hc = new HttpClient();
    var httpResult = await hc.PutAsync(result.Url, new ByteArrayContent(Encoding.UTF8.GetBytes(content)));
    
    Console.WriteLine("PutObject done");  // Print a message indicating that the operation is complete.
    Console.WriteLine($"StatusCode: {httpResult.StatusCode}");  // The HTTP status code of the response.
  2. Other users use the presigned URL for the PUT request to upload an object.

    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. 
            URL signedUrl = new URL("<signedUrl>");
    
            // Specify the full path of the local file that you want to upload. If the path of the local file is not specified, the local file is uploaded from the path of the project to which the sample program belongs. 
            String pathName = "C:\\Users\\demo.txt";
    
            try {
                HttpPut put = new HttpPut(signedUrl.toString());
                System.out.println(put);
                HttpEntity entity = new FileEntity(new File(pathName));
                put.setEntity(entity);
                httpClient = HttpClients.createDefault();
                response = httpClient.execute(put);
    
                System.out.println("Status code:"+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("The object is uploaded by using the library.");
                }
                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 {
    	// Open the local file.
    	file, err := os.Open(filePath)
    	if err != nil {
    		return fmt.Errorf("Unable to open the local file: %w", err)
    	}
    	defer file.Close()
    
    	// Create an HTTP client.
    	client := &http.Client{}
    
    	// Create a PUT request.
    	req, err := http.NewRequest("PUT", signedUrl, file)
    	if err != nil {
    		return fmt.Errorf("Failed to create the request: %w", err)
    	}
    
    	// Send the request.
    	resp, err := client.Do(req)
    	if err != nil {
    		return fmt.Errorf("Failed to send the request:: %w", err)
    	}
    	defer resp.Body.Close()
    
    	// Read the response.
    	body, err := io.ReadAll(resp.Body)
    	if err != nil {
    		return fmt.Errorf("Failed to read the request: %w", err)
    	}
    
    	fmt.Printf("Status code: %d\n", resp.StatusCode)
    	if resp.StatusCode == 200 {
    		fmt.Println("The object is uploaded by using the library.")
    	}
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// Replace <signedUrl> with the presigned URL. 
    	signedUrl := "<signedUrl>"
    
    	// Specify the full path of the local file that you want to upload. If the path of the local file is not specified, the local file is uploaded from the path of the project to which the sample program belongs. 
    	filePath := "C:\\Users\\demo.txt"
    
    	err := uploadFile(signedUrl, filePath)
    	if err != nil {
    		fmt.Println("An error occurred: ", err)
    	}
    }
    

    python

    import requests
    
    def upload_file(signed_url, file_path):
        try:
            # Open the local file that you want to upload.
            with open(file_path, 'rb') as file:
                # Send a PUT request to upload the local file.
                response = requests.put(signed_url, data=file)
         
            print(f"Status code: {response.status_code}")
            if response.status_code == 200:
                print("The object is uploaded by using the library.")
            print(response.text)
     
        except Exception as e:
            print(f"An error occurred: {e}")
    
    if __name__ == "__main__":
        # Replace <signedUrl> with the generated signed URL. 
        signed_url = "<signedUrl>"
        
        # Specify the full path of the local file. If the path of the local file is not specified, the local file is uploaded from the path of the project to which the sample program belongs. 
        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 {
            // Create a read stream.
            const fileStream = fs.createReadStream(filePath);
            
            // Send a PUT request to upload the local file.
            const response = await axios.put(signedUrl, fileStream, {
                headers: {
                    'Content-Type': 'application/octet-stream' // Specify the Content-Type parameter.
                }
            });
    
            console.log(`Status code: ${response.status}`);
            if (response.status === 200) {
                console.log("The object is uploaded by using the library.");
            }
            console.log(response.data);
        } catch (error) {
            console.error(`An error occurred: ${error.message}`);
        }
    }
    
    // Specify the main function.
    (async () => {
        // Replace <signedUrl> with the presigned URL. 
        const signedUrl = '<signedUrl>';
        
        // Specify the full path of the local file that you want to upload. If the path of the local file is not specified, the local file is uploaded from the path of the project to which the sample program belongs. 
        const filePath = 'C:\\Users\\demo.txt';
    
        await uploadFile(signedUrl, filePath);
    })();

    browser.js

    Important

    When uploading an object using Browser.js and a presigned URL, a 403 SignatureNotMatch error that indicates signature inconsistency may occur. This is typically caused by the browser's automatic inclusion of the Content-Type request header, which that was not specified when the presigned URL was generated. To prevent this error, ensure that the Content-Type header is specified when generating a presigned URL.

    <!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>
    
        <! -- Select File -->
        <input type="file" id="fileInput" />
        <button id="uploadButton">Upload File</button>
    
        <script>
            // Replace <signedUrl> with the presigned URL that was generated in 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);
                }
            });
    
            /**
             * Upload a file to OSS.
             * @param {File} file - The file to be uploaded.
             * @param {string} presignedUrl - The presigned URL.
             */
            const upload = async (file, presignedUrl) => {
                const response = await fetch(presignedUrl, {
                    method: 'PUT',
                    body: file,  // Upload the entire 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;
    
    // Specify the full path of the local file that you want to upload. If the path of the local file is not specified, the local file is uploaded from the path of the project to which the sample program belongs. 
    var filePath = "C:\\Users\\demo.txt";
    // Replace <signedUrl> with the presigned URL.
    var presignedUrl = "<signedUrl>";
    
    // Create an HTTP client and a local file stream.
    using var httpClient = new HttpClient(); 
    using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
    using var content = new StreamContent(fileStream);
                
    // Create a PUT request.
    var request = new HttpRequestMessage(HttpMethod.Put, presignedUrl);
    request.Content = content;
    
    // Send a request.
    var response = await httpClient.SendAsync(request);
    
    // Process the response.
    if (response.IsSuccessStatusCode)
    {
        Console.WriteLine($"Uploaded! Status Code: {response.StatusCode}");
        Console.WriteLine("Response Header:");
        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 Code: {response.StatusCode}");
        Console.WriteLine("Response content: " + 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) {
            // Specify the presigned URL.
            curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str());
    
            // Set the request method to PUT.
            curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    
            // Open the local file.
            FILE *file = fopen(filePath.c_str(), "rb");
            if (!file) {
                std::cerr << "Unable to open the file: " << filePath << std::endl;
                return;
            }
    
            // Query the size of the local file.
            fseek(file, 0, SEEK_END);
            long fileSize = ftell(file);
            fseek(file, 0, SEEK_SET);
    
            // Specify the size of the local file.
            curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
    
            // Specify the input file handle.
            curl_easy_setopt(curl, CURLOPT_READDATA, file);
    
            // Execute the request.
            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 << "Status code: " << httpCode << std::endl;
    
                if (httpCode == 200) {
                    std::cout << "The object is uploaded by using the network library." << std::endl;
                }
            }
    
            // Close the local file.
            fclose(file);
    
            // Clear the cURL handle.
            curl_easy_cleanup(curl);
        }
    
        curl_global_cleanup();
    }
    
    int main() {
        // Replace <signedUrl> with the presigned URL. 
        std::string signedUrl = "<signedUrl>";
    
        // Specify the full path of the local file that you want to upload. If the path of the local file is not specified, the local file is uploaded from the path of the project to which the sample program belongs. 
        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, "Status code: " + responseCode);
    
                    if (responseCode == 200) {
                        Log.d(TAG, "The object is uploaded by using the library.");
                    }
    
                    return "Object uploaded. Status code: " + 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. 
            String signedUrl = "<signedUrl>";
            // Specify the full path of the local file that you want to upload. If the path of the local file is not specified, the local file is uploaded from the path of the project to which the sample program belongs. 
            String filePath = "C:\\Users\\demo.txt";
            activity.uploadFile(signedUrl, filePath);
        }
    }
    

Scenarios

Use a presigned URL to upload an object with specified request headers and custom metadata

  1. The object owner generates a presigned URL for a PUT request with specified request headers and custom metadata.

    using System.Text;
    using OSS = AlibabaCloud.OSS.V2;  // Create an alias for the Alibaba Cloud OSS SDK to simplify subsequent use.
    using System.Globalization;
    using System.Net.Http.Headers;
    
    var region = "cn-hangzhou";  // Required. Specify the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the region to cn-hangzhou.
    var endpoint = null as string;  // Optional. Specify the endpoint that is used to access OSS. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com.
    var bucket = "your bucket name";  // Required. The bucket name.
    var key = "your object key";  // Required. The name of the object after it is uploaded to OSS.
    
    // Load the default configurations of the OSS SDK. The configurations automatically read credential information (such as AccessKey) from environment variables.
    var cfg = OSS.Configuration.LoadDefault();
    // Explicitly configure the use of environment variables to obtain credentials for identity verification. Format: OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET.
    cfg.CredentialsProvider = new OSS.Credentials.EnvironmentVariableCredentialsProvider();
    // Set the region of the bucket in the configuration.
    cfg.Region = region;   
    // If an endpoint is specified, it overwrites the default endpoint. 
    if(endpoint != null) 
    { 
        cfg.Endpoint = endpoint;
    } 
    
    // Create an OSS client instance using the configuration information.
    using var client = new OSS.Client(cfg); 
    // Call the Presign method to obtain the presigned upload URL.
    var result = client.Presign(new OSS.Models.PutObjectRequest()
        {
            Bucket = bucket,
            Key = key,
            StorageClass = "Standard",         // Set the storage class to Standard.
            ContentType = "text/plain; charset=utf8",    // Set the content type.
            Metadata = new Dictionary<string, string>() {
                { "key1", "value1" },    // Add custom metadata.
                { "key2", "value2" }
            }
        }
    );
    
    // Create an HTTP client instance.
    using var hc = new HttpClient();
    
    // Prepare the content to be uploaded.
    var content1 = "hello oss";
    var requestMessage = new HttpRequestMessage(HttpMethod.Put, new Uri(result.Url));
    requestMessage.Content = new ByteArrayContent(Encoding.UTF8.GetBytes(content1));
    
    // Add the HTTP headers included in the presigned URL.
    foreach (var item in result.SignedHeaders!)
    {
        switch (item.Key.ToLower())
        {
            case "content-disposition":
                // Set the content disposition.
                requestMessage.Content.Headers.ContentDisposition = ContentDispositionHeaderValue.Parse(item.Value);
                break;
            case "content-encoding":
                // Set the content encoding.
                requestMessage.Content.Headers.ContentEncoding.Add(item.Value);
                break;
            case "content-language":
                // Set the content language.
                requestMessage.Content.Headers.ContentLanguage.Add(item.Value);
                break;
            case "content-type":
                // Set the content type.
                requestMessage.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(item.Value);
                break;
            case "content-md5":
                // Set the MD5 hash of the content.
                requestMessage.Content.Headers.ContentMD5 = Convert.FromBase64String(item.Value);
                break;
            case "content-length":
                // Set the content length.
                requestMessage.Content.Headers.ContentLength = Convert.ToInt64(item.Value);
                break;
            case "expires":
                // Set the expiration time of the content.
                if (DateTime.TryParse(
                        item.Value,
                        CultureInfo.InvariantCulture,
                        DateTimeStyles.None,
                        out var expires
                    ))
                    requestMessage.Content.Headers.Expires = expires;
                break;
            default:
                // Add other general header information.
                requestMessage.Headers.Add(item.Key, item.Value);
                break;
        }
    }
    // Send an HTTP request and obtain the response.
    var httpResult = await hc.SendAsync(requestMessage);
    
    Console.WriteLine("PutObject done");  // Print a message indicating that the operation is complete.
    Console.WriteLine($"StatusCode: {httpResult.StatusCode}");  // The HTTP status code of the response.
  2. Other users use the presigned URL for the PUT request to upload an object.

    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;
    
            // Replace <signedUrl> with the presigned URL. 
            URL signedUrl = new URL("<signedUrl>");
    
            // Specify the full path of the local file that you want to upload. If the path of the local file is not specified, the local file is uploaded from the path of the project to which the sample program belongs. 
            String pathName = "C:\\Users\\demo.txt";
    
            // Specify request headers. Make sure that the values of the request headers are the same as those specified when the presigned URL was generated. 
            Map<String, String> headers = new HashMap<String, String>();
            // Specify the storage class of the object. 
            headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
            // Specify the content type. 
            headers.put(OSSHeaders.CONTENT_TYPE, "text/plain;charset=utf8");
    
            // Specify user metadata. Make sure that the user metadata here is the same as the user metadata specified when the presigned URL was generated. 
            Map<String, String> userMetadata = new HashMap<String, String>();
            userMetadata.put("key1","value1");
            userMetadata.put("key2","value2");
    
            try {
                HttpPut put = new HttpPut(signedUrl.toString());
                System.out.println(put);
                HttpEntity entity = new FileEntity(new File(pathName));
                put.setEntity(entity);
                // If you configure headers such as the user metadata and storage class when the presigned URL was generated, these headers must be sent to the server when the presigned URL is used to upload a file. If the headers that are sent to the server for the signature calculation are different from the headers specified when the presigned URL was generated, a signature error is reported. 
                for(Map.Entry header: headers.entrySet()){
                    put.addHeader(header.getKey().toString(),header.getValue().toString());
                }
                for(Map.Entry meta: userMetadata.entrySet()){
                    // If user metadata is specified, the SDK adds the "x-oss-meta-" prefix to the user metadata key. If you use other methods to upload a file, make sure that the "x-oss-meta-" prefix is also added to the user metadata key. 
                    put.addHeader("x-oss-meta-"+meta.getKey().toString(), meta.getValue().toString());
                }
    
                httpClient = HttpClients.createDefault();
    
                response = httpClient.execute(put);
    
                System.out.println("Status code of the upload: "+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("The object is uploaded by using the library.");
                }
                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 {
    	// Open the local file.
    	file, err := os.Open(filePath)
    	if err != nil {
    		return err
    	}
    	defer file.Close()
    
    	// Read the object content.
    	fileBytes, err := ioutil.ReadAll(file)
    	if err != nil {
    		return err
    	}
    
    	// Create a request.
    	req, err := http.NewRequest("PUT", signedUrl, bytes.NewBuffer(fileBytes))
    	if err != nil {
    		return err
    	}
    
    	// Specify request headers.
    	for key, value := range headers {
    		req.Header.Set(key, value)
    	}
    
    	// Specify user metadata.
    	for key, value := range metadata {
    		req.Header.Set(fmt.Sprintf("x-oss-meta-%s", key), value)
    	}
    
    	// Send a request.
    	client := &http.Client{}
    	resp, err := client.Do(req)
    	if err != nil {
    		return err
    	}
    	defer resp.Body.Close()
    
    	// Process the response.
    	fmt.Printf("Status code: %d\n", resp.StatusCode)
    	if resp.StatusCode == 200 {
    		fmt.Println("The object is uploaded by using the library.")
    	} else {
    		fmt.Println("Upload failed")
    	}
    	body, _ := ioutil.ReadAll(resp.Body)
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// Replace <signedUrl> with the presigned URL. 
    	signedUrl := "<signedUrl>"
    
    	// Specify the full path of the local file that you want to upload. If the path of the local file is not specified, the local file is uploaded from the path of the project to which the sample program belongs. 
    	filePath := "C:\\Users\\demo.txt"
    
    	// Specify request headers. Make sure that the values of the request headers are the same as those specified when the presigned URL was generated. 
    	headers := map[string]string{
    		"Content-Type": "text/plain;charset=utf8",
    		"x-oss-storage-class": "Standard",
    	}
    
    	// Specify user metadata. Make sure that the user metadata here is the same as the user metadata specified when the presigned URL was generated. 
    	metadata := map[string]string{
    		"key1": "value1",
    		"key2": "value2",
    	}
    
    	err := uploadFile(signedUrl, filePath, headers, metadata)
    	if err != nil {
    		fmt.Printf("An error occurred: %v\n", err)
    	}
    }
    

    Python

    import requests
    from requests.auth import HTTPBasicAuth
    import os
    
    def upload_file(signed_url, file_path, headers=None, metadata=None):
        """
        Use a presigned URL to upload an object to OSS. 
    
        :param signed_url: the presigned URL. 
        :param file_path: the full path of the local file that you want to upload. 
        :param headers: request headers. This parameter is optional. 
        :param metadata: user metadata. This parameter is optional. 
        :return: None
        """
        if not headers:
            headers = {}
        if not metadata:
            metadata = {}
    
        # Add the x-oss-meta- prefix to the metadata key.
        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"Status code: {response.status_code}")
                if response.status_code == 200:
                    print("The object is uploaded by using the library.")
                else:
                    print("Upload failed.")
                print(response.text)
        except Exception as e:
            print(f"An error occurred: {e}")
    
    if __name__ == "__main__":
        # Replace <signedUrl> with the generated signed URL. 
        signed_url = "<signedUrl>"
       
        # Specify the full path of the local file. By default, if you do not specify the full path of the local file, the local file is uploaded from the directory in which the script is stored. 
        file_path = "C:\\Users\\demo.txt"
    
        # Specify request headers. Make sure that the values of the request headers are the same as those specified when the presigned URL was generated. 
        headers = {
             "Content-Type": "text/plain;charset=utf8",
             "x-oss-storage-class": "Standard"
        }
    
        # Specify user metadata. Make sure that the user metadata is the same as the user metadata specified when the pesigned URL was generated. 
        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 {
            // Add the x-oss-meta- prefix to the metadata keys.
            for (const [key, value] of Object.entries(metadata)) {
                headers[`x-oss-meta-${key}`] = value;
            }
    
            // Create a readable stream.
            const fileStream = fs.createReadStream(filePath);
    
            // Send a PUT request.
            const response = await axios.put(signedUrl, fileStream, {
                headers: headers
            });
    
            console.log(`Status code: ${response.status}`);
            if (response.status === 200) {
                console.log("The object is uploaded by using the library.");
            } else {
                console.log("Upload failed.");
            }
            console.log(response.data);
        } catch (error) {
            console.error(`An error occurred: ${error.message}`);
        }
    }
    
    // Specify the main function.
    (async () => {
        // Replace <signedUrl> with the presigned URL. 
        const signedUrl = "<signedUrl>";
    
        // Specify the full path of the local file that you want to upload. By default, if you do not specify the full path of the local file, the local file is uploaded from the directory in which the script is stored. 
        const filePath = "C:\\Users\\demo.txt";
    
        // Specify request headers. Make sure that the values of the request headers are the same as those specified when the presigned URL was generated. 
        const headers = {
             "Content-Type": "text/plain;charset=utf8",
             "x-oss-storage-class": "Standard"
        };
    
        // Specify user metadata. Make sure that the user metadata here is the same as the user metadata specified when the presigned URL was generated. 
        const metadata = {
             "key1": "value1",
             "key2": "value2"
        };
    
        await uploadFile(signedUrl, filePath, headers, metadata);
    })();
    

    Browser.js

    Important

    When you use Browser.js code to upload an object based on a presigned URL, you may encounter a 403 error that indicates a signature inconsistency. This error arises from a signature verification failure, which occurs because the browser automatically adds the Content-Type request header, an element that was not specified when the presigned URL was generated. To resolve the error, you must specify the Content-Type header when you generate a presigned URL that is expected to be used in Browser.js code to upload data to OSS.

    <!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 <signedUrl> with the actual presigned URL.
            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) => {
                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;
    
    // Specify the full path of the local file that you want to upload. If the path of the local file is not specified, the local file is uploaded from the path of the project to which the sample program belongs. 
    var filePath = "C:\\Users\\demo.txt";
    // Replace <signedUrl> with the presigned URL.
    var presignedUrl = "<signedUrl>";
    
    // Create an HTTP client and a local file stream.
    using var httpClient = new HttpClient(); 
    using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
    using var content = new StreamContent(fileStream);
                
    // Create a PUT request.
    var request = new HttpRequestMessage(HttpMethod.Put, presignedUrl);
    request.Content = content;
    
    // If you configure headers such as the user metadata and storage class when the presigned URL was generated, these headers must be sent to the server when the presigned URL is used to upload a file.
    // Specify request headers. Make sure that the values of the request headers are the same as those specified when the presigned URL was generated.
    request.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain") { CharSet = "utf8" };  // Specify the content type.      
    // Specify user metadata. Make sure that the user metadata is the same as the user metadata specified when the pesigned URL was generated.
    request.Content.Headers.Add("x-oss-meta-key1", "value1");
    request.Content.Headers.Add("x-oss-meta-key2", "value2");
    // Specify the storage class of the object.
    request.Content.Headers.Add("x-oss-storage-class", "Standard");
    
    // Output the request header.
    Console.WriteLine("Request header:");
    foreach (var header in request.Content.Headers)
    {
        Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
    }
    
    // Send a request.
    var response = await httpClient.SendAsync(request);
    
    // Process the response.
    if (response.IsSuccessStatusCode)
    {
        Console.WriteLine($"Uploaded! Status Code: {response.StatusCode}");
        Console.WriteLine("Response Header:");
        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 Code: {response.StatusCode}");
        Console.WriteLine("Response Content: " + responseContent);
    }

    C++

    #include <iostream>
    #include <fstream>
    #include <curl/curl.h>
    #include <map>
    #include <string>
    
    // Specify the callback function that is used to process the HTTP response.
    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) {
            // Specify the presigned URL.
            curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str());
    
            // Set the request method to PUT.
            curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    
            // Open the local file.
            FILE* file = fopen(filePath.c_str(), "rb");
            if (!file) {
                std::cerr << "Unable to open the file: " << filePath << std::endl;
                return;
            }
    
            // Specify the size of the local file.
            fseek(file, 0, SEEK_END);
            long fileSize = ftell(file);
            rewind(file);
    
            // Configure a file read callback.
            curl_easy_setopt(curl, CURLOPT_READDATA, file);
            curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
    
            // Specify request headers.
            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);
    
            // Specify the callback function that is used to process the response.
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
    
            // Execute the request.
            res = curl_easy_perform(curl);
    
            // Check the response.
            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 << "Status code: " << responseCode << std::endl;
                if (responseCode == 200) {
                    std::cout << "The object is uploaded by using the network library." << std::endl;
                } else {
                    std::cout << "Upload failed." << std::endl;
                }
                std::cout << readBuffer << std::endl;
            }
    
            // Clear the cURL handle.
            fclose(file);
            curl_slist_free_all(chunk);
            curl_easy_cleanup(curl);
        }
    
        curl_global_cleanup();
    }
    
    int main() {
        // Replace <signedUrl> with the presigned URL. 
        std::string signedUrl = "<signedUrl>";
    
        // Specify the full path of the local file that you want to upload. If the path of the local file is not specified, the local file is uploaded from the path of the project to which the sample program belongs. 
        std::string filePath = "C:\\Users\\demo.txt";
    
        // Specify request headers. Make sure that the values of the request headers are the same as those specified when the presigned URL was generated. 
        std::map<std::string, std::string> headers = {
             {"Content-Type", "text/plain;charset=utf8"},
             {"x-oss-storage-class", "Standard"}
        };
    
        // Specify user metadata. Make sure that the user metadata here is the same as the user metadata specified when the presigned URL was generated. 
        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);
    
            // Replace <signedUrl> with the presigned URL. 
            String signedUrl = "<signedUrl>";
    
            // Specify the full path of the local file that you want to upload. If the path of the local file is not specified, the local file is uploaded from the path of the project to which the sample program belongs. 
            String pathName = "/storage/emulated/0/demo.txt";
    
            // Specify request headers. Make sure that the values of the request headers are the same as those specified when the presigned URL was generated. 
            Map<String, String> headers = new HashMap<>();
            headers.put("Content-Type", "text/plain;charset=utf8");
            headers.put("x-oss-storage-class", "Standard");
    
            // Specify user metadata. Make sure that the user metadata here is the same as the user metadata specified when the presigned URL was generated. 
            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);
    
                    // Specify request headers.
                    for (Entry<String, String> header : headers.entrySet()) {
                        connection.setRequestProperty(header.getKey(), header.getValue());
                    }
    
                    // Specify user metadata.
                    for (Entry<String, String> meta : userMetadata.entrySet()) {
                        connection.setRequestProperty("x-oss-meta-" + meta.getKey(), meta.getValue());
                    }
    
                    // Read the local file.
                    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();
    
                    // Obtain the response.
                    int responseCode = connection.getResponseCode();
                    Log.d(TAG, "Status code: " + responseCode);
                    if (responseCode == 200) {
                        Log.d(TAG, "The object is uploaded by using the library.");
                    } else {
                        Log.d(TAG, "Upload failed");
                    }
    
                    InputStream is = connection.getInputStream();
                    byte[] responseBuffer = new byte[1024];
                    StringBuilder responseStringBuilder = new StringBuilder();
                    while ((count = is.read(responseBuffer)) != -1) {
                        responseStringBuilder.append(new String(responseBuffer, 0, count));
                    }
                    Log.d(TAG, responseStringBuilder.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, "Object uploaded", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(SignUrlUploadActivity.this, "Upload failed", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }
    

Use a presigned URL to upload an object using multipart upload

To upload an object using a presigned URL and multipart upload, you must configure the part size and generate a presigned URL for each part. The following sample code provides an example:

using OSS = AlibabaCloud.OSS.V2;  // Create an alias for the Alibaba Cloud OSS SDK to simplify subsequent use.

var region = "cn-hangzhou";  // Required. Specify the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the region to cn-hangzhou.
var endpoint = null as string;  // Optional. Specify the endpoint that is used to access OSS. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com.
var bucket = "you bucket name";  // Required. The bucket name.
var key = "your object key";  // Required. The name of the object to be uploaded.
var partSize = 512*1024;  // Required. The size of each part for each upload. In this example, the part size is 512 KB.
var filePath = "filePath";  // Required. The path of the object to be uploaded.

// Load the default configurations of the OSS SDK. The configurations automatically read credential information (such as AccessKey) from environment variables.
var cfg = OSS.Configuration.LoadDefault();
// Explicitly configure the use of environment variables to obtain credentials for identity verification. Format: OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET.
cfg.CredentialsProvider = new OSS.Credentials.EnvironmentVariableCredentialsProvider();
// Set the region of the bucket in the configuration.
cfg.Region = region;   
// If an endpoint is specified, it overwrites the default endpoint. 
if(endpoint != null) 
{
    cfg.Endpoint = endpoint;
} 

// Create an OSS client instance using the configuration information.
using var client = new OSS.Client(cfg); 

// Initiate a multipart upload.
var initResult = await client.InitiateMultipartUploadAsync(new()
{
    Bucket = bucket,
    Key = key
});

// Open the file to be uploaded.
using var hc = new HttpClient();
using var file = File.OpenRead(filePath);
long fileSize = file.Length;
long partNumber = 1;

// Upload the file in parts.
for (long offset = 0; offset < fileSize; offset += partSize)
{
    // Calculate the size of the current part.
    var size = Math.Min(partSize, fileSize - offset);

    // Generate a presigned URL for direct upload from the client.
    var presignResult = client.Presign(new OSS.Models.UploadPartRequest
    {
        Bucket = bucket,
        Key = key,
        PartNumber = partNumber,
        UploadId = initResult.UploadId,
        Body = new OSS.IO.BoundedStream(file, offset, size)
    });

    // Use the presigned URL to send a PUT request to upload the part.
    var httpResult = await hc.PutAsync(
        presignResult.Url,
        new StreamContent(new OSS.IO.BoundedStream(file, offset, size))
    );

    // Check whether the upload is successful.
    if (!httpResult.IsSuccessStatusCode)
    {
        throw new Exception("upload part fail");
    }
    partNumber++;
}

// List all uploaded parts.
var uploadParts = new List<OSS.Models.UploadPart>();
var paginator = client.ListPartsPaginator(new ()
{
    Bucket = bucket,
    Key = key,
    UploadId = initResult.UploadId,
});

// Obtain all part information by page.
await foreach (var page in paginator.IterPageAsync())
{
    foreach (var part in page.Parts ?? [])
    {
        uploadParts.Add(new() { PartNumber = part.PartNumber, ETag = part.ETag });
    }
}

// Sort the parts by part number.
uploadParts.Sort((left, right) => { return (left.PartNumber > right.PartNumber) ? 1 : -1; });

// Complete the multipart upload.
var cmResult = await client.CompleteMultipartUploadAsync(new()
{
    Bucket = bucket,
    Key = key,
    UploadId = initResult.UploadId,
    CompleteMultipartUpload = new()
    {
        Parts = uploadParts
    }
});

// Print the upload result.
Console.WriteLine("MultipartUpload done");  // A message indicating that the operation is complete.
Console.WriteLine($"StatusCode: {cmResult.StatusCode}");  // The HTTP status code.
Console.WriteLine($"RequestId: {cmResult.RequestId}");  // The request ID, which is used by Alibaba Cloud for troubleshooting.
Console.WriteLine("Response Headers:");  // The response header information.
cmResult.Headers.ToList().ForEach(x => Console.WriteLine(x.Key + " : " + x.Value));  // Traverse and print all response headers.

FAQ

If I use a temporary signature to upload an object and the signature expires during the upload, does the upload fail?

The upload will succeed.

The signature is verified when the upload request is initiated. The upload can proceed if the URL is valid at that time. If you use a temporary token, the validity period of the presigned URL is the shorter of the token's validity period and the URL's specified expiration time.

If I do not set request headers and custom metadata when I generate a URL, do I need to configure them when I use the URL for an upload?

No, you do not.

Request headers and custom metadata are optional parameters. If you do not set request headers and custom metadata, you can remove the related code from the sample code.

References

For the complete sample code that is used to upload an object using a presigned URL, see PresignPutObject.cs.