首頁

目前文章總數:190 篇

  

最後更新:2025年 07月 26日

0098. Google Cloud Storage —「手動開啟並設定 Interoperability(互通性)功能」- 支援 S3-Compatible API 範例

日期:2025年 08月 23日

標籤: Asp.NET Core Windows Forms Amazon S3 Minio Google Cloud Platform Google Cloud Storage

摘要:C# 學習筆記


範例所需: 1. Visual Studio 2022 Asp.net Core
      2. 已開通 Google ,並建立 Bucket 與啟用 Google Cloud Storage 對應權限
      3. 已建構 Minio Server ,並建立 Bucket 與啟用對應權限
範例檔案:本篇範例代碼
相關參考:0075. 架設檔案伺服器 - 高性能的物件存儲系統 - MinIO (開源、高速、免費)
相關參考:0097. Google Cloud Storage 與 C# 整合:憑證設定與檔案操作實例
相關參考:Google Cloud 首頁
解決問題:如何使用 Amazon Simple Storage Service(簡稱 S3),最終都可用相同代碼(S3 SDK)上傳、下載到 Minio Server 與GCS
基本介紹:本篇分為五大部分。
第一部分:問題描述
第二部分:Amazon Simple Storage Service(S3) 介紹
第三部分:設定 GCS 的 Interoperability(互通性)
第四部分:上傳檔案到 CGS、Minio - DEMO
第五部分:下載檔案從 CGS、Minio - DEMO






第一部分:問題描述

Step 1: 目標

本篇的目標是透過 AWS S3 SDK 實現同一份 C# 程式碼,達到上傳/下載 MinioGoogle Cloud Storage 檔案時
可以只替換金鑰,一份 Code 可以多用的目的




第二部分:Amazon Simple Storage Service(S3) 介紹

Step 1: Amazon S3 介紹

官網說明,以下擷取自官方,解釋了 AWS S3 為何:

Amazon Simple Storage Service (Amazon S3) 是物件儲存服務,提供領先業界的可擴展性、資料可用性、安全性及效能。
各種規模和各業界的數百萬家客戶可以存放、管理、分析和保護幾乎任何使用案例的任何資料量,如資料湖、雲端原生應用程式和行動應用程式。
藉助符合成本效益的儲存類別和易於使用的管理功能,您可以最佳化成本、組織和分析資料並設定精密的存取控制,以滿足特定的業務和合規需求。


本質上 AWS S3 是雲端儲存服務; AWS S3 SDK 是讓開發用的程式工具包,用 C# 存取 AWS S3 的API
補充:官方使用 AWS SDKs的 Amazon S3 控制程式碼範例

Step 2:S3 SDK 對於 Minio & GCS 相容程度

因為 Minio 與 S3 SDK 幾乎高度相容,在代碼的實現上需要處理⚠️的部份

項目 AWS S3 (官方) Google Cloud Storage (GCS) MinIO Server
是否原生支援 S3 API ⚠️ 需開啟互通模式
端點 (Endpoint) 自動設定 https://storage.googleapis.com http://:9000 / 代理域名
Region 設定 必填 任何 string 皆可(可設為 auto 任意 string
SDK 相容性 完全相容 ⚠️ 相容性有限(部分 API 不支援) 高相容性
簽名版本 (Signature Version) v4(預設) ⚠️ v2/v4(僅支援特定方法,需調整) v4(完全支援)



Step 3: GCS 遭遇困難 & 解決方式 - 需開啟互通模式

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 金鑰方法。

Step 4: GCS 遭遇困難 & 解決方式 - 相容性有限(部分 API 不支援)

此問題的核心在於 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

Step 5: TransferUtility 介紹

TransferUtility 是 AWS .NET SDK 中提供的一個高階封裝工具,目的是讓開發者更簡單地在 C# 中進行 S3 上傳與下載檔案。
以下是優點:

優點 說明
自動多部分上傳 當檔案超過一定大小(預設 16MB),自動切成多段並平行上傳,提升速度與穩定性。
重試與錯誤處理內建 AWS SDK 已內建 retry/backoff 機制,傳輸失敗時會自動重試。
支援多種來源 可以上傳 stringStreamFilePathDirectory 等。
支援進度監控 可綁定事件 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 等)。




Step 6: GCS 遭遇困難 & 解決方式 - v2/v4(僅支援特定方法,需調整)

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 的 Interoperability(互通性)

Step 1: 進入 GCS 設定

登入 GCS -> 選擇 Cloud Storage -> 設定 -> 互通性


Step 2: 建立金鑰 - HMAC

到中間的服務帳戶的存取金鑰-> 選擇 建立金鑰 -> 這時系統就會產生 HMAC 的 AccessKey 與 SecretKey (新的服務帳戶 HMAC 金鑰)
並且確保狀態為已啟用,並且將此 HMAC 金鑰紀錄,用於 C# GCS 的範例代碼的金鑰




第四部分:上傳檔案到 CGS、Minio - DEMO

Step 1: 安裝 AWS S3 SDK

可參考本篇範例代碼
建立 Visual Studio 專案後 -> Nuget 安裝 AWSSDK.S3 版本(此篇最新版為 : 4.0.6)


Step 2: Minio Server 建立金鑰

為了要訪問 Minio Server ,需要先建立金鑰,可參考之前的文章0075. 架設檔案伺服器 - 高性能的物件存儲系統 - MinIO (開源、高速、免費)
這裡提醒要使用此金鑰


Step 3: Minio 使用 AWS S3 SDK 代碼 - 設定

將 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 4: Minio 使用 AWS S3 SDK 代碼 - 設定

將 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}";
}   



Step 5: Minio 使用 AWS S3 SDK 代碼 - 上傳代碼

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("上傳成功!");
    }
}



Step 6: GCS 使用 AWS S3 SDK 代碼 - 設定

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}";
}



Step 7: GCS 使用 AWS S3 SDK 代碼 - 上傳代碼

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("上傳成功!");

    }
}



Step 8: DEMO 上傳成果

執行範例專案,進行上傳(GCS 與 Minio),如下圖,都會出現成功結果


Minio Server 上對應的 BucketName 可以看到上傳的檔案:


Google Cloud Storage 上對應的 BucketName 也可以看到上傳的檔案:




第五部分:下載檔案從 CGS、Minio - DEMO

Step 1: Minio 使用 AWS S3 SDK 代碼 - 下載代碼

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("下載成功!");
    }
}



Step 2: GCS 使用 AWS S3 SDK 代碼 - 下載代碼

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("下載成功!");
    }
}



Step 3: DEMO 下載成果

執行範例專案,選擇存放檔案的路徑


Minio Server 與 Google Cloud Storage 輸入要下載的檔案名稱,都可以下載對應檔案: