OSS オブジェクトはデフォルトで非公開であり、バケット所有者のみがアップロードできます。OSS SDK for Python 2.0 を使用して署名付き URL を生成すると、認証情報を共有することなく、任意の HTTP クライアントに一時的なアップロードアクセスを付与できます。
署名付き URL には V4 シグネチャが埋め込まれており、有効期限が切れるまで複数回使用できます。同じオブジェクトキーが複数回アップロードされた場合、各アップロードは以前のオブジェクトを上書きします。URL の有効期限が切れた後は、アップロードを続行するために新しい署名付き URL を生成する必要があります。
注意事項
このトピックのサンプルコードでは、リージョン
中国 (杭州)を使用しています。同じリージョン内の別の Alibaba Cloud サービスからバケットにアクセスするには、デフォルトのパブリックエンドポイントではなく、内部エンドポイントを使用します。詳細については、「リージョンとエンドポイント」をご参照ください。URL を生成する呼び出し元は、
oss:PutObject権限を持っている必要があります。 URL 自体を生成するために特別な権限は必要ありません。 詳細については、「RAM ユーザーにバケット内の複数のディレクトリへのアクセスを許可する」をご参照ください。このトピックでは、最大有効期間が 7 日の V4 署名を使用します。詳細については、「(推奨) 事前署名付き URL での V4 署名」をご参照ください。
仕組み
次の図は、HTTP PUT リクエストを許可する署名付き URL を使用したアップロードフローを示しています。
presign() メソッドリファレンス
presign() メソッドを使用して、PutObjectRequest の署名付き URL を生成します。
presign(request: PutObjectRequest, **kwargs) → PresignResult戻り値
presign() は PresignResult オブジェクトを返します。
| フィールド | 型 | 説明 |
|---|---|---|
method | str | アップロードリクエストの HTTP メソッド (PutObject の場合は PUT)。 |
url | str | アップローダーと共有する署名付き URL。 |
expiration | datetime | URL の絶対的な有効期限。 |
signed_headers | MutableMapping | 署名に含まれるリクエストヘッダー。アップロード元は、これらのヘッダーをすべての PUT リクエストとともに送信する必要があります。それらを省略すると、SignatureNotMatch エラーが発生します。 |
API の詳細については、「presign」をご参照ください。
署名付き URL の生成
次の例では、3,600 秒 (1 時間) 有効な署名付き URL を生成します。
URL を生成する際にリクエストヘッダー (Content-Type、ストレージクラス、またはユーザーメタデータなど) を含める場合、それらの同じヘッダーを URL を使用するすべての PUT リクエストとともに送信する必要があります。ヘッダーは署名の一部であり、不一致があると SignatureNotMatch エラーが発生します。
import argparse
import alibabacloud_oss_v2 as oss
from datetime import timedelta
parser = argparse.ArgumentParser(description="presign put object sample")
parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
parser.add_argument('--bucket', help='The name of the bucket.', required=True)
parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
parser.add_argument('--key', help='The name of the object.', required=True)
def main():
args = parser.parse_args()
# Load credentials from environment variables — never hardcode keys in source code.
credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials_provider
cfg.region = args.region
if args.endpoint is not None:
cfg.endpoint = args.endpoint
client = oss.Client(cfg)
# Generate a presigned URL for a PUT request, valid for 1 hour.
pre_result = client.presign(
oss.PutObjectRequest(
bucket=args.bucket,
key=args.key,
),
expires=timedelta(seconds=3600)
)
print(f'method: {pre_result.method}')
print(f'expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")}')
print(f'url: {pre_result.url}')
# signed_headers lists all headers included in the signature.
# The uploader must send these headers with the PUT request.
for key, value in pre_result.signed_headers.items():
print(f'signed header: {key}: {value}')
if __name__ == "__main__":
main()完全なサンプルについては、「presigner_put_object.py」をご参照ください。
署名付き URL を使用したオブジェクトのアップロード
署名付き URL を任意の HTTP クライアントに渡してアップロードします。以下のすべての例では、通常の HTTP PUT を使用しており、クライアント側で OSS SDK は必要ありません。
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******************************************************"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 presigned URL generated above.
signed_url = "<signedUrl>"
file_path = "C:\\Users\\demo.txt"
upload_file(signed_url, file_path)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";
Map<String, String> headers = new HashMap<>();
headers.put("x-oss-callback", "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly93d3cuZXhhbXBsZS5jb20vY2FsbGJhY2siLCJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mbXlfdmFyXzE9JHt4OnZhcjF9Jm15X3Zhcl8yPSR7eDp2YXIyfSJ9");
headers.put("x-oss-callback-var", "eyJ4OnZhcjEiOiJ2YWx1ZTEiLCJ4OnZhcjIiOiJ2YWx1ZTIifQ==");
try {
HttpPut put = new HttpPut(signedUrl.toString());
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("Upload successful.");
}
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 {
file, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("unable to open file: %w", err)
}
defer file.Close()
client := &http.Client{}
req, err := http.NewRequest("PUT", signedUrl, file)
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
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 presigned URL.
signedUrl := "<signedUrl>"
filePath := "C:\\Users\\demo.txt"
if err := uploadFile(signedUrl, filePath); err != nil {
fmt.Println("An error occurred:", err)
}
}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 presigned URL.
const signedUrl = '<signedUrl>';
const filePath = 'C:\\Users\\demo.txt';
await uploadFile(signedUrl, filePath);
})();Browser.js
ブラウザは PUT リクエストに Content-Type ヘッダーを自動的に追加します。署名付き URL の生成時に Content-Type が含まれていなかった場合、署名が一致せず、OSS は 403 SignatureNotMatch を返します。ブラウザで使用する署名付き URL を生成する際は、常に Content-Type を指定してください。
<!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 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);
}
});
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 headers:");
foreach (var header in response.Headers)
{
Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
}
}
else
{
string responseContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Upload failed! Status code: {response.StatusCode}");
Console.WriteLine("Response content: " + responseContent);
}C++
#include <iostream>
#include <fstream>
#include <curl/curl.h>
void uploadFile(const std::string& signedUrl, const std::string& filePath) {
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl) {
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 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 << "Upload successful." << std::endl;
}
}
fclose(file);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
}
int main() {
// Replace <signedUrl> with the presigned URL.
std::string signedUrl = "<signedUrl>";
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, "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 presigned URL.
String signedUrl = "<signedUrl>";
String filePath = "C:\\Users\\demo.txt";
activity.uploadFile(signedUrl, filePath);
}
}適用シナリオ
よくある質問
次のステップ
presigner_put_object.py — シングルパートアップロードの完全なサンプル
presigner_complete_multipart_upload.py — マルチパートアップロードの完全なサンプル