OSS バケットはデフォルトで非公開です。アップロードを実行できるのはバケット所有者のみです。署名付き URL を使用すると、アクセス認証情報を共有することなく、第三者にアップロード権限をデリゲートできます。PUT メソッドと有効期間を指定して URL を生成し、それを共有します。第三者は、この URL を使用して OSS へ直接オブジェクトをアップロードできますが、URL の有効期間が切れるまではこの操作が可能です。
主な動作:
有効期間内であれば、URL を複数回使用できます。各アップロードは、以前のオブジェクトを上書きします。
URL の有効期間が切れた後は、アップロードは失敗します。必要に応じて新しい URL を生成してください。
Signature Version 4 (V4) 署名付き URL の最大有効期間は 7 日間です。Security Token Service (STS) の一時的な認証情報を使用して URL を生成した場合、STS トークンの有効期限が切れた時点で URL も無効になります(コード内でより長い有効期間を設定しても同様です)。
前提条件
開始する前に、以下の条件を満たしていることを確認してください。
Java SDK V1 がインストールされています。詳細については、「インストール」をご参照ください。
アクセス認証情報を使用して設定された環境変数
OSS_ACCESS_KEY_IDおよびOSS_ACCESS_KEY_SECRET。詳細については、「アクセス認証情報の設定」をご参照ください。アップロード先の宛先パスを含む OSS バケットが存在すること。
署名付き URL を生成するアカウントに
oss:PutObject権限が付与されていること。詳細については、「RAM ユーザーへのカスタム権限の付与」をご参照ください。
署名付き URL 自体の生成には権限は不要です。ただし、第三者によるアップロードを成功させるには、URL を生成したアカウントが oss:PutObject 権限を持っている必要があります。仕組み
署名付き URL を使用したオブジェクトのアップロードには、2 つの関係者が関与します。
オブジェクト所有者 が Java SDK を使用して PUT リクエスト用の署名付き URL を生成し、アップローダーに共有します。
アップローダー がその URL に対して HTTP PUT リクエストを送信します。この際、OSS のアクセス認証情報は不要です。
以下の図にフローを示します。
注意事項
すべての例では、中国 (杭州) リージョンのパブリックエンドポイント (
https://oss-cn-hangzhou.aliyuncs.com) を使用します。同じリージョン内の他の Alibaba Cloud サービスから OSS にアクセスする場合は、代わりに内部エンドポイントを使用してください。サポートされているリージョンとエンドポイントについては、「リージョンとエンドポイント」をご参照ください。すべての例では、OSS エンドポイントを使用して OSSClient インスタンスを作成します。カスタムドメインまたはSTSを使用して作成する方法については、詳細については、「一般的なシナリオの設定例」をご参照ください。
すべての例では V4 署名付き URL を使用しています。背景情報については、「Signature Version 4(推奨)」をご参照ください。
署名付き URL を使用したオブジェクトのアップロード
手順 1:署名付き URL の生成(オブジェクト所有者)
HTTP PUT リクエスト用の署名付き URL を Java SDK を使用して生成します。
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import java.net.URL;
import java.util.*;
import java.util.Date;
public class GetSignUrl {
public static void main(String[] args) throws Throwable {
// 中国 (杭州) リージョンのエンドポイント。実際のエンドポイントに置き換えてください。
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// 環境変数からアクセス認証情報を読み込みます。
EnvironmentVariableCredentialsProvider credentialsProvider =
CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// バケット名を置き換えます(例:examplebucket)。
String bucketName = "examplebucket";
// オブジェクトの完全なパスを置き換えます(例:exampleobject.txt)。
// オブジェクトパスにはバケット名を含めないでください。
String objectName = "exampleobject.txt";
// バケットが配置されているリージョンを置き換えます(例:cn-hangzhou)。
String region = "cn-hangzhou";
// Signature Version 4 を使用して OSSClient インスタンスを構築します。
// クライアントが不要になった際には shutdown() を呼び出してリソースを解放してください。
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
URL signedUrl = null;
try {
// 有効期間を設定します。この例では、現在時刻から 1 時間後の時刻を設定しています。
Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
// HTTP PUT リクエスト用の署名付き URL リクエストを構築します。
GeneratePresignedUrlRequest request =
new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
request.setExpiration(expiration);
// 署名付き URL を生成し、出力します。
signedUrl = ossClient.generatePresignedUrl(request);
System.out.println("PUT 用の署名付き URL: " + signedUrl);
} catch (OSSException oe) {
System.out.println("OSS エラー — リクエストは OSS に到達しましたが拒否されました。");
System.out.println("エラーメッセージ: " + oe.getErrorMessage());
System.out.println("エラーコード: " + oe.getErrorCode());
System.out.println("リクエスト ID: " + oe.getRequestId());
System.out.println("ホスト ID: " + oe.getHostId());
} catch (ClientException ce) {
System.out.println("クライアントエラー — OSS との通信ができませんでした。");
System.out.println("エラーメッセージ: " + ce.getMessage());
}
}
}手順 2:署名付き URL を使用したファイルのアップロード(アップローダー)
署名付き URL をアップローダーに共有します。以下のすべての例では、URL に対して HTTP PUT リクエストを送信します。この際、OSS のアクセス認証情報は不要です。
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;
URL signedUrl = new URL("<signedUrl>");
String pathName = "C:\\Users\\demo.txt";
// これらのヘッダーは、手順 1 で署名された内容と一致している必要があります。
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());
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("HTTP ステータス: " + 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()
client := &http.Client{}
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("HTTP ステータス: %d\n", resp.StatusCode)
if resp.StatusCode == 200 {
fmt.Println("アップロードに成功しました。")
}
fmt.Println(string(body))
return nil
}
func main() {
// 手順 1 で生成した署名付き URL を <signedUrl> に置き換えます。
signedUrl := "<signedUrl>"
// アップロードするローカルファイルの完全なパスを置き換えます。
filePath := "C:\\Users\\demo.txt"
if err := uploadFile(signedUrl, filePath); err != nil {
fmt.Println("エラーが発生しました:", 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"HTTP ステータス: {response.status_code}")
if response.status_code == 200:
print("アップロードに成功しました。")
print(response.text)
except Exception as e:
print(f"エラーが発生しました: {e}")
if __name__ == "__main__":
# 手順 1 で生成した署名付き URL を <signedUrl> に置き換えます。
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);
const response = await axios.put(signedUrl, fileStream, {
headers: {
'Content-Type': 'application/octet-stream'
}
});
console.log(`HTTP ステータス: ${response.status}`);
if (response.status === 200) {
console.log('アップロードに成功しました。');
}
console.log(response.data);
} catch (error) {
console.error(`エラーが発生しました: ${error.message}`);
}
}
(async () => {
// 手順 1 で生成した署名付き URL を <signedUrl> に置き換えます。
const signedUrl = '<signedUrl>';
// アップロードするローカルファイルの完全なパスを置き換えます。
const filePath = 'C:\\Users\\demo.txt';
await uploadFile(signedUrl, filePath);
})();Browser.js
ブラウザからのアップロード時に 403 エラー(署名不一致)が発生した場合、通常は、署名付き URL の生成時に含まれていなかった Content-Type リクエストヘッダーがブラウザによって自動的に追加されたためです。これを解決するには、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);
}
});
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";
// 手順 1 で生成した署名付き URL を <signedUrl> に置き換えます。
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($"アップロードに成功しました。ステータス: {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) {
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 << "ファイルを開けません: " << 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 << "HTTP ステータス: " << httpCode << std::endl;
if (httpCode == 200) {
std::cout << "アップロードに成功しました。" << std::endl;
}
}
fclose(file);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
}
int main() {
// 手順 1 で生成した署名付き URL を <signedUrl> に置き換えます。
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, "HTTP ステータス: " + 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();
// 手順 1 で生成した署名付き URL を <signedUrl> に置き換えます。
String signedUrl = "<signedUrl>";
// アップロードするローカルファイルの完全なパスを指定します。
String filePath = "C:\\Users\\demo.txt";
activity.uploadFile(signedUrl, filePath);
}
}その他のシナリオ
リクエストヘッダーおよびユーザーメタデータを含めた署名付き URL を使用したアップロード
署名付き URL にリクエストヘッダー(ストレージクラスや Content-Type など)およびユーザーメタデータを含めることができます。アップローダーは、アップロード時にこれらのヘッダーを同じ内容で送信する必要があります。OSS は、署名済みパラメーターと照合して検証を行い、値が異なる場合は署名エラーを返します。
手順 1:署名付き URL の生成
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.internal.OSSHeaders;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.aliyun.oss.model.StorageClass;
import java.net.URL;
import java.util.*;
import java.util.Date;
public class GetSignUrl {
public static void main(String[] args) throws Throwable {
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
EnvironmentVariableCredentialsProvider credentialsProvider =
CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
String bucketName = "examplebucket";
String objectName = "exampleobject.txt";
String region = "cn-hangzhou";
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
// 署名対象とするリクエストヘッダーを定義します。
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");
// 署名対象とするユーザーメタデータを定義します。
// SDK は自動的に x-oss-meta- プレフィックスを追加します。
Map<String, String> userMetadata = new HashMap<String, String>();
userMetadata.put("key1", "value1");
userMetadata.put("key2", "value2");
URL signedUrl = null;
try {
Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
GeneratePresignedUrlRequest request =
new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
request.setExpiration(expiration);
request.setHeaders(headers);
request.setUserMetadata(userMetadata);
signedUrl = ossClient.generatePresignedUrl(request);
System.out.println("PUT 用の署名付き URL: " + signedUrl);
} catch (OSSException oe) {
System.out.println("OSS エラー: " + oe.getErrorMessage());
System.out.println("エラーコード: " + oe.getErrorCode());
System.out.println("リクエスト ID: " + oe.getRequestId());
} catch (ClientException ce) {
System.out.println("クライアントエラー: " + ce.getMessage());
}
}
}手順 2:一致するヘッダーを含めたファイルのアップロード
以下のすべての例では、手順 1 で署名済みのヘッダーと同じ内容を設定しています。署名済みヘッダーを省略したり、値を変更したりすると、署名エラーが発生します。
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";
// これらのヘッダーは、手順 1 で署名済みのものと完全に一致する必要があります。
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");
// これらのメタデータ項目は、手順 1 で署名済みのものと完全に一致する必要があります。
Map<String, String> userMetadata = new HashMap<String, String>();
userMetadata.put("key1", "value1");
userMetadata.put("key2", "value2");
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());
}
for (Map.Entry meta : userMetadata.entrySet()) {
// HTTP を介してユーザーメタデータを直接設定する場合は、x-oss-meta- プレフィックスを追加します。
put.addHeader("x-oss-meta-" + meta.getKey().toString(), meta.getValue().toString());
}
httpClient = HttpClients.createDefault();
response = httpClient.execute(put);
System.out.println("HTTP ステータス: " + 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 (
"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
}
// リクエストヘッダーを設定します — 手順 1 で署名済みのものと一致する必要があります。
for key, value := range headers {
req.Header.Set(key, value)
}
// ユーザーメタデータを設定します — 手順 1 で署名済みのものと一致する必要があります。
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("HTTP ステータス: %d\n", resp.StatusCode)
if resp.StatusCode == 200 {
fmt.Println("アップロードに成功しました。")
} else {
fmt.Println("アップロードに失敗しました。")
}
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",
}
if err := uploadFile(signedUrl, filePath, headers, metadata); err != nil {
fmt.Printf("エラーが発生しました: %v\n", err)
}
}Python
import requests
def upload_file(signed_url, file_path, headers=None, metadata=None):
if not headers:
headers = {}
if not metadata:
metadata = {}
# ユーザーメタデータを x-oss-meta- プレフィックス付きでヘッダーに統合します。
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"HTTP ステータス: {response.status_code}")
if response.status_code == 200:
print("アップロードに成功しました。")
else:
print("アップロードに失敗しました。")
print(response.text)
except Exception as e:
print(f"エラーが発生しました: {e}")
if __name__ == "__main__":
signed_url = "<signedUrl>"
file_path = "C:\\Users\\demo.txt"
# 手順 1 で署名済みのものと一致する必要があります。
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 {
// ユーザーメタデータを x-oss-meta- プレフィックス付きでヘッダーに統合します。
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(`HTTP ステータス: ${response.status}`);
if (response.status === 200) {
console.log('アップロードに成功しました。');
} else {
console.log('アップロードに失敗しました。');
}
console.log(response.data);
} catch (error) {
console.error(`エラーが発生しました: ${error.message}`);
}
}
(async () => {
const signedUrl = '<signedUrl>';
const filePath = 'C:\\Users\\demo.txt';
// 手順 1 で署名済みのものと一致する必要があります。
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
ブラウザからのアップロード時に 403 エラー(署名不一致)が発生した場合、通常は、署名付き URL の生成時に含まれていなかった Content-Type リクエストヘッダーがブラウザによって自動的に追加されたためです。これを解決するには、URL の生成時に Content-Type を明示的に指定してください(上記の手順 1 を参照)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ファイルアップロードの例(Java SDK)</title>
</head>
<body>
<h1>ファイルアップロードの例(Java SDK)</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) {
try {
await upload(file, signedUrl);
} catch (error) {
console.error('アップロード中のエラー:', error);
alert('アップロードに失敗しました: ' + error.message);
}
} else {
alert('アップロードするファイルを選択してください。');
}
});
const upload = async (file, presignedUrl) => {
// これらのヘッダーは、手順 1 で署名済みのものと一致する必要があります。
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(`アップロードに失敗しました。ステータス: ${response.status}`);
}
alert('ファイルのアップロードに成功しました。');
console.log('ファイルのアップロードに成功しました。');
};
</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;
// これらのヘッダーは、手順 1 で署名済みのものと一致する必要があります。
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("リクエストヘッダー:");
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($"アップロードに成功しました。ステータス: {response.StatusCode}");
}
else
{
string responseContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"アップロードに失敗しました。ステータス: {response.StatusCode}");
Console.WriteLine("応答: " + 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 << "ファイルを開けません: " << 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);
// ヘッダーリストを構築します — 手順 1 で署名済みのものと一致する必要があります。
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() が失敗しました: " << curl_easy_strerror(res) << std::endl;
} else {
long responseCode;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
std::cout << "HTTP ステータス: " << responseCode << std::endl;
if (responseCode == 200) {
std::cout << "アップロードに成功しました。" << std::endl;
} else {
std::cout << "アップロードに失敗しました。" << 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";
// 手順 1 で署名済みのものと一致する必要があります。
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";
// 手順 1 で署名済みのものと一致する必要があります。
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "text/plain;charset=utf8");
headers.put("x-oss-storage-class", "Standard");
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, "HTTP ステータス: " + responseCode);
if (responseCode == 200) {
Log.d(TAG, "アップロードに成功しました。");
} else {
Log.d(TAG, "アップロードに失敗しました。");
}
InputStream is = connection.getInputStream();
byte[] responseBuffer = new byte[1024];
StringBuilder sb = new StringBuilder();
while ((count = is.read(responseBuffer)) != -1) {
sb.append(new String(responseBuffer, 0, count));
}
Log.d(TAG, sb.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, "アップロードに成功しました。", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(SignUrlUploadActivity.this, "アップロードに失敗しました。", Toast.LENGTH_SHORT).show();
}
}
}
}マルチパートアップロードでの署名付き URL の使用
コールバックのアップロードを伴う署名付き URL の使用
アップロード成功後にサーバー側でコールバックをトリガーするには、署名付き URL にコールバックパラメーターを含めます。コールバック本文および変数は Base64 エンコードされ、リクエストヘッダーとして渡されます。
手順 1:コールバックパラメーターを含む署名付き URL の生成
手順 2:コールバックヘッダーを含めたファイルのアップロード
以下のすべての例では、x-oss-callback および x-oss-callback-var ヘッダーを渡します。これらの値は、手順 1 で署名済みのものと一致する必要があります。
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):
if not headers:
headers = {}
try:
with open(file_path, 'rb') as file:
response = requests.put(signed_url, data=file, headers=headers)
print(f"HTTP ステータス: {response.status_code}")
if response.status_code == 200:
print("アップロードに成功しました。")
else:
print("アップロードに失敗しました。")
print(response.text)
except Exception as e:
print(f"エラーが発生しました: {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("HTTP ステータス: %d\n", resp.StatusCode)
if resp.StatusCode == 200 {
fmt.Println("アップロードに成功しました。")
} else {
fmt.Println("アップロードに失敗しました。")
}
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==",
}
if err := uploadFile(signedUrl, filePath, headers); err != nil {
fmt.Printf("エラーが発生しました: %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";
// これらのヘッダーは、手順 1 で署名済みのものと一致する必要があります。
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());
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("HTTP ステータス: " + 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();
}
}
}PHP
<?php
function uploadFile($signedUrl, $filePath, $headers = []) {
if (!file_exists($filePath)) {
echo "ファイルが見つかりません: $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 "HTTP ステータス: $httpCode\n";
if ($httpCode == 200) {
echo "アップロードに成功しました。\n";
} else {
echo "アップロードに失敗しました。\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);
?>よくある質問
アップロード中に署名付き URL の有効期間が切れた場合、アップロードは失敗しますか?
いいえ。アップロードが URL の有効期間内に完了すれば、アップロードは成功します。実際の有効期間は、署名付き URL の有効期限と STS トークンの有効期限(STS の一時的な認証情報を使用して URL を生成した場合)のうち、短い方となります。
署名付き URL の生成時にリクエストヘッダーおよびユーザーメタデータを指定しなかった場合、アップロード時にそれらを設定する必要がありますか?
いいえ。リクエストヘッダーおよびユーザーメタデータは任意です。URL の生成時に指定しなかった場合は、アップロードリクエストから関連するコードを省略してください。