Por padrão, os objetos em um bucket do Object Storage Service (OSS) são privados e podem ser acessados apenas pelo proprietário do objeto. Este tópico descreve como usar o kit de desenvolvimento de software (SDK) do OSS para Go V2 para gerar uma URL assinada para o método PUT com um tempo de vida útil (TTL). Isso permite que outros usuários façam upload temporário de objetos. A URL pode ser usada várias vezes antes de expirar. Após a expiração da URL, você deve gerar uma nova.
Precauções
-
O código de exemplo neste tópico usa o ID da região
cn-hangzhoue um Endpoint público para a região China (Hangzhou). Se você quiser acessar o OSS a partir de outros produtos Alibaba Cloud na mesma região, use um Endpoint de rede interna. Para obter mais informações sobre os mapeamentos entre regiões e Endpoints do OSS, consulte Regiões e Endpoints. -
Os exemplos neste tópico mostram como obter credenciais de acesso a partir de variáveis de ambiente. Para obter mais informações sobre como configurar credenciais de acesso, consulte Configurar credenciais de acesso.
-
Permissões não são necessárias para gerar uma URL pré-assinada. No entanto, um terceiro poderá fazer upload bem-sucedido de um arquivo usando a URL pré-assinada somente se você tiver a permissão
oss:PutObject. Para obter mais informações, consulte Conceder uma política de acesso personalizada a um usuário RAM. -
O código de exemplo neste tópico usa uma URL assinada V4, que tem um TTL máximo de 7 dias. Para obter mais informações, consulte Assinatura V4 (recomendado).
Processo
Para fazer upload de um arquivo usando uma URL assinada e o método PUT, siga este procedimento:
Definição do método
Você pode chamar a operação presign para gerar uma URL assinada. Esta operação concede acesso temporário a um objeto em um bucket. A URL assinada pode ser usada várias vezes antes de expirar.
A operação presign é definida da seguinte forma:
func (c *Client) Presign(ctx context.Context, request any, optFns ...func(*PresignOptions)) (result *PresignResult, err error)
Parâmetros da solicitação
|
Parâmetro |
Tipo |
Descrição |
|
ctx |
context.Context |
O contexto da solicitação. |
|
request |
*PutObjectRequest |
O nome da operação para a qual gerar a URL assinada. |
|
optFns |
...func(*PresignOptions) |
(Opcional) O TTL. Se você não especificar este parâmetro, o TTL padrão é de 15 minutos. |
As PresignOptions estão listadas abaixo:
|
Opção |
Tipo |
Descrição |
|
Expires |
time.Duration |
A duração a partir do horário atual até a expiração da URL. Por exemplo, para definir um TTL de 30 minutos, use 30 * time.Minute. |
|
Expiration |
time.Time |
O horário absoluto de expiração. |
Para assinaturas V4, o TTL máximo é de 7 dias. Se você definir tanto Expiration quanto Expires, Expiration tem precedência.
Valores de retorno
|
Valor de retorno |
Tipo |
Descrição |
|
result |
*PresignResult |
O resultado. Inclui a URL assinada, método HTTP, horário de expiração e cabeçalhos de solicitação assinados. |
|
err |
error |
O status da solicitação. Se a solicitação falhar, err não será nulo. |
Os valores de retorno de PresignResult estão listados abaixo:
|
Parâmetro |
Tipo |
Descrição |
|
Method |
string |
O método HTTP que corresponde à operação. Por exemplo, para a operação PutObject, é retornado PUT. |
|
URL |
string |
URL pré-assinada |
|
Expiration |
time.Time |
O horário de expiração da URL assinada. |
|
SignedHeaders |
map[string]string |
Os cabeçalhos de solicitação assinados. Por exemplo, se você definir Content-Type para a operação PutObject, as informações sobre Content-Type serão retornadas. |
Código de exemplo
-
O proprietário do objeto gera uma URL assinada para o método PUT.
ImportanteAo gerar uma URL assinada para o método PUT, se você especificar cabeçalhos de solicitação, deverá incluir os mesmos cabeçalhos ao usar a URL assinada para enviar a solicitação PUT. Se os cabeçalhos não corresponderem, a solicitação falhará e um erro de assinatura será retornado.
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) } } } -
Outro usuário faz upload de um objeto usando a URL assinada para o 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
ImportanteSe você encontrar um erro de incompatibilidade de assinatura 403 ao usar Node.js para fazer upload de um arquivo, geralmente é porque o cabeçalho de solicitação Content-Type é definido explicitamente na solicitação de upload, mas esse cabeçalho não foi incluído ao gerar a URL pré-assinada. Essa incompatibilidade causa uma falha na verificação da assinatura. Para resolver esse problema, não defina o cabeçalho de solicitação Content-Type na solicitação de upload ou especifique o cabeçalho de solicitação Content-Type ao gerar a URL pré-assinada.
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
ImportanteSe você encontrar um erro de incompatibilidade de assinatura 403 ao usar Browser.js para fazer upload de um arquivo, geralmente é porque o navegador adiciona automaticamente o cabeçalho de solicitação Content-Type, e esse cabeçalho não foi incluído ao gerar a URL pré-assinada. Essa incompatibilidade causa uma falha na verificação da assinatura. Para resolver esse problema, você deve especificar o cabeçalho de solicitação Content-Type ao gerar a URL pré-assinada.
<!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); } }
Cenários comuns
Referências
-
Para o código de exemplo completo para URLs assinadas, consulte exemplo do GitHub.
-
Para obter mais informações sobre a operação de API para URLs assinadas, consulte Presign.