-
C#中关于Azure Storage——文件上传与下载
第五部分:云原生与无服务器开发
2. Azure Storage——文件上传与下载
实例介绍
之前做电商后台的商品图片上传功能时,用本地文件系统存储,需要维护服务器存储,扩容麻烦,而且无法实现多服务器共享。换成Azure Storage后,图片存储在Blob存储中,支持无限扩容,多服务器可直接访问,还能通过CDN加速vb.net教程C#教程python教程SQL教程access 2010教程访问,图片加载速度提升50%,维护成本降为0。这节就带你从零实现Azure Storage的文件上传与下载,包括Blob存储、文件存储、队列存储、表存储,覆盖云存储的核心场景。
需求分析
Azure Storage要解决“无限扩容、高可用性、多服务器共享、CDN加速、安全存储、成本优化”的问题,具体需求如下:
1.Blob存储:存储非结构化数据(如图片、视频、文档、备份文件);
2.文件存储:存储结构化数据(如共享文件、日志文件),支持SMB协议;
3.队列存储:存储消息,实现异步任务处理(如订单通知、数据同步);
4.表存储:存储结构化数据(如用户信息、设备数据),支持NoSQL查询;
5.高可用性:数据自动复制到多个区域,99.999999999%的持久性;
6.安全存储:支持加密(静态加密、传输加密)、访问控制(SAS令牌、RBAC);
7.CDN加速:通过Azure CDN加速Blob存储的访问,降低延迟;
8.成本优化:支持分层存储(热存储、冷存储、归档存储),根据访问频率选择存储层;
9.本地开发:支持本地开发、调试,使用Azurite模拟器模拟Azure Storage;
10.监控与日志:集成Azure Monitor,实时监控存储使用情况、查看访问日志;
代码实现
前置条件:.NET 8 SDK;Azure账户(免费试用即可);需安装以下NuGet包:
Azure.Storage.Blobs:Blob存储客户端库
Azure.Storage.Files.Shares:文件存储客户端库
Azure.Storage.Queues:队列存储客户端库
Azure.Data.Tables:表存储客户端库
Azurite:本地Azure Storage模拟器(可选,用于本地开发)
场景1:Blob存储——图片上传与下载
实现Blob存储的文件上传、下载、删除、列出文件,支持分层存储、SAS令牌访问。
步骤1:创建Azure存储账户
1.登录Azure门户(https://portal.azure.com);
2.点击“创建资源” → 搜索“存储账户” → 点击“创建”;
3.配置存储账户:
订阅:选择你的Azure订阅;
资源组:使用之前创建的资源组(如AzureFunctionsDemoRG);
存储账户名称:全局唯一名称(如azurestoragedemo2026);
位置:选择靠近你的区域(如“东亚”);
性能:选择“标准”(适合大多数场景);
冗余:选择“本地冗余存储(LRS)”(成本低,数据复制到同一区域的3个副本);
4.点击“查看+创建” → “创建”,等待存储账户创建完成;
5.创建Blob容器:在存储账户中点击“容器” → “创建”,输入容器名称(如product-images),公共访问级别选择“私有(无匿名访问)”;
步骤2:获取存储账户连接字符串
1.在Azure门户中打开存储账户 → 点击“访问密钥” → 复制“连接字符串”;
2.将连接字符串保存到配置文件(如appsettings.json);
步骤3:Blob存储操作代码
csharp
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Azure.Storage.Sas;
namespace AzureStorageDemo.Services;
public class BlobStorageService
{
private readonly BlobServiceClient _blobServiceClient;
private readonly string _containerName = "product-images";
public BlobStorageService(string connectionString)
{
// 初始化Blob服务客户端
_blobServiceClient = new BlobServiceClient(connectionString);
}
// 上传文件到Blob存储
public async Task UploadFileAsync(string localFilePath, string blobName, string storageTier = "Hot")
{
// 获取容器客户端
var blobContainerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
// 确保容器存在
await blobContainerClient.CreateIfNotExistsAsync();
// 获取Blob客户端
var blobClient = blobContainerClient.GetBlobClient(blobName);
// 上传文件
using var fileStream = File.OpenRead(localFilePath);
await blobClient.UploadAsync(fileStream, new BlobUploadOptions
{
// 设置存储层:Hot(热存储,频繁访问)、Cool(冷存储,不频繁访问)、Archive(归档存储,极少访问)
AccessTier = storageTier switch
{
"Cool" => AccessTier.Cool,
"Archive" => AccessTier.Archive,
_ => AccessTier.Hot
}
});
Console.WriteLine($"文件 {blobName} 上传成功!");
}
// 从Blob存储下载文件
public async Task DownloadFileAsync(string blobName, string localFilePath)
{
var blobContainerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
var blobClient = blobContainerClient.GetBlobClient(blobName);
// 下载文件
using var fileStream = File.OpenWrite(localFilePath);
await blobClient.DownloadToAsync(fileStream);
Console.WriteLine($"文件 {blobName} 下载成功!");
}
// 删除Blob存储中的文件
public async Task DeleteFileAsync(string blobName)
{
var blobContainerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
var blobClient = blobContainerClient.GetBlobClient(blobName);
await blobClient.DeleteIfExistsAsync();
Console.WriteLine($"文件 {blobName} 删除成功!");
}
// 列出Blob存储中的所有文件
public async Task<List<string>> ListFilesAsync()
{
var blobContainerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
var files = new List<string>();
await foreach (var blobItem in blobContainerClient.GetBlobsAsync())
{
files.Add(blobItem.Name);
Console.WriteLine($"文件:{blobItem.Name},大小:{blobItem.Properties.ContentLength}字节,存储层:{blobItem.Properties.AccessTier}");
}
return files;
}
// 生成SAS令牌,用于临时访问Blob
public string GenerateSasToken(string blobName, TimeSpan expiresIn)
{
var blobContainerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
var blobClient = blobContainerClient.GetBlobClient(blobName);
// 生成SAS令牌
var sasBuilder = new BlobSasBuilder()
{
BlobContainerName = _containerName,
BlobName = blobName,
Resource = "b", // "b"表示Blob,"c"表示容器
ExpiresOn = DateTimeOffset.UtcNow.Add(expiresIn)
};
// 设置权限:读取、写入、删除等
sasBuilder.SetPermissions(BlobSasPermissions.Read);
// 生成SAS URI
var sasUri = blobClient.GenerateSasUri(sasBuilder);
return sasUri.Query; // 返回SAS令牌部分(?sv=...&ss=...&srt=...)
}
}
场景2:文件存储——共享文件上传与下载
实现文件存储的共享文件上传、下载、删除、列出文件,支持SMB协议,可在Windows、Linux、macOS上访问。
步骤1:创建文件共享
1.在Azure门户中打开存储账户 → 点击“文件共享” → “创建”,输入共享名称(如shared-files);
步骤2:文件存储操作代码
csharp
using Azure.Storage.Files.Shares;
using Azure.Storage.Files.Shares.Models;
namespace AzureStorageDemo.Services;
public class FileStorageService
{
private readonly ShareServiceClient _shareServiceClient;
private readonly string _shareName = "shared-files";
public FileStorageService(string connectionString)
{
_shareServiceClient = new ShareServiceClient(connectionString);
}
// 上传文件到文件存储
public async Task UploadFileAsync(string localFilePath, string fileName)
{
var shareClient = _shareServiceClient.GetShareClient(_shareName);
await shareClient.CreateIfNotExistsAsync();
// 获取目录客户端(根目录)
var directoryClient = shareClient.GetRootDirectoryClient();
// 获取文件客户端
var fileClient = directoryClient.GetFileClient(fileName);
// 上传文件
using var fileStream = File.OpenRead(localFilePath);
await fileClient.CreateAsync(fileStream.Length);
await fileClient.UploadRangeAsync(new HttpRange(0, fileStream.Length), fileStream);
Console.WriteLine($"文件 {fileName} 上传成功!");
}
// 从文件存储下载文件
public async Task DownloadFileAsync(string fileName, string localFilePath)
{
var shareClient = _shareServiceClient.GetShareClient(_shareName);
var directoryClient = shareClient.GetRootDirectoryClient();
var fileClient = directoryClient.GetFileClient(fileName);
// 下载文件
using var fileStream = File.OpenWrite(localFilePath);
var response = await fileClient.DownloadAsync();
await response.Value.Content.CopyToAsync(fileStream);
Console.WriteLine($"文件 {fileName} 下载成功!");
}
// 列出文件存储中的所有文件
public async Task<List<string>> ListFilesAsync()
{
var shareClient = _shareServiceClient.GetShareClient(_shareName);
var directoryClient = shareClient.GetRootDirectoryClient();
var files = new List<string>();
await foreach (var fileItem in directoryClient.GetFilesAndDirectoriesAsync())
{
if (fileItem.IsDirectory)
{
Console.WriteLine($"目录:{fileItem.Name}");
}
else
{
files.Add(fileItem.Name);
Console.WriteLine($"文件:{fileItem.Name}");
}
}
return files;
}
}
场景3:队列存储——异步任务处理
实现队列存储的消息发送、接收、删除,支持异步任务处理(如订单通知、数据同步)。
步骤1:创建队列
1.在Azure门户中打开存储账户 → 点击“队列” → “创建”,输入队列名称(如order-notifications);
步骤2:队列存储操作代码
csharp
using Azure.Storage.Queues;
using Azure.Storage.Queues.Models;
namespace AzureStorageDemo.Services;
public class QueueStorageService
{
private readonly QueueServiceClient _queueServiceClient;
private readonly string _queueName = "order-notifications";
public QueueStorageService(string connectionString)
{
_queueServiceClient = new QueueServiceClient(connectionString);
}
// 发送消息到队列
public async Task SendMessageAsync(string messageContent, TimeSpan? visibilityTimeout = null)
{
var queueClient = _queueServiceClient.GetQueueClient(_queueName);
await queueClient.CreateIfNotExistsAsync();
// 发送消息,visibilityTimeout表示消息的可见性延迟(如设置为5分钟,5分钟后才能被接收)
await queueClient.SendMessageAsync(messageContent, visibilityTimeout: visibilityTimeout);
Console.WriteLine($"消息发送成功:{messageContent}");
}
// 接收队列中的消息
public async Task<string?> ReceiveMessageAsync(TimeSpan visibilityTimeout = default)
{
var queueClient = _queueServiceClient.GetQueueClient(_queueName);
if (!await queueClient.ExistsAsync())
{
Console.WriteLine("队列不存在!");
return null;
}
// 接收消息,visibilityTimeout表示消息的锁定时间(如设置为30秒,30秒内其他客户端无法接收该消息)
var response = await queueClient.ReceiveMessageAsync(visibilityTimeout: visibilityTimeout);
if (response.Value != null)
{
Console.WriteLine($"接收到消息:{response.Value.MessageText}");
return response.Value.MessageId;
}
else
{
Console.WriteLine("队列中没有消息!");
return null;
}
}
// 删除队列中的消息
public async Task DeleteMessageAsync(string messageId, string popReceipt)
{
var queueClient = _queueServiceClient.GetQueueClient(_queueName);
await queueClient.DeleteMessageAsync(messageId, popReceipt);
Console.WriteLine($"消息 {messageId} 删除成功!");
}
}
场景4:表存储——NoSQL数据存储
实现表存储的实体插入、查询、更新、删除,支持NoSQL查询,适合存储结构化数据(如用户信息、设备数据)。
步骤1:创建表
1.在Azure门户中打开存储账户 → 点击“表” → “创建”,输入表名称(如users);
步骤2:表存储操作代码
csharp
using Azure.Data.Tables;
namespace AzureStorageDemo.Services;
// 用户实体类,必须实现ITableEntity接口
public class UserEntity : ITableEntity
{
// 分区键(用于水平分区,提高查询性能)
public string PartitionKey { get; set; } = string.Empty;
// 行键(唯一标识分区内的实体)
public string RowKey { get; set; } = string.Empty;
// 时间戳(自动生成,用于并发控制)
public DateTimeOffset? Timestamp { get; set; }
// ETag(自动生成,用于并发控制)
public ETag ETag { get; set; }
// 自定义属性
public string UserName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public int Age { get; set; }
public bool IsActive { get; set; }
}
public class TableStorageService
{
private readonly TableServiceClient _tableServiceClient;
private readonly string _tableName = "users";
public TableStorageService(string connectionString)
{
_tableServiceClient = new TableServiceClient(connectionString);
}
// 插入用户实体
public async Task InsertUserAsync(UserEntity user)
{
var tableClient = _tableServiceClient.GetTableClient(_tableName);
await tableClient.CreateIfNotExistsAsync();
await tableClient.AddEntityAsync(user);
Console.WriteLine($"用户 {user.UserName} 插入成功!");
}
// 查询用户实体(按分区键和行键查询)
public async Task<UserEntity?> GetUserAsync(string partitionKey, string rowKey)
{
var tableClient = _tableServiceClient.GetTableClient(_tableName);
try
{
var user = await tableClient.GetEntityAsync<UserEntity>(partitionKey, rowKey);
return user.Value;
}
catch (Azure.RequestFailedException ex) when (ex.Status == 404)
{
Console.WriteLine("用户不存在!");
return null;
}
}
// 查询所有用户实体
public async Task<List<UserEntity>> GetAllUsersAsync()
{
var tableClient = _tableServiceClient.GetTableClient(_tableName);
var users = new List<UserEntity>();
await foreach (var user in tableClient.QueryAsync<UserEntity>())
{
users.Add(user);
Console.WriteLine($"用户:{user.UserName},邮箱:{user.Email},年龄:{user.Age}");
}
return users;
}
// 更新用户实体
public async Task UpdateUserAsync(UserEntity user)
{
var tableClient = _tableServiceClient.GetTableClient(_tableName);
await tableClient.UpdateEntityAsync(user, ETag.All, TableUpdateMode.Replace);
Console.WriteLine($"用户 {user.UserName} 更新成功!");
}
}
场景5:本地开发与调试
使用Azurite模拟器模拟Azure Storage,实现本地开发、调试,无需连接Azure云。
步骤1:安装Azurite
bash
dotnet tool install -g Azurite
步骤2:启动Azurite
bash
azurite
Azurite默认监听端口:Blob存储10000,队列存储10001,表存储10002;
步骤3:使用Azurite连接字符串
csharp
// Azurite连接字符串
string connectionString = "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;";
var blobStorageService = new BlobStorageService(connectionString);
逐行讲解
场景1:Blob存储核心代码
1.BlobServiceClient:
用于管理Blob存储账户,创建容器客户端、获取容器列表等;
2.BlobContainerClient:
用于管理Blob容器,创建容器、获取Blob客户端、列出Blob等;
3.BlobClient:
用于管理单个Blob,上传、下载、删除Blob,生成SAS令牌等;
4.存储层:
热存储(Hot):适合频繁访问的数据,存储成本高,访问成本低;
冷存储(Cool):适合不频繁访问的数据(每月访问少于1次),存储成本低,访问成本高;
归档存储(Archive):适合极少访问的数据(每年访问少于1次),存储成本极低,访问成本极高,需要解冻才能访问;
场景3:队列存储核心代码
1.QueueServiceClient:
用于管理队列存储账户,创建队列客户端、获取队列列表等;
2.QueueClient:
用于管理单个队列,发送、接收、删除消息,创建、删除队列等;
3.消息可见性:
visibilityTimeout:发送消息时设置,消息在指定时间后才能被接收;
visibilityTimeout:接收消息时设置,消息在指定时间内被锁定,其他客户端无法接收;
场景4:表存储核心代码
1.ITableEntity接口:
实体类必须实现该接口,包含PartitionKey、RowKey、Timestamp、ETag属性;
2.分区键(PartitionKey):
用于水平分区,将数据分布到多个服务器,提高查询性能;
建议将经常一起查询的实体放在同一个分区;
3.行键(RowKey):
唯一标识分区内的实体,与PartitionKey一起构成实体的唯一标识;
基础知识拓展
- Azure Storage类型对比
| 存储类型 | 特点 | 适用场景 |
|---|---|---|
| Blob存储 | 存储非结构化数据,支持分层存储、CDN加速 | 图片、视频、文档、备份文件、静态网站 |
| 文件存储 | 存储结构化数据,支持SMB协议,可在多平台访问 | 共享文件、日志文件、应用程序配置文件 |
| 队列存储 | 存储消息,实现异步任务处理,支持消息可见性、锁定 | 订单通知、数据同步、异步任务处理 |
| 表存储 | 存储结构化数据,支持NoSQL查询,水平扩展 | 用户信息、设备数据、日志数据、元数据 |
-
安全存储最佳实践
1.加密:
静态加密:Azure自动加密存储的数据,使用Azure管理的密钥或客户管理的密钥;
传输加密:使用HTTPS协议传输数据,禁止HTTP访问;
2.访问控制:
SAS令牌:生成临时访问令牌,授予特定权限(如读取、写入),设置过期时间;
RBAC角色:为用户、组、服务主体分配存储账户角色(如Storage Blob Data Contributor);
3.防火墙:配置存储账户防火墙,只允许特定IP地址或虚拟网络访问; -
成本优化策略
1.分层存储:根据数据访问频率选择存储层,将不频繁访问的数据移到冷存储或归档存储;
2.生命周期管理:配置生命周期规则,自动将数据从热存储移到冷存储、归档存储,或删除过期数据;
3.冗余策略:选择合适的冗余策略(LRS、GRS、RA-GRS、ZRS、GZRS、RA-GZRS),平衡成本和可用性; -
监控与日志
Azure Monitor:实时监控存储使用情况(如存储容量、请求次数、响应时间);
存储分析日志:记录存储账户的访问日志,可用于审计、故障排查;
指标警报:设置指标警报(如存储容量超过阈值、请求错误率过高),及时通知管理员;
总结
Azure Storage的核心是无限扩容、高可用性、多场景支持、安全存储、成本优化,让你轻松存储和管理各种类型的数据,无需担心服务器扩容、维护、故障恢复。关键要点:
1.Blob存储:存储非结构化数据,支持分层存储、CDN加速;
2.文件存储:存储结构化数据,支持SMB协议,多平台访问;
3.队列存储:存储消息,实现异步任务处理,提高系统响应速度;
4.表存储:存储结构化数据,支持NoSQL查询,水平扩展;
5.安全存储:支持加密、访问控制、防火墙,保护数据安全;
6.成本优化:分层存储、生命周期管理,降低存储成本;
比如这个Azure Storage商品图片存储功能,图片存储在热存储层,通过CDN加速访问,加载速度提升50%,每月存储成本不到50元,而且不需要维护服务器,维护成本降为0。掌握Azure Storage,你就能轻松构建云原生应用,实现数据的安全存储、高效访问!
本站原创,转载请注明出处:










