Par défaut, les objets stockés dans un compartiment Object Storage Service (OSS) sont privés et seul leur propriétaire peut y accéder. Cette rubrique explique comment utiliser le SDK Go V2 pour OSS afin de générer une URL signée pour la méthode PUT avec une durée de vie (TTL). Cela permet à d'autres utilisateurs de téléverser temporairement des objets. L'URL peut être utilisée plusieurs fois avant son expiration. Une fois l'URL expirée, vous devez en générer une nouvelle.
Précautions
L'exemple de code présenté dans cette rubrique utilise l'ID de région
cn-hangzhouet un endpoint public pour la région Chine (Hangzhou). Si vous souhaitez accéder à OSS depuis d'autres produits Alibaba Cloud situés dans la même région, utilisez un endpoint de réseau interne. Pour plus d'informations sur les correspondances entre les régions OSS et les endpoints, consultez Régions et endpoints.Les exemples de cette rubrique montrent comment obtenir les identifiants d'accès à partir de variables d'environnement. Pour savoir comment configurer vos identifiants d'accès, consultez Configurer les identifiants d'accès.
-
Permissions are not required to generate a presigned URL. However, a third party can successfully upload a file using the presigned URL only if you have the
oss:PutObjectpermission. For more information, see Grant a custom access policy to a RAM user. L'exemple de code de cette rubrique utilise une URL signée V4, dont la durée de vie maximale est de 7 jours. Pour plus d'informations, consultez Signature V4 (recommandée).
Processus
Pour téléverser un fichier à l'aide d'une URL signée et de la méthode PUT, suivez la procédure ci-dessous :
Définition de la méthode
Appelez l'opération Presign pour générer une URL signée. Cette opération accorde un accès temporaire à un objet dans un compartiment. L'URL signée peut être utilisée plusieurs fois avant son expiration.
L'opération Presign est définie comme suit :
func (c *Client) Presign(ctx context.Context, request any, optFns ...func(*PresignOptions)) (result *PresignResult, err error)
Paramètres de la requête
|
Paramètre |
Type |
Description |
|
ctx |
context.Context |
Le contexte de la requête. |
|
request |
*PutObjectRequest |
Le nom de l'opération pour laquelle générer l'URL signée. |
|
optFns |
...func(*PresignOptions) |
(Facultatif) La durée de vie (TTL). Si vous ne spécifiez pas ce paramètre, la durée de vie par défaut est de 15 minutes. |
Les options PresignOptions sont listées ci-dessous :
|
Option |
Type |
Description |
|
Expires |
time.Duration |
La durée restante jusqu'à l'expiration de l'URL. Par exemple, pour définir une durée de vie de 30 minutes, utilisez |
|
Expiration |
time.Time |
L'heure d'expiration absolue. |
Pour les signatures V4, la durée de vie maximale est de 7 jours. Si vous définissez à la fois Expiration et Expires, Expiration prend le dessus.
Valeurs de retour
|
Valeur de retour |
Type |
Description |
|
result |
*PresignResult |
Le résultat. Il inclut l'URL signée, la méthode HTTP, l'heure d'expiration et les en-têtes de requête signés. |
|
err |
error |
Le statut de la requête. Si la requête échoue, |
Les valeurs de retour PresignResult sont listées ci-dessous :
|
Paramètre |
Type |
Description |
|
Method |
string |
La méthode HTTP correspondant à l'opération. Par exemple, pour l'opération |
|
URL |
string |
URL présignée |
|
Expiration |
time.Time |
L'heure d'expiration de l'URL signée. |
|
SignedHeaders |
map[string]string |
Les en-têtes de requête signés. Par exemple, si vous définissez |
Exemple de code
-
Le propriétaire de l'objet génère une URL signée pour la méthode PUT.
ImportantLorsque vous générez une URL signée pour la méthode PUT, si vous spécifiez des en-têtes de requête, vous devez inclure les mêmes en-têtes lors de l'utilisation de l'URL signée pour envoyer la requête PUT. Si les en-têtes ne correspondent pas, la requête échoue et une erreur de signature est renvoyée.
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 storage region. bucketName string // The bucket name. objectName string // The object name. ) // The init function is used to initialize command-line parameters. func init() { flag.StringVar(®ion, "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 if the bucket name is empty. if len(bucketName) == 0 { flag.PrintDefaults() log.Fatalf("invalid parameters, bucket name required") } // Check if the region is empty. if len(region) == 0 { flag.PrintDefaults() log.Fatalf("invalid parameters, region required") } // Check if 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 signed URL for PutObject. 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 result includes signed headers, you must set the corresponding request headers when you send a PUT request using the signed URL. log.Printf("signed headers:\n") for k, v := range result.SignedHeaders { log.Printf("%v: %v\n", k, v) } } } -
Un autre utilisateur téléverse un objet à l'aide de l'URL signée pour 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
ImportantSi vous rencontrez une erreur de non-correspondance de signature 403 lors du téléversement d'un fichier avec Node.js, cela est généralement dû au fait que l'en-tête de requête
Content-Typeest explicitement défini dans la requête de téléversement, mais qu'il 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êteContent-Typedans la requête de téléversement, ou spécifiez l'en-tête de requêteContent-Typelors 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
ImportantSi vous rencontrez une erreur de non-correspondance de signature 403 lors du téléversement 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êteContent-Typelors 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); } }
Scénarios courants
Téléverser un fichier avec des en-têtes de requête spécifiés et des métadonnées personnalisées à l'aide d'une URL signée
Effectuer un téléversement multipartie à l'aide d'une URL signée
Téléverser un fichier et définir des paramètres de rappel de téléversement à l'aide d'une URL signée
Références
Pour l'exemple de code complet concernant les URL signées, consultez Exemple GitHub.
Pour plus d'informations sur l'opération API relative aux URL signées, consultez Presign.