デフォルトでは、Object Storage Service (OSS) バケット内のオブジェクトのアクセス制御リスト (ACL) は非公開に設定されています。つまり、オブジェクト所有者のみがアクセス権限を持ちます。このトピックでは、PHP 用 OSS SDK を使用して、HTTP PUT メソッドを使用して指定された期間内に特定のオブジェクトをユーザーがアップロードできるようにする署名付き URL を生成する方法について説明します。有効期間内は、ユーザーは署名付き URL を使用してオブジェクトに繰り返しアクセスできます。署名付き URL の有効期限が切れた場合は、署名付き URL を再生成して、ユーザーのアクセスを延長できます。
注意事項
このトピックのサンプルコードでは、中国 (杭州) リージョンのリージョン ID
cn-hangzhou
を使用しています。デフォルトでは、パブリックエンドポイントを使用してバケット内のリソースにアクセスします。バケットが配置されているのと同じリージョン内の他の Alibaba Cloud サービスからバケット内のリソースにアクセスする場合は、内部エンドポイントを使用します。サポートされているリージョンとエンドポイントの詳細については、「リージョンとエンドポイント」をご参照ください。署名付き URL を生成するために特定の権限は必要ありません。ただし、他のユーザーが署名付き URL を使用してオブジェクトをアップロードできるようにするには、
oss:PutObject
権限が必要です。詳細については、「RAM ユーザーにバケット内の複数のディレクトリへのアクセスを承認する」をご参照ください。このトピックでは、署名アルゴリズム V4 を使用して、最大 7 日間の有効期間を持つ署名付き URL を生成します。詳細については、「(推奨) URL に V4 署名を含める」をご参照ください。
プロセス
次の図は、HTTP PUT リクエストを許可する署名付き URL を使用して、オブジェクトを OSS にアップロードする方法を示しています。
サンプルコード
オブジェクト所有者は、HTTP PUT リクエストを許可する署名付き URL を生成します。
重要HTTP PUT リクエストを許可する署名付き URL を生成するときにリクエストヘッダーを指定する場合は、署名付き URL を使用して開始された PUT リクエストにリクエストヘッダーが含まれていることを確認してください。これにより、リクエストの失敗と署名エラーを防ぎます。
<?php // 依存関係を読み込むためにオートロードファイルを含めます。 require_once __DIR__ . '/../vendor/autoload.php'; use AlibabaCloud\Oss\V2 as Oss; // コマンドラインオプションを定義し、説明します。 $optsdesc = [ "region" => ['help' => 'バケットが配置されているリージョン。', 'required' => True], // (必須) バケットが配置されているリージョンを指定します。 "endpoint" => ['help' => '他のサービスが OSS にアクセスするために使用できるドメイン名。', 'required' => False], // (オプション) OSS にアクセスするためのエンドポイントを指定します。 "bucket" => ['help' => 'バケットの名前', 'required' => True], // (必須) バケットの名前を指定します。 "key" => ['help' => 'オブジェクトの名前', 'required' => True], // (必須) オブジェクトの名前を指定します。 ]; // getopt に必要な長いオプションのリストに説明を変換します。 // 各オプションの末尾にコロン (:) を追加して、値が必要であることを示します。 $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"]; // オブジェクトの名前 // 環境変数からアクセス認証情報をロードします。 // EnvironmentVariableCredentialsProvider を使用して、環境変数から AccessKey ID と AccessKey シークレットを取得します。 $credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider(); // SDK のデフォルト構成を使用します。 $cfg = Oss\Config::loadDefault(); $cfg->setCredentialsProvider($credentialsProvider); // 認証情報プロバイダーを指定します。 $cfg->setRegion($region); // バケットが配置されているリージョンを指定します。 if (isset($options["endpoint"])) { $cfg->setEndpoint($options["endpoint"]); // エンドポイントが指定されている場合は、エンドポイントを指定します。 } // OSS クライアントインスタンスを作成します。 $client = new Oss\Client($cfg); // データをアップロードするための PutObjectRequest オブジェクトを作成します。 $request = new Oss\Models\PutObjectRequest(bucket: $bucket, key: $key); // presign メソッドを呼び出して、署名付きリクエストを生成します。 $result = $client->presign($request); // presign 操作の結果を表示します。 // 指定されたオブジェクトのアップロードに使用できる署名付き URL を表示します。 print( 'put object presign result:' . var_export($result, true) . PHP_EOL . // presign 結果の詳細。 'put object url:' . $result->url . PHP_EOL // 署名付き URL。 );
ユーザーは署名付き 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 と署名付き URL を使用してオブジェクトをアップロードすると、署名に不整合があることを示す 403 SignatureNotMatch エラーが発生する場合があります。これは通常、ブラウザーが Content-Type リクエストヘッダーを自動的に含めることによって発生します。これは、署名付き URL の生成時に指定されていませんでした。このエラーを防ぐには、Browser.js コードで使用して OSS にデータをアップロードすることが想定されている署名付き URL を生成するときに、Content-Type ヘッダーを指定してください。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="幅=デバイスの幅, 初期スケール=1.0"> <title>ファイルアップロードの例</title> </head> <body> <h1>ファイルアップロードの例</h1> <! -- ファイルを選択 --> <input type="file" id="fileInput" /> <button id="uploadButton">ファイルをアップロード</button> <script> // <signedUrl> を手順 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++
#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() failed: " << 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 ハンドルをクリアします。 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); } }