すべてのプロダクト
Search
ドキュメントセンター

Object Storage Service:署名付き URL を使用したオブジェクトのアップロード (Java SDK V1)

最終更新日:Mar 21, 2026

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 つの関係者が関与します。

  1. オブジェクト所有者 が Java SDK を使用して PUT リクエスト用の署名付き URL を生成し、アップローダーに共有します。

  2. アップローダー がその URL に対して HTTP PUT リクエストを送信します。この際、OSS のアクセス認証情報は不要です。

以下の図にフローを示します。

image

注意事項

  • すべての例では、中国 (杭州) リージョンのパブリックエンドポイント (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 には、partNumber および uploadId のクエリパラメーターが含まれます。すべてのフラグメントのアップロードが完了したら、completeMultipartUpload を使用してマージします。

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.common.utils.CRC64;
import com.aliyun.oss.internal.Mimetypes;
import com.aliyun.oss.model.*;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.Date;

public class MultipartUrl {
    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 pathName = "D:\\localpath\\examplefile.txt";
        // 各フラグメントの署名付き URL の有効期間(ミリ秒単位)。
        long expireTime = 3600 * 1000L;
        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();

        // マルチパートアップロードを初期化します。
        InitiateMultipartUploadRequest initRequest =
            new InitiateMultipartUploadRequest(bucketName, objectName);
        ObjectMetadata metadata = new ObjectMetadata();
        // 拡張子に基づいて Content-Type を設定します。デフォルトは application/octet-stream です。
        if (metadata.getContentType() == null) {
            metadata.setContentType(
                Mimetypes.getInstance().getMimetype(new File(pathName), objectName));
        }
        initRequest.setObjectMetadata(metadata);

        InitiateMultipartUploadResult upResult = ossClient.initiateMultipartUpload(initRequest);
        // upload ID はこのマルチパートアップロードセッションを識別します。
        // この ID を使用して、アップロードをキャンセル、照会、または完了できます。
        String uploadId = upResult.getUploadId();

        List<PartETag> partETags = new ArrayList<PartETag>();
        long partSize = 1 * 100 * 1024L;   // 各フラグメントのサイズ:100 KB

        File sampleFile = new File(pathName);
        long fileLength = sampleFile.length();
        int partCount = (int) (fileLength / partSize);
        if (fileLength % partSize != 0) {
            partCount++;
        }

        Map<String, String> headers = new HashMap<String, String>();
        // 各フラグメントの Content-Type を設定する場合は、次のコメントを解除します:
        // headers.put(OSSHeaders.CONTENT_TYPE, "text/plain");

        // 各フラグメントの署名付き URL を生成し、順次アップロードします。
        for (int i = 0; i < partCount; i++) {
            long startPos = i * partSize;
            long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;

            String signUrl = getSignUrl(ossClient, bucketName, objectName,
                HttpMethod.PUT, expireTime, i + 1, uploadId, headers);

            putObjectWithHttp(signUrl, pathName, startPos, curPartSize, headers);
        }

        // アップロード済みのフラグメントを一覧表示します(別のプロセスからマージする場合に必要です)。
        ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, objectName, uploadId);
        PartListing partListing = ossClient.listParts(listPartsRequest);

        for (PartSummary part : partListing.getParts()) {
            partETags.add(new PartETag(part.getPartNumber(), part.getETag()));
        }

        // フラグメントをマージしてアップロードを完了します。
        CompleteMultipartUploadRequest completeRequest =
            new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
        CompleteMultipartUploadResult completeResult =
            ossClient.completeMultipartUpload(completeRequest);
        System.out.println("マルチパートアップロードが完了しました。");

        // CRC-64 を使用してファイルの整合性を検証します。
        CRC64 crc = new CRC64();
        InputStream inStream = new FileInputStream(pathName);
        byte[] bytes = new byte[1024];
        int cnt;
        while ((cnt = inStream.read(bytes)) != -1) {
            crc.update(bytes, 0, cnt);
        }

        if (crc.getValue() == completeResult.getServerCRC()) {
            System.out.println("ファイルの整合性が検証されました。");
        } else {
            System.out.println("ファイルの整合性チェックに失敗しました。例外を処理してください。");
        }
    }

    public static void putObjectWithHttp(String signedUrl, String pathName,
                                          long startPos, long partSize,
                                          Map<String, String> headers) throws IOException {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;
        try {
            HttpPut put = new HttpPut(signedUrl);

            FileInputStream inStream = new FileInputStream(pathName);
            inStream.skip(startPos);
            InputStreamEntity entity = new InputStreamEntity(inStream, partSize);
            BufferedHttpEntity byteArrayEntity = new BufferedHttpEntity(entity);
            put.setEntity(byteArrayEntity);

            for (Map.Entry header : headers.entrySet()) {
                put.addHeader(header.getKey().toString(), header.getValue().toString());
            }

            // 一時的な障害に対して最大 3 回再試行します。
            httpClient = HttpClients.custom()
                .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true))
                .build();

            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 {
            if (response != null) response.close();
            if (httpClient != null) httpClient.close();
        }
    }

    public static String getSignUrl(OSS ossClient, String bucketName, String objectName,
                                     HttpMethod method, long expireTime, int partNum,
                                     String uploadId, Map<String, String> headers) {
        Date expiration = new Date(new Date().getTime() + expireTime);

        GeneratePresignedUrlRequest request =
            new GeneratePresignedUrlRequest(bucketName, objectName, method);
        request.setExpiration(expiration);
        request.setHeaders(headers);
        // URL にフラグメント番号および upload ID をクエリパラメーターとして埋め込みます。
        request.addQueryParameter("partNumber", String.valueOf(partNum));
        request.addQueryParameter("uploadId", uploadId);

        URL signedUrl = ossClient.generatePresignedUrl(request);
        System.out.println("フラグメント用の署名付き URL: " + signedUrl);
        return signedUrl.toString();
    }
}

コールバックのアップロードを伴う署名付き URL の使用

アップロード成功後にサーバー側でコールバックをトリガーするには、署名付き URL にコールバックパラメーターを含めます。コールバック本文および変数は Base64 エンコードされ、リクエストヘッダーとして渡されます。

手順 1:コールバックパラメーターを含む署名付き URL の生成

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.internal.OSSHeaders;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;

import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;

public class OssPresignExample {
    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();

        URL signedUrl = null;
        try {
            // コールバック本文を構築します。URL および変数を実際の値に置き換えてください。
            String callbackUrl = "http://www.example.com/callback";
            String callbackBody = "{\"callbackUrl\":\"" + callbackUrl
                + "\",\"callbackBody\":\"bucket=${bucket}&object=${object}"
                + "&my_var_1=${x:var1}&my_var_2=${x:var2}\"}";
            String callbackBase64 = Base64.getEncoder().encodeToString(callbackBody.getBytes());

            String callbackVarJson = "{\"x:var1\":\"value1\",\"x:var2\":\"value2\"}";
            String callbackVarBase64 = Base64.getEncoder().encodeToString(callbackVarJson.getBytes());

            // 署名済みリクエストにコールバックヘッダーを追加します。
            Map<String, String> headers = new HashMap<String, String>();
            headers.put(OSSHeaders.OSS_HEADER_CALLBACK, callbackBase64);
            headers.put(OSSHeaders.OSS_HEADER_CALLBACK_VAR, callbackVarBase64);

            Date expiration = new Date(new Date().getTime() + 3600 * 1000);

            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
            dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
            String expirationStr = dateFormat.format(expiration);

            GeneratePresignedUrlRequest request =
                new GeneratePresignedUrlRequest(bucketName, objectName);
            request.setMethod(HttpMethod.PUT);
            request.setExpiration(expiration);
            request.setHeaders(headers);

            System.out.println("callback:     " + callbackBase64);
            System.out.println("callback-var: " + callbackVarBase64);

            URL url = ossClient.generatePresignedUrl(request);
            System.out.println("method:     PUT");
            System.out.println("expiration: " + expirationStr);
            System.out.println("url:        " + url);

        } 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:コールバックヘッダーを含めたファイルのアップロード

以下のすべての例では、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 の生成時に指定しなかった場合は、アップロードリクエストから関連するコードを省略してください。

次のステップ

  • V4 署名付き URL の完全なサンプルコードについては、「GitHub の例」をご参照ください。

  • GeneratePresignedUrlRequest の API リファレンスについては、「Javadoc」をご参照ください。