网站首页 网站源码
using Dpz.Core.Infrastructure;
using Dpz.Core.Infrastructure.PublicStruct;
using Dpz.Core.Shard.Service;
using FluentFTP;
using Microsoft.Extensions.Logging;
namespace Dpz.Core.Service.ObjectStorage.Services.Impl;
public class ObjectStorageService(
UpyunOperator upyunOperator,
HttpClient httpClient,
ILogger<ObjectStorageService> logger,
IFtpLogger ftpLogger,
ParallelChunkBreakpointUpload<UpyunOperator> parallelChunkBreakpointUpload,
UpyunUpload upyunUpload
) : IObjectStorageOperation
{
private const string FtpHost = "v0.ftp.upyun.com";
public string Bucket => upyunOperator.Bucket ?? throw new BusinessException("Bucket is null");
public async Task<UploadResult> UploadAsync(
Stream stream,
ICollection<string> path,
string filename,
CancellationToken cancellationToken = default
)
{
return await UploadAsync(
path,
filename,
() => new StreamContent(stream),
cancellationToken: cancellationToken
);
}
private async Task<UploadResult> UploadAsync(
IEnumerable<string> path,
string filename,
Func<HttpContent> setContent,
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, contentMd5, cancellationToken);
}
private async Task<UploadResult> UploadAsync(
ICollection<string> pathToFile,
Func<HttpContent> setContent,
string? contentMd5 = null,
CancellationToken cancellationToken = default
)
{
return await upyunUpload.UploadAsync(
pathToFile,
setContent,
upyunOperator,
contentMd5,
cancellationToken
);
}
public async Task<FileAddress?> UploadFileAsync(
CloudFile file,
CancellationToken cancellationToken = default
)
{
return await parallelChunkBreakpointUpload.UploadFileAsync(
file,
UploadAsync,
cancellationToken: cancellationToken
);
}
public async Task<UploadResult> UploadAsync(
byte[] bytes,
ICollection<string> path,
string filename,
CancellationToken cancellationToken = default
)
{
return await UploadAsync(
path,
filename,
() => new ByteArrayContent(bytes),
cancellationToken: cancellationToken
);
}
public async Task<Stream> DownloadAsync(
string pathToFile,
CancellationToken cancellationToken = default
)
{
var request = new HttpRequestMessage(HttpMethod.Get, $"/{Bucket}/{pathToFile}")
{
Version = new Version(2, 0),
};
await request.SignatureAsync(upyunOperator, 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;
}
public async Task SaveAsAsync(
string pathToFile,
string path,
CancellationToken cancellationToken = default
)
{
if (string.IsNullOrEmpty(pathToFile))
{
throw new ArgumentNullException(nameof(pathToFile));
}
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException(nameof(path));
}
var stream = await DownloadAsync(pathToFile, cancellationToken);
await using var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
await stream.CopyToAsync(fileStream, cancellationToken);
await fileStream.FlushAsync(cancellationToken);
}
public async Task DeleteAsync(string pathToFile, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(pathToFile))
{
throw new ArgumentNullException(nameof(pathToFile));
}
if (pathToFile.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
pathToFile = pathToFile.Replace(
upyunOperator.Host
?? throw new BusinessException(
"configuration upyun operator host value is null"
),
""
);
}
else
{
pathToFile = !pathToFile.StartsWith('/') ? $"/{pathToFile}" : pathToFile;
}
var request = new HttpRequestMessage(HttpMethod.Delete, $"/{Bucket}{pathToFile}")
{
Version = new Version(2, 0),
};
await request.SignatureAsync(upyunOperator, cancellationToken: cancellationToken);
var response = await httpClient.SendAsync(request, cancellationToken);
if (!response.IsSuccessStatusCode)
{
logger.LogError("delete fail,status code:{StatusCode}", response.StatusCode);
}
}
public async Task<IList<FolderResult>> GetFolderListAsync(
string path,
CancellationToken cancellationToken = default
)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
try
{
await using var client = new AsyncFtpClient(
FtpHost,
$"{upyunOperator.Operator}/{upyunOperator.Bucket}",
upyunOperator.Password,
logger: ftpLogger
);
await client.Connect(cancellationToken);
//await client.AutoConnectAsync();
var list = new List<FolderResult>();
foreach (var item in await client.GetListing(path, cancellationToken))
{
var result = new FolderResult { LastUpdateTime = item.Modified, Name = item.Name };
if (item.Type == FtpObjectType.Directory)
{
result.FileType = FileType.Directory;
}
else
{
result.FileType = FileType.File;
result.Size = await client.GetFileSize(item.FullName, token: cancellationToken);
}
list.Add(result);
}
await client.Disconnect(cancellationToken);
return list;
}
catch (Exception ex)
{
logger.LogError(ex, "get folder list fail");
return new List<FolderResult>();
}
}
public async Task<FileInformation> GetFileInformationAsync(
string pathToFile,
CancellationToken cancellationToken = default
)
{
if (string.IsNullOrEmpty(pathToFile))
{
throw new ArgumentNullException(nameof(pathToFile));
}
pathToFile = !pathToFile.StartsWith('/') ? $"/{pathToFile}" : pathToFile;
var request = new HttpRequestMessage(HttpMethod.Head, $"/{Bucket}{pathToFile}")
{
Version = new Version(2, 0),
};
await request.SignatureAsync(upyunOperator, cancellationToken: cancellationToken);
var response = await httpClient.SendAsync(request, cancellationToken);
if (!response.IsSuccessStatusCode)
{
logger.LogError("delete fail,status code:{StatusCode}", response.StatusCode);
throw new BusinessException($"delete fail,response status code:{response.StatusCode}");
}
// response.Headers.Contains("x-upyun-frames")
// ? response.Headers.GetValues("x-upyun-file-size").FirstOrDefault() ?? "0"
// : "0";
response.Headers.TryGetValues("x-upyun-file-size", out var size);
long.TryParse(size?.FirstOrDefault() ?? "0", out var length);
response.Headers.TryGetValues("x-upyun-file-date", out var date);
long.TryParse(date?.FirstOrDefault() ?? "0", out var timespan);
var dateTime = timespan.ToDateTime();
response.Headers.TryGetValues("Content-Md5", out var md5);
var information = new FileInformation
{
Length = length,
Md5 = md5?.FirstOrDefault() ?? "",
UploadTime = dateTime,
};
return information;
}
}
上述代码定义了一个名为 ObjectStorageService
的类,它实现了 IObjectStorageOperation
接口,主要用于与对象存储服务(如 Upyun)进行交互。该类提供了一系列方法来上传、下载、删除文件,以及获取文件信息和文件夹列表。以下是对代码功能的详细解释:
上传文件:
UploadAsync(Stream stream, ICollection<string> path, string filename)
:通过流上传文件。UploadAsync(byte[] bytes, ICollection<string> path, string filename)
:通过字节数组上传文件。UploadPictureAsync(byte[] bytes, PictureInformation information)
:上传图片并返回图片记录,包括上传者、时间、标签、描述等信息。下载文件:
DownloadAsync(string pathToFile)
:根据文件路径下载文件并返回文件流。SaveAsAsync(string pathToFile, string path)
:将下载的文件保存到指定路径。删除文件:
DeleteAsync(string pathToFile)
:根据文件路径删除文件。获取文件和文件夹信息:
GetFileInformationAsync(string pathToFile)
:获取指定文件的元数据,包括文件大小、MD5 值和上传时间。GetFolderListAsync(string path)
:获取指定路径下的文件夹和文件列表。计算 MD5 值:
CalculateMd5(byte[] bytes)
:计算给定字节数组的 MD5 值。获取图片格式:
GetImageFormatAsync(byte[] bytes)
:根据字节数组获取图片格式,使用 ImageSharp 库来解析图片。UpyunOperator
、HttpClient
、ILogger
等),用于初始化对象存储服务。FtpHost
。Bucket
属性,用于获取存储桶名称。ObjectStorageService
类是一个功能丰富的服务类,封装了与对象存储服务的交互逻辑,提供了上传、下载、删除文件以及获取文件和文件夹信息的功能。它使用了现代的异步编程模式,确保在处理 I/O 操作时不会阻塞线程。