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 是一个用于处理又拍云存储服务文件上传的类,使用了依赖注入模式接收 HttpClientILogger 实例。

主要方法:UploadAsync

方法签名

internal async Task<UploadResult> UploadAsync<TOperator>(
    ICollection<string> pathToFile,           // 文件路径集合
    Func<HttpContent> setContent,             // 设置HTTP内容的委托
    TOperator operatorConfiguration,          // 操作配置(泛型约束为UpyunOperator)
    string? contentMd5 = null,               // 可选的MD5值
    CancellationToken cancellationToken = default
)

功能流程

  1. 参数验证

    • 检查文件路径集合是否为空
    • 验证操作配置中是否包含存储桶名称
  2. 构建上传地址

    var uploadAddress = string.Join("/", pathToFile.Select(Uri.EscapeDataString));
    

    将路径集合转换为URL编码的路径字符串

  3. 创建HTTP请求

    • 使用PUT方法
    • 设置HTTP版本为2.0
    • 构建请求URL:/{bucket}/{uploadAddress}
  4. 请求签名

    await request.SignatureAsync(operatorConfiguration, contentMd5, cancellationToken: cancellationToken);
    

    为请求添加又拍云的认证签名

  5. 发送请求并处理响应

    • 发送HTTP请求
    • 检查响应状态码,失败时抛出业务异常
  6. 构建上传结果 从响应头中提取以下信息:

    • AccessUrl:文件访问URL
    • FileType:文件类型(从 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,确保主机地址以斜杠结尾,然后拼接文件路径。

设计特点

  1. 泛型约束:使用 where TOperator : UpyunOperator 确保操作配置的类型安全
  2. 异步编程:全程使用异步操作,支持取消令牌
  3. 错误处理:包含参数验证和HTTP响应状态检查
  4. 日志记录:记录上传失败和成功的信息
  5. 扩展方法:使用了扩展方法 SignatureAsyncGetResponseHeaderValue

这个类是又拍云存储SDK的核心上传功能实现,提供了完整的文件上传流程和结果处理。

评论加载中...