De forma predeterminada, los objetos de un bucket de Object Storage Service (OSS) son privados. Solo el propietario del objeto tiene el permiso para cargar objetos. Puede usar el SDK de OSS para Python y generar una URL prefirmada que permita a otros usuarios cargar un objeto. Al generar la URL prefirmada, puede establecer un tiempo de expiración. La URL se puede usar varias veces antes de que expire. Si se ejecutan varias operaciones de carga, es posible que el objeto se sobrescriba. Una vez que la URL expira, ya no sirve para realizar cargas; en ese caso, debe generar una nueva URL prefirmada.
Notas de uso
El código de ejemplo de este tema usa la región China (Hangzhou) (
cn-hangzhou) como referencia. De manera predeterminada, se utiliza un endpoint público. Si accede a OSS desde otro servicio de Alibaba Cloud en la misma región, debe usar un endpoint interno. Para obtener más información sobre las regiones y los endpoints de OSS, consulte Regions and endpoints.No se requieren permisos para generar una URL prefirmada. Sin embargo, un tercero solo podrá cargar correctamente un archivo con dicha URL si usted cuenta con el permiso
oss:PutObject. Para obtener más información, consulte Grant a custom access policy to a RAM user.El código de ejemplo de este tema utiliza URL prefirmadas V4, cuyo periodo máximo de validez es de 7 días. Para obtener más información, consulte Signature V4 (Recommended).
Proceso
Para cargar un archivo mediante una URL firmada y el método PUT, siga este procedimiento:
Definición del método
Puede generar una URL prefirmada que otorgue permisos de acceso temporal a los objetos de un bucket. Esta URL se puede utilizar varias veces antes de que expire.
El método presign se define de la siguiente manera:
presign(request: PutObjectRequest, **kwargs) → PresignResult
Parámetros de solicitud
|
Nombre del parámetro |
Tipo |
Descripción |
|
request |
PutObjectRequest |
El nombre del método para el cual se genera la URL prefirmada. Para obtener más información, consulte Client.presign. |
|
expires |
datetime.timedelta |
(Opcional) El tiempo de vida (TTL) de la URL prefirmada, contado a partir de la hora actual. Por ejemplo, para establecer un TTL de 30 minutos, use 30 * time.Minute. Si no se especifica, el TTL predeterminado es de 15 minutos. |
|
expiration |
datetime.datetime |
(Opcional) La fecha y hora exactas en las que expira la URL prefirmada. |
Para las firmas V4, el periodo máximo de validez es de 7 días. Si establece tanto Expiration como Expires, Expiration tiene prioridad.
Valores devueltos
|
Tipo |
Descripción |
|
PresignResult |
El resultado, que incluye la URL prefirmada, el método HTTP, el tiempo de expiración y los encabezados de solicitud firmados. Para obtener más información, consulte PresignResult. |
A continuación se muestran los valores devueltos por PresignResult:
|
Nombre del parámetro |
Tipo |
Descripción |
|
method |
str |
El método HTTP correspondiente a la interfaz. Por ejemplo, para la interfaz PutObject, se devuelve PUT. |
|
url |
str |
La URL prefirmada. |
|
expiration |
datetime |
El tiempo de expiración de la URL prefirmada. |
|
signed_headers |
MutableMapping |
Los encabezados de solicitud firmados. Por ejemplo, si establece content_type, se devuelve la información de content_type. |
Para consultar la definición completa del método presign, vea presign.
Código de ejemplo
-
El propietario del archivo genera una URL prefirmada para el método PUT.
ImportanteAl generar una URL prefirmada para el método PUT, si especifica encabezados de solicitud, debe incluir esos mismos encabezados en la solicitud PUT que utilice la URL prefirmada. De lo contrario, la verificación de la firma fallará.
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. -
Otro usuario carga un archivo mediante la URL prefirmada para el método 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
ImportanteSi encuentra un error 403 de discrepancia de firma al usar Node.js para cargar un archivo, generalmente se debe a que el encabezado de solicitud Content-Type se estableció explícitamente en la carga, pero no se incluyó al generar la URL prefirmada. Esta discrepancia provoca un fallo en la verificación de la firma. Para resolverlo, no establezca el encabezado de solicitud Content-Type en la carga o bien especifíquelo cuando genere la URL prefirmada.
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
ImportanteSi encuentra un error 403 de discrepancia de firma al usar Browser.js para cargar un archivo, suele deberse a que el navegador agrega automáticamente el encabezado de solicitud Content-Type, el cual no se incluyó al generar la URL prefirmada. Esta discrepancia provoca un fallo en la verificación de la firma. Para solucionarlo, debe especificar el encabezado de solicitud Content-Type cuando genere la URL prefirmada.
<!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); } }
Escenarios comunes
Usar una URL prefirmada para cargar un archivo con encabezados de solicitud y metadatos personalizados
Usar una URL prefirmada para ejecutar una carga multiparte
Usar una URL prefirmada para cargar un archivo y configurar parámetros de devolución de llamada de carga
Preguntas frecuentes
Si uso una firma temporal para cargar un archivo y esta caduca durante la carga, ¿fallará el proceso?
Si no establezco encabezados de solicitud ni metadatos personalizados al generar la URL, ¿necesito configurarlos cuando la use para una carga?
Referencias
Para consultar el código de ejemplo completo sobre cómo cargar un archivo mediante una URL prefirmada, consulte presigner_put_object.py.
Para consultar el código de ejemplo completo sobre cómo ejecutar una carga multiparte mediante una URL prefirmada, consulte presigner_complete_multipart_upload.py.