デフォルトでは、バケット内の Object Storage Service (OSS) オブジェクトのアクセス制御リスト (ACL) は非公開です。オブジェクト所有者のみが、バケットにオブジェクトをアップロードする権限を持っています。 OSS SDK for Python を使用して署名付き URL を生成し、その署名付き URL をユーザーと共有することで、ユーザーがオブジェクトをアップロードできるようにすることができます。署名付き URL を生成する際に、ユーザーがオブジェクトをアップロードできる期間を制限するために、署名付き URL の有効期間を指定できます。署名付き URL の有効期間中は、ユーザーはその URL を使用してバケットにオブジェクトを複数回アップロードできます。オブジェクトを複数回アップロードすると、アップロードされたオブジェクトが上書きされる可能性があります。署名付き URL の有効期間が終了すると、ユーザーはその署名付き URL を使用してオブジェクトをアップロードできなくなります。この場合、新しい署名付き URL を生成する必要があります。
使用上の注意
このトピックのサンプルコードでは、中国 (杭州) リージョンのリージョン ID
cn-hangzhouを使用しています。デフォルトでは、バケット内のリソースにアクセスするためにパブリックエンドポイントが使用されます。バケットが配置されているのと同じリージョン内の他の Alibaba Cloud サービスを使用してバケット内のリソースにアクセスする場合は、内部エンドポイントを使用します。 OSS のリージョンとエンドポイントの詳細については、「リージョンとエンドポイント」をご参照ください。署名付き URL を生成するために特定の権限は必要ありません。ただし、サードパーティが署名付き URL を使用してオブジェクトをアップロードできるようにするには、
oss:PutObject権限が必要です。詳細については、「カスタムポリシーを使用して RAM ユーザーに権限を付与する」をご参照ください。このトピックでは、V4 署名を含み、最大 7 日間の有効期間を持つ署名付き URL を使用します。詳細については、「(推奨) 署名付き URL の V4 署名」をご参照ください。
プロセス
次の図は、HTTP PUT リクエストを許可する署名付き URL を使用して OSS にオブジェクトをアップロードする方法を示しています。
メソッド
特定の操作を呼び出して署名付き URL を生成し、その署名付き URL を使用してバケット内のオブジェクトに対する一時的なアクセス権限を付与できます。署名付き URL は、有効期限が切れるまで複数回使用できます。
構文:
presign(request: PutObjectRequest, **kwargs) → PresignResultリクエストパラメーター
パラメーター | タイプ | 説明 |
request | PutObjectRequest | 署名付き URL を生成するために使用される API 操作。詳細については、Client.presign をご参照ください。 |
expires | datetime.timedelta | 署名付き URL の有効期間。これはオプションのパラメーターです。たとえば、有効期間を 30 分に設定するには、expires を 30 * time.Minute に設定します。このパラメーターを指定しない場合、署名付き URL はデフォルト値である 15 分を使用します。 |
expiration | datetime.datetime | 署名付き URL の絶対有効期限。これはオプションのパラメーターです。 |
V4 署名アルゴリズムを使用する場合、有効期間は最大 7 日間です。Expiration と Expires の両方を指定した場合、Expiration が優先されます。
レスポンスパラメーター
タイプ | 説明 |
PresignResult | 署名付き URL、HTTP メソッド、有効期限、リクエストで指定されたリクエストヘッダーを含む、返された結果。詳細については、PresignResult をご参照ください。 |
PresignResult のレスポンスパラメーター
パラメーター | タイプ | 説明 |
method | str | API 操作に対応する HTTP メソッド。たとえば、PutObject 操作の HTTP メソッドは PUT です。 |
url | str | 署名付き URL。 |
expiration | datetime | 署名付き URL の有効期限。 |
signed_headers | MutableMapping | リクエストで指定された署名付きヘッダー。たとえば、Content-Type が指定されている場合、Content-Type に関する情報が返されます。 |
presign メソッドの詳細については、presign をご参照ください。
サンプルコード
HTTP PUT リクエストを許可する署名付き URL を生成します。
重要HTTP PUT リクエストを許可する署名付き URL を生成するときにリクエストヘッダーを指定する場合は、署名付き URL を使用して開始された PUT リクエストにリクエストヘッダーが含まれていることを確認してください。これにより、リクエストの失敗と署名エラーを防ぎます。
import argparse import requests import alibabacloud_oss_v2 as oss from datetime import datetime, timedelta # コマンドラインパラメータパーサーを作成し、スクリプトの目的を記述します。この例では、HTTP PUT リクエストを許可する署名付き URL を生成する方法について説明します。 parser = argparse.ArgumentParser(description="presign put object sample") # 必須のリージョン、バケット名、エンドポイント、オブジェクト名を含むコマンドラインパラメータを指定します。 parser.add_argument('--region', help='バケットが配置されているリージョン。', required=True) parser.add_argument('--bucket', help='バケットの名前。', required=True) parser.add_argument('--endpoint', help='他のサービスが OSS にアクセスするために使用できるドメイン名') parser.add_argument('--key', help='オブジェクトの名前。', required=True) def main(): # コマンドラインパラメータを解析して、ユーザーが指定した値を取得します。 args = parser.parse_args() # 認証のために環境変数からアクセス認証情報を取得します。 credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider() # SDK のデフォルト設定を使用して設定オブジェクトを作成し、認証情報プロバイダーを指定します。 cfg = oss.config.load_default() cfg.credentials_provider = credentials_provider # ユーザーが指定したコマンドラインパラメータに基づいて、設定オブジェクトのリージョン属性を指定します。 cfg.region = args.region # カスタムエンドポイントが提供されている場合は、設定オブジェクトの endpoint パラメータを変更します。 if args.endpoint is not None: cfg.endpoint = args.endpoint # 前述の設定を使用して OSSClient インスタンスを初期化し、インスタンスが OSS と対話できるようにします。 client = oss.Client(cfg) # リクエストを送信して PUT リクエストを開始し、指定されたオブジェクトの署名付き URL を生成します。 pre_result = client.presign(oss.PutObjectRequest( bucket=args.bucket, # バケットの名前。 key=args.key, # オブジェクトの名前。 ),expires=timedelta(seconds=3600)) # リクエストの有効期間を指定します。この例では、有効期間は 3,600 秒に設定されています。 # リクエストで指定されたメソッド、有効期限、署名付き URL を表示して、署名付き URL の有効性を確認します。 print(f'method: {pre_result.method},' f' expiration: {pre_result.expiration.strftime("%Y-%m-%dT%H:%M:%S.000Z")},' f' url: {pre_result.url}' ) # リクエストで署名されたヘッダーを表示します。これらは、リクエストの送信時に HTTP ヘッダーに含まれます。 for key, value in pre_result.signed_headers.items(): print(f'signed headers key: {key}, signed headers value: {value}') # スクリプトが直接実行されたときに main 関数を呼び出して処理ロジックを開始します。 if __name__ == "__main__": main() # スクリプトの関数のエントリポイントを指定します。制御プログラムフローはここから開始されます。HTTP PUT リクエストを許可する署名付き URL を使用してオブジェクトをアップロードします。
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; // <signedUrl> を認証 URL に置き換えます。 URL signedUrl = new URL("<signedUrl>"); // ローカルファイルのフルパスを指定します。ローカルパスを指定しない場合、デフォルトではサンプルプログラムのプロジェクトに対応するパスからファイルがアップロードされます。 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("返されたステータスコード:" + response.getStatusLine().getStatusCode()); if(response.getStatusLine().getStatusCode() == 200){ System.out.println("ネットワークライブラリを使用して正常にアップロードされました。"); } 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("ファイルを開けませんでした: %w", err) } defer file.Close() // 新しい HTTP クライアントを作成します。 client := &http.Client{} // PUT リクエストを作成します。 req, err := http.NewRequest("PUT", signedUrl, file) if err != nil { return fmt.Errorf("リクエストの作成に失敗しました: %w", err) } // リクエストを送信します。 resp, err := client.Do(req) if err != nil { return fmt.Errorf("リクエストの送信に失敗しました: %w", err) } defer resp.Body.Close() // レスポンスを読み取ります。 body, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("レスポンスの読み取りに失敗しました: %w", err) } fmt.Printf("返されたステータスコード: %d\n", resp.StatusCode) if resp.StatusCode == 200 { fmt.Println("ネットワークライブラリを使用して正常にアップロードされました。") } fmt.Println(string(body)) return nil } func main() { // <signedUrl> を認証 URL に置き換えます。 signedUrl := "<signedUrl>" // ローカルファイルのフルパスを指定します。ローカルパスを指定しない場合、デフォルトではサンプルプログラムのプロジェクトに対応するパスからファイルがアップロードされます。 filePath := "C:\\Users\\demo.txt" err := uploadFile(signedUrl, filePath) if err != nil { fmt.Println("エラーが発生しました:", err) } }python
import requests def upload_file(signed_url, file_path): try: # ファイルを開きます。 with open(file_path, 'rb') as file: # PUT リクエストを送信してファイルをアップロードします。 response = requests.put(signed_url, data=file) print(f"返されたステータスコード: {response.status_code}") if response.status_code == 200: print("ネットワークライブラリを使用して正常にアップロードされました。") print(response.text) except Exception as e: print(f"エラーが発生しました: {e}") if __name__ == "__main__": # <signedUrl> を認証 URL に置き換えます。 signed_url = "<signedUrl>" # ローカルファイルのフルパスを指定します。ローカルパスを指定しない場合、デフォルトではサンプルプログラムのプロジェクトに対応するパスからファイルがアップロードされます。 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); // PUT リクエストを送信してファイルをアップロードします。 const response = await axios.put(signedUrl, fileStream, { headers: { 'Content-Type': 'application/octet-stream' // 要件に基づいて Content-Type を調整します。 } }); console.log(`返されたステータスコード: ${response.status}`); if (response.status === 200) { console.log('ネットワークライブラリを使用して正常にアップロードされました。'); } console.log(response.data); } catch (error) { console.error(`エラーが発生しました: ${error.message}`); } } // メイン関数。 (async () => { // <signedUrl> を認証 URL に置き換えます。 const signedUrl = '<signedUrl>'; // ローカルファイルのフルパスを指定します。ローカルパスを指定しない場合、デフォルトではサンプルプログラムのプロジェクトに対応するパスからファイルがアップロードされます。 const filePath = 'C:\\Users\\demo.txt'; await uploadFile(signedUrl, filePath); })();browser.js
重要Browser.js を使用してファイルをアップロードし、403 SignatureDoesNotMatch エラーが発生した場合、通常、ブラウザが自動的に Content-Type リクエストヘッダーを追加するためにエラーが発生します。署名付き URL の生成時にこのヘッダーが指定されていない場合、署名の検証は失敗します。この問題を解決するには、署名付き 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>ファイルアップロードの例</title> </head> <body> <h1>ファイルアップロードの例</h1> <!-- ファイルを選択 --> <input type="file" id="fileInput" /> <button id="uploadButton">ファイルをアップロード</button> <script> // これを手順 1 で生成された署名付き URL に置き換えます。 const signedUrl = "<signedUrl>"; document.getElementById('uploadButton').addEventListener('click', async () => { const fileInput = document.getElementById('fileInput'); const file = fileInput.files[0]; if (!file) { alert('アップロードするファイルを選択してください。'); return; } try { await upload(file, signedUrl); alert('ファイルが正常にアップロードされました!'); } catch (error) { console.error('アップロード中にエラーが発生しました:', error); alert('アップロードに失敗しました: ' + error.message); } }); /** * OSS にファイルをアップロードします。 * @param {File} file - アップロードするファイル。 * @param {string} presignedUrl - 署名付き URL。 */ const upload = async (file, presignedUrl) => { const response = await fetch(presignedUrl, { method: 'PUT', body: file, // ファイル全体をアップロードします。 }); if (!response.ok) { throw new Error(`アップロードに失敗しました、ステータス: ${response.status}`); } console.log('ファイルが正常にアップロードされました'); }; </script> </body> </html>C#
using System.Net.Http.Headers; // ローカルファイルのフルパスを指定します。ローカルパスを指定しない場合、デフォルトではサンプルプログラムのプロジェクトに対応するパスからファイルがアップロードされます。 var filePath = "C:\\Users\\demo.txt"; // <signedUrl> を認証 URL に置き換えます。 var presignedUrl = "<signedUrl>"; // HTTP クライアントを作成し、ローカルファイルストリームを開きます。 using var httpClient = new HttpClient(); using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); using var content = new StreamContent(fileStream); // PUT リクエストを作成します。 var request = new HttpRequestMessage(HttpMethod.Put, presignedUrl); request.Content = content; // リクエストを送信します。 var response = await httpClient.SendAsync(request); // レスポンスを処理します。 if (response.IsSuccessStatusCode) { Console.WriteLine($"正常にアップロードされました! ステータスコード: {response.StatusCode}"); Console.WriteLine("レスポンスヘッダー:"); foreach (var header in response.Headers) { Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}"); } } else { string responseContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"アップロードに失敗しました! ステータスコード: {response.StatusCode}"); Console.WriteLine("レスポンスコンテンツ: " + 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) { // URL を設定します。 curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str()); // リクエストメソッドを PUT に設定します。 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); // ファイルを開きます。 FILE *file = fopen(filePath.c_str(), "rb"); if (!file) { std::cerr << "ファイルを開けませんでした: " << 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() が失敗しました: " << curl_easy_strerror(res) << std::endl; } else { long httpCode = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode); std::cout << "返されたステータスコード: " << httpCode << std::endl; if (httpCode == 200) { std::cout << "ネットワークライブラリを使用して正常にアップロードされました。" << std::endl; } } // ファイルを閉じます。 fclose(file); // クリーンアップします。 curl_easy_cleanup(curl); } curl_global_cleanup(); } int main() { // <signedUrl> を認証 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, "返されたステータスコード: " + responseCode); if (responseCode == 200) { Log.d(TAG, "ネットワークライブラリを使用して正常にアップロードされました。"); } return "アップロード完了。ステータスコード: " + responseCode; } catch (IOException e) { e.printStackTrace(); return "アップロードに失敗しました: " + 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(); // <signedUrl> を認証 URL に置き換えます。 String signedUrl = "<signedUrl>"; // ローカルファイルのフルパスを指定します。ローカルパスを指定しない場合、デフォルトではサンプルプログラムのプロジェクトに対応するパスからファイルがアップロードされます。 String filePath = "C:\\Users\\demo.txt"; activity.uploadFile(signedUrl, filePath); } }
一般的なシナリオ
FAQ
署名付き URL を使用してローカルファイルをアップロードするときに、アップロード中に署名付き URL の有効期限が切れた場合、ファイルはアップロードされますか?
URL を生成するときにリクエストヘッダーとユーザーメタデータを指定しない場合、URL を使用してローカルファイルをアップロードするときにリクエストヘッダーとユーザーメタデータを指定する必要がありますか?
参考資料
署名付き URL を使用してオブジェクトをアップロードするために使用される完全なサンプルコードについては、presigner_put_object.py をご参照ください。
署名付き URL を使用してマルチパートアップロードでオブジェクトをアップロードするための完全なサンプルコードについては、presigner_complete_multipart_upload.py をご参照ください。