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, ...)
工作流程:
- 构建URI路径(对路径各部分进行URL编码)
- 插入 bucket 信息
- 对请求进行签名认证
- 发送HTTP GET请求
- 返回响应流
UploadFileForFtpAsync (FTP上传)
public async Task<FileAddress?> UploadFileForFtpAsync(CloudFile file, ...)
特点:
- 使用 FluentFTP 库通过FTP协议上传
- 连接又拍云FTP服务器(v0.ftp.upyun.com)
- 配置了多个超时参数(连接、读取、数据传输等,最长1小时)
- 包含重试机制(使用
ApplicationTools.RetryAsync) - 完善的异常处理和资源清理
3. 私有辅助方法
提供两个重载版本的 UploadAsync,最终都委托给 upyunUpload.UploadAsync 执行实际上传。
技术亮点
- 异步编程:全面使用 async/await
- 取消令牌:所有方法支持取消操作
- 资源管理:使用
await using自动释放FTP客户端 - 错误处理:完善的日志记录和异常捕获
- 灵活性:支持HTTP和FTP两种上传方式
- 断点续传:支持大文件分块上传
注意事项
- 该服务已标记为过时,建议使用新的 rclone 挂载方案
- FTP上传设置了较长的超时时间,适合大文件传输
- 下载方法会对路径进行URL编码,避免特殊字符问题
AI 正在分析代码…
评论加载中...