OSS buckets are private by default. To let another party upload files to your bucket without sharing your credentials, generate a signed URL using the OSS Harmony SDK. The signed URL embeds a time-limited signature that grants PUT access to a specific object.
Usage notes
The signed URL can be used multiple times before it expires. If multiple upload operations are performed using the same URL, the file may be overwritten.
Signature V4 URLs are valid for up to 7 days.
Once a URL expires, it cannot be reused. Generate a new signed URL.
To generate a signed URL for a PUT request, the credential owner must have the
oss:PutObjectpermission. The recipient of the URL does not need any OSS permissions to perform the upload.If you specify request headers when generating the signed URL, the PUT request must include the exact same headers. Mismatched headers cause a signature mismatch error.
For supported regions and endpoints, see Access OSS through bucket domain names.
For more information about Signature V4, see Signature V4 (Recommended).
How it works
The file owner uses the Harmony SDK to generate a signed URL for a PUT request. The URL encodes the bucket, object key, expiration time, and signature — computed locally from the access credentials, without a network call to OSS. The recipient uses the URL to upload a file directly to OSS using any HTTP client.
Prerequisites
Before you begin, ensure that you have:
An OSS bucket
STS temporary access credentials (
accessKeyId,accessKeySecret,securityToken) for a RAM user with theoss:PutObjectpermission. See Grant custom access policies to RAM users.The Harmony SDK (
@aliyun/oss) installed
Upload a file using a signed URL
Step 1: Generate a signed URL
Use the Harmony SDK to generate a signed URL for a PUT request.
The SDK computes the signature locally using your access credentials. No network request is made to OSS when generating the URL.
import Client, { EHttpMethod } from '@aliyun/oss';
// Create an OSS client instance.
const client = new Client({
// Replace with the AccessKeyId of your STS temporary access credential.
accessKeyId: 'yourAccessKeyId',
// Replace with the AccessKeySecret of your STS temporary access credential.
accessKeySecret: 'yourAccessKeySecret',
// Replace with the SecurityToken of your STS temporary access credential.
securityToken: 'yourSecurityToken',
// Specify the region where the bucket is located. Example: oss-cn-hangzhou.
region: 'oss-cn-hangzhou',
});
// Specify the bucket name and object key.
const bucket = 'yourBucketName';
const key = 'yourObjectName';
const generateSignedUrl = async () => {
// If no signature version is explicitly set, the version from Client.options.signVersion is used.
const url = await client.signatureUrl({
method: EHttpMethod.PUT,
bucket,
key,
expires: 60 * 60, // Expiration time in seconds (60 minutes).
});
console.log(url);
};
generateSignedUrl();The generated URL looks like:
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******************************************************Step 2: Upload a file using the signed URL
Send a PUT request to the signed URL from any HTTP client or language. Replace <signedUrl> with the URL generated in Step 1.
All examples below use a standard HTTP PUT request — no OSS SDK or credentials are required on the upload side.
curl
curl -X PUT -T /path/to/local/file "<signedUrl>"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;
public class SignUrlUpload {
public static void main(String[] args) throws Throwable {
CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null;
// Replace <signedUrl> with the signed URL.
URL signedUrl = new URL("<signedUrl>");
// Specify the full path of the local file.
String pathName = "C:\\Users\\demo.txt";
try {
HttpPut put = new HttpPut(signedUrl.toString());
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("Upload successful.");
}
} 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 file.
file, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("unable to open file: %w", err)
}
defer file.Close()
// 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.
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read response: %w", err)
}
fmt.Printf("Status code: %d\n", resp.StatusCode)
if resp.StatusCode == 200 {
fmt.Println("Upload successful.")
}
fmt.Println(string(body))
return nil
}
func main() {
// Replace <signedUrl> with the signed URL.
signedUrl := "<signedUrl>"
// Specify the full path of the local file.
filePath := "C:\\Users\\demo.txt"
if err := uploadFile(signedUrl, filePath); err != nil {
fmt.Println("Error:", 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("Upload successful.")
print(response.text)
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == "__main__":
# Replace <signedUrl> with the signed URL.
signed_url = "<signedUrl>"
# Specify the full path of the local file.
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('Upload successful.');
}
console.log(response.data);
} catch (error) {
console.error(`An error occurred: ${error.message}`);
}
}
(async () => {
// Replace <signedUrl> with the signed URL.
const signedUrl = '<signedUrl>';
// Specify the full path of the local file.
const filePath = 'C:\\Users\\demo.txt';
await uploadFile(signedUrl, filePath);
})();Browser (JavaScript)
Browsers automatically add a Content-Type request header. If Content-Type was not specified when generating the signed URL, the header causes a signature mismatch (HTTP 403). To prevent this, specify Content-Type when generating the signed URL (see Upload a file with request headers).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>File Upload Example</title>
</head>
<body>
<input type="file" id="fileInput" />
<button id="uploadButton">Upload File</button>
<script>
// Replace with the signed 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('Upload error:', error);
alert('Upload failed: ' + error.message);
}
});
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.
var filePath = "C:\\Users\\demo.txt";
// Replace <signedUrl> with the signed 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($"Upload successful. Status code: {response.StatusCode}");
}
else
{
string responseContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Upload failed. Status code: {response.StatusCode}");
Console.WriteLine("Response: " + responseContent);
}C++
#include <iostream>
#include <fstream>
#include <curl/curl.h>
void uploadFile(const std::string& signedUrl, const std::string& filePath) {
CURL* curl = curl_easy_init();
if (!curl) return;
curl_global_init(CURL_GLOBAL_DEFAULT);
// Set the URL and request method.
curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str());
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;
curl_easy_cleanup(curl);
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);
CURLcode 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 << "Upload successful." << std::endl;
}
}
fclose(file);
curl_easy_cleanup(curl);
curl_global_cleanup();
}
int main() {
// Replace <signedUrl> with the signed URL.
std::string signedUrl = "<signedUrl>";
// Specify the full path of the local file.
std::string filePath = "C:\\Users\\demo.txt";
uploadFile(signedUrl, filePath);
return 0;
}Android (Java)
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, "Upload successful.");
}
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 signed URL.
String signedUrl = "<signedUrl>";
// Specify the full path of the local file.
String filePath = "C:\\Users\\demo.txt";
activity.uploadFile(signedUrl, filePath);
}
}Upload a file with request headers
If your upload requires specific HTTP headers (such as Content-Type or x-oss-storage-class), include them when generating the signed URL. The PUT request must send the exact same headers — any mismatch results in a signature mismatch error.
Step 1: Generate a signed URL with headers
import Client, { EHeaderKey, EHttpMethod } from '@aliyun/oss';
const client = new Client({
accessKeyId: 'yourAccessKeyId',
accessKeySecret: 'yourAccessKeySecret',
securityToken: 'yourSecurityToken',
region: 'oss-cn-hangzhou',
});
const bucket = 'yourBucketName';
const key = 'yourObjectName';
const generateSignedUrl = async () => {
const url = await client.signatureUrl({
method: EHttpMethod.PUT,
bucket,
key,
expires: 60 * 60,
headers: {
[EHeaderKey.CONTENT_TYPE]: 'text/plain;charset=utf-8', // Content-Type to include in the signature.
},
});
console.log(url);
};
generateSignedUrl();Step 2: Upload using the signed URL, passing the same headers
curl
curl -X PUT \
-H "Content-Type: text/plain;charset=utf-8" \
-T "C:\\Users\\demo.txt" \
"<signedUrl>"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 signed URL.
URL signedUrl = new URL("<signedUrl>");
// Specify the full path of the local file.
String pathName = "C:\\Users\\demo.txt";
// Set request headers — must match the headers used to generate the signed URL.
Map<String, String> headers = new HashMap<>();
// headers.put("Content-Type", "text/plain;charset=utf-8");
// headers.put("x-oss-storage-class", "Standard");
// Set user-defined metadata — must match the metadata used to generate the signed URL.
Map<String, String> userMetadata = new HashMap<>();
// userMetadata.put("key1", "value1");
try {
HttpPut put = new HttpPut(signedUrl.toString());
HttpEntity entity = new FileEntity(new File(pathName));
put.setEntity(entity);
for (Map.Entry<String, String> header : headers.entrySet()) {
put.addHeader(header.getKey(), header.getValue());
}
// The SDK adds the x-oss-meta- prefix internally. When using other methods,
// add the prefix manually.
for (Map.Entry<String, String> meta : userMetadata.entrySet()) {
put.addHeader("x-oss-meta-" + meta.getKey(), meta.getValue());
}
httpClient = HttpClients.createDefault();
response = httpClient.execute(put);
System.out.println("Status code: " + response.getStatusLine().getStatusCode());
if (response.getStatusLine().getStatusCode() == 200) {
System.out.println("Upload successful.");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
response.close();
httpClient.close();
}
}
}Python
import requests
def upload_file(signed_url, file_path, headers=None, metadata=None):
if not headers:
headers = {}
if not metadata:
metadata = {}
# Add the x-oss-meta- prefix to each metadata entry.
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("Upload successful.")
else:
print("Upload failed.")
print(response.text)
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == "__main__":
# Replace <signedUrl> with the signed URL.
signed_url = "<signedUrl>"
# Specify the full path of the local file.
file_path = "C:\\Users\\demo.txt"
# Set request headers — must match the headers used to generate the signed URL.
headers = {
"Content-Type": "text/plain; charset=utf8",
"x-oss-storage-class": "Standard",
}
# Set user-defined metadata — must match the metadata used to generate the signed 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 each metadata entry.
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 });
console.log(`Status code: ${response.status}`);
if (response.status === 200) {
console.log('Upload successful.');
} else {
console.log('Upload failed.');
}
console.log(response.data);
} catch (error) {
console.error(`An error occurred: ${error.message}`);
}
}
(async () => {
// Replace <signedUrl> with the signed URL.
const signedUrl = '<signedUrl>';
// Specify the full path of the local file.
const filePath = 'C:\\Users\\demo.txt';
// Set request headers — must match the headers used to generate the signed URL.
const headers = {
// 'Content-Type': 'text/plain;charset=utf-8',
// 'x-oss-storage-class': 'Standard',
};
// Set user-defined metadata — must match the metadata used to generate the signed URL.
const metadata = {
// 'key1': 'value1',
// 'key2': 'value2',
};
await uploadFile(signedUrl, filePath, headers, metadata);
})();Browser (JavaScript)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>File Upload Example (Harmony SDK)</title>
</head>
<body>
<input type="file" id="fileInput" />
<button id="uploadButton">Upload File</button>
<script>
// Replace with the actual signed URL.
const signedUrl = "<signedUrl>";
// These must match the headers specified when generating the signed URL.
const contentType = 'text/plain';
const storageClass = 'Standard';
const metadata = {
'x-oss-meta-key1': 'value1',
'x-oss-meta-key2': 'value2',
};
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('Upload error:', error);
alert('Upload failed: ' + error.message);
}
});
const upload = async (file, presignedUrl) => {
const chunkSize = 1024 * 1024;
let start = 0;
while (start < file.size) {
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
const headers = {
'Content-Type': contentType,
'x-oss-storage-class': storageClass,
...metadata,
};
const response = await fetch(presignedUrl, {
method: 'PUT',
headers,
body: chunk,
});
if (!response.ok) {
throw new Error(`Upload failed for chunk, status: ${response.status}`);
}
console.log('Chunk uploaded successfully.');
start = end;
}
console.log('File uploaded successfully.');
};
</script>
</body>
</html>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) {
output->append((char*)contents, size * nmemb);
return size * nmemb;
}
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 = curl_easy_init();
if (!curl) return;
curl_global_init(CURL_GLOBAL_DEFAULT);
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 << "Failed to open file: " << filePath << std::endl;
curl_easy_cleanup(curl);
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);
// Build the request headers.
struct curl_slist* chunk = nullptr;
for (const auto& h : headers) {
chunk = curl_slist_append(chunk, (h.first + ": " + h.second).c_str());
}
for (const auto& m : metadata) {
chunk = curl_slist_append(chunk, ("x-oss-meta-" + m.first + ": " + m.second).c_str());
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
std::string readBuffer;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
CURLcode 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 << "Upload successful." << 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 signed URL.
std::string signedUrl = "<signedUrl>";
// Specify the full path of the local file.
std::string filePath = "C:\\Users\\demo.txt";
// Set request headers — must match the headers used to generate the signed URL.
std::map<std::string, std::string> headers = {
// {"Content-Type", "text/plain;charset=utf-8"},
// {"x-oss-storage-class", "Standard"},
};
// Set user-defined metadata — must match the metadata used to generate the signed URL.
std::map<std::string, std::string> metadata = {
// {"key1", "value1"},
// {"key2", "value2"},
};
uploadFile(signedUrl, filePath, headers, metadata);
return 0;
}Go
package main
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"os"
)
func uploadFile(signedUrl, filePath string, headers, 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
}
// Set request headers — must match the headers used to generate the signed URL.
for key, value := range headers {
req.Header.Set(key, value)
}
// Set user-defined metadata — must match the metadata used to generate the signed URL.
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("Upload successful.")
} else {
fmt.Println("Upload failed.")
}
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
return nil
}
func main() {
// Replace <signedUrl> with the signed URL.
signedUrl := "<signedUrl>"
// Specify the full path of the local file.
filePath := "C:\\Users\\demo.txt"
// Set request headers — must match the headers used to generate the signed URL.
headers := map[string]string{
// "Content-Type": "text/plain;charset=utf-8",
// "x-oss-storage-class": "Standard",
}
// Set user-defined metadata — must match the metadata used to generate the signed URL.
metadata := map[string]string{
// "key1": "value1",
// "key2": "value2",
}
if err := uploadFile(signedUrl, filePath, headers, metadata); err != nil {
fmt.Printf("Error: %v\n", err)
}
}Android (Java)
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;
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 signed URL.
String signedUrl = "<signedUrl>";
// Specify the full path of the local file.
String pathName = "/storage/emulated/0/demo.txt";
// Set request headers — must match the headers used to generate the signed URL.
Map<String, String> headers = new HashMap<>();
// headers.put("Content-Type", "text/plain;charset=utf-8");
// headers.put("x-oss-storage-class", "Standard");
// Set user-defined metadata — must match the metadata used to generate the signed URL.
Map<String, String> userMetadata = new HashMap<>();
// userMetadata.put("key1", "value1");
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 (Map.Entry<String, String> header : headers.entrySet()) {
connection.setRequestProperty(header.getKey(), header.getValue());
}
for (Map.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, "Upload successful.");
}
return responseCode;
} catch (IOException e) {
e.printStackTrace();
return -1;
}
}
@Override
protected void onPostExecute(Integer result) {
if (result == 200) {
Toast.makeText(SignUrlUploadActivity.this, "Upload successful.", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(SignUrlUploadActivity.this, "Upload failed.", Toast.LENGTH_SHORT).show();
}
}
}
}Troubleshooting
Signature mismatch (HTTP 403)
If you get a 403 error, the most common cause is a mismatch between the headers used when generating the signed URL and the headers sent in the PUT request.
Check that every request header included in the
signatureUrlcall is also present in the PUT request, with the same values.Browser clients automatically add
Content-Type. If this header was not signed into the URL, it causes a 403. Fix this by specifyingContent-Typein theheadersoption when callingsignatureUrl.
Expired URL
If the signed URL has expired, it becomes invalid for uploads. Generate a new signed URL.
What's next
Signature V4 (Recommended) — Learn how OSS V4 signatures are computed.
Access OSS through bucket domain names — Find the endpoint for your region.
Grant custom access policies to RAM users — Configure the
oss:PutObjectpermission.