By default, objects in an OSS bucket are private. A presigned URL grants temporary, credential-free upload access to third parties without exposing your AccessKey. This document explains how to use OSS SDK for PHP (V2) to generate a presigned URL for HTTP PUT uploads and how to use that URL to upload objects from multiple environments.
Usage notes
The sample code uses the region ID
cn-hangzhou(China (Hangzhou)) with a public endpoint. To access OSS from another Alibaba Cloud service in the same region, use the internal endpoint instead. See Regions and endpoints.No specific permissions are required to generate a presigned URL. However, the URL can only be used to upload objects if the URL generator has the
oss:PutObjectpermission. See Authorize a RAM user to access multiple directories in a bucket.This document uses the V4 signature algorithm, which supports a maximum validity period of 7 days. See (Recommended) Include a V4 signature in a URL.
Access credentials are read from environment variables. See Configure access credentials for OSS SDK for PHP.
Header consistency: If you include request headers when generating the presigned URL, send those same headers in the actual PUT request. Mismatched headers cause signature verification failures.
How it works
The object owner uses the PHP SDK to generate a presigned URL for HTTP PUT requests.
The URL is shared with a third party.
The third party sends an HTTP PUT request to the URL to upload an object directly to OSS—no credentials required.
Upload using the presigned URL
The following examples show how a third party can use the presigned URL to upload an object. Replace <signedUrl> with the URL generated above.
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 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()
client := &http.Client{}
// Send a PUT request with the file as the body.
req, err := http.NewRequest("PUT", signedUrl, file)
if err != nil {
return fmt.Errorf("Failed to create the request: %w", err)
}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("Failed to send the request: %w", err)
}
defer resp.Body.Close()
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:
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 {
const fileStream = fs.createReadStream(filePath);
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
When uploading from a browser using a presigned URL, the browser automatically adds a Content-Type header that was not included when the URL was signed. This mismatch causes a 403 SignatureNotMatch error. To avoid this, include the Content-Type header when generating the presigned URL.
<!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 presigned URL generated in the previous step.
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>";
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;
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>
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) {
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);
fseek(file, 0, SEEK_SET);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
curl_easy_setopt(curl, CURLOPT_READDATA, file);
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);
}
}Common scenarios
Upload with request headers and custom metadata
Use this approach when you need the uploaded object to have a specific content type, storage class, or custom metadata. Include these parameters when generating the presigned URL so they are part of the signature. Send the same headers in the actual PUT request.
Step 1: Generate the presigned URL (PHP)
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use AlibabaCloud\Oss\V2 as Oss;
$optsdesc = [
"region" => ['help' => 'The region in which the bucket is located.', 'required' => True],
"endpoint" => ['help' => 'The domain names that other services can use to access OSS.', 'required' => False],
"bucket" => ['help' => 'The name of the bucket', 'required' => True],
"key" => ['help' => 'The name of the object', 'required' => True],
];
$longopts = \array_map(function ($key) {
return "$key:";
}, array_keys($optsdesc));
$options = getopt("", $longopts);
foreach ($optsdesc as $key => $value) {
if ($value['required'] === True && empty($options[$key])) {
$help = $value['help'];
echo "Error: the following arguments are required: --$key, $help" . PHP_EOL;
exit(1);
}
}
$region = $options["region"];
$bucket = $options["bucket"];
$key = $options["key"];
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();
$cfg = Oss\Config::loadDefault();
$cfg->setCredentialsProvider($credentialsProvider);
$cfg->setRegion($region);
if (isset($options["endpoint"])) {
$cfg->setEndpoint($options["endpoint"]);
}
$client = new Oss\Client($cfg);
// Include contentType and metadata in the signature.
$contentType = "text/plain; charset=utf-8";
$metadata = [
"key1" => "value1",
"key2" => "value2",
];
$request = new Oss\Models\PutObjectRequest(
bucket: $bucket,
key: $key,
contentType: $contentType,
metadata: $metadata
);
$result = $client->presign($request);
print(
'put object presign result:' . var_export($result, true) . PHP_EOL .
'put object url:' . $result->url . PHP_EOL
);Step 2: Upload using the presigned URL
All upload examples below send the same headers that were included in the signature. The x-oss-meta- prefix is required for user metadata keys.
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://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 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;
URL signedUrl = new URL("<signedUrl>");
String pathName = "C:\\Users\\demo.txt";
// Must match the headers specified when generating the presigned 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");
// Must match the user metadata specified when generating the presigned 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);
for (Map.Entry header : headers.entrySet()) {
put.addHeader(header.getKey().toString(), header.getValue().toString());
}
for (Map.Entry meta : userMetadata.entrySet()) {
// The SDK adds the "x-oss-meta-" prefix automatically when generating
// the URL, but you must add it manually in HTTP client requests.
put.addHeader("x-oss-meta-" + meta.getKey().toString(), meta.getValue().toString());
}
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 (
"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
}
for key, value := range headers {
req.Header.Set(key, value)
}
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() {
signedUrl := "<signedUrl>"
filePath := "C:\\Users\\demo.txt"
headers := map[string]string{
"Content-Type": "text/plain;charset=utf8",
"x-oss-storage-class": "Standard",
}
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: 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 all 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__":
signed_url = "<signedUrl>"
file_path = "C:\\Users\\demo.txt"
headers = {
"Content-Type": "text/plain;charset=utf8",
"x-oss-storage-class": "Standard"
}
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 all 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 () => {
const signedUrl = "<signedUrl>";
const filePath = "C:\\Users\\demo.txt";
const headers = {
"Content-Type": "text/plain;charset=utf8",
"x-oss-storage-class": "Standard"
};
const metadata = {
"key1": "value1",
"key2": "value2"
};
await uploadFile(signedUrl, filePath, headers, metadata);
})();Browser.js
When using Browser.js to upload with a presigned URL, include the Content-Type header when generating the URL. The browser adds this header automatically, and if it was not part of the signature, OSS returns a 403 SignatureNotMatch error.
<!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;
var filePath = "C:\\Users\\demo.txt";
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;
// Must match the headers specified when generating the presigned 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");
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>
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);
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());
}
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() {
std::string signedUrl = "<signedUrl>";
std::string filePath = "C:\\Users\\demo.txt";
// Must match the headers specified when generating the presigned URL.
std::map<std::string, std::string> headers = {
{"Content-Type", "text/plain;charset=utf8"},
{"x-oss-storage-class", "Standard"}
};
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);
String signedUrl = "<signedUrl>";
String pathName = "/storage/emulated/0/demo.txt";
// Must match the headers specified when generating the presigned URL.
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "text/plain;charset=utf8");
headers.put("x-oss-storage-class", "Standard");
// Must match the user metadata specified when generating the presigned 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);
for (Entry<String, String> header : headers.entrySet()) {
connection.setRequestProperty(header.getKey(), header.getValue());
}
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();
}
}
}
}Multipart upload with presigned URLs
For large files, split the upload into multiple parts. Each part requires its own presigned URL. The following PHP code handles the full multipart upload workflow: initialize, upload each part, then complete.
<?php
// Load the SDK and Guzzle HTTP client.
require_once __DIR__ . '/../vendor/autoload.php';
use AlibabaCloud\Oss\V2 as Oss;
use GuzzleHttp\Psr7\LazyOpenStream;
use GuzzleHttp\Client;
$optsdesc = [
"region" => ['help' => 'The region in which the bucket is located.', 'required' => true],
"bucket" => ['help' => 'The name of the bucket', 'required' => true],
"key" => ['help' => 'The name of the object', 'required' => true],
];
$longopts = array_map(function ($key) {
return "$key:";
}, array_keys($optsdesc));
$options = getopt("", $longopts);
foreach ($optsdesc as $key => $value) {
if ($value['required'] === true && empty($options[$key])) {
echo "Error: the following arguments are required: --$key, " . $value['help'] . "\n";
exit(1);
}
}
$region = $options["region"];
$bucket = $options["bucket"];
$key = $options["key"];
$bigFileName = "/Users/localpath/yourfilename"; // Replace with the path to your file.
$partSize = 1 * 1024 * 1024; // Part size: 1 MB.
$fileSize = filesize($bigFileName);
$partsNum = intdiv($fileSize, $partSize) + (int)($fileSize % $partSize > 0 ? 1 : 0);
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();
$cfg = Oss\Config::loadDefault();
$cfg->setCredentialsProvider($credentialsProvider);
$cfg->setRegion($region);
$client = new Oss\Client($cfg);
// Step 1: Initialize the multipart upload and get the upload ID.
$initResult = $client->initiateMultipartUpload(
new Oss\Models\InitiateMultipartUploadRequest(
bucket: $bucket,
key: $key
)
);
$uploadId = $initResult->uploadId;
// Step 2: Generate a presigned URL for each part and upload it.
$parts = [];
for ($i = 1; $i <= $partsNum; $i++) {
$request = new Oss\Models\UploadPartRequest(
bucket: $bucket,
key: $key,
partNumber: $i,
uploadId: $uploadId
);
$signedResult = $client->presign($request);
$signedUrl = $signedResult->url;
$method = $signedResult->method;
$offset = ($i - 1) * $partSize;
$length = min($partSize, $fileSize - $offset);
$file = new LazyOpenStream($bigFileName, 'rb');
$file->seek($offset);
$content = $file->read($length);
$http = new Client();
$response = $http->request($method, $signedUrl, ['body' => $content]);
$etag = $response->getHeaderLine('ETag');
$parts[] = new Oss\Models\UploadPart(
partNumber: $i,
etag: $etag
);
echo "Upload part {$i} success. ETag: {$etag}\n";
}
// Step 3: Complete the multipart upload.
$completeRequest = new Oss\Models\CompleteMultipartUploadRequest(
bucket: $bucket,
key: $key,
uploadId: $uploadId,
completeMultipartUpload: new Oss\Models\CompleteMultipartUpload(
parts: $parts
)
);
$comResult = $client->completeMultipartUpload($completeRequest);
printf(
'status code: %s' . PHP_EOL .
'request id: %s' . PHP_EOL .
'complete multipart upload result: %s' . PHP_EOL,
$comResult->statusCode,
$comResult->requestId,
var_export($comResult, true)
);Upload with upload callback
An upload callback lets OSS notify your server after a successful upload. Specify the callback parameters when generating the presigned URL so they are part of the signature.
Step 1: Generate the presigned URL with callback parameters (PHP)
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use AlibabaCloud\Oss\V2 as Oss;
$optsdesc = [
"region" => ['help' => 'The region in which the bucket is located.', 'required' => True],
"endpoint" => ['help' => 'The domain names that other services can use to access OSS.', 'required' => False],
"bucket" => ['help' => 'The name of the bucket', 'required' => True],
"key" => ['help' => 'The name of the object', 'required' => True],
];
$longopts = \array_map(function ($key) {
return "$key:";
}, array_keys($optsdesc));
$options = getopt("", $longopts);
foreach ($optsdesc as $key => $value) {
if ($value['required'] === True && empty($options[$key])) {
$help = $value['help'];
echo "Error: the following arguments are required: --$key, $help" . PHP_EOL;
exit(1);
}
}
$region = $options["region"];
$bucket = $options["bucket"];
$key = $options["key"];
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();
$cfg = Oss\Config::loadDefault();
$cfg->setCredentialsProvider($credentialsProvider);
$cfg->setRegion($region);
if (isset($options["endpoint"])) {
$cfg->setEndpoint($options["endpoint"]);
}
$client = new Oss\Client($cfg);
// Construct the callback parameter (Base64-encoded JSON).
$call_back_url = "http://www.example.com/callback";
$callback_body_template = "bucket={bucket}&object={object}&my_var_1={var1}&my_var_2={var2}";
$callback_body_replaced = str_replace(
['{bucket}', '{object}', '{var1}', '{var2}'],
[$bucket, $key, 'value1', 'value2'],
$callback_body_template
);
$callback = base64_encode(json_encode([
"callbackUrl" => $call_back_url,
"callbackBody" => $callback_body_replaced
]));
// Construct the custom variable (Base64-encoded JSON).
$callback_var = base64_encode(json_encode([
"x:var1" => "value1",
"x:var2" => "value2"
]));
$request = new Oss\Models\PutObjectRequest(
bucket: $bucket,
key: $key,
callback: $callback,
callbackVar: $callback_var,
);
$result = $client->presign($request);
print(
'put object presign result:' . var_export($result, true) . PHP_EOL .
'put object url:' . $result->url . PHP_EOL
);Step 2: Upload using the presigned URL
All requests must include the x-oss-callback and x-oss-callback-var headers that match the values used when generating the URL.
curl
curl -X PUT \
-H "x-oss-callback: eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9" \
-H "x-oss-callback-var: eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==" \
-T "C:\\Users\\demo.txt" \
"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******************************************************"Python
import requests
def upload_file(signed_url, file_path, headers=None):
"""
Upload an object to OSS using a presigned URL.
:param signed_url: The presigned URL.
:param file_path: Full path of the local file to upload.
:param headers: Request headers (optional).
"""
if not headers:
headers = {}
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__":
signed_url = "<signedUrl>"
file_path = "C:\\Users\\demo.txt"
headers = {
"x-oss-callback": "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9",
"x-oss-callback-var": "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==",
}
upload_file(signed_url, file_path, headers)Go
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func uploadFile(signedUrl string, filePath string, headers map[string]string) error {
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
fileBytes, err := io.ReadAll(file)
if err != nil {
return err
}
req, err := http.NewRequest("PUT", signedUrl, bytes.NewBuffer(fileBytes))
if err != nil {
return err
}
for key, value := range headers {
req.Header.Add(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, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
return nil
}
func main() {
signedUrl := "<signedUrl>"
filePath := "C:\\Users\\demo.txt"
headers := map[string]string{
"x-oss-callback": "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9",
"x-oss-callback-var": "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==",
}
err := uploadFile(signedUrl, filePath, headers)
if err != nil {
fmt.Printf("An error occurred: %v\n", err)
}
}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;
URL signedUrl = new URL("<signedUrl>");
String pathName = "C:\\Users\\demo.txt";
// Must match the callback headers specified when generating the presigned URL.
Map<String, String> headers = new HashMap<String, String>();
headers.put("x-oss-callback", "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9");
headers.put("x-oss-callback-var", "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==");
try {
HttpPut put = new HttpPut(signedUrl.toString());
System.out.println(put);
HttpEntity entity = new FileEntity(new File(pathName));
put.setEntity(entity);
for (Map.Entry header : headers.entrySet()) {
put.addHeader(header.getKey().toString(), header.getValue().toString());
}
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();
}
}
}PHP
<?php
function uploadFile($signedUrl, $filePath, $headers = []) {
if (!file_exists($filePath)) {
echo "The file does not exist: $filePath\n";
return;
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $signedUrl);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_INFILE, fopen($filePath, 'rb'));
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($filePath));
curl_setopt($ch, CURLOPT_HTTPHEADER, array_map(function($key, $value) {
return "$key: $value";
}, array_keys($headers), $headers));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "Status code: $httpCode\n";
if ($httpCode == 200) {
echo "The object is uploaded by using the library.\n";
} else {
echo "Upload failed.\n";
}
echo $response . "\n";
}
$signedUrl = "<signedUrl>";
$filePath = "C:\\Users\\demo.txt";
$headers = [
"x-oss-callback" => "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9",
"x-oss-callback-var" => "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==",
];
uploadFile($signedUrl, $filePath, $headers);
?>References
Complete sample code for presigned URLs: GitHub examples
API reference: Presign