All Products
Search
Document Center

Object Storage Service:Form upload

Last Updated:Jul 29, 2024

Form upload allows you to directly upload objects to Object Storage Service (OSS) by using standard HTML forms from web applications. This way, after an object is selected on the front-end page, the browser initiates a PostObject request to directly upload the object to the OSS server without using the website server. This reduces the workload on the website server and improves the efficiency and stability of object upload.

Limits

The size of the object that you want to upload by using form upload cannot exceed 5 GB.

Scenarios

Form upload is widely used in web applications, including but not limited to the following scenarios:

  • User data upload: Users upload an avatar, ID card photo, or other identity verification materials when they register an account. They upload a new avatar or background image when they modify information in the personal center.

  • Object sharing and storage: Users upload objects in various formats, such as documents, images, audio files, and video files, to Alibaba Cloud for storage and sharing by using forms on online disks and collaborative working platforms.

  • Content creation and publishing: Users write articles on platforms, such as blogs, forums, and Q&A communities, and upload images and attachments as content supplements by using forms.

  • E-commerce management: Merchants upload information, such as commodity pictures, detailed description documents, and qualifications, in the background of e-commerce platforms. Consumers upload invoice requirements or other supporting materials during the purchase process.

  • Online education platform: Students upload homework or projects, such as documents, PPTs, and video files. Teachers upload teaching materials and courseware.

  • Recruitment website: Job seekers upload resumes, portfolios and other materials. Enterprises upload materials, such as their company logos and recruitment documents, when they post positions.

  • Questionnaires and feedback: Users upload additional evidence or explanatory documents when they fill out the online questionnaires.

  • Software development collaboration: Developers upload code files or project documents from code hosting platforms such as GitHub and GitLab.

Solution

The client requests the signature and POST policy required by a PostObject request from the application server. Then, the client can use the signature and POST policy to upload objects without using OSS SDKs. You can use the POST policy generated by the application server to limit the attributes of the object, such as the object size and type. This solution is suitable for scenarios in which you want to upload objects by using HTML forms. This solution does not support multipart upload and resumable upload. For more information, see PostObject.

The following figure shows how to upload an object from a client to OSS by using the signature and POST policy that are obtained from the application server.

image
  1. The client requests information, such as a signature and POST policy, from the application server.

  2. The application server generates and returns information, such as the signature and POST policy, to the client.

  3. The client uses information, such as the signature and POST policy, to call the PostObject operation to upload the object to OSS by using an HTML form.

  4. OSS returns a success response to the client.

Procedure

  1. The server generates information, such as the signature and POST policy.

    • Sample project: postsignature.zip

    • Examples

      The following sample code provides examples on how the application server generates a signature and POST policy for PostObject:

      Note

      The sample code can be deployed in Function Compute. oss-upload-post-signature-app

      import os
      from hashlib import sha1 as sha
      import json
      import base64
      import hmac
      import datetime
      import time
      
      # Obtain the AccessKey ID from the OSS_ACCESS_KEY_ID environment variable. 
      access_key_id = os.environ.get('OSS_ACCESS_KEY_ID')
      # Obtain the AccessKey secret from the OSS_ACCESS_KEY_SECRET environment variable. 
      access_key_secret = os.environ.get('OSS_ACCESS_KEY_SECRET')
      # Replace <YOUR_BUCKET> with the name of the bucket. 
      bucket = '<YOUR_BUCKET>'
      # Set host to a value that is in the BucketName.Endpoint format. Replace <YOUR_BUCKET> with the name of the bucket. Replace <YOUR_ENDPOINT> with the endpoint of the region in which the bucket is located, such as oss-cn-hangzhou.aliyuncs.com. 
      host = 'https://<YOUR_BUCKET>.<YOUR_ENDPOINT>'
      # Specify the prefix in the name of the object that you want to upload to OSS. 
      upload_dir = 'user-dir-prefix/'
      # Specify the validity period. Unit: seconds. 
      expire_time = 3600
      
      
      def generate_expiration(seconds):
          """
          The expiration time is calculated based on the specified validity period. Unit: seconds. 
          :param seconds: the validity period (seconds). 
          :return: the time string in the ISO 8601 standard. Example: 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):
          """
          Generate a signature string. 
          :param access_key_secret: the AccessKey secret that has the permissions to access the bucket. 
          :param expiration: the time when the signature expires. Specify the time in the ISO 8601 standard in the yyyy-MM-ddTHH:mm:ssZ format. Example: 2014-12-01T12:00:00.000Z. 
          :param conditions: the policy conditions that can be used to limit the values that you can specify during form upload. 
          :param policy_extra_props: the additional policy parameters. You can pass additional parameters as a dict. 
          :return: the signature string. 
          """
          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 = {
              # Specify the validity period. 
              "expiration": generate_expiration(expire_time),
              # Specify the policy conditions. 
              "conditions": [
                  # If success_action_redirect is not specified, HTTP status code 204 is returned after the object is uploaded. 
                  ["eq", "$success_action_status", "200"],
                  # The value of the form field must start with the specified prefix. For example, if the value of the key form field starts with user/user1, the condition is ["starts-with", "$key", "user/user1"]. 
                  ["starts-with", "$key", upload_dir],
                  # Specify the minimum and maximum sizes of the object that can be uploaded. Unit: bytes. 
                  ["content-length-range", 1, 1000000],
                  # Specify allowable file types.
                  ["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
              # Specify additional parameters.
          }
          return json.dumps(response)
      
      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("Caught an OSSException, which means your request made it to OSS, "
                      + "but was rejected with an error response for some reason.");
              // Assume that the method exists.
              System.out.println("HTTP Status Code: " + oe.getRawResponseError()); 
              System.out.println("Error Message: " + oe.getErrorMessage());
              System.out.println("Error Code:       " + oe.getErrorCode());
              System.out.println("Request ID:      " + oe.getRequestId());
              System.out.println("Host ID:           " + oe.getHostId());
          } catch (ClientException ce) {
              System.out.println("Caught an ClientException, which means the client encountered "
                      + "a serious internal problem while trying to communicate with OSS, "
                      + "such as not being able to access the network.");
              System.out.println("Error Message: " + ce.getMessage());
          } finally {
              if (ossClient != null) {
                  ossClient.shutdown();
              }
              return response.toString();
          }
        }
      }
      
      @Configuration
      public class OssConfig {
          /**
           * Replace <YOUR-ENDPOINT> with the endpoint of the region in which the bucket is located, such as oss-cn-hangzhou.aliyuncs.com.
           */
          private String endpoint = "<YOUR-ENDPOINT>";
          /**
           * Replace <YOUR-BUCKET> with the name of the bucket.
           */
          private String bucket = "<YOUR-BUCKET>";
          /**
           * Specify the prefix in the name of the object in OSS.
           */
          private String dir = "user-dir-prefix/";
          /**
           * Specify the validity period. Unit: seconds.
           */
          private long expireTime = 3600;
          /**
           * Create a host.
           */
          private String host = "http://" + bucket + "." + endpoint;
          /**
           * Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable.
           */
          private String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
          /**
           * Obtain the AccessKey secret from the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable.
           */
          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();
          }
      }
      
      
      package main
      
      import (
          "crypto/hmac"
          "crypto/sha1"
          "encoding/base64"
          "encoding/json"
          "fmt"
          "io"
          "net/http"
          "os"
          "time"
      )
      
      var (
          // Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable. 
          accessKeyId = os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
          // Obtain the AccessKey secret from the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable. 
          accessKeySecret = os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
          // Specify the host. The host is in the BucketName.Endpoint format. Replace ${your-bucket} with the name of the bucket. Replace ${your-endpoint} with the OSS endpoint, such as oss-cn-hangzhou.aliyuncs.com. 
          host = "http://${your-bucket}.${your-endpoint}"
          // Specify the prefix in the name of the object that you want to upload to OSS. 
          uploadDir = "user-dir-prefix/"
          // Specify the validity period. Unit: seconds. 
          expireTime = int64(3600)
      )
      
      type ConfigStruct struct {
          Expiration string     `json:"expiration"`
          Conditions [][]string `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("callback json err:", 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:", 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
      function gmt_iso8601($time)
      {
          return str_replace('+00:00', '.000Z', gmdate('c', $time));
      }
      
      // Obtain access credentials from environment variables. Before you run the sample code, make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables are configured. 
      $accessKeyId = getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
      $accessKeySecret = getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
      // Specify a host. Format: <YOUR-BUCKET>.<YOUR-ENDPOINT>. 
      $host = 'http://<YOUR-BUCKET>.<YOUR-ENDPOINT>';
      // Specify the prefix in the name of the object you want to upload. 
      $dir = 'user-dir-prefix/';          
      
      $now = time();
      // Specify the expiration time of the policy. If the expiration time elapses, the access is denied. In this example, the expiration time is set to 10 seconds. 
      $expire = 30;  
      $end = $now + $expire;
      $expiration = gmt_iso8601($end);
      
      // Specify the maximum size of the object that can be uploaded. 
      $condition = array(0 => 'content-length-range', 1 => 0, 2 => 1048576000);
      $conditions[] = $condition;
      
      // Specify the object that can be uploaded. In this example, only an object whose name starts with the value of the $dir parameter can be uploaded. This is an optional setting that prevents the object from being uploaded to a different directory in the bucket by using the policy. 
      $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);
      
      const express = require("express");
      const { Buffer } = require("buffer");
      const OSS = require("ali-oss");
      const app = express();
      const path = require("path");
      const config = {
        // Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable. 
        accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID,
        // Obtain the AccessKey secret from the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable. 
        accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET,
        // Replace <YOUR-BUCKET> with the name of the bucket. 
        bucket: "<YOUR-BUCKET>",
        // Specify the prefix in the name of the object that you want to upload to 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();
        // Specify the validity period of the signature. Unit: seconds. 
        date.setSeconds(date.getSeconds() + 3600);
        const policy = {
          expiration: date.toISOString(),
          conditions: [
            // Specify the size limit on the object that can be uploaded. 
            ["content-length-range", 0, 1048576000],
            // Specify the bucket to which the object can be uploaded. 
            { 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");
      });
      
      require 'sinatra'
      require 'base64'
      require 'open-uri'
      require 'cgi'
      require 'openssl'
      require 'json'
      require 'sinatra/reloader'
      require 'sinatra/content_for'
      
      # Set the path of the public-folder folder to the templates subfolder within the current folder.
      set :public_folder, File.dirname(__FILE__) + '/templates'
      
      # Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable. 
      $access_key_id = ENV['ALIBABA_CLOUD_ACCESS_ID']
      # Obtain the AccessKey secret from the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable. 
      $access_key_secret = ENV['ALIBABA_CLOUD_ACCESS_SECRET']
      
      # Specify the validity period. Unit: seconds. 
      $host = 'http://<bucketname>.<endpoint>';
      
      # Specify the prefix in the name of the object you want to upload. 
      $upload_dir = 'user-dir-prefix/'
      # Specify the validity period. Unit: seconds. 
      $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 "App server is running on: 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}"
        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 "App server is running on: 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
      
      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;
              // Obtain the AccessKey ID from the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable. 
              public string AccessKeyId { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID");
              // Obtain the AccessKey secret from the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable. 
              public string AccessKeySecret { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
              // Specify the host. Format: BucketName.Endpoint. Replace <YOUR-BUCKET> with the name of the bucket. Replace <YOUR-ENDPOINT> with the endpoint of the region in which the bucket is located, such as oss-cn-hangzhou.aliyuncs.com. 
              public string Host { get; set; } = "<YOUR-BUCKET>.<YOUR-ENDPOINT>";
              // Specify the prefix in the name of the object that you want to upload to OSS. 
              public string UploadDir { get; set; } = "user-dir-prefix/";
              // Specify the validity period. Unit: seconds. 
              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");
                      var htmlContent = await File.ReadAllTextAsync(filePath);
                      await context.Response.WriteAsync(htmlContent);
                      logger.LogInformation("GET request to root path");
                  });
      
                  app.MapGet("/get_post_signature_for_oss_upload", async (context) =>
                  {
                      var program = new Program(logger);
                      var token = program.GetPolicyToken();
      
                      logger.LogInformation($"Token: {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. The client uses information, such as the signature and POST policy, to call the PostObject operation to upload the object to OSS by using an HTML form.

    JavaScript is used in the following example:

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Upload an object to OSS</title>
    </head>
    <body>
      <div class="container">
        <form>
          <div class="mb-3">
            <label for="file" class="form-label">Select the object</label>
            <input type="file" class="form-control" id="file" name="file" required>
          </div>
          <button type="submit" class="btn btn-primary">Upload</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);
                // Set file to the last form field. No particular order is required for other form fields. 
                formData.append('file', file);
                fetch(data.host, { method: 'POST', body: formData},).then((res) => {
                  console.log(res);
                  alert ('Object Uploaded');
                });
              })
              .catch(error => {
                console.log('Error occurred while getting OSS upload parameters:', error);
              });
          });
      </script>
    </body>
    </html>
    

Usage notes

Data security

Object overwriting

By default, OSS overwrites existing objects with the uploaded objects that have the same names. You can use the following methods to prevent the existing objects from being unexpectedly overwritten:

  • Enable versioning for the bucket

    If you enable versioning for a bucket, objects that are overwritten in the bucket are saved as previous versions. You can restore the previous versions of the objects at any time. For more information, see Overview.

  • Include the x-oss-forbid-overwrite parameter in the upload request

    You can add the x-oss-forbid-overwrite parameter to the header of the upload request and set this parameter to true. If you upload an object that has the same name as an existing object, the object fails to be uploaded and the FileAlreadyExists error code is returned. If you do not add this parameter to the request header or if you set this parameter to false, the object overwrites an existing object that has the same name in the bucket.

Authorized upload

  • OSS provides access control at the bucket and object levels to prevent unauthorized data uploads to your bucket by third parties. For more information, see Overview.

  • You can use a signed URL to grant a third-party user the permissions to upload a specific object. This way, the third-party user can upload data without access credentials or authorization. OSS stores the uploaded data as an object in the bucket. For more information, see Upload local files with signed URLs.

PUT request costs

If you want to upload a large number of objects and set the storage classes of the objects to Deep Cold Archive, you are charged high PUT request fees. We recommend that you set the storage classes of the objects to Standard when you upload the objects, and configure lifecycle rules to convert the storage classes of the Standard objects to Deep Cold Archive. This reduces PUT request fees.

Object upload to a bucket for which OSS-HDFS is enabled

To maintain OSS-HDFS stability and prevent data loss, do not upload an object to the .dlsdata/ directory of a bucket for which OSS-HDFS is enabled by using methods that are not supported by OSS-HDFS.

Upload performance tuning

If you upload a large number of objects and the names of the objects contain sequential prefixes such as timestamps and letters, multiple object indexes may be stored in a single partition. As a result, latency increases when a large number of requests are sent to query these objects. If you want to upload a large number of objects, we recommend that you use random prefixes instead of sequential prefixes to specify object names. For more information, see OSS performance and scalability best practices.