Tous les produits
Search
Centre de documentation

Object Storage Service:Use a presigned URL to handle object uploads

Dernière mise à jour :Sep 24, 2026

Par défaut, la liste de contrôle d'accès (ACL) d'un objet dans un compartiment Object Storage Service (OSS) est définie sur « privé », ce qui signifie que seul le propriétaire de l'objet est autorisé à y accéder. Toutefois, le propriétaire peut générer une URL présignée pour accorder un accès temporaire permettant à des tiers de télécharger des objets pendant une période donnée. Cette fonctionnalité permet de mettre en œuvre des scénarios tels que l'autorisation de partenaires à télécharger des contrats ou la soumission d'images de profil par les utilisateurs.

Remarques relatives à l'utilisation

  • Cette rubrique utilise l'endpoint public de la région Chine (Hangzhou). Pour accéder à OSS depuis d'autres services Alibaba Cloud situés dans la même région, utilisez un endpoint interne. Pour plus d'informations sur les régions et endpoints pris en charge, consultez Régions et endpoints.

  • Aucune autorisation n'est requise pour générer une URL présignée. Cependant, un tiers ne pourra télécharger un fichier avec succès via l'URL présignée que si vous disposez de l'autorisation oss:PutObject. Pour plus d'informations, consultez Accorder une stratégie d'accès personnalisée à un utilisateur RAM.

  • Les URL présignées ne prennent pas en charge les téléchargements via FormData. Pour les téléchargements FormData, utilisez plutôt les téléchargements par formulaire.

Flux de travail

Le schéma suivant illustre le processus de téléchargement d'un objet vers OSS à l'aide d'une URL présignée.

image

Utiliser une URL présignée pour télécharger un objet

  1. Le propriétaire du compartiment génère une URL présignée autorisant les requêtes PUT.

    Remarque

    Une URL présignée générée à l'aide du SDK OSS a une durée de validité maximale de 7 jours. Si vous générez une URL présignée à l'aide d'un jeton Security Token Service (STS), celle-ci peut être valide jusqu'à 12 heures (43 200 secondes).

    Java

    Pour savoir comment utiliser le SDK OSS for Java afin de générer une URL présignée, consultez SDK OSS for Java.

    import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.*;
    import com.aliyun.oss.common.comm.SignVersion;
    import com.aliyun.oss.model.GeneratePresignedUrlRequest;
    import java.net.URL;
    import java.util.*;
    import java.util.Date;
    
    public class GetSignUrl {
        public static void main(String[] args) throws Throwable {
            // The public endpoint of China (Hangzhou) is used as an example. For other regions, replace the value with your actual information.
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // Retrieve access credentials from environment variables. Before running this code, ensure that environment variables OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET are set.
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            //  Specify the bucket name, for example, "examplebucket".
            String bucketName = "examplebucket";
            // Specify the full path of the object, for example, "exampleobject.txt". The object path should not include the bucket name.
            String objectName = "exampleobject.txt";
            // Specify the region where the bucket is located. For example, if the bucket is in China (Hangzhou), set the region to cn-hangzhou.
            String region = "cn-hangzhou";
    
            // Create an OSSClient instance.
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region(region)
                    .build();
    
            URL signedUrl = null;
            try {
                // Specify the validity period for the generated presigned URL in milliseconds. In this example, the expiration time is set to 1 hour.
                Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
    
                // Generate a presigned URL.
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
                // Specify the expiration time of the presigned URL.
                request.setExpiration(expiration);
                // Generate a presigned URL that allows the HTTP PUT method.
                signedUrl = ossClient.generatePresignedUrl(request);
                // Display the presigned URL.
                System.out.println("signed url for putObject: " + signedUrl);
    
            } catch (OSSException oe) {
                System.out.println("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                System.out.println("Error Message:" + oe.getErrorMessage());
                System.out.println("Error Code:" + oe.getErrorCode());
                System.out.println("Request ID:" + oe.getRequestId());
                System.out.println("Host ID:" + oe.getHostId());
            } catch (ClientException ce) {
                System.out.println("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                System.out.println("Error Message:" + ce.getMessage());
            }
        }
    }       

    Go

    Pour savoir comment utiliser le SDK OSS for Go afin de générer une URL présignée, consultez SDK OSS for Go.

    package main
    
    import (
    	"context"
    	"flag"
    	"log"
    	"time"
    
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
    )
    
    // Define global variables.
    var (
    	region     string // The region of the bucket.
    	bucketName string // The name of the bucket.
    	objectName string // The name of the object.
    )
    
    //  Define the init function to initialize the command-line parameters.
    func init() {
    	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
    	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
    	flag.StringVar(&objectName, "object", "", "The name of the object.")
    }
    
    func main() {
    	// Parse the command-line parameters.
    	flag.Parse()
    
    	// Check whether the bucket name is empty.
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, bucket name required")
    	}
    
    	// Check whether the region is empty.
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, region required")
    	}
    
    	// Check whether the object name is empty.
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, object name required")
    	}
    
    	// Load the default configuration and specify the credential provider and region.
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	// Create an OSS client instance.
    	client := oss.NewClient(cfg)
    
    	// Generate a presigned URL with the PutObject method.
    	result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
    		Bucket: oss.Ptr(bucketName),
    		Key:    oss.Ptr(objectName),
    	},
    		oss.PresignExpires(10*time.Minute),
    	)
    	if err != nil {
    		log.Fatalf("failed to put object presign %v", err)
    	}
    
    	log.Printf("request method:%v\n", result.Method)
    	log.Printf("request expiration:%v\n", result.Expiration)
    	log.Printf("request url:%v\n", result.URL)
    	if len(result.SignedHeaders) > 0 {
    		//If the returned result contains signature headers, ensure you include these headers when making a PUT request with the presigned URL.
    		log.Printf("signed headers:\n")
    		for k, v := range result.SignedHeaders {
    			log.Printf("%v: %v\n", k, v)
    		}
    	}
    }
    

    Python

    Pour savoir comment utiliser le SDK OSS for Python afin de générer une URL présignée, consultez SDK OSS for Python.

    import argparse
    import requests
    import alibabacloud_oss_v2 as oss
    
    from datetime import datetime, timedelta
    
    # Create a command-line argument parser and describe the script's purpose: to generate a presigned PUT request URL for an object (Presign Put Object).
    parser = argparse.ArgumentParser(description="presign put object sample")
    
    # Define command-line arguments, including the required region, bucket name, endpoint, and object key.
    parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
    parser.add_argument('--bucket', help='The name of the bucket.', required=True)
    parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
    parser.add_argument('--key', help='The name of the object.', required=True)
    
    def main():
        # Parse command-line arguments to get user-provided values.
        args = parser.parse_args()
    
        # Load access credential information from environment variables for identity verification.
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    
        # Create a configuration object using the SDK's default settings and set the authentication provider.
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
    
        # Set the region property of the configuration object based on the command-line arguments.
        cfg.region = args.region
    
        # If a custom endpoint is provided, update the endpoint property in the configuration object.
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
    
        # Initialize the OSS client with the above configuration to interact with OSS.
        client = oss.Client(cfg)
    
        # Send a request to generate a presigned PUT request for the specified object.
        pre_result = client.presign(oss.PutObjectRequest(
            bucket=args.bucket,  # Bucket name
            key=args.key,  # Object key
        ),expires=timedelta(seconds=3600)) # Set the expiration time in seconds. Here, it is set to 3600 seconds.
    
        # Print the method, expiration time, and URL of the presigned request to confirm the validity of the link.
        print(f'method: {pre_result.method},'
              f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
              f' url: {pre_result.url}'
              )
    
        # Print the signed header information of the presigned request. This information will be included in the HTTP headers when the actual request is sent.
        for key, value in pre_result.signed_headers.items():
            print(f'signed headers key: {key}, signed headers value: {value}')
    
    # When this script is executed directly, call the main function to start the processing logic.
    if __name__ == "__main__":
        main()  # Script entry point. The program flow starts here.

    Node.js

    L'exemple suivant génère une URL présignée pour les téléchargements standards. Pour savoir comment utiliser le SDK OSS for Node.js afin de générer une URL présignée avec des paramètres de traitement d'image ou un ID de version, consultez SDK OSS for Node.js.

    const OSS = require("ali-oss");
    
    // Define a function to generate a signed URL.
    async function generateSignatureUrl(fileName) {
      // Obtain the presigned URL.
      const client = await new OSS({
          accessKeyId: 'yourAccessKeyId',
          accessKeySecret: 'yourAccessKeySecret',
          bucket: 'examplebucket',
          region: 'oss-cn-hangzhou',
          authorizationV4: true
      });
    
      return await client.signatureUrlV4('PUT', 3600, {
          headers: {} // Set the request headers based on the actual request headers.
      }, fileName);
    }
    // Call the function and pass the file name.
    generateSignatureUrl('yourFileName').then(url => {
      console.log('Generated Signature URL:', url);
    }).catch(err => {
      console.error('Error generating signature URL:', err);
    });

    PHP

    L'exemple suivant génère une URL présignée pour les téléchargements standards. Pour savoir comment utiliser le SDK OSS for PHP afin de générer une URL présignée avec un ID de version, consultez SDK OSS for PHP.

    <?php
    if (is_file(__DIR__ . '/../autoload.php')) {
        require_once __DIR__ . '/../autoload.php';
    }
    if (is_file(__DIR__ . '/../vendor/autoload.php')) {
        require_once __DIR__ . '/../vendor/autoload.php';
    }
    
    use OSS\OssClient;
    use OSS\Core\OssException;
    use OSS\Http\RequestCore;
    use OSS\Http\ResponseCore;
    use OSS\Credentials\EnvironmentVariableCredentialsProvider;
    
    // 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 = new EnvironmentVariableCredentialsProvider();
    // 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. 
    $endpoint = "yourEndpoint";
    // Specify the name of the bucket. 
    $bucket= "examplebucket";
    // Specify the full path of the object. Do not include the bucket name in the full path of the object. 
    $object = "exampleobject.txt";
    // Set the validity period of the presigned URL to 600 seconds. Maximum value: 32400. 
    $timeout = 600;
    try {
        $config = array(  
            "provider" => $provider,
            "endpoint" => $endpoint,
            'signatureVersion'=>OssClient::OSS_SIGNATURE_VERSION_V4,
            "region"=> "cn-hangzhou"
        );
        $ossClient = new OssClient($config);
        // Generate the presigned URL. 
        $signedUrl = $ossClient->signUrl($bucket, $object, $timeout, "PUT");
        print_r($signedUrl);
    } catch (OssException $e) {
        printf(__FUNCTION__ . ": FAILED\n");
        printf($e->getMessage() . "\n");
        return;
    }           

    Android

    Pour savoir comment utiliser le SDK OSS for Android afin de générer une URL présignée, consultez Autoriser l'accès (SDK Android).

    // Specify the bucket name, for example, examplebucket.
    String bucketName = "examplebucket";
    // Specify the full path of the source object, without the bucket name, for example, exampleobject.txt.
    String objectKey = "exampleobject.txt";
    // Set the content-type.
    String contentType = "application/octet-stream";
    String url = null;
    try {
        // Generate a presigned URL for uploading the file.
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectKey);
        // Set the expiration time of the presigned URL to 30 minutes.
        request.setExpiration(30*60);
        request.setContentType(contentType);    
        request.setMethod(HttpMethod.PUT);
        url = oss.presignConstrainedObjectURL(request);
        Log.d("url", url);
    } catch (ClientException e) {
        e.printStackTrace();
    }

    C++

    Pour savoir comment utiliser le SDK OSS for C++ afin de générer une URL présignée, consultez Autoriser l'accès (SDK C++).

    #include <alibabacloud/oss/OssClient.h>
    using namespace AlibabaCloud::OSS;
    
    int main(void)
    {
        /* Initialize information about the account that is used to access OSS. */
                
        /* 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. */
        std::string Endpoint = "yourEndpoint";
        /* 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.   * /
        std::string Region = "yourRegion";
        /* Specify the name of the bucket. Example: examplebucket. */
        std::string BucketName = "examplebucket";
        /* Specify the full path of the object. Do not include the bucket name in the full path. Example: exampledir/exampleobject.txt. */   
        std::string PutobjectUrlName = "exampledir/exampleobject.txt";
    
         /* Initialize resources, such as network resources. */
        InitializeSdk();
    
        ClientConfiguration conf;
        conf.signatureVersion = SignatureVersionType::V4;
        /* 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. */
        auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
        OssClient client(Endpoint, credentialsProvider, conf);
        client.SetRegion(Region);
    
        /* Specify the validity period of the pre-signed URL. The maximum validity period is 32,400. Unit: seconds. */
        std::time_t t = std::time(nullptr) + 1200;
        /* Generate a pre-signed URL. */
        auto genOutcome = client.GeneratePresignedUrl(BucketName, PutobjectUrlName, t, Http::Put);
        if (genOutcome.isSuccess()) {
            std::cout << "GeneratePresignedUrl success, Gen url:" << genOutcome.result().c_str() << std::endl;
        }
        else {
            /* Handle exceptions. */
            std::cout << "GeneratePresignedUrl fail" <<
            ",code:" << genOutcome.error().Code() <<
            ",message:" << genOutcome.error().Message() <<
            ",requestId:" << genOutcome.error().RequestId() << std::endl;
            return -1;
        }
    
        /* Release resources, such as network resources. */
        ShutdownSdk();
        return 0;
    }

    iOS

    Pour savoir comment utiliser le SDK OSS for iOS afin de générer une URL présignée, consultez Autoriser l'accès (SDK iOS).

    // Specify the name of the bucket. 
    NSString *bucketName = @"examplebucket";
    // Specify the name of the object. 
    NSString *objectKey = @"exampleobject.txt";
    NSURL *file = [NSURL fileURLWithPath:@"<filePath>"];
    NSString *contentType = [OSSUtil detemineMimeTypeForFilePath:file.absoluteString uploadName:objectKey];
    __block NSString *urlString;
    // Generate a presigned URL with a validity period for uploading the object. In this example, the validity period of the URL is 30 minutes. 
    OSSTask *task = [client presignConstrainURLWithBucketName:bucketName
                                                withObjectKey:objectKey
                                                   httpMethod:@"PUT"
                                       withExpirationInterval:30 * 60
                                               withParameters:@{}
                                                  contentType:contentType
                                                   contentMd5:nil];
    [task continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
        if (task.error) {
            NSLog(@"presign error: %@", task.error);
        } else {
            urlString = task.result;
            NSLog(@"url: %@", urlString);
        }
        return nil;
    }];

    .NET

    Pour savoir comment utiliser le SDK OSS for .NET afin de générer une URL présignée, consultez Télécharger un fichier à l'aide d'une URL signée (SDK C# V1).

    using Aliyun.OSS;
    using Aliyun.OSS.Common;
    // Specify the Endpoint for the region where the bucket is located. For example, if the bucket is in the China (Hangzhou) region, set the Endpoint to https://oss-cn-hangzhou.aliyuncs.com.
    var endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
    // Obtain access credentials from environment variables. Before you run this code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured.
    var accessKeyId = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_ID");
    var accessKeySecret = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_SECRET");
    // Specify the bucket name, for example, examplebucket.
    var bucketName = "examplebucket";
    // Specify the full path of the object. The full path does not include the bucket name. For example, exampledir/exampleobject.txt.
    var objectName = "exampledir/exampleobject.txt";
    var objectContent = "More than just cloud.";
    // Specify the region where the bucket is located. For example, if the bucket is in the China (Hangzhou) region, set the region to cn-hangzhou.
    const string region = "cn-hangzhou";
    
    // Create a ClientConfiguration instance and modify the default parameters as needed.
    var conf = new ClientConfiguration();
    
    // Set the signature version to V4.
    conf.SignatureVersion = SignatureVersion.V4;
    
    // Create an OssClient instance.
    var client = new OssClient(endpoint, accessKeyId, accessKeySecret, conf);
    client.SetRegion(region);
    try
    {
        // Generate a signed URL.
        var generatePresignedUriRequest = new GeneratePresignedUriRequest(bucketName, objectName, SignHttpMethod.Put)
        {
            // Set the expiration time of the signed URL. The default value is 3600 seconds.
            Expiration = DateTime.Now.AddHours(1),
        };
        var signedUrl = client.GeneratePresignedUri(generatePresignedUriRequest);
    }
    catch (OssException ex)
    {
        Console.WriteLine("Failed with error code: {0}; Error info: {1}. \nRequestID:{2}\tHostID:{3}",
            ex.ErrorCode, ex.Message, ex.RequestId, ex.HostId);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Failed with error info: {0}", ex.Message);
    }

    C

    Pour savoir comment utiliser le SDK OSS for C afin de générer une URL présignée, consultez Autoriser l'accès (SDK C).

    #include "oss_api.h"
    #include "aos_http_io.h"
    /* Set yourEndpoint to the endpoint of the region where the bucket is located. For example, if the bucket is in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. */
    const char *endpoint = "yourEndpoint";
    
    /* Specify the bucket name. For example, examplebucket. */
    const char *bucket_name = "examplebucket";
    /* Specify the full path of the object. The full path cannot contain the bucket name. For example, exampledir/exampleobject.txt. */
    const char *object_name = "exampledir/exampleobject.txt";
    /* Specify the full path of the local file. */
    const char *local_filename = "yourLocalFilename";
    void init_options(oss_request_options_t *options)
    {
        options->config = oss_config_create(options->pool);
        /* Initialize the aos_string_t type with a char* string. */
        aos_str_set(&options->config->endpoint, endpoint);
        /* Obtain access credentials from environment variables. Before you run this sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are set. */
        aos_str_set(&options->config->access_key_id, getenv("OSS_ACCESS_KEY_ID"));
        aos_str_set(&options->config->access_key_secret, getenv("OSS_ACCESS_KEY_SECRET"));
        /* Specify whether to use a CNAME to access OSS. A value of 0 indicates that a CNAME is not used. */
        options->config->is_cname = 0;
        /* Set network parameters, such as the timeout period. */
        options->ctl = aos_http_controller_create(options->pool, 0);
    }
    int main(int argc, char *argv[])
    {
        /* Call the aos_http_io_initialize method at the program entry to initialize global resources, such as the network and memory. */
        if (aos_http_io_initialize(NULL, 0) != AOSE_OK) {
            exit(1);
        }
        /* The memory pool (pool) for memory management, which is equivalent to apr_pool_t. Its implementation code is in the APR library. */
        aos_pool_t *pool;
        /* Create a new memory pool. The second parameter is NULL, which indicates that the pool does not inherit from other memory pools. */
        aos_pool_create(&pool, NULL);
        /* Create and initialize options. This parameter includes global configuration information, such as endpoint, access_key_id, access_key_secret, is_cname, and curl. */
        oss_request_options_t *oss_client_options;
        /* Allocate memory for options in the memory pool. */
        oss_client_options = oss_request_options_create(pool);
        /* Initialize the client option oss_client_options. */
        init_options(oss_client_options);
        /* Initialize parameters. */
        aos_string_t bucket;
        aos_string_t object;
        aos_string_t file;    
        aos_http_request_t *req;
        apr_time_t now;
        char *url_str;
        aos_string_t url;
        int64_t expire_time; 
        int one_hour = 3600;
        aos_str_set(&bucket, bucket_name);
        aos_str_set(&object, object_name);
        aos_str_set(&file, local_filename);
        expire_time = now / 1000000 + one_hour;    
        req = aos_http_request_create(pool);
        req->method = HTTP_PUT;
        now = apr_time_now(); 
        /* Unit: microseconds. */
        expire_time = now / 1000000 + one_hour;
        /* Generate a presigned URL. */
        url_str = oss_gen_signed_url(oss_client_options, &bucket, &object, expire_time, req);
        aos_str_set(&url, url_str);
        printf("Temporary upload URL: %s\n", url_str);    
        /* Release the memory pool. This is equivalent to releasing the memory allocated for various resources during the request. */
        aos_pool_destroy(pool);
        /* Release the previously allocated global resources. */
        aos_http_io_deinitialize();
        return 0;
    }
  1. Les tiers téléchargent des objets en envoyant une requête PUT à l'aide de l'URL pré-signée.

    Important

    Une URL pré-signée peut être utilisée plusieurs fois avant son expiration. Toutefois, si vous téléchargez le même objet à plusieurs reprises, chaque nouveau téléchargement écrase le précédent. Une fois l'URL expirée, le propriétaire doit générer une nouvelle URL pré-signée (comme indiqué dans Étape 1) afin de permettre aux tiers d'accéder aux ressources sur une période prolongée.

Utiliser des URL pré-signées pour effectuer un chargement multipartie d'un objet

Si vous devez charger de grands objets mais qu'il est difficile ou impossible d'intégrer un SDK OSS dans votre client, envisagez les chargements multiparties à l'aide d'URL pré-signées. Dans cette approche, le serveur initie le processus de chargement et chaque partie reçoit une URL pré-signée permettant à votre client ou à un service tiers de la charger individuellement. Une fois toutes les parties chargées avec succès, le serveur les combine pour former un objet complet.

Important

Le chargement d'un objet en plusieurs parties à l'aide d'URL pré-signées est plus complexe que l'utilisation d'un SDK OSS. Lorsque vous utilisez cette méthode, assurez-vous que votre client charge la partie correspondant au numéro de partie spécifié dans l'URL pré-signée. Une partie manquante ou une incompatibilité entre les parties entraînera l'échec de la combinaison. Si votre client prend en charge l'intégration du SDK OSS, nous vous recommandons d'utiliser les chargements directs depuis le client basés sur des jetons STS pour plus de simplicité et de stabilité.

image

  1. Le client envoie une demande de chargement multipartie.

    Le client soumet une demande de chargement multipartie incluant le nom de l'objet, sa taille et la taille des parties. La taille d'une partie est limitée à 5 Go maximum, avec une valeur recommandée de 5 Mo.

    Le code suivant illustre un exemple de demande multipartie envoyée par le client. Lors de l'utilisation de cet exemple, n'oubliez pas de remplacer https://yourserver.com/init-upload par votre point de terminaison d'initialisation réel sur votre serveur.

    curl -X POST https://yourserver.com/init-upload \
      -H "Content-Type: application/json" \
      -d '{
        "fileName": "exampleobject.jpg",
        "fileSize": 104857600,
        "partSize": 5242880
    }'
  2. Le serveur initie le chargement multipartie et génère des URL pré-signées.

    1. Appelle l'opération InitiateMultipartUpload pour initier le chargement et obtenir l'ID de chargement.

    2. Calcule le nombre de parties en fonction de la taille de l'objet et de la taille des parties.

    3. Génère une URL pré-signée pour chaque partie.

    4. Renvoie une liste d'URL pré-signées au format JSON.

    Remarque

    Le serveur enregistre l'ID de chargement, qui identifie de manière unique le chargement multipartie et sert à vérifier et combiner les parties. Vous pouvez stocker l'ID de chargement à l'aide d'un cache, de bases de données ou d'autres méthodes.

    Exemple de code pour initier un chargement multipartie et générer des URL pré-signées

    Java

    import com.aliyun.oss.ClientBuilderConfiguration;
    import com.aliyun.oss.HttpMethod;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.common.auth.CredentialsProviderFactory;
    import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
    import com.aliyun.oss.common.comm.SignVersion;
    import com.aliyun.oss.model.GeneratePresignedUrlRequest;
    import com.aliyun.oss.model.InitiateMultipartUploadRequest;
    import com.aliyun.oss.model.InitiateMultipartUploadResult;
    
    import java.net.URL;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    public class InitAndGenerateURL {
        public static void main(String[] args) throws Throwable {
            // Simulate the client-provided file size and part size in bytes.
            long fileSize = 15 * 1024 * 1024L;   // Simulate a file size of 15 MB.
            long partSize = 5 * 1024 * 1024L;    // Simulate a part size of 5 MB.
    
            // Calculate the number of parts.
            int totalParts = (int) ((fileSize + partSize - 1) / partSize);
    
            // Specify the endpoint for accessing OSS. 
            // In this example, the endpoint for the China (Hangzhou) region is used.
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            String region = "cn-hangzhou";
            String bucketName = "exampleBucket";
            String objectName = "exampleObject.jpeg";
            long expireTime = 3600 * 1000L; // Specify that the URL is valid for 1 hour.
    
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            ClientBuilderConfiguration config = new ClientBuilderConfiguration();
            config.setSignatureVersion(SignVersion.V4);
    
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .region(region)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(config)
                    .build();
    
            // Initiate a multipart upload.
            InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(bucketName, objectName);
            InitiateMultipartUploadResult initResult = ossClient.initiateMultipartUpload(initRequest);
            String uploadId = initResult.getUploadId();
            System.out.println("Upload ID: " + uploadId);
            System.out.println("Total parts: " + totalParts);
    
            // Generate a presigned URL for each part.
            for (int i = 1; i <= totalParts; i++) {
                Map<String, String> headers = new HashMap<>();
                String signedUrl = generatePresignedUrl(ossClient, bucketName, objectName, HttpMethod.PUT,
                        expireTime, i, uploadId, headers);
                System.out.println("Part " + i + " URL: " + signedUrl);
            }
    
            ossClient.shutdown();
        }
    
        public static String generatePresignedUrl(OSS ossClient, String bucketName, String objectName, HttpMethod method,
                                                  long expireTime, int partNum, String uploadId, Map<String, String> headers) {
            Date expiration = new Date(System.currentTimeMillis() + expireTime);
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, method);
            request.setExpiration(expiration);
            request.setHeaders(headers);
            request.addQueryParameter("partNumber", String.valueOf(partNum));
            request.addQueryParameter("uploadId", uploadId);
            URL url = ossClient.generatePresignedUrl(request);
            return url.toString();
        }
    }
    

    Go

    package main
    
    import (
    	"context"
    	"encoding/json"
    	"flag"
    	"fmt"
    	"log"
    	"time"
    
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
    )
    
    type SignedPart struct {
    	PartNumber int    `json:"part_number"`
    	URL        string `json:"url"`
    	Method     string `json:"method"`
    }
    
    type UploadInitResponse struct {
    	UploadId string       `json:"upload_id"`
    	Parts    []SignedPart `json:"parts"`
    }
    
    // Define global variables.
    var (
    	region     string
    	bucketName string
    	objectName string
    )
    
    func init() {
    	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
    	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
    	flag.StringVar(&objectName, "object", "", "The name of the object.")
    }
    
    func main() {
    	flag.Parse()
    
    	// Assume that the file size is 15 MB and that the part size is 5 MB.
    	fileSize := int64(15 * 1024 * 1024)
    	partSize := int64(5 * 1024 * 1024)
    	totalParts := int((fileSize + partSize - 1) / partSize)
    
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	client := oss.NewClient(cfg)
    
    	// Initiate a multipart upload.
    	resp, err := client.InitiateMultipartUpload(context.TODO(), &oss.InitiateMultipartUploadRequest{
    		Bucket: oss.Ptr(bucketName),
    		Key:    oss.Ptr(objectName),
    	})
    	if err != nil {
    		log.Fatalf("Failed to initiate multipart upload: %v", err)
    	}
    	uploadId := resp.UploadId
    	fmt.Println("Upload ID:", *uploadId)
    	fmt.Println("Total parts:", totalParts)
    
    	// Generate a presigned URL for each part.
    	expire := time.Hour // Set the validity period of the presigned URL to 1 hour.
    	var parts []SignedPart
    	for i := 1; i <= totalParts; i++ {
    		req := &oss.UploadPartRequest{
    			Bucket:     oss.Ptr(bucketName),
    			Key:        oss.Ptr(objectName),
    			PartNumber: int32(i),
    			UploadId:   uploadId,
    		}
    		presignResult, err := client.Presign(context.TODO(), req, oss.PresignExpiration(time.Now().Add(expire)))
    		if err != nil {
    			log.Fatalf("Failed to generate signed URL for part %d: %v", i, err)
    		}
    		parts = append(parts, SignedPart{
    			PartNumber: i,
    			URL:        presignResult.URL,
    			Method:     "PUT",
    		})
    	}
    
    	// Display response in JSON.
    	out := UploadInitResponse{
    		UploadId: *uploadId,
    		Parts:    parts,
    	}
    	outJSON, _ := json.MarshalIndent(out, "", "  ")
    	fmt.Println(string(outJSON))
    }
    

    Python

    import argparse
    import json
    import alibabacloud_oss_v2 as oss
    
    # Stimulate parameters.
    FILE_SIZE = 15 * 1024 * 1024  # Stimulate a file size of 15 MB.
    PART_SIZE = 5 * 1024 * 1024   # Stimulate a part size of 5 MB.
    EXPIRE_TIME = 3600            # Stimulate a URL validity period of 3,600 seconds.
    
    def main():
        # Create a command-line argument parser and describe the purpose of the script.
        parser = argparse.ArgumentParser(description="presign multipart upload sample")
    
        # Add the --region argument to specify the region of the bucket. This argument is required.
        parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
        # Add the --bucket argument to specify the bucket. This argument is required.
        parser.add_argument('--bucket', help='The name of the bucket.', required=True)
        # Add the --endpoint argument to specify the endpoint for accessing OSS. This argument is optional.
        parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
        # Add the --key argument to specify the name of the object. This argument is required.
        parser.add_argument('--key', help='The name of the object.', required=True)
    
        args = parser.parse_args()
    
        # Create a configuration for initializing an OSS client.
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
        cfg.region = args.region
        # If an endpoint is provided, use it.
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
        
        # Initialize an OSS client.
        client = oss.Client(cfg)
    
        # Send a multipart upload initiation request and get the upload ID.
        init_result = client.presign(oss.InitiateMultipartUploadRequest(
            bucket=args.bucket,
            key=args.key,
        ))
    
        # Use the presingned URL to initiate the upload and get the upload ID.
        import requests
        with requests.post(init_result.url, headers=init_result.signed_headers) as resp:
            obj = oss.InitiateMultipartUploadResult()
            oss.serde.deserialize_xml(xml_data=resp.content, obj=obj)
            upload_id = obj.upload_id
    
        # Calculate the number of parts and generate a presigned URL for each part.
        total_parts = (FILE_SIZE + PART_SIZE - 1) // PART_SIZE
        parts = []
    
        for part_number in range(1, total_parts + 1):
            req = oss.UploadPartRequest(
                bucket=args.bucket,
                key=args.key,
                part_number=part_number,
                upload_id=upload_id,
                expiration_in_seconds=EXPIRE_TIME
            )
            presign_result = client.presign(req)
            parts.append({
                "part_number": part_number,
                "url": presign_result.url,
                "method": "PUT"
            })
    
        # Display the upload ID and presigned URLs of all the parts in JSON.
        output = {
            "upload_id": upload_id,
            "parts": parts
        }
    
        print(json.dumps(output, indent=2))
    
    if __name__ == "__main__":
        main()
  3. Le client charge l'objet en plusieurs parties.

    Le client envoie des requêtes PUT pour charger les parties individuelles à l'aide des URL pré-signées renvoyées par le serveur. Chaque partie est chargée de la même manière que lors du chargement d'un objet avec une URL pré-signée. Une fois toutes les parties chargées, le client notifie au serveur de combiner les parties.

    Remarque

    Les chargements multiparties via des URL pré-signées prennent en charge le traitement simultané, mais il est crucial de garantir que chaque URL est utilisée exclusivement pour la partie désignée. Par exemple, le numéro de partie N dans l'URL doit être utilisé pour charger la Nième partie du fichier ; toute réutilisation ou omission de numéros de partie est interdite.

    La commande suivante permet de charger la première partie (/path/to/local/file correspond au chemin du fichier local) :

    curl -X PUT -T /path/to/local/file "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.jpg?partNumber=1\u0026uploadId=BE2D0BC931BE4DE1B23F339AABFA49EE\u0026x-oss-credential=LTAI********************%2F20250520%2Fcn-hangzhou%2Foss%2Faliyun_v4_request\u0026x-oss-date=20250520T082728Z\u0026x-oss-expires=3600\u0026x-oss-signature=81f3d2e5eaa67c432291577ed20af3b3f60df05ab3cddedcdce168ef707f7ad0\u0026x-oss-signature-version=OSS4-HMAC-SHA256"
  4. (Facultatif) Le serveur vérifie les parties chargées.

    Vous pouvez éventuellement configurer le serveur pour qu'il vérifie les parties chargées lorsqu'il est notifié de l'achèvement du chargement des parties.

    • Vérifiez l'intégrité des parties chargées.

    • Vérifiez que chaque partie a la taille attendue.

    Remarque

    La vérification des parties nécessite l'ID de chargement enregistré précédemment.

    Exemple de code pour vérifier les parties

    Java

    import com.aliyun.oss.ClientBuilderConfiguration;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.common.auth.CredentialsProviderFactory;
    import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
    import com.aliyun.oss.common.comm.SignVersion;
    import com.aliyun.oss.model.*;
    
    import java.util.ArrayList;
    import java.util.List;
    public class complete {
    
        public static void main(String[] args) throws Exception {
            // Specify the endpoint for the region of your bucket. In this example, it is the endpoint for the China (Hangzhou) region.
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // Specify the name of the bucket.
            String bucketName = "examplebucket";
            // Specify the full path of the object. Do not include the bucket name in the full path.
            String objectName = "exampleobject.jpeg";
            // Specify the upload ID.
            String uploadId = "4B78****************************";
            // Specify the ID of the region of the bucket.
            String region = "cn-hangzhou";
    
            // Use credentials stored in environment variables to create an OSS client.
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            ClientBuilderConfiguration config = new ClientBuilderConfiguration();
            config.setSignatureVersion(SignVersion.V4);
    
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .region(region)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(config)
                    .build();
    
            try {
                // List all parts that have been uploaded in the multipart upload.
                ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, objectName, uploadId);
                PartListing partListing = ossClient.listParts(listPartsRequest);
    
                // Collect the ETags of parts. ETags will be used for part combination later.
                List<PartETag> partETags = new ArrayList<>();
                for (PartSummary part : partListing.getParts()) {
                    partETags.add(new PartETag(part.getPartNumber(), part.getETag()));
                }
    
                if (partETags.isEmpty()) {
                    System.out.println("No uploaded parts. The operation stopped.");
                    return;
                }
    
                // Combine parts.
                System.out.println("Combining parts...");
                CompleteMultipartUploadRequest completeRequest =
                        new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
    
                CompleteMultipartUploadResult result = ossClient.completeMultipartUpload(completeRequest);
                System.out.println("Parts combined.");
                System.out.println("ETag: " + result.getETag());
    
            } catch (Exception e) {
                System.err.println("Part combination failed: " + e.getMessage());
                e.printStackTrace();
            } finally {
                if (ossClient != null) {
                    ossClient.shutdown();
                }
            }
        }
    }
    

    Go

    package main
    
    import (
    	"context"
    	"flag"
    	"log"
    
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
    )
    
    var (
    	region     string
    	bucketName string
    	objectName string
    	uploadId   string
    )
    
    func init() {
    	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
    	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
    	flag.StringVar(&objectName, "object", "", "The name of the object.")
    	flag.StringVar(&uploadId, "uploadId", "", "The upload ID.")
    }
    
    func main() {
    	flag.Parse()
    
    	if region == "" || bucketName == "" || objectName == "" || uploadId == "" {
    		flag.PrintDefaults()
    		log.Fatal("Missing required parameters.")
    	}
    
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	client := oss.NewClient(cfg)
    
    	// List uploaded parts.
    	partListResp, err := client.ListParts(context.TODO(), &oss.ListPartsRequest{
    		Bucket:   oss.Ptr(bucketName),
    		Key:      oss.Ptr(objectName),
    		UploadId: oss.Ptr(uploadId),
    	})
    	if err != nil {
    		log.Fatalf("failed to list parts: %v", err)
    	}
    
    	var parts []oss.UploadPart
    	for _, p := range partListResp.Parts {
    		parts = append(parts, oss.UploadPart{
    			PartNumber: p.PartNumber,
    			ETag:       p.ETag,
    		})
    	}
    
    	// Combine uploaded parts.
    	completeResult, err := client.CompleteMultipartUpload(context.TODO(), &oss.CompleteMultipartUploadRequest{
    		Bucket:   oss.Ptr(bucketName),
    		Key:      oss.Ptr(objectName),
    		UploadId: oss.Ptr(uploadId),
    		CompleteMultipartUpload: &oss.CompleteMultipartUpload{
    			Parts: parts,
    		},
    	})
    	if err != nil {
    		log.Fatalf("failed to complete multipart upload: %v", err)
    	}
    
    	// Display the combination result.
    	log.Println("Upload completed successfully.")
    	log.Printf("Bucket:   %s\n", oss.ToString(completeResult.Bucket))
    	log.Printf("Key:      %s\n", oss.ToString(completeResult.Key))
    	log.Printf("ETag:     %s\n", oss.ToString(completeResult.ETag))
    	log.Printf("Status:   %s\n", completeResult.Status)
    }
    

    Python

    # -*- coding: utf-8 -*-
    import argparse
    import alibabacloud_oss_v2 as oss
    
    def main():
        # Create a command-line argument parse and describe the purpose of the script.
        parser = argparse.ArgumentParser(description="presign multipart upload sample")
        # Add the --region argument to specify the region of the bucket. This argument is required.
        parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
        # Add the --bucket argument to specify the bucket. This argument is required.
        parser.add_argument('--bucket', help='The name of the bucket.', required=True)
        # Add the --endpoint argument to specify the endpoint for accessing OSS. This argument is optional.
        parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
        # Add the --key argument to specify the name of the object. This argument is required.
        parser.add_argument('--key', help='The name of the object.', required=True)
        # Add the --upload_id argument to specify the upload ID. This argument is required.
        parser.add_argument('--upload_id', help='The upload ID to list parts for.',required=True)
    
        args = parser.parse_args()
    
        # Specify the configuration and credentials for initializing an OSS client.
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
        cfg.region = args.region
        if args.endpoint:
            cfg.endpoint = args.endpoint
    
        client = oss.Client(cfg)
    
        try:
            # List all uploaded parts.
            list_parts_result = client.list_parts(oss.ListPartsRequest(
                bucket=args.bucket,
                key=args.key,
                upload_id=args.upload_id
            ))
    
            # Construct a list of uploaded parts.
            upload_parts = [
                oss.UploadPart(part_number=part.part_number, etag=part.etag)
                for part in list_parts_result.parts
            ]
    
            if not upload_parts:
                print("No uploaded parts found. The operation stopped.")
                return
    
            # Construct and execute a part combination request.
            request = oss.CompleteMultipartUploadRequest(
                bucket=args.bucket,
                key=args.key,
                upload_id=args.upload_id,
                complete_multipart_upload=oss.CompleteMultipartUpload(parts=upload_parts)
            )
    
            result = client.complete_multipart_upload(request)
    
            print("Parts combined.")
            print(f"ETag: {result.etag}")
            print(f"Bucket: {result.bucket}")
            print(f"Key: {result.key}")
    
        except Exception as e:
            print("Part combination failed:", e)
    
    if __name__ == "__main__":
        main()
  5. Le serveur combine les parties et renvoie le résultat du chargement.

    Une fois la vérification des parties effectuée avec succès, le serveur appelle l'opération CompleteMultipartUpload pour combiner les parties en un seul objet et renvoie le résultat du chargement au client.

    Exemple de code pour combiner les parties

    Java

    import com.aliyun.oss.ClientBuilderConfiguration;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.common.auth.CredentialsProviderFactory;
    import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
    import com.aliyun.oss.common.comm.SignVersion;
    import com.aliyun.oss.model.*;
    
    import java.util.ArrayList;
    import java.util.List;
    public class complete {
    
        public static void main(String[] args) throws Exception {
            // Specify the endpoint for the region of your bucket. In this example, it is the endpoint for the China (Hangzhou) region.
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // Specify the name of your bucket.
            String bucketName = "examplebucket";
            // Specify the full path of your object. Do not include the bucket name in the full path.
            String objectName = "exampleobject.jpeg";
            // Specify the upload ID.
            String uploadId = "4B78****************************";
            // Specify the ID of the region of your bucket.
            String region = "cn-hangzhou";
    
            // Use credentials stored in environment variables to create an OSS client.
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            ClientBuilderConfiguration config = new ClientBuilderConfiguration();
            config.setSignatureVersion(SignVersion.V4);
    
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .region(region)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(config)
                    .build();
    
            try {
                // List all parts that have been uploaded in the multipart upload.
                ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, objectName, uploadId);
                PartListing partListing = ossClient.listParts(listPartsRequest);
    
                // Collect the ETags of parts. ETags will be used for part combination later.
                List<PartETag> partETags = new ArrayList<>();
                for (PartSummary part : partListing.getParts()) {
                    partETags.add(new PartETag(part.getPartNumber(), part.getETag()));
                }
    
                if (partETags.isEmpty()) {
                    System.out.println("No uploaded parts. The operation stopped.");
                    return;
                }
    
                // Combine parts.
                System.out.println("Combining parts...");
                CompleteMultipartUploadRequest completeRequest =
                        new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
    
                CompleteMultipartUploadResult result = ossClient.completeMultipartUpload(completeRequest);
                System.out.println("Parts combined.");
                System.out.println("ETag: " + result.getETag());
    
            } catch (Exception e) {
                System.err.println("Part combination failed: " + e.getMessage());
                e.printStackTrace();
            } finally {
                if (ossClient != null) {
                    ossClient.shutdown();
                }
            }
        }
    }
    

    Go

    package main
    
    import (
    	"context"
    	"flag"
    	"log"
    
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
    )
    
    var (
    	region     string
    	bucketName string
    	objectName string
    	uploadId   string
    )
    
    func init() {
    	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
    	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
    	flag.StringVar(&objectName, "object", "", "The name of the object.")
    	flag.StringVar(&uploadId, "uploadId", "", "The upload ID.")
    }
    
    func main() {
    	flag.Parse()
    
    	if region == "" || bucketName == "" || objectName == "" || uploadId == "" {
    		flag.PrintDefaults()
    		log.Fatal("Missing required parameters.")
    	}
    
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	client := oss.NewClient(cfg)
    
    	// List uploaded parts.
    	partListResp, err := client.ListParts(context.TODO(), &oss.ListPartsRequest{
    		Bucket:   oss.Ptr(bucketName),
    		Key:      oss.Ptr(objectName),
    		UploadId: oss.Ptr(uploadId),
    	})
    	if err != nil {
    		log.Fatalf("failed to list parts: %v", err)
    	}
    
    	var parts []oss.UploadPart
    	for _, p := range partListResp.Parts {
    		parts = append(parts, oss.UploadPart{
    			PartNumber: p.PartNumber,
    			ETag:       p.ETag,
    		})
    	}
    
    	// Combine uploaded parts.
    	completeResult, err := client.CompleteMultipartUpload(context.TODO(), &oss.CompleteMultipartUploadRequest{
    		Bucket:   oss.Ptr(bucketName),
    		Key:      oss.Ptr(objectName),
    		UploadId: oss.Ptr(uploadId),
    		CompleteMultipartUpload: &oss.CompleteMultipartUpload{
    			Parts: parts,
    		},
    	})
    	if err != nil {
    		log.Fatalf("failed to complete multipart upload: %v", err)
    	}
    
    	// Display the combination result.
    	log.Println("Upload completed successfully.")
    	log.Printf("Bucket:   %s\n", oss.ToString(completeResult.Bucket))
    	log.Printf("Key:      %s\n", oss.ToString(completeResult.Key))
    	log.Printf("ETag:     %s\n", oss.ToString(completeResult.ETag))
    	log.Printf("Status:   %s\n", completeResult.Status)
    }
    

    Python

    # -*- coding: utf-8 -*-
    import argparse
    import alibabacloud_oss_v2 as oss
    
    def main():
        # Create a command-line argument parse and describe the purpose of the script.
        parser = argparse.ArgumentParser(description="presign multipart upload sample")
        # Add the --region argument to specify the region of the bucket. This argument is required.
        parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
        # Add the --bucket argument to specify the bucket. This argument is required.
        parser.add_argument('--bucket', help='The name of the bucket.', required=True)
        # Add the --endpoint argument to specify the endpoint for accessing OSS. This argument is optional.
        parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
        # Add the --key argument to specify the name of the object. This argument is required.
        parser.add_argument('--key', help='The name of the object.', required=True)
        # Add the --upload_id argument to specify the upload ID. This argument is required.
        parser.add_argument('--upload_id', help='The upload ID to list parts for.',required=True)
    
        args = parser.parse_args()
    
        # Specify the configuration and credentials for initializing an OSS client.
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
        cfg.region = args.region
        if args.endpoint:
            cfg.endpoint = args.endpoint
    
        client = oss.Client(cfg)
    
        try:
            # List all uploaded parts.
            list_parts_result = client.list_parts(oss.ListPartsRequest(
                bucket=args.bucket,
                key=args.key,
                upload_id=args.upload_id
            ))
    
            # Construct a list of uploaded parts.
            upload_parts = [
                oss.UploadPart(part_number=part.part_number, etag=part.etag)
                for part in list_parts_result.parts
            ]
    
            if not upload_parts:
                print("No uploaded parts found. The operation stopped.")
                return
    
            # Construct and execute a part combination request.
            request = oss.CompleteMultipartUploadRequest(
                bucket=args.bucket,
                key=args.key,
                upload_id=args.upload_id,
                complete_multipart_upload=oss.CompleteMultipartUpload(parts=upload_parts)
            )
    
            result = client.complete_multipart_upload(request)
    
            print("Parts combined.")
            print(f"ETag: {result.etag}")
            print(f"Bucket: {result.bucket}")
            print(f"Key: {result.key}")
    
        except Exception as e:
            print("Part combination failed:", e)
    
    if __name__ == "__main__":
        main()

Configurer les en-têtes pour spécifier la politique de téléchargement

Lors de la génération d'une URL présignée, vous pouvez définir une politique de téléchargement en spécifiant le paramètre Header. Par exemple, vous pouvez définir des en-têtes tels que x-oss-storage-class (pour définir la classe de stockage) et Content-Type (pour spécifier le type d'objet).

Important

Si vous incluez des en-têtes lors de la génération de l'URL présignée, vous devez fournir les mêmes en-têtes lors du téléchargement de l'objet. Si les en-têtes ne correspondent pas, OSS renverra une erreur 403 en raison d'un échec de vérification de la signature.

Pour plus d'informations sur les en-têtes pris en charge, consultez la rubrique PutObject. Vous pouvez également configurer des en-têtes personnalisés pour gérer les métadonnées des objets. Pour plus d'informations, consultez la rubrique Gérer les métadonnées des objets.

  1. Le propriétaire de l'objet génère une URL présignée avec le paramètre Header.

    Java

    import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.*;
    import com.aliyun.oss.common.comm.SignVersion;
    import com.aliyun.oss.internal.OSSHeaders;
    import com.aliyun.oss.model.GeneratePresignedUrlRequest;
    import com.aliyun.oss.model.StorageClass;
    
    import java.net.URL;
    import java.util.*;
    import java.util.Date;
    
    public class GetSignUrl {
        public static void main(String[] args) throws Throwable {
            // This example uses the public endpoint of the China (Hangzhou) region. Specify the actual endpoint.
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // 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.
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            // Specify the bucket name, for example, examplebucket.
            String bucketName = "examplebucket";
            // Specify the full path of the object, for example, exampleobject.txt. The full path of the object cannot contain the bucket name.
            String objectName = "exampleobject.txt";
            // Specify the region where the bucket is located. For example, if the bucket is in the China (Hangzhou) region, set the region to cn-hangzhou.
            String region = "cn-hangzhou";
    
            // Create an OSSClient instance.
            // When the OSSClient instance is no longer in use, call the shutdown method to release resources.
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region(region)
                    .build();
    
            // Set the request headers.
            Map<String, String> headers = new HashMap<String, String>();
            // Specify the StorageClass.
            headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
            // Specify the ContentType.
            headers.put(OSSHeaders.CONTENT_TYPE, "text/plain; charset=utf8");
    
            // Set the custom user metadata.
            Map<String, String> userMetadata = new HashMap<String, String>();
            userMetadata.put("key1","value1");
            userMetadata.put("key2","value2");
    
            URL signedUrl = null;
            try {
                // Specify the expiration time of the generated presigned URL in milliseconds. This example sets the expiration time to 1 hour.
                Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
    
                // Generate a presigned URL.
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
                // Set the expiration time.
                request.setExpiration(expiration);
    
                // Add the request headers to the request.
                request.setHeaders(headers);
                // Add the custom user metadata.
                request.setUserMetadata(userMetadata);
    
                // Generate a presigned URL for an HTTP PUT request.
                signedUrl = ossClient.generatePresignedUrl(request);
                // Print the presigned URL.
                System.out.println("signed url for putObject: " + signedUrl);
    
            } catch (OSSException oe) {
                System.out.println("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                System.out.println("Error Message:" + oe.getErrorMessage());
                System.out.println("Error Code:" + oe.getErrorCode());
                System.out.println("Request ID:" + oe.getRequestId());
                System.out.println("Host ID:" + oe.getHostId());
            } catch (ClientException ce) {
                System.out.println("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                System.out.println("Error Message:" + ce.getMessage());
            }
        }
    }       

    Go

    package main
    
    import (
    	"context"
    	"flag"
    	"log"
    	"time"
    
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
    )
    
    // Define global variables
    var (
    	region     string // Storage region
    	bucketName string // Bucket name
    	objectName string // Object name
    )
    
    // The init function initializes command-line parameters
    func init() {
    	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
    	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
    	flag.StringVar(&objectName, "object", "", "The name of the object.")
    }
    
    func main() {
    	// Parse command-line parameters
    	flag.Parse()
    
    	// Check whether the bucket name is empty
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, bucket name required")
    	}
    
    	// Check whether the region is empty
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, region required")
    	}
    
    	// Check whether the object name is empty
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, object name required")
    	}
    
    	// Load the default configuration and set the credential provider and region
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	// Create an OSS client
    	client := oss.NewClient(cfg)
    
    	// Generate a presigned URL for PutObject
    	result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
    		Bucket:      oss.Ptr(bucketName),
    		Key:         oss.Ptr(objectName),
    		ContentType:  oss.Ptr("text/plain;charset=utf8"),                    // Make sure that the ContentType specified when generating the presigned URL on the server is the same as the ContentType specified when using the URL
    		StorageClass: oss.StorageClassStandard,                              // Make sure that the StorageClass specified when generating the presigned URL on the server is the same as the StorageClass specified when using the URL
    		Metadata:    map[string]string{"key1": "value1", "key2": "value2"}, // Make sure that the Metadata specified when generating the presigned URL on the server is the same as the Metadata specified when using the URL
    	},
    		oss.PresignExpires(10*time.Minute),
    	)
    	if err != nil {
    		log.Fatalf("failed to put object presign %v", err)
    	}
    
    	log.Printf("request method:%v\n", result.Method)
    	log.Printf("request expiration:%v\n", result.Expiration)
    	log.Printf("request url:%v\n", result.URL)
    	if len(result.SignedHeaders) > 0 {
    		// If the returned result contains signature headers, set the corresponding request headers when sending a PUT request by using the presigned URL
    		log.Printf("signed headers:\n")
    		for k, v := range result.SignedHeaders {
    			log.Printf("%v: %v\n", k, v)
    		}
    	}
    }
    

    Python

    import argparse
    import requests
    import alibabacloud_oss_v2 as oss
    
    from datetime import datetime, timedelta
    
    # Create a command-line argument parser and describe the script's purpose: to generate a presigned PUT request URL for an object (Presign Put Object).
    parser = argparse.ArgumentParser(description="presign put object sample")
    
    # Define command-line arguments, including the required region, bucket name, endpoint, and object key.
    parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
    parser.add_argument('--bucket', help='The name of the bucket.', required=True)
    parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
    parser.add_argument('--key', help='The name of the object.', required=True)
    
    def main():
        # Parse command-line arguments to get user-provided values.
        args = parser.parse_args()
    
        # Load access credential information from environment variables for identity verification.
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    
        # Create a configuration object using the SDK's default settings and set the authentication provider.
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
    
        # Set the region property of the configuration object based on the command-line arguments.
        cfg.region = args.region
    
        # If a custom endpoint is provided, update the endpoint property in the configuration object.
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
    
        # Initialize the OSS client with the above configuration to interact with OSS.
        client = oss.Client(cfg)
    
        # Send a request to generate a presigned PUT request for the specified object.
        pre_result = client.presign(oss.PutObjectRequest(
            bucket=args.bucket,  # Bucket name
            key=args.key,  # Object key
            content_type='text/plain;charset=utf8',  # Specify the content type.
            storage_class='Standard',  # Specify the storage class.
            metadata={
                'key1': 'value1',   # Specify the metadata.
                'key2': 'value2'    # Specify the metadata.
            }
        ),expires=timedelta(seconds=3600)) # Set the expiration time in seconds. Here, it is set to 3600 seconds.
    
        # Print the method, expiration time, and URL of the presigned request to confirm the validity of the link.
        print(f'method: {pre_result.method},'
              f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
              f' url: {pre_result.url}'
              )
    
        # Print the signed header information of the presigned request. This information will be included in the HTTP headers when the actual request is sent.
        for key, value in pre_result.signed_headers.items():
            print(f'signed headers key: {key}, signed headers value: {value}')
    
    # When this script is executed directly, call the main function to start the processing logic.
    if __name__ == "__main__":
        main()  # Script entry point. The program flow starts here.
  2. Les autres utilisateurs téléchargent des objets à l'aide de l'URL présignée tout en fournissant les mêmes en-têtes.

    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  with the signed URL.
            URL signedUrl = new URL("");
    
            // 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 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 that of the request headers when the signed URL is generated.
            Map headers = new HashMap();
            //Specify the storage class.
            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 is the same as the user metadata when the signed URL is generated.
            Map userMetadata = new HashMap();
            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 inconsistent with those 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 network 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 the 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 network library")
    	} else {
    		fmt.Println("Upload failed")
    	}
    	body, _ := ioutil.ReadAll(resp.Body)
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// Replace  with the 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 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 is the same as the user metadata when the signed URL is generated.
    	metadata := map[string]string{
    		"key1": "value1",
    		"key2": "value2",
    	}, 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 pre-signed 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: Optional. Specify the request headers.
        :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 network library")
                else:
                    print("Upload failed")
                print(response.text)
        except Exception as e:
            print(f"An error occurred: {e}")
    
    if __name__ == "__main__":
        # Replace  with the signed URL.
        signed_url = ""
       
        # Specify the full path of the local file. By default, if you do not specify the full path of a 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 when the signed URL is 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 {
            // Update the request headers and specify the metadata prefix.
            for (const [key, value] of Object.entries(metadata)) {
                headers[`x-oss-meta-${key}`] = value;
            }
    
            // Read the file 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 network 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  with the signed URL.
        const signedUrl = "";
    
        // Specify the full path of the local file. By default, if you do not specify the full path of a 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 is the same as the user metadata when the signed URL is generated.
        const metadata = {
             "key1": "value1",
             "key2": "value2"
        };
    
        await uploadFile(signedUrl, filePath, headers, metadata);
    })();
    

    Browser.js

    Important

    Lorsque vous utilisez le code Browser.js pour télécharger un objet via une URL présignée, vous pouvez rencontrer une erreur 403 indiquant une incohérence de signature. Cette erreur survient en raison d'un échec de vérification de la signature, car le navigateur ajoute automatiquement l'en-tête de requête Content-Type, un élément qui n'était pas spécifié lors de la génération de l'URL présignée. Pour résoudre cette erreur, vous devez spécifier l'en-tête Content-Type lorsque vous générez une URL présignée destinée à être utilisée dans le code Browser.js pour télécharger des données vers OSS.

    C++

    #include <iostream>
    #include <fstream>
    #include <curl/curl.h>
    #include <map>
    #include <string>
    
    // 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 signed 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 local 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 << "Upload 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;
            }
    
            // Clean up
            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. By default, if you do not specify the full path of the local file, 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;
    }
    

    C

    Contrairement à l'exemple C++, qui s'appuie sur std::string et <iostream>/<fstream> et nécessite donc un compilateur C++, l'exemple suivant est écrit en C standard (en utilisant char*, <stdio.h> et libcurl) afin qu'il puisse être compilé et exécuté avec une chaîne d'outils C simple.

    #include <stdio.h>
    #include <stdlib.h>
    #include <curl/curl.h>
    
    void upload_file(const char *signed_url, const char *file_path) {
        CURL *curl;
        CURLcode res;
        curl_global_init(CURL_GLOBAL_DEFAULT);
        curl = curl_easy_init();
        if (curl) {
            curl_easy_setopt(curl, CURLOPT_URL, signed_url);
            curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
            FILE *file = fopen(file_path, "rb");
            if (!file) {
                fprintf(stderr, "Failed to open file: %s\n", file_path);
                return;
            }
            fseek(file, 0, SEEK_END);
            long file_size = ftell(file);
            fseek(file, 0, SEEK_SET);
            curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_size);
            curl_easy_setopt(curl, CURLOPT_READDATA, file);
            res = curl_easy_perform(curl);
            if (res != CURLE_OK)
                fprintf(stderr, "Upload failed: %s\n", curl_easy_strerror(res));
            else {
                long http_code = 0;
                curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
                printf("HTTP status code: %ld\n", http_code);
            }
            fclose(file);
            curl_easy_cleanup(curl);
        }
        curl_global_cleanup();
    }
    
    int main(int argc, char *argv[]) {
        if (argc != 3) {
            printf("Usage: %s <presigned URL> <file path>\n", argv[0]);
            return 1;
        }
        upload_file(argv[1], argv[2]);
        return 0;
    }
    

    Compilez et exécutez :

    gcc -o oss_upload oss_upload.c -lcurl
    ./oss_upload <presigned URL> <file path>
    

    Le programme lit la taille du fichier local situé à file_path, l'envoie en tant que corps d'une requête PUT vers signed_url en utilisant libcurl, et affiche le code d'état HTTP renvoyé (200 indique que le téléchargement a réussi).

    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. By default, if you do not specify the full path, 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, "Upload status code: " + responseCode);
                    if (responseCode == 200) {
                        Log.d(TAG, "The object is uploaded by using the network 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();
                }
            }
        }
    }
    

Configurer le rappel de téléchargement

Lors du téléchargement d'un objet, vous pouvez inclure un paramètre de rappel dans l'URL pré-signée afin de notifier automatiquement votre serveur d'application une fois le téléchargement terminé. Pour plus d'informations sur le fonctionnement des rappels, consultez la rubrique Rappel.

  1. Le propriétaire de l'objet génère une URL pré-signée (avec la méthode PUT) contenant les paramètres de rappel de téléchargement requis.

    Python

    import argparse
    import requests
    import alibabacloud_oss_v2 as oss
    
    from datetime import datetime, timedelta
    
    # Create a command-line argument parser and describe the script's purpose: to generate a presigned PUT request URL for an object (Presign Put Object).
    parser = argparse.ArgumentParser(description="presign put object sample")
    
    # Define command-line arguments, including the required region, bucket name, endpoint, and object key.
    parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
    parser.add_argument('--bucket', help='The name of the bucket.', required=True)
    parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
    parser.add_argument('--key', help='The name of the object.', required=True)
    
    def main():
        # Parse command-line arguments to get user-provided values.
        args = parser.parse_args()
    
        # Load access credential information from environment variables for identity verification.
        credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    
        # Create a configuration object using the SDK's default settings and set the authentication provider.
        cfg = oss.config.load_default()
        cfg.credentials_provider = credentials_provider
    
        # Set the region property of the configuration object based on the command-line arguments.
        cfg.region = args.region
    
        # If a custom endpoint is provided, update the endpoint property in the configuration object.
        if args.endpoint is not None:
            cfg.endpoint = args.endpoint
    
        # Initialize the OSS client with the above configuration to interact with OSS.
        client = oss.Client(cfg)
    
        # Send a request to generate a presigned PUT request for the specified object.
        pre_result = client.presign(oss.PutObjectRequest(
            bucket=args.bucket,  # Bucket name
            key=args.key,  # Object key
            content_type='text/plain;charset=utf8',  # Specify the content type.
            storage_class='Standard',  # Specify the storage class.
            metadata={
                'key1': 'value1',   # Specify the metadata.
                'key2': 'value2'    # Specify the metadata.
            }
        ),expires=timedelta(seconds=3600)) # Set the expiration time in seconds. Here, it is set to 3600 seconds.
    
        # Print the method, expiration time, and URL of the presigned request to confirm the validity of the link.
        print(f'method: {pre_result.method},'
              f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},'
              f' url: {pre_result.url}'
              )
    
        # Print the signed header information of the presigned request. This information will be included in the HTTP headers when the actual request is sent.
        for key, value in pre_result.signed_headers.items():
            print(f'signed headers key: {key}, signed headers value: {value}')
    
    # When this script is executed directly, call the main function to start the processing logic.
    if __name__ == "__main__":
        main()  # Script entry point. The program flow starts here.

    Go

    package main
    
    import (
    	"context"
    	"encoding/base64"
    	"encoding/json"
    	"flag"
    	"log"
    	"time"
    
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
    	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
    )
    
    // Define global variables.
    var (
    	region     string // The region in which the bucket is located.
    	bucketName string // The name of the bucket.
    	objectName string // The name of the object.
    )
    
    // Define the init function to initialize the command-line parameters.
    func init() {
    	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
    	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
    	flag.StringVar(&objectName, "object", "", "The name of the object.")
    }
    
    func main() {
    	// Parse the command-line parameters.
    	flag.Parse()
    
    	// Check whether the bucket name is empty.
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, bucket name required")
    	}
    
    	// Check whether the region is empty.
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, region required")
    	}
    
    	// Check whether the object name is empty.
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, object name required")
    	}
    
    	// Load the default configurations and specify the credential provider and region.
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	// Create an OSS client.
    	client := oss.NewClient(cfg)
    
    	// Specify the callback parameters.
    	callbackMap := map[string]string{
    		"callbackUrl":      "http://example.com:23450",                                                                   // Specify the URL of the callback server, such as https://example.com:23450.
    		"callbackBody":     "bucket=${bucket}&object=${object}&size=${size}&my_var_1=${x:my_var1}&my_var_2=${x:my_var2}", // Specify the callback request body.
    		"callbackBodyType": "application/x-www-form-urlencoded",                                                          // Specify the Content-Type of the callback request body.
    	}
    
    	// Convert the callback parameter configurations into a JSON string, then Base64-encode it to pass the callback settings.
    	callbackStr, err := json.Marshal(callbackMap)
    	if err != nil {
    		log.Fatalf("failed to marshal callback map: %v", err)
    	}
    	callbackBase64 := base64.StdEncoding.EncodeToString(callbackStr)
    
    	callbackVarMap := map[string]string{}
    	callbackVarMap["x:my_var1"] = "thi is var 1"
    	callbackVarMap["x:my_var2"] = "thi is var 2"
    	callbackVarStr, err := json.Marshal(callbackVarMap)
    	if err != nil {
    		log.Fatalf("failed to marshal callback var: %v", err)
    	}
    	callbackVarBase64 := base64.StdEncoding.EncodeToString(callbackVarStr)
    
    	// Generate a presigned URL with the PutObject method.
    	result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
    		Bucket:      oss.Ptr(bucketName),
    		Key:         oss.Ptr(objectName),
    		Callback:    oss.Ptr(callbackBase64),    // Specify the callback parameters, which are Base64-encoded JSON strings.
    		CallbackVar: oss.Ptr(callbackVarBase64), // Specify the custom callback parameters, which are Base64-encoded JSON strings.
    	},
    		oss.PresignExpires(10*time.Minute),
    	)
    	if err != nil {
    		log.Fatalf("failed to put object presign %v", err)
    	}
    
    	log.Printf("request method:%v\n", result.Method)
    	log.Printf("request expiration:%v\n", result.Expiration)
    	log.Printf("request url:%v\n", result.URL)
    	if len(result.SignedHeaders) > 0 {
    // When the response includes signature headers, you must set the corresponding request headers when sending a PUT request with the presigned URL.
    		log.Printf("signed headers:\n")
    		for k, v := range result.SignedHeaders {
    			log.Printf("%v: %v\n", k, v)
    		}
    	}
    }
    

    Java

    import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.CredentialsProviderFactory;
    import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
    import com.aliyun.oss.common.comm.SignVersion;
    import com.aliyun.oss.internal.OSSHeaders;
    import com.aliyun.oss.model.GeneratePresignedUrlRequest;
    
    import java.net.URL;
    import java.text.SimpleDateFormat;
    import java.util.*;
    
    public class OssPresignExample {
        public static void main(String[] args) throws Throwable {
            // This example uses the public endpoint of the China (Hangzhou) region. Specify the actual endpoint.
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // 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.
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            // Specify the bucket name, for example, examplebucket.
            String bucketName = "examplebucket";
            // Specify the full path of the object, for example, exampleobject.txt. The full path of the object cannot contain the bucket name.
            String objectName = "exampleobject.txt";
            // Specify the region where the bucket is located. For example, if the bucket is in the China (Hangzhou) region, set the region to cn-hangzhou.
            String region = "cn-hangzhou";
    
            // Create an OSSClient instance.
            // When the OSSClient instance is no longer in use, call the shutdown method to release resources.
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region(region)
                    .build();
    
            URL signedUrl = null;
            try {
                // Construct the callback parameters.
                String callbackUrl = "http://www.example.com/callback";
                String callbackBody = "{\"callbackUrl\":\"" + callbackUrl + "\",\"callbackBody\":\"bucket=${bucket}&object=${object}&my_var_1=${x:var1}&my_var_2=${x:var2}\"}";
                String callbackBase64 = Base64.getEncoder().encodeToString(callbackBody.getBytes());
    
                String callbackVarJson = "{\"x:var1\":\"value1\",\"x:var2\":\"value2\"}";
                String callbackVarBase64 = Base64.getEncoder().encodeToString(callbackVarJson.getBytes());
                // Set the request headers.
                Map<String, String> headers = new HashMap<String, String>();
                // Specify CALLBACK.
                headers.put(OSSHeaders.OSS_HEADER_CALLBACK, callbackBase64);
                // Specify CALLBACK-VAR.
                headers.put(OSSHeaders.OSS_HEADER_CALLBACK_VAR, callbackVarBase64);
    
                // Set the expiration time (3600 seconds later).
                Date expiration = new Date(new Date().getTime() + 3600 * 1000);
    
                // Format the expiration time.
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
                dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
                String expirationStr = dateFormat.format(expiration);
    
                // Construct the request.
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName);
                request.setMethod(HttpMethod.PUT);
                request.setExpiration(expiration);
                // Add the request headers to the request.
                request.setHeaders(headers);
    
                // Print the callback and callback-var parameters.
                System.out.println("callback:"+callbackBase64);
                System.out.println("callback-var:"+callbackVarBase64);
    
                // Generate the presigned URL.
                URL url = ossClient.generatePresignedUrl(request);
    
                // Print the result.
                System.out.println("method: PUT,");
                System.out.println(" expiration: " + expirationStr + ",");
                System.out.println(" url: " + url);
    
            } catch (OSSException oe) {
                System.out.println("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                System.out.println("Error Message:" + oe.getErrorMessage());
                System.out.println("Error Code:" + oe.getErrorCode());
                System.out.println("Request ID:" + oe.getRequestId());
                System.out.println("Host ID:" + oe.getHostId());
            } catch (ClientException ce) {
                System.out.println("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                System.out.println("Error Message:" + ce.getMessage());
            }
        }
    }

    PHP

    <?php
    
    // Include the autoload file so that the required dependencies can be loaded.
    require_once __DIR__ . '/../vendor/autoload.php';
    
    use AlibabaCloud\Oss\V2 as Oss;
    
    // Specify descriptions for command line arguments.
    $optsdesc = [
        "region" => ['help' => 'The region in which the bucket is located.', 'required' => True], // (Required) Specify the region in which the bucket is located.
        "endpoint" => ['help' => 'The domain names that other services can use to access OSS.', 'required' => False], // (Optional) Specify the endpoint that can be used by other services to access OSS.
        "bucket" => ['help' => 'The name of the bucket', 'required' => True], // (Required) Specify the name of the bucket.
        "key" => ['help' => 'The name of the object', 'required' => True], // (Required) Specify the name of the object.
    ];
    
    // Convert the descriptions to a list of long options required by getopt.
    // Add a colon (:) to the end of each parameter to indicate that a value is required.
    $longopts = \array_map(function ($key) {
        return "$key:";
    }, array_keys($optsdesc));
    
    // Parse the command-line parameters.
    $options = getopt("", $longopts);
    
    // Check whether the required parameters are provided.
    foreach ($optsdesc as $key => $value) {
        if ($value['required'] === True && empty($options[$key])) {
            $help = $value['help']; // Get the help information for the parameter
            echo "Error: the following arguments are required: --$key, $help" . PHP_EOL;
            exit(1); // Exit the program if the required parameters are not configured.
        }
    }
    
    // Obtain values from the parsed parameters.
    $region = $options["region"]; // The region in which the bucket is located
    $bucket = $options["bucket"]; // The name of the bucket
    $key = $options["key"];       // The name of the object
    
    // Load access credentials from environment variables.
    // Use EnvironmentVariableCredentialsProvider to retrieve the AccessKey ID and AccessKey secret from environment variables.
    $credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();
    
    // Use the default configurations of the SDK.
    $cfg = Oss\Config::loadDefault();
    $cfg->setCredentialsProvider($credentialsProvider); // Set the credentials provider
    $cfg->setRegion($region); // Set the region in which the bucket is located
    if (isset($options["endpoint"])) {
        $cfg->setEndpoint($options["endpoint"]); // Set the endpoint if provided
    }
    
    // Create an OSS client instance.
    $client = new Oss\Client($cfg);
    
    // Add x-oss-callback and x-oss-callback-var headers
    // Define the webhook address
    $call_back_url = "http://www.example.com/callback";
    
    // Construct callback parameters: specify the webhook address and request body, and use Base64 encoding
    // Use placeholders {var1} and {var2} to replace ${x:var1} and ${x:var2}
    $callback_body_template = "bucket={bucket}&object={object}&my_var_1={var1}&my_var_2={var2}";
    $callback_body_replaced = str_replace(
        ['{bucket}', '{object}', '{var1}', '{var2}'],
        [$bucket, $key, 'value1', 'value2'],
        $callback_body_template
    );
    $callback = base64_encode(json_encode([
        "callbackUrl" => $call_back_url,
        "callbackBody" => $callback_body_replaced
    ]));
    
    // Construct custom variables (callback-var) using Base64 encoding
    $callback_var = base64_encode(json_encode([
        "x:var1" => "value1",
        "x:var2" => "value2"
    ]));
    
    // Create a PutObjectRequest object to upload the object.
    // Note: contentType, metadata, and headers parameters are added here for signature calculation.
    $request = new Oss\Models\PutObjectRequest(
        bucket: $bucket,
        key: $key,
        callback:$callback,
        callbackVar:$callback_var,
    );
    
    // Call the presign method to generate a presigned URL.
    $result = $client->presign($request);
    
    // Print the presigned result, output the presigned URL that users can directly use to upload objects
    print(
        'put object presign result:' . var_export($result, true) . PHP_EOL .
        'put object url:' . $result->url . PHP_EOL
    );
    
  2. Les autres utilisateurs téléchargent des objets en utilisant l'URL pré-signée avec la méthode PUT.

    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 authorized URL.
            URL signedUrl = new URL("<signedUrl>");
    
            // Specify the full path of the local file. If you do not specify a local path, the file is uploaded from the project path of the sample program by default.
            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 returned for the upload:"+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("Upload successful using network library.");
                }
                System.out.println(response.toString());
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                 if (response != null) {
                     response.close();
                 }
                 if (httpClient != null) {
                     httpClient.close();
                 }
             }
        }
    }       

    Go

    package main
    
    import (
    	"fmt"
    	"io"
    	"net/http"
    	"os"
    )
    
    func uploadFile(signedUrl, filePath string) error {
    	// Open the file.
    	file, err := os.Open(filePath)
    	if err != nil {
    		return fmt.Errorf("Unable to open file: %w", err)
    	}
    	defer file.Close()
    
    	// Create a new 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 request: %w", err)
    	}
    
    	// Send the request.
    	resp, err := client.Do(req)
    	if err != nil {
    		return fmt.Errorf("Failed to send 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 response: %w", err)
    	}
    
    	fmt.Printf("Status code returned for the upload: %d\n", resp.StatusCode)
    	if resp.StatusCode == 200 {
    		fmt.Println("Upload successful using network library.")
    	}
    	fmt.Println(string(body))
    
    	return nil
    }
    
    func main() {
    	// Replace <signedUrl> with the authorized URL.
    	signedUrl := "<signedUrl>"
    
    	// Specify the full path of the local file. If you do not specify a local path, the file is uploaded from the project path of the sample program by default.
    	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 file.
            with open(file_path, 'rb') as file:
                # Send a PUT request to upload the file.
                response = requests.put(signed_url, data=file)
         
            print(f"Status code returned for the upload: {response.status_code}")
            if response.status_code == 200:
                print("Upload successful using network library.")
            print(response.text)
     
        except Exception as e:
            print(f"An error occurred: {e}")
    
    if __name__ == "__main__":
        # Replace <signedUrl> with the authorized URL.
        signed_url = "<signedUrl>"
        
        # Specify the full path of the local file. If you do not specify a local path, the file is uploaded from the project path of the sample program by default.
        file_path = "C:\\Users\\demo.txt"
    
        upload_file(signed_url, file_path)
    

    Node.js

    Important

    Si vous rencontrez une erreur de non-correspondance de signature 403 lors du téléchargement d'un fichier avec Node.js, cela est généralement dû au fait que l'en-tête de requête Content-Type est explicitement défini dans la demande de téléchargement, mais n'a pas été inclus lors de la génération de l'URL pré-signée. Cette incompatibilité entraîne un échec de la vérification de la signature. Pour résoudre ce problème, ne définissez pas l'en-tête de requête Content-Type dans la demande de téléchargement ou spécifiez l'en-tête de requête Content-Type lors de la génération de l'URL pré-signée.

    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 file.
            const response = await axios.put(signedUrl, fileStream);
    
            console.log(`Status code returned for the upload: ${response.status}`);
            if (response.status === 200) {
                console.log('Upload successful using network library.');
            }
            console.log(response.data);
        } catch (error) {
            console.error(`An error occurred: ${error.message}`);
        }
    }
    
    // Main function.
    (async () => {
        // Replace <signedUrl> with the authorized URL.
        const signedUrl = '<signedUrl>';
        
        // Specify the full path of the local file. If you do not specify a local path, the file is uploaded from the project path of the sample program by default.
        const filePath = 'C:\\Users\\demo.txt';
    
        await uploadFile(signedUrl, filePath);
    })();

    browser.js

    Important

    Si vous rencontrez une erreur de non-correspondance de signature 403 lors du téléchargement d'un fichier avec Browser.js, cela est généralement dû au fait que le navigateur ajoute automatiquement l'en-tête de requête Content-Type et que cet en-tête n'a pas été inclus lors de la génération de l'URL pré-signée. Cette incompatibilité entraîne un échec de la vérification de la signature. Pour résoudre ce problème, vous devez spécifier l'en-tête de requête Content-Type lors de la génération de l'URL pré-signée.

    <!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 a file -->
        <input type="file" id="fileInput" />
        <button id="uploadButton">Upload File</button>
    
        <script>
            // Replace this 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 the 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,  // Upload the entire file directly.
                });
    
                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. If you do not specify a local path, the file is uploaded from the project path of the sample program by default.
    var filePath = "C:\\Users\\demo.txt";
    // Replace <signedUrl> with the authorized URL.
    var presignedUrl = "<signedUrl>";
    
    // Create an HTTP client and open the 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($"Upload successful! Status code: {response.StatusCode}");
        Console.WriteLine("Response headers:");
        foreach (var header in response.Headers)
        {
            Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
        }
    }
    else
    {
        string responseContent = await response.Content.ReadAsStringAsync();
        Console.WriteLine($"Upload failed! Status 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) {
            // Set the 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 file.
            FILE *file = fopen(filePath.c_str(), "rb");
            if (!file) {
                std::cerr << "Unable to open file: " << filePath << std::endl;
                return;
            }
    
            // Get the file size.
            fseek(file, 0, SEEK_END);
            long fileSize = ftell(file);
            fseek(file, 0, SEEK_SET);
    
            // Set the file size.
            curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
    
            // Set 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 returned for the upload: " << httpCode << std::endl;
    
                if (httpCode == 200) {
                    std::cout << "Upload successful using network library." << std::endl;
                }
            }
    
            // Close the file.
            fclose(file);
    
            // Clean up.
            curl_easy_cleanup(curl);
        }
    
        curl_global_cleanup();
    }
    
    int main() {
        // Replace <signedUrl> with the authorized URL.
        std::string signedUrl = "<signedUrl>";
    
        // Specify the full path of the local file. If you do not specify a local path, the file is uploaded from the project path of the sample program by default.
        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 returned for the upload: " + responseCode);
    
                    if (responseCode == 200) {
                        Log.d(TAG, "Upload successful using network library.");
                    }
    
                    return "Upload complete. 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 authorized URL.
            String signedUrl = "<signedUrl>";
            // Specify the full path of the local file. If you do not specify a local path, the file is uploaded from the project path of the sample program by default.
            String filePath = "C:\\Users\\demo.txt";
            activity.uploadFile(signedUrl, filePath);
        }
    }
    

Informations complémentaires

Qu'est-ce qu'une URL pré-signée ?

Une URL pré-signée est un lien sécurisé à durée limitée qui accorde un accès temporaire à un objet spécifique dans OSS. Pour générer une URL pré-signée, le client crée une signature cryptographique en utilisant la paire AccessKey, le chemin de la ressource et l'heure d'expiration. Cette signature est ajoutée à l'URL d'origine, produisant ainsi un lien d'accès limité dans le temps. Le format type d'une URL pré-signée est https://BucketName.Endpoint/Object?Signature.

Lorsqu'un tiers accède à l'URL pré-signée, OSS valide la signature. Si la signature est invalide ou expirée, l'accès est refusé.

Exemple d'URL pré-signée :

https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-process=image%2Fresize%2Cp_10&x-oss-date=20241115T095058Z&x-oss-expires=3600&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI****************%2F20241115%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-signature=6e7a********************************

Grâce à cette méthode, les propriétaires d'objets peuvent fournir un accès limité aux tiers en toute sécurité, sans jamais révéler leurs clés privées.

Scénarios applicables

  • Partage temporaire d'objets : pour permettre à un tiers de télécharger ou d'envoyer un objet spécifique de manière sécurisée, le backend génère une URL pré-signée à durée limitée et la fournit au frontend. Le tiers peut ensuite utiliser cette URL pour effectuer l'opération dans la plage horaire autorisée, garantissant ainsi un accès aux données contrôlé et sécurisé.

  • Partage flexible d'objets : les propriétaires d'objets peuvent partager leurs objets en toute sécurité en générant des URL pré-signées et en les distribuant par e-mail ou via des applications de messagerie aux tiers. Les destinataires n'ont qu'à coller les URL dans leur navigateur pour télécharger les objets souhaités.

FAQ

Comment puis-je accorder des permissions plus larges sur mes ressources OSS à d'autres utilisateurs, au lieu de me limiter uniquement à l'accès en envoi ?

En plus des URL pré-signées, OSS propose une méthode d'autorisation temporaire plus flexible : les identifiants d'accès temporaires STS. Contrairement aux URL pré-signées, qui sont limitées à des actions spécifiques, les identifiants STS permettent aux tiers d'effectuer un éventail plus large d'opérations, telles que la liste, la copie ou la suppression d'objets, avec des permissions limitées dans le temps. Pour plus d'informations, consultez Accéder à OSS à l'aide d'identifiants temporaires STS.

Puis-je restreindre l'accès à mon objet afin que seules les requêtes provenant de sites web spécifiques soient autorisées, tout en bloquant toutes les autres ?

Oui, vous pouvez activer la protection contre le hotlinking basée sur le Referer en définissant une liste d'autorisation pour restreindre l'accès à vos ressources OSS. Cela garantit que seuls les sites web autorisés (comme le vôtre) peuvent accéder aux ressources, en bloquant les requêtes directes provenant de sources non autorisées ou malveillantes. Pour plus d'informations, consultez Configurer une liste d'autorisation ou de blocage Referer pour empêcher d'autres sites web de créer des liens vers vos objets OSS.

Pourquoi mon navigateur affiche-t-il des erreurs CORS et comment les résoudre ?

Votre bucket ne dispose d'aucune politique de partage de ressources cross-origin (CORS) configurée ou les paramètres CORS actuels sont incorrects. Vérifiez et ajustez les paramètres en consultant la rubrique CORS.

Pourquoi mon navigateur affiche-t-il une erreur « 405 Method Not Allowed » et comment la résoudre ?

La méthode de requête est incorrecte. Lors de l'envoi d'un objet à l'aide d'une URL pré-signée, assurez-vous d'utiliser la méthode PUT, et non la méthode POST.

Pourquoi OSS stocke-t-il une chaîne URL au lieu du fichier réel lorsque je passe une URL comme corps de l'envoi ?

Lorsque vous utilisez une URL pré-signée pour envoyer des données, le mécanisme d'URL pré-signée constitue un canal d'envoi direct du client vers OSS. OSS stocke les octets bruts du corps de la requête PUT en tant qu'objet ; il n'interprète pas le corps comme une URL ni ne récupère le contenu depuis une adresse externe pour votre compte.

Si vous passez une chaîne URL (par exemple, https://example.com/video.mp4) comme corps de la requête PUT, OSS stocke cette chaîne URL en tant que contenu de l'objet. Par exemple, une chaîne URL de 66 octets donne lieu à un objet de 66 octets contenant uniquement le texte de l'URL, et non le fichier situé à cette adresse.

Pour stocker un fichier hébergé à une URL externe dans un bucket OSS, votre serveur doit d'abord télécharger les données du fichier, puis envoyer les octets réels à OSS en utilisant PutObject ou l'envoi multipartie. L'exemple Python suivant montre comment implémenter ce relais côté serveur :

import requests
import alibabacloud_oss_v2 as oss

def relay_url_to_oss(external_url, bucket_name, object_key, region):
    # Step 1: Download the file from the external URL.
    response = requests.get(external_url, stream=True)
    response.raise_for_status()
    file_content = response.content

    # Step 2: Configure the OSS client.
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider
    cfg.region = region
    client = oss.Client(cfg)

    # Step 3: Upload the downloaded data to OSS.
    result = client.put_object(oss.PutObjectRequest(
        bucket=bucket_name,
        key=object_key,
        body=file_content,
    ))
    print(f"Upload successful. ETag: {result.etag}")

# Example usage
relay_url_to_oss(
    external_url="https://example.com/sample.jpg",
    bucket_name="examplebucket",
    object_key="sample.jpg",
    region="cn-hangzhou"
)