OSS表單上傳允許網頁應用通過標準HTML表單直接將檔案上傳至OSS。本文介紹如何使用C# SDK V2產生Post簽名和Post Policy等資訊,並調用HTTP Post方法上傳檔案到OSS。
注意事項
本文範例程式碼以華東1(杭州)的地區ID
cn-hangzhou為例,預設使用外網Endpoint,如果您希望通過與OSS同地區的其他阿里雲產品訪問OSS,請使用內網Endpoint。關於OSS支援的Region與Endpoint的對應關係,請參見OSS地區和訪問網域名稱。通過表單上傳的方式上傳的Object大小不能超過5 GB。
範例程式碼
以下程式碼範例實現了表單上傳的完整過程,主要步驟如下:
初始化配置與參數:設定地區(region)、儲存桶(bucket)、對象鍵(key)和產品(product),並從環境變數中載入訪問憑證和準備上傳的內容content。
建立Post Policy:定義上傳請求的有效時間和條件。
序列化並編碼Policy:將Policy序列化為JSON字串並進行Base64編碼。
產生簽名密鑰:使用HMAC-SHA256演算法,基於AccessKeySecret產生初始密鑰,再依次對日期、地區、產品和請求類型進行雜湊計算,產生最終的簽名密鑰。
構建請求體:建立符合HTTP POST規範的Multipart表單資料,添加對象鍵、Base64編碼的Policy、簽名版本、憑證資訊、請求日期、簽名和要上傳的資料。
建立並執行請求:建立HTTP用戶端並發送POST請求到OSS服務端點,處理響應結果,輸出狀態代碼和回應標頭資訊。
using OSS = AlibabaCloud.OSS.V2; // 為阿里雲OSS SDK建立別名,簡化後續使用
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
var region = "cn-hangzhou"; // 必須項,設定Bucket所在的地區(Region)。以華東1(杭州)為例,Region填寫為cn-hangzhou
var bucket = "you bucket name"; // 必須項,Bucket名稱
var key = "your object key"; // 必須項,上傳的目標對象名稱
var product = "oss"; // 必須項,OSS產品標識
// 將位元組數群組轉換為十六進位字串
static string ToHexString(byte[] data, bool lowercase)
{
var sb = new StringBuilder();
for (var i = 0; i < data.Length; i++) sb.Append(data[i].ToString(lowercase ? "x2" : "X2"));
return sb.ToString();
}
// 給字串添加雙引號
static string quote(string value)
{
return $"\"{value}\"";
}
// 載入OSS SDK的預設配置,此配置會自動從環境變數中讀取憑證資訊(如AccessKey)
var cfg = OSS.Configuration.LoadDefault();
// 顯式設定使用環境變數擷取憑證,用於身分識別驗證(格式:OSS_ACCESS_KEY_ID、OSS_ACCESS_KEY_SECRET)
var credentialsProvider = new OSS.Credentials.EnvironmentVariableCredentialsProvider();
var credentials = credentialsProvider.GetCredentials();
// 要上傳的內容
var content = "hello oss";
// 構建Policy(上傳策略)
var utcTime = DateTime.UtcNow;
var date = utcTime.ToUniversalTime().ToString("yyyyMMdd", CultureInfo.InvariantCulture);
var dateTime = utcTime.ToUniversalTime().ToString("yyyyMMdd'T'HHmmss'Z'", CultureInfo.InvariantCulture);
var expiration = utcTime.AddHours(1);// 簽名有效期間1小時
// 構建憑證資訊
var credentialInfo = $"{credentials.AccessKeyId}/{date}/{region}/{product}/aliyun_v4_request";
// 構建Policy JSON
var policyMap = new Dictionary<string, Object>()
{
// 設定策略到期時間
{ "expiration",
expiration.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss.000'Z'", CultureInfo.InvariantCulture)
},
// 設定上傳條件
{ "conditions", new Object[]{
// 必須上傳到指定的Bucket
new Dictionary<string, string>() {{ "bucket", bucket } },
// 指定簽名版本
new Dictionary<string, string>() {{ "x-oss-signature-version", "OSS4-HMAC-SHA256" } },
// 指定憑證資訊
new Dictionary<string, string>() {{ "x-oss-credential", credentialInfo } },
// 指定請求時間
new Dictionary<string, string>() {{ "x-oss-date", dateTime } },
// 限制上傳內容長度範圍
new Object[]{"content-length-range", 1, 1024 },
//new Object[]{"eq", "$success_action_status", "201"},
//new Object[]{"starts-with", "$key", "user/eric/"},
//new Object[]{"in", "$content-type", new string[]{"image/jpg", "image/png"}},
//new Object[]{ "not-in", "$cache-control", new string[]{ "no-cache" } },
}
},
};
// 序列化為JSON字串
var policy = JsonSerializer.Serialize(policyMap);
// 對Policy進行Base64編碼
var stringToSign = Convert.ToBase64String(Encoding.UTF8.GetBytes(policy));
// 產生簽名密鑰
using var kha = new HMACSHA256();
// 初始密鑰由AccessKeySecret產生
var ksecret = Encoding.UTF8.GetBytes("aliyun_v4" + credentials.AccessKeySecret);
// 逐級產生簽名密鑰
kha.Key = ksecret;
var hashDate = kha.ComputeHash(Encoding.UTF8.GetBytes(date));
kha.Key = hashDate;
var hashRegion = kha.ComputeHash(Encoding.UTF8.GetBytes(region));
kha.Key = hashRegion;
var hashProduct = kha.ComputeHash(Encoding.UTF8.GetBytes(product));
kha.Key = hashProduct;
var signingKey = kha.ComputeHash(Encoding.UTF8.GetBytes("aliyun_v4_request"));
// 計算最終簽名
kha.Key = signingKey;
var signature = ToHexString(kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)), true);
// 構建Multipart表單資料
using var formData = new MultipartFormDataContent();
// 處理boundary引號問題
var boundary = formData.Headers.ContentType!.Parameters.ElementAt(0).Value!;
formData.Headers.ContentType.Parameters.ElementAt(0).Value = boundary.Trim('"');
// 添加表單欄位 - 對象鍵名
formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(key)), quote("key"));
// 可以添加中繼資料
//formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(value)), quote("x-oss-"));
// 添加Policy
formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(stringToSign)), quote("policy"));
// 添加簽名相關資訊
formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes("OSS4-HMAC-SHA256")), quote("x-oss-signature-version"));
formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(credentialInfo)), quote("x-oss-credential"));
formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(dateTime)), quote("x-oss-date"));
formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(signature)), quote("x-oss-signature"));
// 添加要上傳的內容
formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(content)), quote("file"));
// 發送POST請求到OSS
using var hc = new HttpClient();
var result = await hc.PostAsync($"http://{bucket}.oss-{region}.aliyuncs.com/", formData);
// 列印上傳結果
Console.WriteLine("PostObject done"); // 提示操作完成
Console.WriteLine($"StatusCode: {result.StatusCode}"); // HTTP狀態代碼
Console.WriteLine("Response Headers:"); // 回應標頭資訊
result.Headers.ToList().ForEach(x => Console.WriteLine(x.Key + " : " + String.Join(",", x.Value.ToList()))); // 遍曆並列印所有回應標頭相關文檔
關於表單上傳的完整樣本,請參見postObject.cs。