Por padrão, a lista de controle de acesso (ACL) de um objeto em um bucket do Object Storage Service (oss) é privada. Para permitir que outros usuários façam upload de objetos sem compartilhar suas credenciais, gere uma url pré-assinada com o oss SDK for Go e compartilhe-a com eles. A url incorpora sua assinatura, eliminando a necessidade de cabeçalhos de autenticação adicionais durante o upload.
Defina um período de validade ao gerar a url para controlar por quanto tempo a janela de upload permanece aberta. Durante esse período, você pode reutilizar a url para múltiplos uploads. Após a expiração, gere novamente a url para restaurar o acesso.
Como funciona
O proprietário do objeto gera uma url pré-assinada usando o oss SDK for Go.
O proprietário do objeto compartilha a url com o usuário.
O usuário envia uma solicitação HTTP PUT para a url para fazer upload do arquivo — sem necessidade de credenciais do oss.
Pré-requisitos
Antes de começar, verifique se você tem:
oss SDK for Go instalado
As variáveis de ambiente
OSS_ACCESS_KEY_IDeOSS_ACCESS_KEY_SECRETconfiguradas. Para mais detalhes, consulte Configurar credenciais de acesso.A permissão
oss:PutObjectno bucket de destino. Para mais detalhes, consulte Autorizar um usuário RAM a acessar vários diretórios em um bucket.
Nenhuma permissão específica é necessária para gerar uma url pré-assinada. A permissão oss:PutObject é exigida apenas para que a url permita uploads.
Gerar uma url pré-assinada
Todos os exemplos utilizam o algoritmo de assinatura V4, que suporta um período máximo de validade de 7 dias. Os exemplos usam o endpoint público da região China (Hangzhou) (https://oss-cn-hangzhou.aliyuncs.com). Para acessar o oss a partir de outros serviços da Alibaba Cloud na mesma região, use um endpoint interno. Para regiões e endpoints suportados, consulte Regiões e endpoints.
O código a seguir gera uma url pré-assinada para um upload simples. A url é válida por 600 segundos.
package main
import (
"log"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)
func main() {
// 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, err := oss.NewEnvironmentVariableCredentialsProvider()
if err != nil {
log.Printf("Error: %v", err)
}
// Create an OSSClient instance.
// 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.
// Specify the region of the bucket. For example, if your bucket is located in the China (Hangzhou) region, set the region to cn-hangzhou.
clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
clientOptions = append(clientOptions, oss.Region("cn-hangzhou"))
// Specify the signature algorithm version.
clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
client, err := oss.New("https://oss-cn-hangzhou.aliyuncs.com", "", "", clientOptions...)
if err != nil {
log.Printf("Error: %v", err)
}
// Specify the name of the bucket. Example: examplebucket.
bucketName := "examplebucket"
// Specify the full path of the object. Example: exampleobject.txt. Do not include the bucket name in the full path.
objectName := "exampleobject.txt"
bucket, err := client.Bucket(bucketName)
if err != nil {
log.Printf("Error: %v", err)
}
// Generate a presigned URL and set the validity period to 600 seconds.
signedURL, err := bucket.SignURL(objectName, oss.HTTPPut, 600)
if err != nil {
log.Printf("Error: %v", err)
}
log.Printf("Sign Url:%s\n", signedURL)
}
Para crie uma instância OSSClient usando um domínio personalizado ou Security Token Service (STS), consulte Exemplos de configuração para cenários comuns.
Estrutura da url pré-assinada
Uma url pré-assinada gerada tem o seguinte formato:
https://examplebucket.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******************************************************
|
Parâmetro |
Descrição |
|
|
Data e hora de criação da assinatura, no formato ISO 8601 (UTC). |
|
|
Duração da validade da url em segundos, contada a partir de |
|
|
Algoritmo de assinatura. |
|
|
Escopo da credencial usada para calcular a assinatura, incluindo ID da chave de acesso, data, região, serviço e tipo de solicitação. |
|
|
Assinatura calculada. Qualquer modificação na url ou nos cabeçalhos da solicitação invalida este valor. |
Fazer upload usando uma url pré-assinada
Passe a url pré-assinada para qualquer cliente HTTP que suporte solicitações PUT. A assinatura está incorporada na url — não são necessários cabeçalhos de autenticação adicionais.
curl
curl -X PUT -T /path/to/local/file "https://examplebucket.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******************************************************"
Dica: Coloque a url entre aspas duplas ao usar curl para evitar que o shell interprete caracteres especiais na string de consulta.
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 presigned URL.
URL signedUrl = new URL("<signedUrl>");
// Specify the full path of the local file to upload.
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:" + response.getStatusLine().getStatusCode());
if (response.getStatusLine().getStatusCode() == 200) {
System.out.println("The object is uploaded by using the library.");
}
System.out.println(response.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
response.close();
httpClient.close();
}
}
}
Go
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func uploadFile(signedUrl, filePath string) error {
// Open the local file.
file, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("Unable to open the local file: %w", err)
}
defer file.Close()
// Create an 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 the request: %w", err)
}
// Send the request.
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("Failed to send the 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 the response: %w", err)
}
fmt.Printf("Status code: %d\n", resp.StatusCode)
if resp.StatusCode == 200 {
fmt.Println("The object is uploaded by using the library.")
}
fmt.Println(string(body))
return nil
}
func main() {
// Replace <signedUrl> with the presigned URL.
signedUrl := "<signedUrl>"
// Specify the full path of the local file to upload.
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 local file and send a PUT request to upload it.
with open(file_path, 'rb') as file:
response = requests.put(signed_url, data=file)
print(f"Status code: {response.status_code}")
if response.status_code == 200:
print("The object is uploaded by using the library.")
print(response.text)
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == "__main__":
# Replace <signedUrl> with the presigned URL.
signed_url = "<signedUrl>"
# Specify the full path of the local file to upload.
file_path = "C:\\Users\\demo.txt"
upload_file(signed_url, file_path)
Node.js
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 local file.
const response = await axios.put(signedUrl, fileStream, {
headers: {
'Content-Type': 'application/octet-stream'
}
});
console.log(`Status code: ${response.status}`);
if (response.status === 200) {
console.log("The object is uploaded by using the library.");
}
console.log(response.data);
} catch (error) {
console.error(`An error occurred: ${error.message}`);
}
}
(async () => {
// Replace <signedUrl> with the presigned URL.
const signedUrl = '<signedUrl>';
// Specify the full path of the local file to upload.
const filePath = 'C:\\Users\\demo.txt';
await uploadFile(signedUrl, filePath);
})();
Browser.js
Ao fazer upload de um navegador usando uma url pré-assinada, pode ocorrer um erro 403 SignatureNotMatch. Isso geralmente acontece porque o navegador adiciona automaticamente o cabeçalho de solicitação Content-Type, que não foi incluído quando a url pré-assinada foi gerada. Para evitar isso, sempre especifique Content-Type ao gerar a url pré-assinada para uploads baseados em navegador.
<!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 file -->
<input type="file" id="fileInput" />
<button id="uploadButton">Upload File</button>
<script>
// Replace <signedUrl> 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 a 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,
});
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 to upload.
var filePath = "C:\\Users\\demo.txt";
// Replace <signedUrl> with the presigned URL.
var presignedUrl = "<signedUrl>";
// Create an HTTP client and a 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($"Uploaded! Status Code: {response.StatusCode}");
Console.WriteLine("Response Header:");
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) {
// Specify the presigned 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 file: " << filePath << std::endl;
return;
}
// Get the file size.
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
fseek(file, 0, SEEK_SET);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
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: " << httpCode << std::endl;
if (httpCode == 200) {
std::cout << "The object is uploaded by using the network library." << std::endl;
}
}
fclose(file);
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 to upload.
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: " + responseCode);
if (responseCode == 200) {
Log.d(TAG, "The object is uploaded by using the library.");
}
return "Object uploaded. 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 presigned URL.
String signedUrl = "<signedUrl>";
// Specify the full path of the local file to upload.
String filePath = "C:\\Users\\demo.txt";
activity.uploadFile(signedUrl, filePath);
}
}
Cenários comuns
Upload com cabeçalhos de solicitação e metadados de usuário
Etapa 2: Fazer upload usando a url pré-assinada
Todos os clientes de upload devem incluir os mesmos cabeçalhos de Content-Type, classe de armazenamento e metadados de usuário especificados durante a geração da url.
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://examplebucket.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 <signedUrl> with the presigned URL.
URL signedUrl = new URL("<signedUrl>");
// Specify the full path of the local file to upload.
String pathName = "C:\\Users\\demo.txt";
// Specify request headers. Values must match those used when generating the URL.
Map<String, String> headers = new HashMap<String, String>();
headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
headers.put(OSSHeaders.CONTENT_TYPE, "text/plain;charset=utf8");
// Specify user metadata. Values must match those used when generating the URL.
Map<String, String> userMetadata = new HashMap<String, String>();
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);
// Add request headers. If these headers were included when generating the URL,
// they must be sent here. Mismatched headers cause a signature error.
for (Map.Entry header : headers.entrySet()) {
put.addHeader(header.getKey().toString(), header.getValue().toString());
}
// If user metadata is specified, the SDK adds the "x-oss-meta-" prefix to the key.
// When using other HTTP clients, add this prefix manually.
for (Map.Entry meta : userMetadata.entrySet()) {
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 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 {
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
fileBytes, err := ioutil.ReadAll(file)
if err != nil {
return err
}
req, err := http.NewRequest("PUT", signedUrl, bytes.NewBuffer(fileBytes))
if err != nil {
return err
}
// Add request headers. Values must match those used when generating the URL.
for key, value := range headers {
req.Header.Set(key, value)
}
// Add user metadata with the "x-oss-meta-" prefix.
for key, value := range metadata {
req.Header.Set(fmt.Sprintf("x-oss-meta-%s", key), value)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
fmt.Printf("Status code: %d\n", resp.StatusCode)
if resp.StatusCode == 200 {
fmt.Println("The object is uploaded by using the library.")
} else {
fmt.Println("Upload failed")
}
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
return nil
}
func main() {
// Replace <signedUrl> with the presigned URL.
signedUrl := "<signedUrl>"
// Specify the full path of the local file to upload.
filePath := "C:\\Users\\demo.txt"
// Request headers. Values must match those used when generating the URL.
headers := map[string]string{
"Content-Type": "text/plain;charset=utf8",
"x-oss-storage-class": "Standard",
}
// User metadata. Values must match those used when generating the URL.
metadata := map[string]string{
"key1": "value1",
"key2": "value2",
}
err := uploadFile(signedUrl, filePath, headers, metadata)
if err != nil {
fmt.Printf("An error occurred: %v\n", err)
}
}
Python
import requests
def upload_file(signed_url, file_path, headers=None, metadata=None):
"""
Upload an object to OSS using a presigned URL.
:param signed_url: The presigned URL.
:param file_path: The full path of the local file to upload.
:param headers: Request headers. Optional.
:param metadata: User metadata. Optional.
"""
if not headers:
headers = {}
if not metadata:
metadata = {}
# Add the x-oss-meta- prefix to metadata keys.
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 library.")
else:
print("Upload failed.")
print(response.text)
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == "__main__":
# Replace <signedUrl> with the presigned URL.
signed_url = "<signedUrl>"
# Specify the full path of the local file to upload.
file_path = "C:\\Users\\demo.txt"
# Request headers. Values must match those used when generating the URL.
headers = {
"Content-Type": "text/plain;charset=utf8",
"x-oss-storage-class": "Standard"
}
# User metadata. Values must match those used when generating the URL.
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 {
// Add the x-oss-meta- prefix to metadata keys.
for (const [key, value] of Object.entries(metadata)) {
headers[`x-oss-meta-${key}`] = value;
}
const fileStream = fs.createReadStream(filePath);
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 library.");
} else {
console.log("Upload failed.");
}
console.log(response.data);
} catch (error) {
console.error(`An error occurred: ${error.message}`);
}
}
(async () => {
// Replace <signedUrl> with the presigned URL.
const signedUrl = "<signedUrl>";
// Specify the full path of the local file to upload.
const filePath = "C:\\Users\\demo.txt";
// Request headers. Values must match those used when generating the URL.
const headers = {
"Content-Type": "text/plain;charset=utf8",
"x-oss-storage-class": "Standard"
};
// User metadata. Values must match those used when generating the URL.
const metadata = {
"key1": "value1",
"key2": "value2"
};
await uploadFile(signedUrl, filePath, headers, metadata);
})();
Browser.js
Ao fazer upload de um navegador usando uma url pré-assinada que inclui parâmetros personalizados, especifique Content-Type ao gerar a url. O navegador adiciona automaticamente este cabeçalho e, se ele não corresponder ao valor incorporado na assinatura, o upload falhará com um erro 403.
<!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>
<input type="file" id="fileInput" />
<button id="uploadButton">Upload File</button>
<script>
// Replace <signedUrl> with the actual presigned URL.
const signedUrl = "<signedUrl>";
document.getElementById('uploadButton').addEventListener('click', async () => {
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
if (file) {
try {
await upload(file, signedUrl);
} catch (error) {
console.error('Error during upload:', error);
alert('Upload failed: ' + error.message);
}
} else {
alert('Please select a file to upload.');
}
});
const upload = async (file, presignedUrl) => {
const headers = {
"Content-Type": "text/plain;charset=utf8",
'x-oss-storage-class': 'Standard',
'x-oss-meta-key1': 'value1',
'x-oss-meta-key2': 'value2'
};
const response = await fetch(presignedUrl, {
method: 'PUT',
headers: headers,
body: file
});
if (!response.ok) {
throw new Error(`Upload failed, status: ${response.status}`);
}
alert('File uploaded successfully');
console.log('File uploaded successfully');
};
</script>
</body>
</html>
C#
using System.Net.Http.Headers;
// Specify the full path of the local file to upload.
var filePath = "C:\\Users\\demo.txt";
// Replace <signedUrl> with the presigned URL.
var presignedUrl = "<signedUrl>";
using var httpClient = new HttpClient();
using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
using var content = new StreamContent(fileStream);
var request = new HttpRequestMessage(HttpMethod.Put, presignedUrl);
request.Content = content;
// Add request headers. Values must match those used when generating the URL.
request.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain") { CharSet = "utf8" };
request.Content.Headers.Add("x-oss-meta-key1", "value1");
request.Content.Headers.Add("x-oss-meta-key2", "value2");
request.Content.Headers.Add("x-oss-storage-class", "Standard");
// Output the request headers.
Console.WriteLine("Request header:");
foreach (var header in request.Content.Headers)
{
Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
}
var response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
Console.WriteLine($"Uploaded! Status Code: {response.StatusCode}");
Console.WriteLine("Response Header:");
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>
#include <map>
#include <string>
// Callback to process the HTTP response body.
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) {
curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str());
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
FILE* file = fopen(filePath.c_str(), "rb");
if (!file) {
std::cerr << "Unable to open the file: " << filePath << std::endl;
return;
}
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
rewind(file);
curl_easy_setopt(curl, CURLOPT_READDATA, file);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
// Add request headers. Values must match those used when generating the URL.
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());
}
// Add user metadata with the "x-oss-meta-" prefix.
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);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
res = curl_easy_perform(curl);
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 << "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;
}
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 to upload.
std::string filePath = "C:\\Users\\demo.txt";
// Request headers. Values must match those used when generating the URL.
std::map<std::string, std::string> headers = {
{"Content-Type", "text/plain;charset=utf8"},
{"x-oss-storage-class", "Standard"}
};
// User metadata. Values must match those used when generating the URL.
std::map<std::string, std::string> metadata = {
{"key1", "value1"},
{"key2", "value2"}
};
uploadFile(signedUrl, filePath, headers, metadata);
return 0;
}
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 to upload.
String pathName = "/storage/emulated/0/demo.txt";
// Request headers. Values must match those used when generating the URL.
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "text/plain;charset=utf8");
headers.put("x-oss-storage-class", "Standard");
// User metadata. Values must match those used when generating the URL.
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);
// Add request headers.
for (Entry<String, String> header : headers.entrySet()) {
connection.setRequestProperty(header.getKey(), header.getValue());
}
// Add user metadata with the "x-oss-meta-" prefix.
for (Entry<String, String> meta : userMetadata.entrySet()) {
connection.setRequestProperty("x-oss-meta-" + meta.getKey(), meta.getValue());
}
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();
int responseCode = connection.getResponseCode();
Log.d(TAG, "Status code: " + responseCode);
if (responseCode == 200) {
Log.d(TAG, "The object is uploaded by using the 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();
}
}
}
}
Upload multipartido
Solução de problemas
SignatureNotMatch (403)
Um erro 403 SignatureNotMatch significa que os cabeçalhos de solicitação enviados durante o upload não correspondem aos cabeçalhos assinados na url. Verifique os seguintes itens:
|
Causa |
Solução |
|
Incompatibilidade de Content-Type |
Se |
|
Incompatibilidade de metadados de usuário |
Se metadados de usuário ( |
|
Incompatibilidade de classe de armazenamento |
Caso |
|
String de consulta modificada |
Use a url exatamente como foi gerada. Qualquer modificação na string de consulta invalida a assinatura. |
|
Desvio do relógio do sistema |
As assinaturas incluem um carimbo de data/hora. Sincronize o relógio do seu sistema com um servidor NTP se a diferença de tempo for grande. |
|
Caracteres especiais no curl |
Coloque a url entre aspas duplas ao usar curl para evitar que o shell interprete |
Perguntas frequentes
Se a url pré-assinada expirar durante um upload, o upload falha?
Se eu não especificar cabeçalhos de solicitação ou metadados de usuário ao gerar a url, preciso incluí-los na solicitação de upload?
Próximos passos
Para o código de exemplo completo, consulte o repositório do GitHub.
Para a referência da API do método
SignURL, consulte SignURL.