using Dpz.Core.Infrastructure;
using Microsoft.Extensions.Logging;
namespace Dpz.Core.Service.ObjectStorage;
public class UpyunUpload(HttpClient httpClient, ILogger<UpyunUpload> logger)
{
internal async Task<UploadResult> UploadAsync<TOperator>(
ICollection<string> pathToFile,
Func<HttpContent> setContent,
TOperator operatorConfiguration,
string? contentMd5 = null,
CancellationToken cancellationToken = default
)
where TOperator : UpyunOperator
{
if (pathToFile.Count == 0)
{
throw new ArgumentException(
"argument pathToFile does not any contain item.",
nameof(pathToFile)
);
}
if (string.IsNullOrEmpty(operatorConfiguration.Bucket))
{
throw new ArgumentException(
"argument operatorConfiguration does not have bucket.",
nameof(operatorConfiguration)
);
}
var uploadAddress = string.Join("/", pathToFile.Select(Uri.EscapeDataString));
var request = new HttpRequestMessage(
HttpMethod.Put,
$"/{operatorConfiguration.Bucket}/{uploadAddress}"
)
{
Version = new Version(2, 0),
};
await request.SignatureAsync(
operatorConfiguration,
contentMd5,
cancellationToken: cancellationToken
);
request.Content = setContent();
var response = await httpClient.SendAsync(request, cancellationToken);
if (!response.IsSuccessStatusCode)
{
logger.LogError("upload fail,status code:{StatusCode}", response.StatusCode);
throw new BusinessException($"upload fail,response status code:{response.StatusCode}");
}
#region upload result
var uploadResult = new UploadResult
{
AccessUrl = GetAccessUrl(uploadAddress, operatorConfiguration),
FileType = response.Headers.GetResponseHeaderValue("x-upyun-file-type", ""),
};
var frames = response.Headers.GetResponseHeaderValue("x-upyun-frames", "0");
if (int.TryParse(frames, out var framesInt))
{
uploadResult.Frames = framesInt;
}
var width = response.Headers.GetResponseHeaderValue("x-upyun-width", "0");
if (int.TryParse(width, out var widthInt))
{
uploadResult.Width = widthInt;
}
var height = response.Headers.GetResponseHeaderValue("x-upyun-height", "0");
if (int.TryParse(height, out var heightInt))
{
uploadResult.Height = heightInt;
}
if (response.Content.Headers.ContentMD5 != null)
{
var md5Value = BitConverter
.ToString(response.Content.Headers.ContentMD5)
.Replace("-", "")
.ToLowerInvariant();
logger.LogInformation(
"upload file:{AccessUrl},MD5 value:{MD5Value}",
uploadResult.AccessUrl,
md5Value
);
uploadResult.Md5 = md5Value;
}
#endregion
return uploadResult;
}
private static string GetAccessUrl<TOperator>(string pathToFile, TOperator upyunOperator)
where TOperator : UpyunOperator
{
return (
upyunOperator.Host?.LastOrDefault() == '/'
? upyunOperator.Host
: upyunOperator.Host + "/"
) + pathToFile;
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
这是一个用于又拍云(Upyun)对象存储服务的文件上传类。让我详细解释一下代码的功能和结构:
类概述
UpyunUpload 是一个用于处理又拍云存储服务文件上传的类,使用了依赖注入模式接收 HttpClient 和 ILogger 实例。
主要方法:UploadAsync
方法签名
internal async Task<UploadResult> UploadAsync<TOperator>(
ICollection<string> pathToFile, // 文件路径集合
Func<HttpContent> setContent, // 设置HTTP内容的委托
TOperator operatorConfiguration, // 操作配置(泛型约束为UpyunOperator)
string? contentMd5 = null, // 可选的MD5值
CancellationToken cancellationToken = default
)
功能流程
参数验证
- 检查文件路径集合是否为空
- 验证操作配置中是否包含存储桶名称
构建上传地址
var uploadAddress = string.Join("/", pathToFile.Select(Uri.EscapeDataString));将路径集合转换为URL编码的路径字符串
创建HTTP请求
- 使用PUT方法
- 设置HTTP版本为2.0
- 构建请求URL:
/{bucket}/{uploadAddress}
请求签名
await request.SignatureAsync(operatorConfiguration, contentMd5, cancellationToken: cancellationToken);为请求添加又拍云的认证签名
发送请求并处理响应
- 发送HTTP请求
- 检查响应状态码,失败时抛出业务异常
构建上传结果 从响应头中提取以下信息:
AccessUrl:文件访问URLFileType:文件类型(从 x-upyun-file-type 头获取)Frames:帧数(从 x-upyun-frames 头获取)Width:宽度(从 x-upyun-width 头获取)Height:高度(从 x-upyun-height 头获取)Md5:MD5值(从Content-MD5头获取)
辅助方法:GetAccessUrl
private static string GetAccessUrl<TOperator>(string pathToFile, TOperator upyunOperator)
用于构建文件的访问URL,确保主机地址以斜杠结尾,然后拼接文件路径。
设计特点
- 泛型约束:使用
where TOperator : UpyunOperator确保操作配置的类型安全 - 异步编程:全程使用异步操作,支持取消令牌
- 错误处理:包含参数验证和HTTP响应状态检查
- 日志记录:记录上传失败和成功的信息
- 扩展方法:使用了扩展方法
SignatureAsync和GetResponseHeaderValue
这个类是又拍云存储SDK的核心上传功能实现,提供了完整的文件上传流程和结果处理。
评论加载中...