分享程式代碼相關筆記
目前文章總數:190 篇
最後更新:2025年 07月 26日
本篇的目標是透過 AWS S3 SDK 實現同一份 C# 程式碼,達到上傳/下載 Minio、Google Cloud Storage 檔案時
可以只替換金鑰,一份 Code 可以多用的目的
官網說明,以下擷取自官方,解釋了 AWS S3 為何:
Amazon Simple Storage Service (Amazon S3) 是物件儲存服務,提供領先業界的可擴展性、資料可用性、安全性及效能。
各種規模和各業界的數百萬家客戶可以存放、管理、分析和保護幾乎任何使用案例的任何資料量,如資料湖、雲端原生應用程式和行動應用程式。
藉助符合成本效益的儲存類別和易於使用的管理功能,您可以最佳化成本、組織和分析資料並設定精密的存取控制,以滿足特定的業務和合規需求。
本質上 AWS S3 是雲端儲存服務; AWS S3 SDK 是讓開發用的程式工具包,用 C# 存取 AWS S3 的API
補充:官方使用 AWS SDKs的 Amazon S3 控制程式碼範例
因為 Minio 與 S3 SDK 幾乎高度相容,在代碼的實現上需要處理⚠️的部份
項目 | AWS S3 (官方) | Google Cloud Storage (GCS) | MinIO Server |
---|---|---|---|
是否原生支援 S3 API | 是 | ⚠️ 需開啟互通模式 | 是 |
端點 (Endpoint) | 自動設定 | https://storage.googleapis.com | http:// |
Region 設定 | 必填 | 任何 string 皆可(可設為 auto ) |
任意 string |
SDK 相容性 | 完全相容 | ⚠️ 相容性有限(部分 API 不支援) | 高相容性 |
簽名版本 (Signature Version) | v4(預設) | ⚠️ v2/v4(僅支援特定方法,需調整) | v4(完全支援) |
GCP官網的互通性,目的是為了支援與 AWS S3 相容的工具或 SDK(例如 AWS S3 SDK for C#)所設計的一種兼容性
最關鍵的幾點設定如下(不支援出處連結):
項目 | 說明 |
---|---|
認證方式 | 使用類似 AWS 的 Access Key / Secret Key(HMAC) 需在 GCP IAM 中手動建立 |
存取端點 | https://storage.googleapis.com |
支援 API | 只支援基本的 GET / PUT / DELETE Object 與 Bucket 操作 |
簽名格式 | 支援 AWS Signature Version 2 和部分 Version 4(有限) |
不支援的功能 | Multipart Upload、Transfer Acceleration、部分 ACL/Bucket Policy、S3 Events 等 |
在 第三部分:設定 GCS 的 Interoperability(互通性)說明啟用流程 & 開啟 HMAC 金鑰方法。
此問題的核心在於 GCS 只支援 PutObject ,也就是 支援基本的 GET / PUT / DELETE Object 與 Bucket 操作
以下列表列出此 Minio 與 GCS 的儲存空間體的差異:
AWS S3 SDK 具備功能 | MinIO | GCS (互通模式) | |
---|---|---|---|
PutObject 單一上傳 |
✅ | ✅ | ✅ |
Multipart Upload | ✅ | ✅ | ❌ |
TransferUtility 支援 |
✅ | ✅ | ❌ |
Bucket Policy / ACLs | ✅ | ✅ | ❌ |
簡要說 : GCS 的 S3 API 模擬只支援基本的單檔上傳 API 因此只能用 PutObject
而 Minio 完整實作 Multipart Upload 所需的 S3 API,因此也完成了 TransferUtility
TransferUtility 是 AWS .NET SDK 中提供的一個高階封裝工具,目的是讓開發者更簡單地在 C# 中進行 S3 上傳與下載檔案。
以下是優點:
優點 | 說明 |
---|---|
自動多部分上傳 | 當檔案超過一定大小(預設 16MB),自動切成多段並平行上傳,提升速度與穩定性。 |
重試與錯誤處理內建 | AWS SDK 已內建 retry/backoff 機制,傳輸失敗時會自動重試。 |
支援多種來源 | 可以上傳 string 、Stream 、FilePath 、Directory 等。 |
支援進度監控 | 可綁定事件 UploadProgressEvent ,顯示上傳進度、速率等資訊。 |
自動設定 Metadata / ACL / StorageClass | 可直接設置相關參數,不需手動呼叫 API。 |
上傳整個資料夾 | 可以用 UploadDirectory 一次性遞迴上傳整個資料夾結構。 |
簡化程式碼 | 與原始 PutObjectRequest / MultipartUpload 相比,程式碼更精簡可讀性更高。 |
以下是缺點:
缺點 | 說明 |
---|---|
封裝太深,彈性受限 | 若你要精細控制每段檔案的上傳行為、簽名方式、分段邏輯,不易擴充或介入流程。 |
不支援非 S3-compatible 完整實作(如 GCS) | 如果後端不是完整 S3 API,像 GCS 不支援 Multipart Upload,TransferUtility 會報錯。 |
預設緩衝區大小不適合所有情境 | 預設分段大小與記憶體用量可能導致記憶體爆增(特別是在大型檔案或低 RAM 裝置)。 |
相依於完整 AWS S3 支援(如簽名 V4、region) | 若換成其他相容服務,需要自己額外調整參數(如 path-style、endpoint、region name 等)。 |
在 Cloud Storage XML API 和 Amazon S3 之間存在一些差異,這段落舉出差異性的部份需遵守。
這是因為 AWS S3 SDK 預設會在 PutObjectRequest 上使用 HTTP 的 Transfer-Encoding: chunked(這是一種邊上傳邊串流,不預先告訴總大小的技術,好處是可以提高傳輸效能)
chunked encoding 簡要說明如下:
HTTP/1.1 支援的一種資料傳輸方式,不需要在開始時知道 Content-Length |
AWS S3 原生支援這種傳輸模式 → 可以邊上傳邊寫入 |
GCS 要求你 明確指定 Content-Length,所以不接受 chunked 上傳 |
因此為了符合 GCS 的要求,應在 C# 中添加以下配置:
var request = new PutObjectRequest
{
UseChunkEncoding = false // 需 UseChunkEncoding 強制設為 false 遵守差異的規範
};
致謝:遇到此問題時,AWS SDK 返回的錯誤捕捉並不能很好的意識到是此問題(如下圖),感謝工作上的同事提供此解法,再次衷心感謝。
AWS SDK 捕捉到的訊息:Error making request with Error Code BadRequest and Http Status Code BadRequest. No further error information was returned by the service.
登入 GCS -> 選擇 Cloud Storage -> 設定 -> 互通性
到中間的服務帳戶的存取金鑰-> 選擇 建立金鑰 -> 這時系統就會產生 HMAC 的 AccessKey 與 SecretKey (新的服務帳戶 HMAC 金鑰)
並且確保狀態為已啟用,並且將此 HMAC 金鑰紀錄,用於 C# GCS 的範例代碼的金鑰
可參考本篇範例代碼
建立 Visual Studio 專案後 -> Nuget 安裝 AWSSDK.S3 版本(此篇最新版為 : 4.0.6)
為了要訪問 Minio Server ,需要先建立金鑰,可參考之前的文章0075. 架設檔案伺服器 - 高性能的物件存儲系統 - MinIO (開源、高速、免費)
這裡提醒要使用此金鑰
將 Step 2. 取得的金鑰(AccessKey 與 SercetKey 填入),並且依照實際自己的 ServiceURL
/// <summary>
/// 使用 Minio 憑證
/// </summary>
private void MinioCredentialbutton_Click(object sender, EventArgs e)
{
var useMessage = "Minio";
currentServer = ServerEnum.Minio;
_BucketName = $@"";
_AccessKey = "";
_SecretKey = "";
_Config = new AmazonS3Config
{
ServiceURL = "http://stg.minio.mg",
ForcePathStyle = true
};
UsedCredentialLable.Text = $@"當前使用憑證:{useMessage}";
}
將 Step 2. 取得的金鑰(AccessKey 與 SercetKey 填入),並且依照實際自己的 ServiceURL、BucketName
/// <summary>
/// 使用 Minio 憑證
/// </summary>
private void MinioCredentialbutton_Click(object sender, EventArgs e)
{
var useMessage = "Minio";
currentServer = ServerEnum.Minio;
_BucketName = $@"";
_AccessKey = "";
_SecretKey = "";
_Config = new AmazonS3Config
{
ServiceURL = "http://stg.minio.mg",
ForcePathStyle = true
};
UsedCredentialLable.Text = $@"當前使用憑證:{useMessage}";
}
Minio 與 TransferUtility 高度兼容,代碼較為精簡,可呼叫 SDK 進行上傳
// AWS S3 SDK 上傳到 Minio Server
async Task MinioServerUpload()
{
using (var s3Client = new AmazonS3Client(_AccessKey, _SecretKey, _Config))
{
var transferUtility = new TransferUtility(s3Client);
// 上傳檔案資訊
string filePath = GCS_textBox_filepath.Text;
string objectName = Path.GetFileName(filePath);
// Minio 高度兼容 S3 API ,可使用最新的 transferUtility 方法
await transferUtility.UploadAsync(filePath, _BucketName, objectName);
await ShowMessageAsync("上傳成功!");
}
}
Minio 與 TransferUtility 高度兼容,代碼較為精簡,可呼叫 SDK 進行上傳
/// <summary>
/// 使用 GCS 憑證
/// </summary>
private void GCSCredentialbutton_Click(object sender, EventArgs e)
{
var useMessage = "GCS";
currentServer = ServerEnum.GoogleCloudStorage;
_BucketName = $@"";
_AccessKey = "";
_SecretKey = "";
_Config = new AmazonS3Config
{
ServiceURL = "https://storage.googleapis.com",
ForcePathStyle = true,
};
UsedCredentialLable.Text = $@"當前使用憑證:{useMessage}";
}
GCS 必須用 PutObjectRequest,代碼較為冗長,多了些設定 UseChunkEncoding = false 一定要設定
// AWS S3 SDK 上傳到 GCS Server
async Task GoogleCloudStorageUpload()
{
using (var s3Client = new AmazonS3Client(_AccessKey, _SecretKey, _Config))
{
// 上傳檔案資訊
string filePath = GCS_textBox_filepath.Text;
string objectName = Path.GetFileName(filePath);
// GCS 屬於兼容 S3 API ,因此必須用 PutObjectRequest
var request = new PutObjectRequest
{
BucketName = _BucketName,
Key = objectName,
FilePath = filePath,
ContentType = "application/octet-stream",
UseChunkEncoding = false
};
var response = await s3Client.PutObjectAsync(request);
await ShowMessageAsync("上傳成功!");
}
}
執行範例專案,進行上傳(GCS 與 Minio),如下圖,都會出現成功結果
Minio Server 上對應的 BucketName 可以看到上傳的檔案:
Google Cloud Storage 上對應的 BucketName 也可以看到上傳的檔案:
Minio 與 TransferUtility 高度兼容,下載代碼如下
// AWS S3 SDK 從 Minio Server 下載檔案
async Task MinioServerDownload()
{
using (var s3Client = new AmazonS3Client(_AccessKey, _SecretKey, _Config))
{
string objectName = GCS_Download_FileNameTextBox.Text;
string downloadFilePath = Path.Combine(GCS_textBox_downloadPathFile.Text, objectName);
// 可用更精簡、高校的 TransferUtility
var transferUtility = new TransferUtility(s3Client);
await transferUtility.DownloadAsync(downloadFilePath, _BucketName, objectName);
await ShowMessageAsync("下載成功!");
}
}
GCS 必須用 GetObjectRequest ,下載時不用設定 UseChunkEncoding
// AWS S3 SDK 從 GCS Server 下載檔案 (Minio 亦支援此方法)
async Task GoogleCloudStorageDownload()
{
using (var s3Client = new AmazonS3Client(_AccessKey, _SecretKey, _Config))
{
string objectName = GCS_Download_FileNameTextBox.Text;
string downloadFilePath = Path.Combine(GCS_textBox_downloadPathFile.Text, objectName);
var request = new GetObjectRequest
{
BucketName = _BucketName,
Key = objectName
};
using var response = await s3Client.GetObjectAsync(request);
await using var responseStream = response.ResponseStream;
await using var fileStream = File.Create(downloadFilePath);
await responseStream.CopyToAsync(fileStream);
await ShowMessageAsync("下載成功!");
}
}
執行範例專案,選擇存放檔案的路徑
Minio Server 與 Google Cloud Storage 輸入要下載的檔案名稱,都可以下載對應檔案: