Sage.CloudStorage.Qiniu
1.0.0.3
dotnet add package Sage.CloudStorage.Qiniu --version 1.0.0.3
NuGet\Install-Package Sage.CloudStorage.Qiniu -Version 1.0.0.3
<PackageReference Include="Sage.CloudStorage.Qiniu" Version="1.0.0.3" />
<PackageVersion Include="Sage.CloudStorage.Qiniu" Version="1.0.0.3" />
<PackageReference Include="Sage.CloudStorage.Qiniu" />
paket add Sage.CloudStorage.Qiniu --version 1.0.0.3
#r "nuget: Sage.CloudStorage.Qiniu, 1.0.0.3"
#:package Sage.CloudStorage.Qiniu@1.0.0.3
#addin nuget:?package=Sage.CloudStorage.Qiniu&version=1.0.0.3
#tool nuget:?package=Sage.CloudStorage.Qiniu&version=1.0.0.3
Sage.CloudStorage.Qiniu - 现代化七牛云存储 .NET SDK
简介
Sage.CloudStorage.Qiniu 是一个基于 .NET 平台的现代化七牛云存储 SDK,采用完全异步设计,提供了对七牛云对象存储、CDN 等服务的简单易用的 API 封装。该库基于 Sage.Http 构建,具有高性能、可扩展的七牛云服务访问能力,特别适合企业级应用和大文件处理场景。
核心优势
- 现代化API设计:完全异步,符合.NET最佳实践
- 模块化架构:各组件职责明确,易于扩展和维护
- 丰富的事件机制:提供上传进度通知和完成事件
- 智能上传策略:自动选择最佳上传方式和分片大小
- 完善的错误处理:提供详细的错误信息和恢复机制
功能特性
- 完整的对象存储支持:上传、下载、管理、删除等操作
- 高级上传功能:
- 智能分片上传(自动优化分片大小)
- 断点续传支持
- 并发控制
- 实时进度监控
- CDN管理:刷新、预取、带宽查询、日志下载
- 数据处理:图片处理、音视频转码等
- 批量操作:批量上传、删除等
- AOT编译:使用了源生成,支持AOT,提升性能
安装
dotnet add package Sage.CloudStorage.Qiniu
快速入门
初始化
using Sage.CloudStorage.Qiniu;
using Sage.CloudStorage.Qiniu.Models;
using Sage.CloudStorage.Qiniu.Config;
// 创建七牛云客户端
var httpManager = new HttpRequestManager(HttpClientSingleton.Instance);
var client = new QiniuClient("your-access-key", "your-secret-key", httpManager);
// 使用自定义的HttpRequestManager
using Sage.Http.Core;
// 创建自定义的HttpRequestManager并进行配置
var httpManager = new HttpRequestManager(HttpClientSingleton.Instance);
// 可以对httpManager进行自定义配置
// 使用配置好的HttpRequestManager创建七牛云客户端
var clientWithConfig = new QiniuClient("your-access-key", "your-secret-key", httpManager, new QiniuConfig
{
UseHttps = true,
BlockSize = 4 * 1024 * 1024
});
自定义配置
// 创建高级配置
var config = new QiniuConfig
{
// 基础配置
UseHttps = true, // 使用HTTPS协议
BlockSize = 4 * 1024 * 1024, // 4MB分片大小
ConnectionTimeout = 30000, // 连接超时时间(毫秒)
ReadTimeout = 60000, // 读取超时时间(毫秒)
WriteTimeout = 60000, // 写入超时时间(毫秒)
MaxRetryTimes = 3, // 最大重试次数
// 区域配置
AutoSelectUploadHost = true, // 自动选择最优上传域名
// 或手动指定域名
// UpHost = "upload.qiniup.com", // 自定义上传域名
// 高级配置
UseCdnForDownload = true, // 使用CDN加速下载
ChunkUploadConcurrency = 3, // 分片上传的并发数
ProgressCallbackInterval = 1024 * 1024 // 上传进度回调间隔(字节)
};
// 创建客户端
var client = new QiniuClient("your-access-key", "your-secret-key", config);
模块化访问
Sage.Qiniu采用模块化设计,每个功能领域由专门的管理器类处理:
// 存储桶操作
var bucketManager = client.Bucket;
// 文件上传
var uploadManager = client.Upload;
// 文件下载
var downloadManager = client.Download;
// CDN管理
var cdnManager = client.Cdn;
// 数据处理
var operationManager = client.Operation;
功能示例
数据处理操作
// 持久化数据处理
var pfopResult = await client.Operation.PfopAsync(
"bucket-name",
"file-key",
"avthumb/mp4/s/640x360/vb/1.25m",
"example.com",
true);
// 查询持久化处理状态
var prefopResult = await client.Operation.PrefopAsync(pfopResult.PersistentId);
// 对URL进行数据处理
var processedData = await client.Operation.DfopAsync(
"http://domain.com/file-key",
"imageView2/1/w/200/h/200");
// 对本地文件进行数据处理
var processedFile = await client.Operation.DfopAsync(
"local-file-path",
"imageView2/1/w/200/h/200",
isLocalFile: true);
// 对文本内容进行数据处理
string text = "Hello, Qiniu!";
var processedText = await client.Operation.DfopAsync(
text,
"qrcode/0",
isText: true);
存储桶操作
// 获取存储桶列表
var buckets = await client.Bucket.GetBucketsAsync();
// 获取文件信息
var fileInfo = await client.Bucket.GetFileInfoAsync("bucket-name", "file-key");
// 列举文件
var files = await client.Bucket.ListFilesAsync("bucket-name", prefix: "prefix-", limit: 100);
// 删除文件
bool success = await client.Bucket.DeleteFileAsync("bucket-name", "file-key");
// 复制文件
bool success = await client.Bucket.CopyFileAsync("src-bucket", "src-key", "dest-bucket", "dest-key");
// 移动文件
bool success = await client.Bucket.MoveFileAsync("src-bucket", "src-key", "dest-bucket", "dest-key");
文件上传
简单上传
适用于小文件(<10MB)的快速上传:
// 简单上传文件
var result = await client.Upload.UploadFileAsync(
filePath: "C:\\path\\to\\image.jpg",
key: "images/photo.jpg",
bucket: "my-bucket");
Console.WriteLine($"上传成功: {result.Key}, 哈希值: {result.Hash}");
带进度的上传
实时监控上传进度:
// 创建进度回调
var progress = new Progress<double>(percent =>
{
Console.WriteLine($"上传进度: {percent:P2}");
// 更新UI进度条
// progressBar.Value = (int)(percent * 100);
});
// 上传文件并监控进度
var result = await client.Upload.UploadFileAsync(
filePath: "C:\\path\\to\\video.mp4",
key: "videos/intro.mp4",
bucket: "my-bucket",
progress: progress);
使用自定义上传策略
控制上传行为和文件属性:
// 创建自定义上传策略
var policy = new UploadPolicy
{
// 基本设置
Scope = "my-bucket:videos/intro.mp4", // 指定上传空间和文件名
Deadline = DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds(), // 上传凭证有效期
// 文件属性设置
MimeLimit = "video/*", // 限制文件类型
FsizeLimit = 500 * 1024 * 1024, // 限制文件大小(500MB)
// 回调设置
CallbackUrl = "https://api.example.com/qiniu/callback", // 回调URL
CallbackBody = "{\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"size\":$(fsize),\"bucket\":\"$(bucket)\"}", // 回调内容
CallbackBodyType = "application/json", // 回调格式
// 返回内容设置
ReturnBody = "{\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"size\":$(fsize),\"width\":$(imageInfo.width),\"height\":$(imageInfo.height)}"
};
// 使用自定义策略上传
var result = await client.Upload.UploadFileAsync(
filePath: "C:\\path\\to\\video.mp4",
key: "videos/intro.mp4",
bucket: "my-bucket",
policy: policy);
上传字节数组或内存流
直接上传内存中的数据:
// 上传字节数组
byte[] imageData = File.ReadAllBytes("C:\\path\\to\\image.jpg");
var result = await client.Upload.UploadDataAsync(
data: imageData,
key: "images/memory-image.jpg",
bucket: "my-bucket");
// 上传内存流
using var stream = new MemoryStream();
// 向stream写入数据...
stream.Position = 0;
result = await client.Upload.UploadStreamAsync(
stream: stream,
key: "files/memory-data.bin",
bucket: "my-bucket");
智能分片上传
自动处理大文件上传,内部优化分片大小和并发:
// 导入分片上传服务
using Sage.CloudStorage.Qiniu.Upload;
// 创建分片上传服务
var multipartService = new QiniuMultipartUploadService(
httpManager: new Sage.Http.Core.HttpRequestManager(),
qiniuKey: new QiniuKey("your-access-key", "your-secret-key"));
// 注册进度事件
multipartService.ProgressChanged += (sender, e) =>
{
Console.WriteLine($"上传进度: {e.Percent:P2}, 已上传: {e.BytesUploaded}, 总大小: {e.TotalBytes}");
};
// 注册完成事件
multipartService.UploadCompleted += (sender, e) =>
{
if (e.IsSuccess)
{
Console.WriteLine($"上传成功: {e.Key}, 哈希值: {e.Hash}, 耗时: {e.Duration.TotalSeconds:F1}秒");
}
else
{
Console.WriteLine($"上传失败: {e.ErrorMessage}");
}
};
// 配置上传选项
var options = new UploadOptions
{
ChunkSize = 4 * 1024 * 1024, // 4MB分片,也可以不指定使用智能分片
MaxConcurrency = 3, // 最大并发数
EnableConsoleLog = true // 启用控制台日志
};
// 执行分片上传
var result = await multipartService.UploadFileAsync(
filePath: "C:\\path\\to\\large-video.mp4",
bucket: "my-bucket",
key: "videos/large-video.mp4",
options: options);
批量上传
一次性上传多个文件:
// 准备文件列表
var files = Directory.GetFiles("C:\\path\\to\\images", "*.jpg");
// 定义文件名生成器
string KeyGenerator(string filePath) => $"images/{Path.GetFileName(filePath)}";
// 定义上传策略生成器(可选)
UploadPolicy? PolicyGenerator(string filePath) => new UploadPolicy
{
Scope = $"my-bucket:{KeyGenerator(filePath)}",
Deadline = DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds()
};
// 配置批量上传选项
var options = new UploadOptions
{
BatchConcurrency = 5, // 同时上传5个文件
EnableConsoleLog = true
};
// 执行批量上传
var results = await multipartService.UploadBatchAsync(
filePaths: files,
bucket: "my-bucket",
keyGenerator: KeyGenerator,
policyGenerator: PolicyGenerator,
options: options);
// 处理结果
foreach (var item in results)
{
if (item.IsSuccess)
{
Console.WriteLine($"文件 {item.FilePath} 上传成功,Key: {item.Key}");
}
else
{
Console.WriteLine($"文件 {item.FilePath} 上传失败: {item.ErrorMessage}");
}
}
### 文件下载
#### 下载私有资源
七牛云私有资源需要签名才能访问:
```csharp
// 创建私有资源下载URL(有效期1小时)
string privateUrl = client.Download.CreatePrivateUrl(
url: "http://example.com/my-bucket/documents/report.pdf",
expireSeconds: 3600);
Console.WriteLine($"下载链接: {privateUrl}");
// 输出: http://example.com/my-bucket/documents/report.pdf?e=1630000000&token=abcdef...
下载到本地文件
将文件直接下载到本地磁盘:
// 下载公开资源
bool success = await client.Download.DownloadToFileAsync(
url: "http://example.com/my-bucket/images/photo.jpg",
saveToPath: "C:\\Downloads\\photo.jpg");
// 下载私有资源
success = await client.Download.DownloadToFileAsync(
url: "http://example.com/my-bucket/documents/report.pdf",
saveToPath: "C:\\Downloads\\report.pdf",
isPrivate: true); // 自动添加签名
if (success)
{
Console.WriteLine("文件下载成功");
}
下载到内存
将文件下载到内存中进行处理:
// 创建进度回调
var progress = new Progress<double>(percent =>
{
Console.WriteLine($"下载进度: {percent:P2}");
});
// 下载到字节数组
byte[] imageData = await client.Download.DownloadToByteArrayAsync(
url: "http://example.com/my-bucket/images/photo.jpg",
isPrivate: false,
progress: progress);
// 使用下载的数据
using var memoryStream = new MemoryStream(imageData);
using var image = System.Drawing.Image.FromStream(memoryStream);
Console.WriteLine($"图片尺寸: {image.Width}x{image.Height}");
// 下载到流
using var fileStream = File.Create("C:\\Downloads\\document.docx");
await client.Download.DownloadToStreamAsync(
url: "http://example.com/my-bucket/documents/document.docx",
targetStream: fileStream,
isPrivate: true,
progress: progress);
断点续传下载
支持从指定位置开始下载:
// 获取已下载的部分大小
long existingFileSize = 0;
string filePath = "C:\\Downloads\\large-video.mp4";
if (File.Exists(filePath))
{
existingFileSize = new FileInfo(filePath).Length;
}
// 从断点继续下载
using var fileStream = new FileStream(
filePath,
FileMode.OpenOrCreate,
FileAccess.Write);
fileStream.Position = existingFileSize;
bool success = await client.Download.DownloadToStreamAsync(
url: "http://example.com/my-bucket/videos/large-video.mp4",
targetStream: fileStream,
startPosition: existingFileSize);
CDN 管理
刷新缓存
当资源更新后,刷新CDN缓存:
// 刷新单个文件
var singleRefreshResult = await client.Cdn.RefreshUrlsAsync(
new[] { "http://cdn.example.com/images/logo.png" });
// 批量刷新文件
var urls = new[]
{
"http://cdn.example.com/css/style.css",
"http://cdn.example.com/js/app.js",
"http://cdn.example.com/images/banner.jpg"
};
var refreshResult = await client.Cdn.RefreshUrlsAsync(urls);
Console.WriteLine($"刷新成功: {refreshResult.IsSuccess}, 刷新数量: {refreshResult.SuccessCount}");
// 刷新目录(递归刷新该目录下所有文件)
var dirs = new[]
{
"http://cdn.example.com/css/",
"http://cdn.example.com/js/"
};
var refreshDirResult = await client.Cdn.RefreshDirsAsync(dirs);
Console.WriteLine($"目录刷新成功: {refreshDirResult.IsSuccess}, 刷新数量: {refreshDirResult.SuccessCount}");
预取资源
提前将资源缓存到CDN节点,加速用户访问:
// 预取单个文件
var singlePrefetchResult = await client.Cdn.PrefetchUrlsAsync(
new[] { "http://cdn.example.com/videos/intro.mp4" });
// 批量预取文件
var urls = new[]
{
"http://cdn.example.com/videos/product1.mp4",
"http://cdn.example.com/videos/product2.mp4"
};
var prefetchResult = await client.Cdn.PrefetchUrlsAsync(urls);
Console.WriteLine($"预取成功: {prefetchResult.IsSuccess}, 预取数量: {prefetchResult.SuccessCount}");
获取带宽和流量统计
查询CDN使用情况:
// 定义查询域名
var domains = new[] { "cdn.example.com" };
// 获取带宽数据(最近7天,按天统计)
var bandwidthData = await client.Cdn.GetBandwidthDataAsync(
domains: domains,
startDate: DateTime.Now.AddDays(-7),
endDate: DateTime.Now,
granularity: "day");
// 输出带宽数据
foreach (var item in bandwidthData.Data)
{
Console.WriteLine($"日期: {item.Time}, 带宽: {item.Bandwidth / 1024 / 1024:F2} Mbps");
}
// 获取流量数据(最近30天,按天统计)
var fluxData = await client.Cdn.GetFluxDataAsync(
domains: domains,
startDate: DateTime.Now.AddDays(-30),
endDate: DateTime.Now,
granularity: "day");
// 输出流量数据
foreach (var item in fluxData.Data)
{
Console.WriteLine($"日期: {item.Time}, 流量: {item.Flux / 1024 / 1024 / 1024:F2} GB");
}
获取CDN日志
下载CDN访问日志进行分析:
// 获取昨天的日志下载链接
var domains = new[] { "cdn.example.com" };
var logLinks = await client.Cdn.GetCdnLogsAsync(
domains: domains,
date: DateTime.Today.AddDays(-1));
// 输出日志链接
foreach (var domain in logLinks.Data)
{
Console.WriteLine($"域名: {domain.Domain}");
foreach (var log in domain.Logs)
{
Console.WriteLine($" 日期: {log.Date}, 日志名: {log.Name}");
Console.WriteLine($" 下载链接: {log.Url}");
// 下载日志文件
await client.Download.DownloadToFileAsync(
url: log.Url,
saveToPath: $"C:\\Logs\\{domain.Domain}_{log.Date}_{log.Name}");
}
}
错误处理与异常管理
Sage.CloudStorage.Qiniu
提供了丰富的错误处理机制,帮助您更好地诊断和处理问题:
try
{
// 尝试创建存储空间
await client.Bucket.CreateBucketAsync("new-bucket", "z0");
}
catch (QiniuException ex) when (ex.Code == 401)
{
// 处理认证错误
Console.WriteLine("认证失败,请检查您的密钥是否正确");
Console.WriteLine($"详细信息: {ex.Message}");
}
catch (QiniuException ex) when (ex.Code == 614)
{
// 处理存储空间已存在的情况
Console.WriteLine("存储空间已存在,无需重复创建");
}
catch (QiniuException ex)
{
// 处理其他七牛云特定错误
Console.WriteLine($"七牛云错误 - 代码: {ex.Code}, 信息: {ex.Message}");
Console.WriteLine($"请求ID: {ex.RequestId}");
Console.WriteLine($"响应详情: {ex.ResponseJson}");
// 记录到日志系统
_logger.LogError(ex, "七牛云操作失败");
}
catch (HttpRequestException ex)
{
// 处理网络请求错误
Console.WriteLine($"网络请求错误: {ex.Message}");
if (ex.InnerException != null)
{
Console.WriteLine($"内部错误: {ex.InnerException.Message}");
}
}
catch (Exception ex)
{
// 处理其他未预期的错误
Console.WriteLine($"未预期错误: {ex.Message}");
Console.WriteLine($"堆栈跟踪: {ex.StackTrace}");
}
错误重试策略
对于网络不稳定的环境,可以实现重试策略:
public async Task<bool> UploadWithRetryAsync(string filePath, string key, int maxRetries = 3)
{
int attempts = 0;
while (attempts < maxRetries)
{
try
{
attempts++;
Console.WriteLine($"尝试上传,第 {attempts} 次");
// 执行上传操作
var result = await client.Upload.UploadFileAsync(filePath, key);
return result.IsSuccess;
}
catch (QiniuException ex) when (ex.IsTransientError() && attempts < maxRetries)
{
// 对于临时错误,等待后重试
int delayMs = 1000 * attempts; // 递增延迟
Console.WriteLine($"遇到临时错误,{delayMs/1000}秒后重试: {ex.Message}");
await Task.Delay(delayMs);
}
catch (Exception ex)
{
// 对于非临时错误,立即失败
Console.WriteLine($"上传失败: {ex.Message}");
return false;
}
}
Console.WriteLine($"达到最大重试次数 {maxRetries},上传失败");
return false;
}
资源管理与释放
QiniuClient
实现了 IDisposable
接口,提供多种资源释放方式:
// 1. 使用 using 语句(推荐)
using (var client = new QiniuClient("access-key", "secret-key"))
{
// 在此块中执行所有操作
await client.Bucket.ListBucketsAsync();
// 离开块时自动释放资源
}
// 2. 使用 using 声明(C# 8.0+)
using var longLivedClient = new QiniuClient("access-key", "secret-key");
// 在方法结束时自动释放资源
// 3. 手动释放(不推荐,除非有特殊需求)
var manualClient = new QiniuClient("access-key", "secret-key");
try
{
// 执行操作
}
finally
{
// 确保资源被释放
manualClient.Dispose();
}
依赖注入集成
在 ASP.NET Core 或其他支持依赖注入的应用中,可以注册为服务:
// 在 Startup.cs 或 Program.cs 中
public void ConfigureServices(IServiceCollection services)
{
// 注册HttpClient和HttpRequestManager
services.AddHttpClient("QiniuHttpClient", client =>
{
// 配置HttpClient,例如设置超时、默认请求头等
client.Timeout = TimeSpan.FromSeconds(60);
});
// 注册为单例服务
services.AddSingleton<IQiniuClient>(provider =>
{
var configuration = provider.GetRequiredService<IConfiguration>();
var accessKey = configuration["Qiniu:AccessKey"];
var secretKey = configuration["Qiniu:SecretKey"];
// 创建HttpRequestManager
var httpManager = new HttpRequestManager(HttpClientSingleton.Instance);
return new QiniuClient(accessKey, secretKey, httpManager, new QiniuConfig
{
UseHttps = true,
AutoSelectUploadHost = true
});
});
// 或注册为作用域服务(使用自定义HttpRequestManager)
services.AddScoped<IQiniuClient>(provider =>
{
var configuration = provider.GetRequiredService<IConfiguration>();
var accessKey = configuration["Qiniu:AccessKey"];
var secretKey = configuration["Qiniu:SecretKey"];
// 获取HttpClientFactory并创建自定义的HttpRequestManager
var httpClientFactory = provider.GetRequiredService<IHttpClientFactory>();
var httpClient = httpClientFactory.CreateClient("QiniuHttpClient");
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
var httpManager = new HttpRequestManager(httpClient, loggerFactory.CreateLogger<HttpRequestManager>());
// 使用自定义HttpRequestManager创建QiniuClient
return new QiniuClient(accessKey, secretKey, httpManager, new QiniuConfig
{
UseHttps = true,
AutoSelectUploadHost = true
});
});
}
然后在控制器或服务中注入:
public class FileController : Controller
{
private readonly IQiniuClient _qiniuClient;
public FileController(IQiniuClient qiniuClient)
{
_qiniuClient = qiniuClient;
}
public async Task<IActionResult> Upload(IFormFile file)
{
if (file == null) return BadRequest("No file provided");
using var stream = file.OpenReadStream();
var result = await _qiniuClient.Upload.UploadStreamAsync(
stream,
file.FileName,
"my-bucket");
if (result.IsSuccess)
{
return Ok(new { url = result.FileUrl });
}
return StatusCode(500, result.ErrorMessage);
}
}
最佳实践与注意事项
性能优化
- 大文件处理:对于大于10MB的文件,始终使用分片上传
- 并发控制:根据服务器资源调整并发上传数量,避免资源耗尽
- CDN加速:对于频繁访问的资源,使用CDN预取功能提前缓存
- 连接复用:长时间运行的应用应使用单例模式的客户端实例
安全建议
- 密钥保护:永远不要在客户端代码中硬编码密钥,使用配置文件或环境变量
- 上传策略:使用严格的上传策略限制文件大小、类型和存储位置
- 私有资源:对敏感资源使用私有存储,并设置合理的链接有效期
- 防盗链:为重要资源配置Referer防盗链和IP白名单
常见问题
- 上传失败:检查网络连接、存储空间配额和文件大小限制
- 授权错误:验证AccessKey和SecretKey是否正确,以及是否有对应权限
- 域名绑定:确保自定义域名已正确CNAME到七牛云域名
许可证
本项目采用 Apache 2.0 许可证。详情请参阅 LICENSE 文件。
贡献
欢迎提交问题报告和改进建议。如果您想贡献代码,请提交拉取请求。
作者
- LiuPengLai - 甲壳虫科技 欢迎提交问题和功能请求。 QQ Group: 1054304346
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- Sage.Data.Extensions (>= 1.0.0.4)
- Sage.Data.Mask (>= 1.0.0.2)
- Sage.Data.Validation (>= 1.0.0.2)
- Sage.Http (>= 1.0.0.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
修复客户端初始化时不能传递sage.http的对象问题