這邊不詳細介紹什麼是 Azure Storage,這裡簡單介紹一下其組成部分,包括 storage account
、Container
和 blob
,如下圖所示。
詳細資訊可以參考Blob (物件) 儲存體簡介 - Azure Storage | Microsoft Learn。
由於專案時間緊迫,我只拿到了 Azure Storage 的 ConnectionString,需要在短時間內完成上傳功能。以下是使用 .NET 實作 Azure Storage SAS 上傳檔案的步驟。
心智圖
mindmap
root(Azure Storage SASUrl)
Container 單位上傳
Blob單位上傳
SAS上傳檔案
x-ms-blob-type: BlockBlob
專案需求
透過前端可以上傳檔案到 Azure Storage,這樣減少Server 負擔,剛好上傳東西後端也不需要做關聯,這邊這個功能符合我的需求。
大致流程
使用 .NET 實作 Azure Storage SAS 上傳檔案
-
建立 Storage Account:
- 登入 Azure 入口網站。
- 建立一個新的 Storage Account,並記下其 ConnectionString。
-
安裝 Azure.Storage.Blobs 套件:
在你的 .NET 專案中,安裝 Azure.Storage.Blobs
套件:
1
|
dotnet add package Azure.Storage.Blobs
|
-
生成 SAS Token
-
使用生成的 SAS URI 上傳檔案
SAS 上傳檔案方法
上面介紹了如何透過前端上傳檔案,使用 SAS 讓前端取得憑證(會是一個網址)。透過這個網址,使用者就可以進行上傳操作。
詳細的教學可以參考這篇文章:Azure Blob Storage 使用指南 – 金鑰與存取簽章篇 | 辛比誌。我參考了這篇文章後,對 SAS 的使用有了更深入的了解。
Tip
-
什麼是 SAS:
SAS(Shared Access Signature)是一種安全的 URL,允許你授予對 Azure Storage 資源的有限存取權限,而不需要暴露你的帳戶金鑰。
-
生成 SAS Token 的步驟:
- 使用 Azure Portal 或程式碼生成 SAS Token。
- 設定 SAS Token 的有效期限和權限。
-
使用 SAS Token 上傳檔案:
- 使用生成的 SAS URL 進行檔案上傳。
- 確保上傳過程中的安全性和有效性。
Container 產生 SAS 網址(不推薦使用)
這邊發現 Container SAS ,不太適合用在前端上傳,上傳權限會太大。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
BlobContainerClient container = new("connectioningString", "containerName");
if (!container.CanGenerateSasUri)
throw new Exception("The container can't generate SAS URI");
var sasBuilder = new BlobSasBuilder
{
BlobContainerName = container.Name,
Resource = "c",
ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(20)
};
sasBuilder.SetPermissions(BlobContainerSasPermissions.All);
var sasUri = container.GenerateSasUri(sasBuilder);
|
產生 SAS Url 可以給 Client 做上傳動作。
單純透過程式做 SAS 上傳動作
下面是單純透過程式做SAS上傳動作。但通常有 ConnectionString
,不太需要這樣做。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
async static Task<Uri> GetUserDelegationSasBlob(BlobClient blobClient)
{
BlobServiceClient blobServiceClient = blobClient.GetParentBlobContainerClient().GetParentBlobServiceClient();
// Get a user delegation key for the Blob service that's valid for 7 days.
// You can use the key to generate any number of shared access signatures over the lifetime of the key.
UserDelegationKey userDelegationKey = await blobServiceClient.GetUserDelegationKeyAsync(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(7));
// Create a SAS token that's valid for 7 days.
BlobSasBuilder sasBuilder = new BlobSasBuilder()
{
BlobContainerName = blobClient.BlobContainerName,
BlobName = blobClient.Name,
Resource = "b",
StartsOn = DateTimeOffset.UtcNow,
ExpiresOn = DateTimeOffset.UtcNow.AddDays(7)
};
// Specify read permissions for the SAS.
sasBuilder.SetPermissions(BlobSasPermissions.Read);
// Use the key to get the SAS token.
string sasToken = sasBuilder.ToSasQueryParameters(userDelegationKey, blobServiceClient.AccountName).ToString();
// Construct the full URI, including the SAS token.
UriBuilder fullUri = new UriBuilder()
{
Scheme = "https",
Host = string.Format("{0}.blob.core.windows.net", blobServiceClient.AccountName),
Path = string.Format("{0}/{1}", blobClient.BlobContainerName, blobClient.Name),
Query = sasToken
};
// Create a BlobClient object that can be used to upload a file with the SAS token
BlobClient sasBlobClient = new BlobClient(fullUri.Uri);
// Upload a file
using (FileStream stream = File.OpenRead(localFilePath))
{
await sasBlobClient.UploadAsync(stream);
}
return fullUri.Uri;
}
|
Info
網址結構如下:
https://{azure storage ccount}.blob.core.windows.net/{blob container name}/test23.png?sv=2022-11-02&se=2023-04-24T06%3A45%3A33Z&sr=c&sp=racwdxltfi&sig=30%2By6%2F%2BGfT%2BIA2fzZKCsDTzqlArt0Fkn44rXOZ%2Baboo%3D
https://{azure storage ccount}.blob.core.windows.net/{blob container name}/test23.png
?sv=2022-11-02&se=2023-04-24T06%3A45%3A33Z&sr=c&sp=racwdxltfi&sig=30%2By6%2F%2BGfT%2BIA2fzZKCsDTzqlArt0Fkn44rXOZ%2Baboo%3D:
sv=2022-11-02: Storage 服務版本號。
se=2023-04-24T06%3A45%3A33Z: SAS Token 的到期時間(UTC 時間)。
sr=c: 資源類型,c 表示容器(container)。
sp=racwdxltfi: 許可權限,包含讀取(r)、添加(a)、建立(c)、寫入(w)、刪除(d)、列表(l)。
sig=30%2By6%2F%2BGfT%2BIA2fzZKCsDTzqlArt0Fkn44rXOZ%2Baboo%3D: SAS Token 的簽名,用於驗證 URL 的有效性和完整性。
產生 SASURL API範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
[ApiController]
[Route("[controller]")]
public class AttachmentController : ControllerBase
{
private readonly IConfiguration _configuration;
public AttachmentController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpGet("token")]
[ProducesResponseType(StatusCodes.Status409Conflict)]
[ProducesResponseType(typeof(AzureStorageSASResult), StatusCodes.Status200OK)]
public IActionResult SASToken()
{
var azureStorageConfig = _configuration.GetSection("AppSettings:AzureStorage").Get<AzureStorageConfig>();
BlobContainerClient container = new(azureStorageConfig.ConnectionString, azureStorageConfig.ContainerName);
if (!container.CanGenerateSasUri)
return Conflict("The container can't generate SAS URI");
var sasBuilder = new BlobSasBuilder
{
BlobContainerName = container.Name,
Resource = "c",
ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(azureStorageConfig.TokenExpirationMinutes)
};
sasBuilder.SetPermissions(BlobContainerSasPermissions.All);
var sasUri = container.GenerateSasUri(sasBuilder);
return Ok(result);
}
}
|
1
2
3
4
5
6
7
|
"AppSettings": {
"AzureStorage": {
"ConnectionString": "<connection string>",
"ContainerName": "attachments",
"TokenExpirationMinutes": 20
}
}
|
Blob SAS 上傳(推薦使用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
BlobContainerClient container = new("connectionstring", "containerName");
BlobServiceClient blobServiceClient = new BlobServiceClient("connectionstring");
BlobContainerClient blobContainerClient = blobServiceClient.GetBlobContainerClient("containerName");
BlobClient blobClient = blobContainerClient.GetBlobClient("test.png");
Uri sasUri = GetServiceSasUriForBlob(blobClient);
GetServiceSasUriForBlob(blobClient).Dump();
static Uri GetServiceSasUriForBlob(BlobClient blobClient,
string storedPolicyName = null)
{
// Check whether this BlobClient object has been authorized with Shared Key.
if (blobClient.CanGenerateSasUri)
{
// Create a SAS token that's valid for one hour.
BlobSasBuilder sasBuilder = new BlobSasBuilder()
{
BlobContainerName = blobClient.GetParentBlobContainerClient().Name,
BlobName = blobClient.Name,
Resource = "b"
};
if (storedPolicyName == null)
{
sasBuilder.ExpiresOn = DateTimeOffset.UtcNow.AddHours(1);
sasBuilder.SetPermissions(BlobSasPermissions.Read |
BlobSasPermissions.Write);
}
else
{
sasBuilder.Identifier = storedPolicyName;
}
Uri sasUri = blobClient.GenerateSasUri(sasBuilder);
Console.WriteLine("SAS URI for blob is: {0}", sasUri);
Console.WriteLine();
return sasUri;
}
else
{
Console.WriteLine(@"BlobClient must be authorized with Shared Key
credentials to create a service SAS.");
return null;
}
}
|
PostMan 上傳檔案
需要在Header 加上 x-ms-blob-type
,這邊就可以上傳檔案。前端Web需要加上跨域設定,這邊我前台是APP,所以不需要做這件事情。
1
2
3
4
|
curl --location --request PUT 'https://xxxxx.blob.core.windows.net/.......' \
--header 'x-ms-blob-type: BlockBlob' \
--header 'Content-Type: image/png' \
--data '@/C:/Users/steve/OneDrive/圖片/螢幕擷取畫面/螢幕擷取畫面_20230222_100659.png'
|
彩蛋