using Dpz.Core.Entity.Base.PublicStruct;
using Dpz.Core.Infrastructure;
using Dpz.Core.Shard.Service;
using FluentFTP;
using Microsoft.Extensions.Logging;

namespace Dpz.Core.Service.ObjectStorage.Services.Impl;

[Obsolete("已弃用,改为rclone挂载网盘实现")]
public class SafeFileService(
    HttpClient httpClient,
    SafeFileConfiguration safeFileOperator,
    ParallelChunkBreakpointUpload<SafeFileConfiguration> parallelChunkBreakpointUpload,
    UpyunUpload upyunUpload,
    ILogger<SafeFileService> logger,
    IFtpLogger ftpLogger
) : ISafeFileService
{
    public async Task<IFileMetadata> UploadAsync(
        Stream stream,
        ICollection<string> path,
        string filename,
        CancellationToken cancellationToken = default
    )
    {
        var size = stream.CanSeek ? stream.Length : 0;
        return await UploadAsync(
            path,
            filename,
            () => new StreamContent(stream),
            size,
            cancellationToken: cancellationToken
        );
    }

    public async Task<FileAddress?> UploadFileAsync(
        CloudFile file,
        CancellationToken cancellationToken = default
    )
    {
        return await parallelChunkBreakpointUpload.UploadFileAsync(
            file,
            UploadAsync,
            cancellationToken: cancellationToken
        );
    }

    public async Task<Stream> DownloadAsync(
        string pathToFile,
        CancellationToken cancellationToken = default
    )
    {
        // 构建 URI
        var uriParts = pathToFile.Split('/').Select(Uri.EscapeDataString).ToList();
        uriParts.Insert(0, "");
        uriParts.Insert(
            1,
            safeFileOperator.Bucket ?? throw new BusinessException("bucket is null")
        );
        var uri = string.Join('/', uriParts);

        var request = new HttpRequestMessage(HttpMethod.Get, uri);
        await request.SignatureAsync(safeFileOperator, cancellationToken: cancellationToken);
        var response = await httpClient.SendAsync(request, cancellationToken);
        if (!response.IsSuccessStatusCode)
        {
            logger.LogError("download fail,status code:{StatusCode}", response.StatusCode);
            throw new BusinessException(
                $"download fail,response status code:{response.StatusCode}"
            );
        }

        var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
        return stream;
    }

    private async Task<IFileMetadata> UploadAsync(
        ICollection<string> pathToFile,
        Func<HttpContent> setContent,
        long size = 0,
        string? hash = null,
        string? contentMd5 = null,
        CancellationToken cancellationToken = default
    )
    {
        return await upyunUpload.UploadAsync(
            pathToFile,
            setContent,
            safeFileOperator,
            size,
            hash,
            contentMd5,
            cancellationToken
        );
    }

    private async Task<IFileMetadata> UploadAsync(
        IEnumerable<string> path,
        string filename,
        Func<HttpContent> setContent,
        long size = 0,
        string? hash = null,
        string? contentMd5 = null,
        CancellationToken cancellationToken = default
    )
    {
        if (string.IsNullOrEmpty(filename))
        {
            throw new ArgumentNullException(nameof(filename));
        }

        var pathList = path.ToList();
        pathList.Add(filename);

        return await UploadAsync(pathList, setContent, size, hash, contentMd5, cancellationToken);
    }

    public async Task<FileAddress?> UploadFileForFtpAsync(
        CloudFile file,
        CancellationToken cancellationToken = default
    )
    {
        var remotePath = string.Join("/", file.PathToFile);

        #region new Fluent FTP Client

        await using var client = new AsyncFtpClient(
            "v0.ftp.upyun.com",
            $"{safeFileOperator.Operator}/{safeFileOperator.Bucket}",
            safeFileOperator.Password,
            logger: ftpLogger
        );
        client.Config.ConnectTimeout = 1000 * 60 * 3;
        client.Config.ReadTimeout = 1000 * 60 * 60;
        client.Config.DataConnectionConnectTimeout = 1000 * 60 * 60;
        client.Config.DataConnectionReadTimeout = 1000 * 60 * 60;

        #endregion

        var status = FtpStatus.Skipped;

        try
        {
            await client.Connect(cancellationToken);
            status = await ApplicationTools.RetryAsync(
                async () =>
                    await client.UploadStream(file.Stream, remotePath, token: cancellationToken),
                TimeSpan.FromSeconds(3)
            );
        }
        catch (Exception ex) when (status != FtpStatus.Success)
        {
            logger.LogError(ex, "upload fail");
            return null;
        }
        finally
        {
            await client.Disconnect(cancellationToken);
            try
            {
                await file.Stream.DisposeAsync();
            }
            catch (Exception e)
            {
                logger.LogError(e, "dispose stream fail");
            }
        }

        return new FileAddress(remotePath, "");
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

代码解释

这是一个已弃用的文件存储服务类(SafeFileService),用于实现文件的上传和下载功能,主要针对又拍云(Upyun)存储服务。

主要组成部分

1. 类声明与依赖注入

[Obsolete("已弃用,改为rclone挂载网盘实现")]
public class SafeFileService(...)
  • 标记为过时,已被 rclone 挂载方案替代
  • 使用主构造函数(C# 12特性)注入多个依赖项:
    • HttpClient: HTTP请求客户端
    • SafeFileConfiguration: 配置信息
    • ParallelChunkBreakpointUpload: 分块断点续传上传服务
    • UpyunUpload: 又拍云上传服务
    • 日志相关组件

2. 核心方法

UploadAsync (流上传)

public async Task<IFileMetadata> UploadAsync(Stream stream, ...)
  • 接收流和路径信息,上传文件
  • 自动检测流大小
  • 将流包装为 StreamContent 进行上传

UploadFileAsync (文件上传)

public async Task<FileAddress?> UploadFileAsync(CloudFile file, ...)
  • 使用分块断点续传方式上传文件
  • 委托给 parallelChunkBreakpointUpload 处理

DownloadAsync (文件下载)

public async Task<Stream> DownloadAsync(string pathToFile, ...)

工作流程:

  1. 构建URI路径(对路径各部分进行URL编码)
  2. 插入 bucket 信息
  3. 对请求进行签名认证
  4. 发送HTTP GET请求
  5. 返回响应流

UploadFileForFtpAsync (FTP上传)

public async Task<FileAddress?> UploadFileForFtpAsync(CloudFile file, ...)

特点:

  • 使用 FluentFTP 库通过FTP协议上传
  • 连接又拍云FTP服务器(v0.ftp.upyun.com)
  • 配置了多个超时参数(连接、读取、数据传输等,最长1小时)
  • 包含重试机制(使用 ApplicationTools.RetryAsync
  • 完善的异常处理和资源清理

3. 私有辅助方法

提供两个重载版本的 UploadAsync,最终都委托给 upyunUpload.UploadAsync 执行实际上传。

技术亮点

  1. 异步编程:全面使用 async/await
  2. 取消令牌:所有方法支持取消操作
  3. 资源管理:使用 await using 自动释放FTP客户端
  4. 错误处理:完善的日志记录和异常捕获
  5. 灵活性:支持HTTP和FTP两种上传方式
  6. 断点续传:支持大文件分块上传

注意事项

  • 该服务已标记为过时,建议使用新的 rclone 挂载方案
  • FTP上传设置了较长的超时时间,适合大文件传输
  • 下载方法会对路径进行URL编码,避免特殊字符问题
评论加载中...