All Products
Search
Document Center

Object Storage Service:Form upload

Last Updated:Jan 06, 2025

Form upload allows you to directly upload objects to Object Storage Service (OSS) by using standard HTML forms from web applications. This topic describes how to generate information such as a signature and an upload policy by using OSS SDK for Go 2.0 and use the HTTP Post method to upload an object to OSS.

Usage notes

  • The sample code in this topic uses the region ID cn-hangzhou of the China (Hangzhou) region. By default, the public endpoint is used to access resources in a bucket. If you want to access resources in the bucket by using other Alibaba Cloud services in the same region in which the bucket is located, use the internal endpoint. For more information about OSS regions and endpoints, see Regions and endpoints.

  • In this topic, access credentials are obtained from environment variables. For more information about how to configure the access credentials, see Configure access credentials.

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

Examples

The following sample code provides an example on how to perform form upload. The complete process of form upload consists of the following steps:

  1. Create an upload policy: Specify the validity period and conditions of an upload request, including the bucket name, signature version, credentials, request date, and length range of the request body.

  2. Process the upload policy: Convert the upload policy to a JSON string and Base64-encode the string.

  3. Generate a signing key: Use the HMAC-SHA256 algorithm to generate a signing key including the date, region, service, and request type.

  4. Calculate a signature: Use the generated signing key to sign the Base64-encoded policy string and convert the signature to a hexadecimal string.

  5. Build a request body: Create a multipart form writer, specify the object key, policy, signature version, credentials, request date, and signature in the form, and write the data that you want to upload to the form.

  6. Create and execute a request: Create an HTTP POST request and specify the request headers. Send the request and check the response status code to ensure that the request is successful.

package main

import (
	"bytes"
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"hash"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"time"

	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)

var (
	region     string // The region in which the bucket is located.
	bucketName string // The name of the bucket.
	objectName string // The name of the object.
	product    = "oss"
)

// Specify the init function used to initialize command line parameters.
func init() {
	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
	flag.StringVar(&objectName, "object", "", "The name of the object.")
}

func main() {
	// Parse command line parameters.
	flag.Parse()

	// Check whether the bucket name is empty.
	if len(bucketName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, bucket name required")
	}

	// Check whether the region is empty.
	if len(region) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, region required")
	}

	// Check whether the object name is empty.
	if len(objectName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, object name required")
	}

	// Create a credential provider.
	credentialsProvider := credentials.NewEnvironmentVariableCredentialsProvider()
	cred, err := credentialsProvider.GetCredentials(context.TODO())
	if err != nil {
		log.Fatalf("GetCredentials fail, err:%v", err)
	}

	// Specify the content that you want to upload.
	content := "hi oss"

	// Create an upload policy.
	utcTime := time.Now().UTC()
	date := utcTime.Format("20060102")
	expiration := utcTime.Add(1 * time.Hour)
	policyMap := map[string]any{
		"expiration": expiration.Format("2006-01-02T15:04:05.000Z"),
		"conditions": []any{
			map[string]string{"bucket": bucketName},
			map[string]string{"x-oss-signature-version": "OSS4-HMAC-SHA256"},
			map[string]string{"x-oss-credential": fmt.Sprintf("%v/%v/%v/%v/aliyun_v4_request",
				cred.AccessKeyID, date, region, product)}, // Specify the credentials.
			map[string]string{"x-oss-date": utcTime.Format("20060102T150405Z")},
			// Specify other conditions.
			[]any{"content-length-range", 1, 1024},
			// []any{"eq", "$success_action_status", "201"},
			// []any{"starts-with", "$key", "user/eric/"},
			// []any{"in", "$content-type", []string{"image/jpg", "image/png"}},
			// []any{"not-in", "$cache-control", []string{"no-cache"}},
		},
	}

	// Convert the upload policy to a JSON string.
	policy, err := json.Marshal(policyMap)
	if err != nil {
		log.Fatalf("json.Marshal fail, err:%v", err)
	}

	// Base64-encode the JSON string.
	stringToSign := base64.StdEncoding.EncodeToString([]byte(policy))

	// Generate a signing key.
	hmacHash := func() hash.Hash { return sha256.New() }
	signingKey := "aliyun_v4" + cred.AccessKeySecret
	h1 := hmac.New(hmacHash, []byte(signingKey))
	io.WriteString(h1, date)
	h1Key := h1.Sum(nil)

	h2 := hmac.New(hmacHash, h1Key)
	io.WriteString(h2, region)
	h2Key := h2.Sum(nil)

	h3 := hmac.New(hmacHash, h2Key)
	io.WriteString(h3, product)
	h3Key := h3.Sum(nil)

	h4 := hmac.New(hmacHash, h3Key)
	io.WriteString(h4, "aliyun_v4_request")
	h4Key := h4.Sum(nil)

	// Calculate the signature.
	h := hmac.New(hmacHash, h4Key)
	io.WriteString(h, stringToSign)
	signature := hex.EncodeToString(h.Sum(nil))

	// Build a Post request body.
	bodyBuf := &bytes.Buffer{}

	// Create a multipart form writer to build the request body.
	bodyWriter := multipart.NewWriter(bodyBuf)

	// Specify the object information, including the key and metadata.
	bodyWriter.WriteField("key", objectName) // Specify the object key (object name).
	// bodyWriter.WriteField("x-oss-", value) // Specify the metadata. This parameter is optional.

	// Specify the Base64-encoded policy string.
	bodyWriter.WriteField("policy", stringToSign)

	// Specify the signature version.
	bodyWriter.WriteField("x-oss-signature-version", "OSS4-HMAC-SHA256")

	// Specify the credentials.
	bodyWriter.WriteField("x-oss-credential", fmt.Sprintf("%v/%v/%v/%v/aliyun_v4_request", cred.AccessKeyID, date, region, product))

	// Specify the request date.
	bodyWriter.WriteField("x-oss-date", utcTime.Format("20060102T150405Z"))

	// Specify the signature.
	bodyWriter.WriteField("x-oss-signature", signature)

	// Create a form field named "file" to upload data.
	w, _ := bodyWriter.CreateFormField("file")

	// Write the data that you want to upload to the form field.
	w.Write([]byte(content))

	// Close the form writer and make sure that all data is written to the request body as expected.
	bodyWriter.Close()

	// Create a Post request.
	req, _ := http.NewRequest("POST", fmt.Sprintf("http://%v.oss-%v.aliyuncs.com/", bucketName, region), bodyBuf)
	req.Header.Set("Content-Type", bodyWriter.FormDataContentType())

	// Execute the Post request.
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatalf("Do fail, err:%v", err)
	}
	defer resp.Body.Close()

	// Check the response status code.
	if resp.StatusCode/100 != 2 {
		log.Fatalf("Post Object Fail, status code:%v, reason:%v", resp.StatusCode, resp.Status)
	}

	log.Printf("post object done, status code:%v, request id:%v\n", resp.StatusCode, resp.Header.Get("X-Oss-Request-Id"))
}

References

  • For the complete sample code that is used to perform form upload, visit GitHub.