网站首页 网站源码
using System.Security.Cryptography;
using Dpz.Core.Infrastructure;
using Dpz.Core.Infrastructure.PublicStruct;
using Dpz.Core.Public.ViewModel.V4;
using Dpz.Core.Shard.Service;
using FluentFTP;
using Microsoft.Extensions.Logging;
using SixLabors.ImageSharp;
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)
{
return await UploadAsync(path, filename, () => new StreamContent(stream));
}
private async Task<UploadResult> UploadAsync(
IEnumerable<string> path,
string filename,
Func<HttpContent> setContent,
string? contentMd5 = null)
{
if (string.IsNullOrEmpty(filename))
throw new ArgumentNullException(nameof(filename));
var pathList = path.ToList();
pathList.Add(filename);
return await UploadAsync(pathList, setContent, contentMd5);
}
private async Task<UploadResult> UploadAsync(
ICollection<string> pathToFile,
Func<HttpContent> setContent,
string? contentMd5 = null)
{
return await upyunUpload.UploadAsync(
pathToFile,
setContent,
upyunOperator,
contentMd5);
}
public async Task<FileAddress?> UploadFileAsync(CloudFile file)
{
return await parallelChunkBreakpointUpload.UploadFileAsync(file, UploadAsync);
}
public async Task<UploadResult> UploadAsync(byte[] bytes, ICollection<string> path, string filename)
{
return await UploadAsync(path, filename, () => new ByteArrayContent(bytes));
}
private string CalculateMd5(byte[] bytes)
{
using var md5 = MD5.Create();
var hash = md5.ComputeHash(bytes);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
private async Task<string> GetImageFormatAsync(byte[] bytes)
{
try
{
using var stream = new MemoryStream(bytes);
using var image = await Image.LoadAsync(stream);
return image.Metadata.DecodedImageFormat?.FileExtensions.FirstOrDefault() ?? "jpg";
}
catch (Exception e)
{
logger.LogError(e, "get image format fail");
return "jpg";
}
}
public async Task<VmPictureRecord> UploadPictureAsync(byte[] bytes, PictureInformation information)
{
if (bytes == null)
throw new ArgumentNullException(nameof(bytes));
if (information == null)
throw new ArgumentNullException(nameof(information));
var pictureRecord = new VmPictureRecord
{
Creator = information.Uploader,
UploadTime = DateTime.Now,
Tags = information.Tags.ToList(),
Description = information.Description,
Category = information.Category
};
var filename = $"{information.Name ?? Guid.NewGuid().ToString("N")}.{await GetImageFormatAsync(bytes)}";
var uploadResult = await UploadAsync(new[] { "images", "album", DateTime.Now.ToString("yyyy-MM-dd") }, filename,
() => new ByteArrayContent(bytes));
pictureRecord.AccessUrl = uploadResult.AccessUrl;
pictureRecord.Length = bytes.Length;
pictureRecord.ObjectStorageUploadTime = DateTime.Now;
pictureRecord.Md5 = CalculateMd5(bytes);
pictureRecord.Width = uploadResult.Width;
pictureRecord.Height = uploadResult.Height;
return pictureRecord;
}
public async Task<Stream> DownloadAsync(string pathToFile)
{
var request = new HttpRequestMessage(HttpMethod.Get, $"/{Bucket}/{pathToFile}")
{
Version = new Version(2, 0)
};
await request.SignatureAsync(upyunOperator);
var response = await httpClient.SendAsync(request);
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();
return stream;
}
public async Task SaveAsAsync(string pathToFile, string path)
{
if (string.IsNullOrEmpty(pathToFile))
throw new ArgumentNullException(nameof(pathToFile));
if (string.IsNullOrEmpty(path))
throw new ArgumentNullException(nameof(path));
var stream = await DownloadAsync(pathToFile);
await using var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
await stream.CopyToAsync(fileStream);
await fileStream.FlushAsync();
}
public async Task DeleteAsync(string pathToFile)
{
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);
var response = await httpClient.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
logger.LogError("delete fail,status code:{StatusCode}", response.StatusCode);
}
}
public async Task<IList<FolderResult>> GetFolderListAsync(string path)
{
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();
//await client.AutoConnectAsync();
var list = new List<FolderResult>();
foreach (var item in await client.GetListing(path))
{
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);
}
list.Add(result);
}
await client.Disconnect();
return list;
}
catch (Exception ex)
{
logger.LogError(ex, "get folder list fail");
return new List<FolderResult>();
}
}
public async Task<FileInformation> GetFileInformationAsync(string pathToFile)
{
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);
var response = await httpClient.SendAsync(request);
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 操作时不会阻塞线程。