OSS表單上傳允許網頁應用通過標準HTML表單直接將檔案上傳至OSS。本文介紹如何使用Go SDK V2產生Post簽名和Post Policy等資訊,並調用HTTP Post方法上傳檔案到OSS。
注意事項
本文範例程式碼以華東1(杭州)的地區ID
cn-hangzhou為例,預設使用外網Endpoint,如果您希望通過與OSS同地區的其他阿里雲產品訪問OSS,請使用內網Endpoint。關於OSS支援的Region與Endpoint的對應關係,請參見OSS地區和訪問網域名稱。本文以從環境變數讀取存取憑證為例。如何配置訪問憑證,請參見配置訪問憑證。
通過表單上傳的方式上傳的Object大小不能超過5 GB。
範例程式碼
以下程式碼範例實現了表單上傳的完整過程,主要步驟如下:
建立Post Policy:定義上傳請求的有效時間和條件,包括儲存桶名稱、簽名版本、憑證資訊、請求日期和請求體長度範圍。
序列化並編碼Policy:將Policy序列化為JSON字串,並進行Base64編碼。
產生簽名密鑰:使用HMAC-SHA256演算法產生簽名密鑰,包括日期、地區、產品和請求類型。
計算簽名:使用產生的金鑰組Base64編碼後的Policy字串進行簽名,並將簽名結果轉換為十六進位字串。
構建請求體:建立一個multipart表單寫入器,添加對象鍵、策略、簽名版本、憑證資訊、請求日期和簽名到表單中,並將要上傳的資料寫入表單。
建立並執行請求:建立一個HTTP POST請求,佈建要求頭,並發送請求,檢查響應狀態代碼確保請求成功。
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 // 儲存地區
bucketName string // 儲存桶名稱
objectName string // 對象名稱
product = "oss"
)
// init 函數用於初始化命令列參數
func init() {
flag.StringVar(®ion, "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() {
// 解析命令列參數
flag.Parse()
// 檢查儲存桶名稱是否為空白
if len(bucketName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, bucket name required")
}
// 檢查儲存地區是否為空白
if len(region) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, region required")
}
// 檢查對象名稱是否為空白
if len(objectName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, object name required")
}
// 建立憑證提供者
credentialsProvider := credentials.NewEnvironmentVariableCredentialsProvider()
cred, err := credentialsProvider.GetCredentials(context.TODO())
if err != nil {
log.Fatalf("GetCredentials fail, err:%v", err)
}
// 填寫要上傳的內容
content := "hi oss"
// 構建Post 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)}, // 憑證
map[string]string{"x-oss-date": utcTime.Format("20060102T150405Z")},
// 其他條件
[]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"}},
},
}
// 將Post Policy序列化為JSON字串
policy, err := json.Marshal(policyMap)
if err != nil {
log.Fatalf("json.Marshal fail, err:%v", err)
}
// 將Post Policy編碼為Base64字串
stringToSign := base64.StdEncoding.EncodeToString([]byte(policy))
// 產生簽名密鑰
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)
// 計算Post簽名
h := hmac.New(hmacHash, h4Key)
io.WriteString(h, stringToSign)
signature := hex.EncodeToString(h.Sum(nil))
// 構建Post請求體
bodyBuf := &bytes.Buffer{}
// 建立一個multipart表單寫入器,用於構建請求體
bodyWriter := multipart.NewWriter(bodyBuf)
// 設定對象資訊,包括鍵和中繼資料
bodyWriter.WriteField("key", objectName) // 設定對象鍵(檔案名稱)
// bodyWriter.WriteField("x-oss-", value) // 設定中繼資料(可選)
// 設定Base64編碼後的策略字串
bodyWriter.WriteField("policy", stringToSign)
// 設定簽名版本
bodyWriter.WriteField("x-oss-signature-version", "OSS4-HMAC-SHA256")
// 設定憑證資訊
bodyWriter.WriteField("x-oss-credential", fmt.Sprintf("%v/%v/%v/%v/aliyun_v4_request", cred.AccessKeyID, date, region, product))
// 佈建要求日期
bodyWriter.WriteField("x-oss-date", utcTime.Format("20060102T150405Z"))
// 設定簽名
bodyWriter.WriteField("x-oss-signature", signature)
// 建立一個名為"file"的表單欄位,用於上傳檔案內容
w, _ := bodyWriter.CreateFormField("file")
// 將要上傳的資料寫入表單欄位
w.Write([]byte(content))
// 關閉表單寫入器,確保所有資料都被正確寫入請求體
bodyWriter.Close()
// 建立Post請求
req, _ := http.NewRequest("POST", fmt.Sprintf("http://%v.oss-%v.aliyuncs.com/", bucketName, region), bodyBuf)
req.Header.Set("Content-Type", bodyWriter.FormDataContentType())
// 執行Post請求
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatalf("Do fail, err:%v", err)
}
defer resp.Body.Close()
// 檢查響應狀態代碼
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"))
}
相關文檔
關於表單上傳的完整樣本,請參見Github樣本。