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

Object Storage Service:フォームアップロード

最終更新日:May 23, 2025

フォームアップロードを使用すると、Web アプリケーションから標準の HTML フォームを使用して、Object Storage Service (OSS) にオブジェクトを直接アップロードできます。 この方法では、フロントエンドページでオブジェクトが選択されると、ブラウザは PostObject リクエストを開始し、Web サイトサーバーを使用せずにオブジェクトを OSS サーバーに直接アップロードします。 これにより、Web サイトサーバーのワークロードが軽減され、オブジェクトアップロードの効率と安定性が向上します。

制限事項

フォームアップロードを使用してアップロードするオブジェクトのサイズは、5 GB を超えることはできません。

シナリオ

フォームアップロードは、Web アプリケーションで広く使用されています。以下はそのシナリオの一部です。

  • ユーザーデータのアップロード: ユーザーがアカウントを登録する際に、アバター、ID カードの写真、その他の本人確認資料をアップロードします。 個人センターで情報を変更する際に、新しいアバターまたは背景画像をアップロードします。

  • オブジェクトの共有と保存: ユーザーは、オンラインディスクや共同作業プラットフォームのフォームを使用して、ドキュメント、画像、オーディオファイル、ビデオファイルなど、さまざまな形式のオブジェクトを保存および共有するために Alibaba Cloud にアップロードします。

  • コンテンツの作成と公開: ユーザーは、ブログ、フォーラム、Q&A コミュニティなどのプラットフォームで記事を作成し、フォームを使用して画像や添付ファイルをコンテンツの補足としてアップロードします。

  • E コマース管理: 販売者は、E コマースプラットフォームのバックグラウンドで、商品写真、詳細な説明ドキュメント、資格などの情報をアップロードします。 消費者は、購入プロセス中に請求書の要件またはその他の補助資料をアップロードします。

  • オンライン教育プラットフォーム: 学生は、ドキュメント、PPT、ビデオファイルなどの宿題やプロジェクトをアップロードします。 教師は教材やコースウェアをアップロードします。

  • 求人 Web サイト: 求職者は履歴書、ポートフォリオ、その他の資料をアップロードします。 企業は、ポジションを投稿する際に、会社のロゴや募集要項などの資料をアップロードします。

  • アンケートとフィードバック: ユーザーは、オンラインアンケートに記入する際に、追加の証拠または説明資料をアップロードします。

  • ソフトウェア開発コラボレーション: 開発者は、GitHub や GitLab などのコードホスティングプラットフォームからコードファイルやプロジェクトドキュメントをアップロードします。

ソリューション

クライアントは、アプリケーションサーバーから PostObject リクエストに必要な署名と POST ポリシーをリクエストします。 その後、クライアントは署名と POST ポリシーを使用して、OSS SDK を使用せずにオブジェクトをアップロードできます。 アプリケーションサーバーによって生成された POST ポリシーを使用して、オブジェクトサイズやタイプなどのオブジェクトの属性を制限できます。 このソリューションは、HTML フォームを使用してオブジェクトをアップロードする場合に適しています。 このソリューションは、マルチパートアップロードと再開可能なアップロードをサポートしていません。 詳細については、「PostObject」をご参照ください。

次の図は、アプリケーションサーバーから取得した署名と POST ポリシーを使用して、クライアントから OSS にオブジェクトをアップロードする方法を示しています。

  1. クライアントは、署名や POST ポリシーなどの情報をアプリケーションサーバーにリクエストします。

  2. アプリケーションサーバーは、署名や POST ポリシーなどの情報を生成し、クライアントに返します。

  3. クライアントは、署名や POST ポリシーなどの情報を使用して PostObject 操作を呼び出し、HTML フォームを使用してオブジェクトを OSS にアップロードします。

  4. OSS は、成功レスポンスをクライアントに返します。

手順

  1. サーバーは、署名や POST ポリシーなどの情報を生成します。

    • サンプルプロジェクト: postsignature.zip

    • 次のサンプルコードは、アプリケーションサーバーが PostObject の署名と POST ポリシーを生成する方法の例を示しています。

      説明

      サンプルコードは Function Compute にデプロイできます。 oss-upload-post-signature-app

      Python

      import os
      from hashlib import sha1 as sha
      import json
      import base64
      import hmac
      import datetime
      import time
      
      # OSS_ACCESS_KEY_ID 環境変数から AccessKey ID を取得します。
      access_key_id = os.environ.get('OSS_ACCESS_KEY_ID')
      # OSS_ACCESS_KEY_SECRET 環境変数から AccessKey シークレットを取得します。
      access_key_secret = os.environ.get('OSS_ACCESS_KEY_SECRET')
      # <YOUR_BUCKET> をバケット名に置き換えます。
      bucket = '<YOUR_BUCKET>'
      # host を BucketName.Endpoint 形式の値に設定します。<YOUR_BUCKET> をバケット名に置き換えます。<YOUR_ENDPOINT> をバケットが配置されているリージョンのエンドポイント (例: oss-cn-hangzhou.aliyuncs.com) に置き換えます。
      host = 'https://<YOUR_BUCKET>.<YOUR_ENDPOINT>'
      # OSS にアップロードするオブジェクトの名前のプレフィックスを指定します。
      upload_dir = 'user-dir-prefix/'
      # 有効期間を指定します。単位: 秒。
      expire_time = 3600
      
      
      def generate_expiration(seconds):
          """
          指定された有効期間に基づいて有効期限を計算します。単位: 秒。
          :param seconds: 有効期間 (秒)。
          :return: ISO 8601 標準の時間文字列。例: 2014-12-01T12:00:00.000Z。
          """
          now = int(time.time())
          expiration_time = now + seconds
          gmt = datetime.datetime.utcfromtimestamp(expiration_time).isoformat()
          gmt += 'Z'
          return gmt
      
      
      def generate_signature(access_key_secret, expiration, conditions, policy_extra_props=None):
          """
          署名文字列を生成します。
          :param access_key_secret: バケットにアクセスするための権限を持つ AccessKey シークレット。
          :param expiration: 署名が期限切れになる時刻。時刻は ISO 8601 標準 (yyyy-MM-ddTHH:mm:ssZ 形式) で指定します。例: 2014-12-01T12:00:00.000Z。
          :param conditions: フォームアップロード中に指定できる値を制限するために使用できるポリシー条件。
          :param policy_extra_props: 追加のポリシーパラメーター。追加のパラメーターを辞書として渡すことができます。
          :return: 署名文字列。
          """
          policy_dict = {
              'expiration': expiration,
              'conditions': conditions
          }
          if policy_extra_props is not None:
              policy_dict.update(policy_extra_props)
          policy = json.dumps(policy_dict).strip()
          policy_encode = base64.b64encode(policy.encode())
          h = hmac.new(access_key_secret.encode(), policy_encode, sha)
          sign_result = base64.b64encode(h.digest()).strip()
          return sign_result.decode()
      
      def generate_upload_params():
          policy = {
              # 有効期間を指定します。
              "expiration": generate_expiration(expire_time),
              # ポリシー条件を指定します。
              "conditions": [
                  # success_action_redirect が指定されていない場合、オブジェクトのアップロード後に HTTP ステータスコード 204 が返されます。
                  ["eq", "$success_action_status", "200"],
                  # フォームフィールドの値は、指定されたプレフィックスで始まる必要があります。たとえば、key フォームフィールドの値が user/user1 で始まる場合、条件は ["starts-with", "$key", "user/user1"] です。
                  ["starts-with", "$key", upload_dir],
                  # アップロードできるオブジェクトの最小サイズと最大サイズを指定します。単位: バイト。
                  ["content-length-range", 1, 1000000],
                  # 許可されるファイルタイプを指定します。
                  ["in", "$content-type", ["image/jpg", "image/png"]]
              ]
          }
          signature = generate_signature(access_key_secret, policy.get('expiration'), policy.get('conditions'))
          response = {
              'policy': base64.b64encode(json.dumps(policy).encode('utf-8')).decode(),
              'ossAccessKeyId': access_key_id,
              'signature': signature,
              'host': host,
              'dir': upload_dir
              # 追加のパラメーターを指定します。
          }
          return json.dumps(response)
      

      Java

      import com.aliyun.help.demo.uploading_to_oss_directly_postsignature.config.OssConfig;
      import com.aliyun.oss.ClientException;
      import com.aliyun.oss.OSS;
      import com.aliyun.oss.OSSException;
      import com.aliyun.oss.common.utils.BinaryUtil;
      import com.aliyun.oss.model.MatchMode;
      import com.aliyun.oss.model.PolicyConditions;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.ResponseBody;
      import org.codehaus.jettison.json.JSONObject;
      import java.util.Date;
      import com.aliyun.oss.OSSClientBuilder;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Bean;
      import javax.annotation.PreDestroy;
      
      @Controller
      public class PostSignatureController {
          @Autowired
          private OSS ossClient;
      
          @Autowired
          private OssConfig ossConfig;
      
          @GetMapping("/get_post_signature_for_oss_upload")
          @ResponseBody
          public String generatePostSignature() {
              JSONObject response = new JSONObject();
              try {
                  long expireEndTime = System.currentTimeMillis() + ossConfig.getExpireTime() * 1000;
                  Date expiration = new Date(expireEndTime);
                  PolicyConditions policyConds = new PolicyConditions();
                  policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
                  policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, ossConfig.getDir());
                  String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
                  byte[] binaryData = postPolicy.getBytes("utf-8");
                  String encodedPolicy = BinaryUtil.toBase64String(binaryData);
                  String postSignature = ossClient.calculatePostSignature(postPolicy);
                  response.put("ossAccessKeyId", ossConfig.getAccessKeyId());
                  response.put("policy", encodedPolicy);
                  response.put("signature", postSignature);
                  response.put("dir", ossConfig.getDir());
                  response.put("host", ossConfig.getHost());
              } catch (
          OSSException oe) {
              System.out.println("OSSException が発生しました。これは、リクエストは OSS に到達しましたが、"
                      + "何らかの理由でエラーレスポンスで拒否されたことを意味します。");
              // メソッドが存在すると仮定します。
              System.out.println("HTTP ステータスコード: " + oe.getRawResponseError()); 
              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("ClientException が発生しました。これは、クライアントが OSS と通信しようとしているときに、"
                      + "ネットワークにアクセスできないなど、重大な内部問題が発生したことを意味します。");
              System.out.println("エラーメッセージ: " + ce.getMessage());
          } finally {
              if (ossClient != null) {
                  ossClient.shutdown();
              }
              return response.toString();
          }
        }
      }
      
      @Configuration
      public class OssConfig {
          /**
           * <YOUR-ENDPOINT> をバケットが配置されているリージョンのエンドポイント (例: oss-cn-hangzhou.aliyuncs.com) に置き換えます。
           */
          private String endpoint = "<YOUR-ENDPOINT>";
          /**
           * <YOUR-BUCKET> をバケット名に置き換えます。
           */
          private String bucket = "<YOUR-BUCKET>";
          /**
           * OSS 内のオブジェクトの名前のプレフィックスを指定します。
           */
          private String dir = "user-dir-prefix/";
          /**
           * 有効期間を指定します。単位: 秒。
           */
          private long expireTime = 3600;
          /**
           * ホストを作成します。
           */
          private String host = "http://" + bucket + "." + endpoint;
          /**
           * ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数から AccessKey ID を取得します。
           */
          private String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
          /**
           * ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数から AccessKey シークレットを取得します。
           */
          private String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
      
          private OSS ossClient;
          @Bean
          public OSS getOssClient() {
              ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
              return ossClient;
          }
          @Bean
          public String getHost() {
              return host;
          }
          @Bean
          public String getAccessKeyId() {
              return accessKeyId;
          }
          @Bean
          public long getExpireTime() {
              return expireTime;
          }
          @Bean
          public String getDir() {
              return dir;
          }
      
          @PreDestroy
          public void onDestroy() {
              ossClient.shutdown();
          }
      }
      
      

      Go

      package main
      
      import (
          "crypto/hmac"
          "crypto/sha1"
          "encoding/base64"
          "encoding/json"
          "fmt"
          "io"
          "net/http"
          "os"
          "time"
      )
      
      var (
          // OSS_ACCESS_KEY_ID 環境変数から AccessKey ID を取得します。
          accessKeyId = os.Getenv("OSS_ACCESS_KEY_ID")
          // OSS_ACCESS_KEY_SECRET 環境変数から AccessKey シークレットを取得します。
          accessKeySecret = os.Getenv("OSS_ACCESS_KEY_SECRET")
          // ホストを指定します。形式: BucketName.Endpoint。${your-bucket} をバケット名に置き換えます。${your-endpoint} を OSS エンドポイント (例: oss-cn-hangzhou.aliyuncs.com) に置き換えます。
          host = "http://${your-bucket}.${your-endpoint}"
          // OSS にアップロードするオブジェクトの名前のプレフィックスを指定します。
          uploadDir = "user-dir-prefix/"
          // 有効期間を指定します。単位: 秒。
          expireTime = int64(3600)
      )
      
      type ConfigStruct struct {
          Expiration string     `json:"expiration"`
          Conditions [][]interface{} `json:"conditions"`
      }
      type PolicyToken struct {
          AccessKeyId string `json:"ossAccessKeyId"`
          Host        string `json:"host"`
          Signature   string `json:"signature"`
          Policy      string `json:"policy"`
          Directory   string `json:"dir"`
      }
      
      func getGMTISO8601(expireEnd int64) string {
          return time.Unix(expireEnd, 0).UTC().Format("2006-01-02T15:04:05Z")
      }
      func getPolicyToken() string {
          now := time.Now().Unix()
          expireEnd := now + expireTime
          tokenExpire := getGMTISO8601(expireEnd)
          var config ConfigStruct
          config.Expiration = tokenExpire
          var condition []string
          condition = append(condition, "starts-with")
          condition = append(condition, "$key")
          condition = append(condition, uploadDir)
          config.Conditions = append(config.Conditions, condition)
          result, err := json.Marshal(config)
          if err != nil {
          fmt.Println("コールバック json エラー:", err)
          return ""
          }
          encodedResult := base64.StdEncoding.EncodeToString(result)
          h := hmac.New(sha1.New, []byte(accessKeySecret))
          io.WriteString(h, encodedResult)
          signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
          policyToken := PolicyToken{
          AccessKeyId: accessKeyId,
          Host:        host,
          Signature:   signedStr,
          Policy:      encodedResult,
          Directory:   uploadDir,
          }
          response, err := json.Marshal(policyToken)
          if err != nil {
          fmt.Println("json エラー:", err)
          return ""
          }
          return string(response)
      }
      func handler(w http.ResponseWriter, r *http.Request) {
          if r.URL.Path == "/" {
          http.ServeFile(w, r, "templates/index.html")
          return
          } else if r.URL.Path == "/get_post_signature_for_oss_upload" {
          policyToken := getPolicyToken()
          w.Header().Set("Content-Type", "application/json")
          w.Write([]byte(policyToken))
          return
          }
          http.NotFound(w, r)
      }
      func main() {
          http.HandleFunc("/", handler)
          http.ListenAndServe(":8080", nil)
      }
      

      PHP

      <?php
      function gmt_iso8601($time)
      {
          return str_replace('+00:00', '.000Z', gmdate('c', $time));
      }
      
      // 環境変数からアクセス認証情報を取得します。サンプルコードを実行する前に、ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数と ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数が構成されていることを確認してください。
      $accessKeyId = getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
      $accessKeySecret = getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
      // ホストを指定します。形式: <YOUR-BUCKET>.<YOUR-ENDPOINT>。
      $host = 'http://<YOUR-BUCKET>.<YOUR-ENDPOINT>';
      // アップロードするオブジェクトの名前のプレフィックスを指定します。
      $dir = 'user-dir-prefix/';          
      
      $now = time();
      // ポリシーの有効期限を指定します。有効期限が過ぎると、アクセスは拒否されます。この例では、有効期限は 10 秒に設定されています。
      $expire = 30;  
      $end = $now + $expire;
      $expiration = gmt_iso8601($end);
      
      // アップロードできるオブジェクトの最大サイズを指定します。
      $condition = array(0 => 'content-length-range', 1 => 0, 2 => 1048576000);
      $conditions[] = $condition;
      
      // アップロードできるオブジェクトを指定します。この例では、名前が $dir パラメーターの値で始まるオブジェクトのみをアップロードできます。これは、ポリシーを使用してオブジェクトがバケット内の別のディレクトリにアップロードされるのを防ぐオプションの設定です。
      $start = array(0 => 'starts-with', 1 => '$key', 2 => $dir);
      $conditions[] = $start;
      
      
      $arr = array('expiration' => $expiration, 'conditions' => $conditions);
      $policy = json_encode($arr);
      $base64_policy = base64_encode($policy);
      $string_to_sign = $base64_policy;
      $signature = base64_encode(hash_hmac('sha1', $string_to_sign, $accessKeySecret, true));
      
      $response = array();
      $response['ossAccessKeyId'] = $accessKeyId;
      $response['host'] = $host;
      $response['policy'] = $base64_policy;
      $response['signature'] = $signature;
      $response['dir'] = $dir;  
      echo json_encode($response);
      

      Node.js

      const express = require("express");
      const { Buffer } = require("buffer");
      const OSS = require("ali-oss");
      const app = express();
      const path = require("path");
      const config = {
        // ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数から AccessKey ID を取得します。
        accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID,
        // ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数から AccessKey シークレットを取得します。
        accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET,
        // <YOUR-BUCKET> をバケット名に置き換えます。
        bucket: "<YOUR-BUCKET>",
        // OSS にアップロードするオブジェクトの名前のプレフィックスを指定します。
        dir: "prefix/",
      };
      
      app.use(express.static(path.join(__dirname, "templates")));
      
      app.get("/get_post_signature_for_oss_upload", async (req, res) => {
        const client = new OSS(config);
        const date = new Date();
        // 署名の有効期間を指定します。単位: 秒。
        date.setSeconds(date.getSeconds() + 3600);
        const policy = {
          expiration: date.toISOString(),
          conditions: [
            // アップロードできるオブジェクトのサイズ制限を指定します。
            ["content-length-range", 0, 1048576000],
            // オブジェクトをアップロードできるバケットを指定します。
            { bucket: client.options.bucket },
          ],
        };
        const formData = await client.calculatePostSignature(policy);
        const host = `http://${config.bucket}.${
          (await client.getBucketLocation()).location
        }.aliyuncs.com`.toString();
        const params = {
          policy: formData.policy,
          signature: formData.Signature,
          ossAccessKeyId: formData.OSSAccessKeyId,
          host,
          dir: config.dir,
        };
        res.json(params);
      });
      
      app.get(/^(.+)*\.(html|js)$/i, async (req, res) => {
        res.sendFile(path.join(__dirname, "./templates", req.originalUrl));
      });
      
      app.listen(8000, () => {
        console.log("http://127.0.0.1:8000");
      });
      

      Ruby

      require 'sinatra'
      require 'base64'
      require 'open-uri'
      require 'cgi'
      require 'openssl'
      require 'json'
      require 'sinatra/reloader'
      require 'sinatra/content_for'
      
      # public-folder フォルダーのパスを現在のフォルダー内の templates サブフォルダーに設定します。
      set :public_folder, File.dirname(__FILE__) + '/templates'
      
      # ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数から AccessKey ID を取得します。
      $access_key_id = ENV['ALIBABA_CLOUD_ACCESS_ID']
      # ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数から AccessKey シークレットを取得します。
      $access_key_secret = ENV['ALIBABA_CLOUD_ACCESS_SECRET']
      
      # ホストを指定します。ホストは <bucketname>.<endpoint> 形式です。
      $host = 'http://<bucketname>.<endpoint>';
      
      # アップロードするオブジェクトの名前のプレフィックスを指定します。
      $upload_dir = 'user-dir-prefix/'
      # 有効期間を指定します。単位: 秒。
      $expire_time = 30
      $server_ip = "0.0.0.0"
      $server_port = 8000
      
      if ARGV.length == 1 
        $server_port = ARGV[0]
      elsif ARGV.length == 2
        $server_ip = ARGV[0]
        $server_port = ARGV[1]
      end
      
      puts "アプリケーションサーバーは次の場所で実行されています: http://#{$server_ip}:#{$server_port}"
      
      def hash_to_jason(source_hash)
        jason_string = source_hash.to_json;    
      
        jason_string.gsub!("\":[", "\": [")
        jason_string.gsub!("\",\"", "\", \"")
        jason_string.gsub!("],\"", "], \"")
        jason_string.gsub!("\":\"", "\": \"")
      
        jason_string
      end
      
      
      def get_token()
        expire_syncpoint = Time.now.to_i + $expire_time
        expire = Time.at(expire_syncpoint).utc.iso8601()
        response.headers['expire'] = expire
        policy_dict = {}
        condition_arrary = Array.new
        array_item = Array.new
        array_item.push('starts-with')
        array_item.push('$key')
        array_item.push($upload_dir)
        condition_arrary.push(array_item)
        policy_dict["conditions"] = condition_arrary
        policy_dict["expiration"] = expire
        policy = hash_to_jason(policy_dict)
        policy_encode = Base64.strict_encode64(policy).chomp;
        h = OpenSSL::HMAC.digest('sha1', $access_key_secret, policy_encode)
        hs = Digest::MD5.hexdigest(h)
        sign_result = Base64.strict_encode64(h).strip()
        token_dict = {}
        token_dict['ossAccessKeyId'] = $access_key_id
        token_dict['host'] = $host
        token_dict['policy'] = policy_encode
        token_dict['signature'] = sign_result 
        token_dict['expire'] = expire_syncpoint
        token_dict['dir'] = $upload_dir
        result = hash_to_jason(token_dict)
        result
      end
      
      set :bind, $server_ip
      set :port, $server_port
      
      get '/get_post_signature_for_oss_upload' do
        token = get_token()
        puts "トークン: #{token}"
        token
      end
      
      get '/*' do
        puts "********************* GET "
        send_file File.join(settings.public_folder, 'index.html')
      end
      
      end
      
      if ARGV.length == 1 
        $server_port = ARGV[0]
      elsif ARGV.length == 2
        $server_ip = ARGV[0]
        $server_port = ARGV[1]
      end
      
      $server_ip = "0.0.0.0"
      $server_port = 8000
      
      puts "アプリケーションサーバーは次の場所で実行されています: http://#{$server_ip}:#{$server_port}"
      
      set :bind, $server_ip
      set :port, $server_port
      
      get '/get_sts_token_for_oss_upload' do
        token = get_sts_token_for_oss_upload()
        response = {
          "AccessKeyId" => token["Credentials"]["AccessKeyId"],
          "AccessKeySecret" => token["Credentials"]["AccessKeySecret"],
          "SecurityToken" => token["Credentials"]["SecurityToken"]
        }
        response.to_json
      end
      
      get '/*' do
        puts "********************* GET "
        send_file File.join(settings.public_folder, 'index.html')
      end
      

      C#

      using Microsoft.AspNetCore.Builder;
      using Microsoft.Extensions.DependencyInjection;
      using Microsoft.AspNetCore.Http;
      using System.IO;
      using System.Collections.Generic;
      using System;
      using System.Globalization;
      using System.Text;
      using System.Security.Cryptography;
      using Newtonsoft.Json;
      using Microsoft.AspNetCore.Http.Extensions;
      using Microsoft.AspNetCore.Mvc;
      using Microsoft.Extensions.Logging;
      
      namespace YourNamespace
      {
          public class Program
          {
              private ILogger<Program> _logger;
              // ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数から AccessKey ID を取得します。
              public string AccessKeyId { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID");
              // ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数から AccessKey シークレットを取得します。
              public string AccessKeySecret { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
              // ホストを指定します。形式: BucketName.Endpoint。<YOUR-BUCKET> をバケット名に置き換えます。<YOUR-ENDPOINT> をバケットが配置されているリージョンのエンドポイント (例: oss-cn-hangzhou.aliyuncs.com) に置き換えます。
              public string Host { get; set; } = "<YOUR-BUCKET>.<YOUR-ENDPOINT>";
              // OSS にアップロードするオブジェクトの名前のプレフィックスを指定します。
              public string UploadDir { get; set; } = "user-dir-prefix/";
              // 有効期間を指定します。単位: 秒。
              public int ExpireTime { get; set; } = 3600;
              public class PolicyConfig
              {
                  public string expiration { get; set; }
                  public List<List<object>> conditions { get; set; }
              }
              public class PolicyToken
              {
                  public string Accessid { get; set; }
                  public string Policy { get; set; }
                  public string Signature { get; set; }
                  public string Dir { get; set; }
                  public string Host { get; set; }
                  public string Expire { get; set; }
              }
              public static void Main(string[] args)
              {
                  var builder = WebApplication.CreateBuilder(args);
                  var app = builder.Build();
      
                  builder.Logging.AddConsole();
                  var logger = builder.Services.BuildServiceProvider().GetRequiredService<ILogger<Program>>();
      
                  app.UseStaticFiles(); 
      
                  app.MapGet("/", async (context) =>
                  {
                      var filePath = Path.Combine(Directory.GetCurrentDirectory(), "templates/index.html");
                      varhtmlContent = await File.ReadAllTextAsync(filePath);
                      await context.Response.WriteAsync(htmlContent);
                      logger.LogInformation("ルートパスへの GET リクエスト");
                  });
      
                  app.MapGet("/get_post_signature_for_oss_upload", async (context) =>
                  {
                      var program = new Program(logger);
                      var token = program.GetPolicyToken();
      
                      logger.LogInformation($"トークン: {token}");
      
                      context.Response.ContentType = "application/json";
                      await context.Response.WriteAsync(token);
                  });
      
                  app.Run();
              }
      
              public Program(ILogger<Program> logger)
              {
                  _logger = logger;
              }
      
              private string ToUnixTime(DateTime dateTime)
              {
                  return ((DateTimeOffset)dateTime).ToUnixTimeSeconds().ToString();
              }
      
              private string GetPolicyToken()
              {
                  var expireDateTime = DateTime.Now.AddSeconds(ExpireTime);
                  var config = new PolicyConfig
                  {
                      expiration = FormatIso8601Date(expireDateTime),
                      conditions = new List<List<object>>()
                  };
                  config.conditions.Add(new List<object>
                  {
                      "content-length-range", 0, 1048576000
                  });
                  var policy = JsonConvert.SerializeObject(config);
                  var policyBase64 = EncodeBase64("utf-8", policy);
                  var signature = ComputeSignature(AccessKeySecret, policyBase64);
                  var policyToken = new PolicyToken
                  {
                      Accessid = AccessKeyId,
                      Host = Host,
                      Policy = policyBase64,
                      Signature = signature,
                      Expire = ToUnixTime(expireDateTime),
                      Dir = UploadDir
                  };
                  return JsonConvert.SerializeObject(policyToken);
              }
      
              private string FormatIso8601Date(DateTime dtime)
              {
                  return dtime.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'",
                                          CultureInfo.CurrentCulture);
              }
      
              private string EncodeBase64(string codeType, string code)
              {
                  string encode = "";
                  byte[] bytes = Encoding.GetEncoding(codeType).GetBytes(code);
                  try
                  {
                      encode = Convert.ToBase64String(bytes);
                  }
                  catch
                  {
                      encode = code;
                  }
                  return encode;
              }
      
              private string ComputeSignature(string key, string data)
              {
                  using (var algorithm = new HMACSHA1(Encoding.UTF8.GetBytes(key)))
                  {
                      return Convert.ToBase64String(algorithm.ComputeHash(Encoding.UTF8.GetBytes(data)));
                  }
              }
          }
      }
      
  2. クライアントは、署名や POST ポリシーなどの情報を使用して PostObject 操作を呼び出し、HTML フォームを使用してオブジェクトを OSS にアップロードします。

    次の例では、JavaScript を使用しています。

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>OSS にオブジェクトをアップロードする</title>
    </head>
    <body>
      <div class="container">
        <form>
          <div class="mb-3">
            <label for="file" class="form-label">オブジェクトを選択</label>
            <input type="file" class="form-control" id="file" name="file" required>
          </div>
          <button type="submit" class="btn btn-primary">アップロード</button>
        </form>
      </div>
      <script type="text/javascript">
          const form = document.querySelector('form');
          const fileInput = document.querySelector('#file');
          form.addEventListener('submit', (event) => {
            event.preventDefault();
            let file = fileInput.files[0];
            let filename = fileInput.files[0].name;
            fetch('/get_post_signature_for_oss_upload', { method: 'GET' })
              .then(response => response.json())
              .then(data => {
                const formData = new FormData();
                formData.append('name',filename);
                formData.append('policy', data.policy);
                formData.append('OSSAccessKeyId', data.ossAccessKeyId);
                formData.append('success_action_status', '200');
                formData.append('signature', data.signature);
                formData.append('key', data.dir + filename);
                // file を最後のフォームフィールドに設定します。他のフォームフィールドには特定の順序は必要ありません。
                formData.append('file', file);
                fetch(data.host, { method: 'POST', body: formData},).then((res) => {
                  console.log(res);
                  alert ('オブジェクトがアップロードされました');
                });
              })
              .catch(error => {
                console.log('OSS アップロードパラメーターの取得中にエラーが発生しました:', error);
              });
          });
      </script>
    </body>
    </html>
    

使用上の注意

データセキュリティ

オブジェクトの上書き

デフォルトでは、OSS は既存のオブジェクトを、同じ名前のアップロードされたオブジェクトで上書きします。 以下の方法を使用して、既存のオブジェクトが予期せず上書きされるのを防ぐことができます。

  • バケットのバージョニングを有効にする

    バケットのバージョニングを有効にすると、バケット内で上書きされたオブジェクトは以前のバージョンとして保存されます。 オブジェクトの以前のバージョンはいつでも復元できます。 詳細については、「バージョニング」をご参照ください。

  • アップロードリクエストに x-oss-forbid-overwrite パラメーターを含める

    x-oss-forbid-overwrite パラメーターをアップロードリクエストのフォームフィールドに追加し、このパラメーターを true に設定できます。 既存のオブジェクトと同じ名前のオブジェクトをアップロードすると、オブジェクトのアップロードは失敗し、FileAlreadyExists エラーコードが返されます。 このパラメーターをリクエストヘッダーに追加しない場合、またはこのパラメーターを false に設定した場合、オブジェクトはバケット内の同じ名前の既存のオブジェクトを上書きします。

承認済みアップロード

  • OSS は、バケットレベルとオブジェクトレベルでアクセス制御を提供し、サードパーティによるバケットへの不正なデータアップロードを防ぎます。 詳細については、「アクセス制御」をご参照ください。

  • 署名付き URL を使用して、サードパーティユーザーに特定のオブジェクトをアップロードするための権限を付与できます。 この方法では、サードパーティユーザーはアクセス認証情報や承認なしでデータをアップロードできます。 OSS は、アップロードされたデータをオブジェクトとしてバケットに保存します。 詳細については、「事前署名済み URL を使用してオブジェクトのアップロードを処理する」をご参照ください。

PUT リクエストのコスト

多数のオブジェクトをアップロードし、オブジェクトのストレージタイプをディープコールドアーカイブに設定すると、高額な PUT リクエスト料金 が発生します。 オブジェクトをアップロードするときにオブジェクトのストレージタイプを標準に設定し、ライフサイクルルール を構成して標準オブジェクトのストレージタイプをディープコールドアーカイブに変換することをお勧めします。 これにより、PUT リクエスト料金が削減されます。

OSS-HDFS が有効になっているバケットへのオブジェクトのアップロード

OSS-HDFS の安定性を維持し、データ損失を防ぐために、OSS-HDFS でサポートされていない方法を使用して、OSS-HDFS が有効になっているバケットの .dlsdata/ ディレクトリにオブジェクトをアップロードしないでください。

アップロードパフォーマンスのチューニング

多数のオブジェクトをアップロードし、オブジェクトの名前にタイムスタンプや文字などの連続したプレフィックスが含まれている場合、複数のオブジェクトインデックスが単一のパーティションに保存される可能性があります。 その結果、これらのオブジェクトをクエリするために多数のリクエストが送信されると、レイテンシが増加します。 多数のオブジェクトをアップロードする場合は、オブジェクト名を指定するために、連続したプレフィックスではなくランダムなプレフィックスを使用することをお勧めします。 詳細については、「OSS パフォーマンスのベストプラクティス」をご参照ください。