Por padrão, os objetos em um bucket do Object Storage Service (OSS) são privados. Somente o proprietário do objeto tem permissão para fazer upload. Use o OSS SDK para Java para gerar uma URL pré-assinada e permitir que outros usuários façam upload de objetos. Ao gerar a URL pré-assinada, você pode definir um prazo de validade para limitar sua vigência. A URL pode ser usada várias vezes antes de expirar. Se forem realizados múltiplos uploads, o objeto poderá ser sobrescrito. Após a expiração, a URL não pode mais ser usada para uploads, sendo necessário gerar uma nova.
Observações de uso
Neste tópico, é usado o endpoint público da região China (Hangzhou). Para acessar o OSS a partir de outros serviços da Alibaba Cloud na mesma região, use um endpoint interno. Para obter detalhes sobre regiões e endpoints compatíveis, consulte Regions and endpoints.
Neste tópico, as credenciais de acesso são obtidas a partir de variáveis de ambiente. Para mais informações, consulte 配置访问凭证.
Este tópico demonstra a criação de uma instância OSSClient com um endpoint do OSS. Para configurações alternativas, como uso de domínio personalizado ou autenticação com credenciais do Security Token Service (STS), consulte Client configuration.
Não são necessárias permissões para gerar uma URL pré-assinada. No entanto, para que um terceiro consiga fazer upload de um arquivo usando a URL pré-assinada, você precisa ter a permissão
oss:PutObject. Para mais informações, consulte Grant a custom access policy to a RAM user.Este tópico usa uma URL pré-assinada V4 como exemplo. O período de validade máximo é de 7 dias. Para mais informações, consulte Signature Version 4 (recommended).
Processo
Para fazer upload de um arquivo usando uma URL assinada e o método PUT, siga este procedimento:
Exemplos de código
-
O proprietário do objeto gera uma URL pré-assinada para uma requisição PUT.
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 { // 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 { // 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); // 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()); } } } -
Outro usuário faz upload de um arquivo usando a URL pré-assinada para a requisição 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 o Node.js para fazer upload de um arquivo, isso geralmente ocorre porque o cabeçalho de requisição Content-Type foi definido explicitamente na requisição de upload, mas esse cabeçalho não foi incluído na geração da URL pré-assinada. Essa divergência causa falha na verificação da assinatura. Para resolver o problema, não defina o cabeçalho Content-Type na requisição de upload, ou inclua o cabeçalho 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 o Browser.js para fazer upload de um arquivo, isso geralmente ocorre porque o navegador adiciona automaticamente o cabeçalho de requisição Content-Type, e esse cabeçalho não foi incluído na geração da URL pré-assinada. Essa divergência causa falha na verificação da assinatura. Para resolver o problema, inclua o cabeçalho 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); } }
Outros cenários
Use uma URL pré-assinada para fazer upload de um arquivo com cabeçalhos de requisição especificados e metadados personalizados
Use uma URL pré-assinada para realizar um upload multipart
Use uma URL pré-assinada para fazer upload de um arquivo e definir parâmetros de callback de upload
FAQ
Se uma assinatura temporária expirar durante o upload de um arquivo, o upload falha?
Se eu não defina cabeçalhos de solicitação nem metadados personalizados ao gerar a URL, preciso configure-los ao usar a URL para upload?
Referências
Para acessar o código de exemplo completo para geração de uma URL pré-assinada V4, consulte o exemplo no GitHub.
Para obter mais informações sobre a operação de API para upload de um arquivo usando uma URL pré-assinada, consulte GeneratePresignedUrlRequest.