Todos os produtos
Search
Central de documentação

Object Storage Service:Use a presigned URL to upload a file

Última atualização: Jul 03, 2026

Por padrão, a lista de controle de acesso (ACL) de um objeto em um bucket do Object Storage Service (oss) é privada. Para permitir que outros usuários façam upload de objetos sem compartilhar suas credenciais, gere uma url pré-assinada com o oss SDK for Go e compartilhe-a com eles. A url incorpora sua assinatura, eliminando a necessidade de cabeçalhos de autenticação adicionais durante o upload.

Defina um período de validade ao gerar a url para controlar por quanto tempo a janela de upload permanece aberta. Durante esse período, você pode reutilizar a url para múltiplos uploads. Após a expiração, gere novamente a url para restaurar o acesso.

Como funciona

Presigned URL upload flow

  1. O proprietário do objeto gera uma url pré-assinada usando o oss SDK for Go.

  2. O proprietário do objeto compartilha a url com o usuário.

  3. O usuário envia uma solicitação HTTP PUT para a url para fazer upload do arquivo — sem necessidade de credenciais do oss.

Pré-requisitos

Antes de começar, verifique se você tem:

Nenhuma permissão específica é necessária para gerar uma url pré-assinada. A permissão oss:PutObject é exigida apenas para que a url permita uploads.

Gerar uma url pré-assinada

Todos os exemplos utilizam o algoritmo de assinatura V4, que suporta um período máximo de validade de 7 dias. Os exemplos usam o endpoint público da região China (Hangzhou) (https://oss-cn-hangzhou.aliyuncs.com). Para acessar o oss a partir de outros serviços da Alibaba Cloud na mesma região, use um endpoint interno. Para regiões e endpoints suportados, consulte Regiões e endpoints.

O código a seguir gera uma url pré-assinada para um upload simples. A url é válida por 600 segundos.

package main

import (
	"log"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
)

func main() {
	// Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured.
	provider, err := oss.NewEnvironmentVariableCredentialsProvider()
	if err != nil {
		log.Printf("Error: %v", err)
	}

	// Create an OSSClient instance.
	// Specify the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com.
	// Specify the region of the bucket. For example, if your bucket is located in the China (Hangzhou) region, set the region to cn-hangzhou.
	clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
	clientOptions = append(clientOptions, oss.Region("cn-hangzhou"))
	// Specify the signature algorithm version.
	clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
	client, err := oss.New("https://oss-cn-hangzhou.aliyuncs.com", "", "", clientOptions...)
	if err != nil {
		log.Printf("Error: %v", err)
	}

	// Specify the name of the bucket. Example: examplebucket.
	bucketName := "examplebucket"
	// Specify the full path of the object. Example: exampleobject.txt. Do not include the bucket name in the full path.
	objectName := "exampleobject.txt"
	bucket, err := client.Bucket(bucketName)
	if err != nil {
		log.Printf("Error: %v", err)
	}

	// Generate a presigned URL and set the validity period to 600 seconds.
	signedURL, err := bucket.SignURL(objectName, oss.HTTPPut, 600)
	if err != nil {
		log.Printf("Error: %v", err)
	}
	log.Printf("Sign Url:%s\n", signedURL)
}

Para crie uma instância OSSClient usando um domínio personalizado ou Security Token Service (STS), consulte Exemplos de configuração para cenários comuns.

Estrutura da url pré-assinada

Uma url pré-assinada gerada tem o seguinte formato:

https://examplebucket.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******************************************************

Parâmetro

Descrição

x-oss-date

Data e hora de criação da assinatura, no formato ISO 8601 (UTC).

x-oss-expires

Duração da validade da url em segundos, contada a partir de x-oss-date.

x-oss-signature-version

Algoritmo de assinatura. OSS4-HMAC-SHA256 indica a versão V4.

x-oss-credential

Escopo da credencial usada para calcular a assinatura, incluindo ID da chave de acesso, data, região, serviço e tipo de solicitação.

x-oss-signature

Assinatura calculada. Qualquer modificação na url ou nos cabeçalhos da solicitação invalida este valor.

Fazer upload usando uma url pré-assinada

Passe a url pré-assinada para qualquer cliente HTTP que suporte solicitações PUT. A assinatura está incorporada na url — não são necessários cabeçalhos de autenticação adicionais.

curl

curl -X PUT -T /path/to/local/file "https://examplebucket.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******************************************************"
Dica: Coloque a url entre aspas duplas ao usar curl para evitar que o shell interprete caracteres especiais na string de consulta.

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 to upload.
        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 response: %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 to upload.
	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 and send a PUT request to upload it.
        with open(file_path, 'rb') as 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 presigned URL.
    signed_url = "<signedUrl>"

    # Specify the full path of 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 {
        // 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'
            }
        });

        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}`);
    }
}

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

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

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

Browser.js

Importante

Ao fazer upload de um navegador usando uma url pré-assinada, pode ocorrer um erro 403 SignatureNotMatch. Isso geralmente acontece porque o navegador adiciona automaticamente o cabeçalho de solicitação Content-Type, que não foi incluído quando a url pré-assinada foi gerada. Para evitar isso, sempre especifique Content-Type ao gerar a url pré-assinada para uploads baseados em navegador.

<!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 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 upload.
         * @param {string} presignedUrl - The presigned URL.
         */
        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;

// Specify the full path of the local file to upload.
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 the 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;
        }

        // Get the file size.
        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);

        // 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;
            }
        }

        fclose(file);
        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 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, "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 to upload.
        String filePath = "C:\\Users\\demo.txt";
        activity.uploadFile(signedUrl, filePath);
    }
}

Cenários comuns

Upload com cabeçalhos de solicitação e metadados de usuário

Para anexar metadados de usuário ou controlar a classe de armazenamento no momento do upload, inclua esses parâmetros ao gerar a url pré-assinada. O cliente deve enviar os mesmos cabeçalhos ao usar a url — qualquer divergência causa um erro de assinatura.

Etapa 1: Gerar a url pré-assinada

package main

import (
	"log"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
)

func main() {
	// Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured.
	provider, err := oss.NewEnvironmentVariableCredentialsProvider()
	if err != nil {
		log.Printf("Error: %v", err)
	}

	// Create an OSSClient instance.
	clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
	clientOptions = append(clientOptions, oss.Region("cn-hangzhou"))
	clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
	client, err := oss.New("https://oss-cn-hangzhou.aliyuncs.com", "", "", clientOptions...)
	if err != nil {
		log.Printf("Error: %v", err)
	}

	bucketName := "examplebucket"
	objectName := "exampleobject.txt"
	bucket, err := client.Bucket(bucketName)
	if err != nil {
		log.Printf("Error: %v", err)
	}

	// Specify request headers and user metadata to include in the signed URL.
	// Note: when using this presigned URL on the frontend, the client must send
	// the same Content-Type header as specified here.
	options := []oss.Option{
		oss.Meta("key1", "value1"),
		oss.Meta("key2", "value2"),
		oss.ContentType("text/plain; charset=utf8"),
	}

	// Generate a presigned URL with a validity period of 600 seconds.
	signedURL, err := bucket.SignURL(objectName, oss.HTTPPut, 600, options...)
	if err != nil {
		log.Printf("Error: %v", err)
	}
	log.Printf("Sign Url:%s\n", signedURL)
}

Etapa 2: Fazer upload usando a url pré-assinada

Todos os clientes de upload devem incluir os mesmos cabeçalhos de Content-Type, classe de armazenamento e metadados de usuário especificados durante a geração da url.

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://examplebucket.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 to upload.
        String pathName = "C:\\Users\\demo.txt";

        // Specify request headers. Values must match those used when generating the URL.
        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");

        // Specify user metadata. Values must match those used when generating the URL.
        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);

            // Add request headers. If these headers were included when generating the URL,
            // they must be sent here. Mismatched headers cause a signature error.
            for (Map.Entry header : headers.entrySet()) {
                put.addHeader(header.getKey().toString(), header.getValue().toString());
            }
            // If user metadata is specified, the SDK adds the "x-oss-meta-" prefix to the key.
            // When using other HTTP clients, add this prefix manually.
            for (Map.Entry meta : userMetadata.entrySet()) {
                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 {
	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
	}

	// Add request headers. Values must match those used when generating the URL.
	for key, value := range headers {
		req.Header.Set(key, value)
	}

	// Add user metadata with the "x-oss-meta-" prefix.
	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("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 to upload.
	filePath := "C:\\Users\\demo.txt"

	// Request headers. Values must match those used when generating the URL.
	headers := map[string]string{
		"Content-Type":        "text/plain;charset=utf8",
		"x-oss-storage-class": "Standard",
	}

	// User metadata. Values must match those used when generating the URL.
	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

def upload_file(signed_url, file_path, headers=None, metadata=None):
    """
    Upload an object to OSS using a presigned URL.

    :param signed_url: The presigned URL.
    :param file_path: The full path of the local file to upload.
    :param headers: Request headers. Optional.
    :param metadata: User metadata. Optional.
    """
    if not headers:
        headers = {}
    if not metadata:
        metadata = {}

    # Add the x-oss-meta- prefix to metadata keys.
    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 presigned URL.
    signed_url = "<signedUrl>"

    # Specify the full path of the local file to upload.
    file_path = "C:\\Users\\demo.txt"

    # Request headers. Values must match those used when generating the URL.
    headers = {
        "Content-Type": "text/plain;charset=utf8",
        "x-oss-storage-class": "Standard"
    }

    # User metadata. Values must match those used when generating the URL.
    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 metadata keys.
        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: 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}`);
    }
}

(async () => {
    // Replace <signedUrl> with the presigned URL.
    const signedUrl = "<signedUrl>";

    // Specify the full path of the local file to upload.
    const filePath = "C:\\Users\\demo.txt";

    // Request headers. Values must match those used when generating the URL.
    const headers = {
        "Content-Type": "text/plain;charset=utf8",
        "x-oss-storage-class": "Standard"
    };

    // User metadata. Values must match those used when generating the URL.
    const metadata = {
        "key1": "value1",
        "key2": "value2"
    };

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

Browser.js

Importante

Ao fazer upload de um navegador usando uma url pré-assinada que inclui parâmetros personalizados, especifique Content-Type ao gerar a url. O navegador adiciona automaticamente este cabeçalho e, se ele não corresponder ao valor incorporado na assinatura, o upload falhará com um erro 403.

<!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 to upload.
var filePath = "C:\\Users\\demo.txt";
// Replace <signedUrl> with the presigned URL.
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;

// Add request headers. Values must match those used when generating the URL.
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");

// Output the request headers.
Console.WriteLine("Request header:");
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($"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>

// Callback to process the HTTP response body.
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 the 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);

        // Add request headers. Values must match those used when generating the URL.
        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());
        }
        // Add user metadata with the "x-oss-meta-" prefix.
        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 << "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;
        }

        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 to upload.
    std::string filePath = "C:\\Users\\demo.txt";

    // Request headers. Values must match those used when generating the URL.
    std::map<std::string, std::string> headers = {
        {"Content-Type", "text/plain;charset=utf8"},
        {"x-oss-storage-class", "Standard"}
    };

    // User metadata. Values must match those used when generating the URL.
    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 to upload.
        String pathName = "/storage/emulated/0/demo.txt";

        // Request headers. Values must match those used when generating the URL.
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "text/plain;charset=utf8");
        headers.put("x-oss-storage-class", "Standard");

        // User metadata. Values must match those used when generating the URL.
        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);

                // Add request headers.
                for (Entry<String, String> header : headers.entrySet()) {
                    connection.setRequestProperty(header.getKey(), header.getValue());
                }

                // Add user metadata with the "x-oss-meta-" prefix.
                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, "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();
            }
        }
    }
}

Upload multipartido

Para arquivos grandes, divida o arquivo em partes e gere uma url pré-assinada separada para cada parte. O exemplo a seguir divide um arquivo local em três partes e usa urls pré-assinadas para fazer o upload delas.

package main

import (
	"crypto/md5"
	"encoding/base64"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
)

func main() {
	// Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured.
	provider, err := oss.NewEnvironmentVariableCredentialsProvider()
	if err != nil {
		log.Printf("Error:%v", err)
	}
	// Create an OSSClient instance.
	// Specify the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com.
	client, err := oss.New("https://oss-cn-hangzhou.aliyuncs.com", "", "", oss.SetCredentialsProvider(&provider))
	if err != nil {
		log.Printf("Error:%v", err)
	}
	bucketName := "examplebucket"

	bucket, err := client.Bucket(bucketName)
	if err != nil {
		log.Printf("Error:%v", err)
	}
	// Specify the full path of the object. Do not include the bucket name in the full path.
	objectName := "example.txt"
	// Specify the full path of the local file to upload.
	localFile := "D:\\download\\demo.txt"

	// Split the local file into three parts.
	chunks, err := oss.SplitFileByPartNum(localFile, 3)
	fd, err := os.Open(localFile)
	defer fd.Close()

	// Step 1: Initiate a multipart upload task.
	imur, err := bucket.InitiateMultipartUpload(objectName)

	// Step 2: Upload each part using a presigned URL.
	var options []oss.Option

	for _, chunk := range chunks {
		// Configure MD5 verification.
		buf := make([]byte, chunk.Size)
		fd.ReadAt(buf, chunk.Size)
		sum := md5.Sum(buf)
		b64 := base64.StdEncoding.EncodeToString(sum[:])
		options = []oss.Option{
			oss.ContentMD5(b64),
		}

		options = append(options, oss.AddParam("partNumber", strconv.Itoa(chunk.Number)))
		options = append(options, oss.AddParam("uploadId", imur.UploadID))

		// Generate a presigned URL for this part.
		signedURL, err := bucket.SignURL(objectName, oss.HTTPPut, 60, options...)
		if err != nil {
			log.Printf("Error:%v", err)
		}
		fmt.Printf("Presigned URL: %s\n", signedURL)
		err = bucket.PutObjectWithURL(signedURL, strings.NewReader(string(buf)), options...)
		if err != nil {
			log.Printf("Error:%v", err)
		}
	}

	lsRes, err := bucket.ListUploadedParts(imur)
	if err != nil {
		log.Printf("Error:%v", err)
	}

	// Collect the uploaded parts.
	var parts []oss.UploadPart
	for _, p := range lsRes.UploadedParts {
		parts = append(parts, oss.UploadPart{XMLName: p.XMLName, PartNumber: p.PartNumber, ETag: p.ETag})
	}

	// Step 3: Complete the multipart upload.
	_, err = bucket.CompleteMultipartUpload(imur, parts)
	if err != nil {
		log.Printf("Error:%v", err)
	}
	fmt.Println("Uploaded")
}

Solução de problemas

SignatureNotMatch (403)

Um erro 403 SignatureNotMatch significa que os cabeçalhos de solicitação enviados durante o upload não correspondem aos cabeçalhos assinados na url. Verifique os seguintes itens:

Causa

Solução

Incompatibilidade de Content-Type

Se Content-Type foi especificado ao gerar a url, o cliente deve enviar exatamente o mesmo valor. Esta é a causa mais comum para uploads via navegador — os navegadores adicionam automaticamente Content-Type, o que entra em conflito com uma url gerada sem esse cabeçalho.

Incompatibilidade de metadados de usuário

Se metadados de usuário (x-oss-meta-*) foram incorporados à url, o cliente deve enviar os mesmos cabeçalhos de metadados com valores idênticos.

Incompatibilidade de classe de armazenamento

Caso x-oss-storage-class tenha sido especificado na geração da url, inclua-o na solicitação de upload.

String de consulta modificada

Use a url exatamente como foi gerada. Qualquer modificação na string de consulta invalida a assinatura.

Desvio do relógio do sistema

As assinaturas incluem um carimbo de data/hora. Sincronize o relógio do seu sistema com um servidor NTP se a diferença de tempo for grande.

Caracteres especiais no curl

Coloque a url entre aspas duplas ao usar curl para evitar que o shell interprete & e outros caracteres especiais.

Perguntas frequentes

Se a url pré-assinada expirar durante um upload, o upload falha?

Não. Você pode usar uma url pré-assinada para fazer upload de objetos a qualquer momento dentro do seu período de validade. O período de validade efetivo é determinado pelo menor tempo entre a validade da assinatura e a validade do token STS (caso credenciais STS sejam utilizadas).

Se eu não especificar cabeçalhos de solicitação ou metadados de usuário ao gerar a url, preciso incluí-los na solicitação de upload?

Não. Cabeçalhos de solicitação e metadados de usuário são opcionais. Se você não os incluiu ao gerar a url, omita-os também na solicitação de upload.

Próximos passos