This commit is contained in:
2026-08-31 14:42:22 +08:00
72 changed files with 2479 additions and 168 deletions
@@ -16,6 +16,60 @@ namespace FileService.Infrastructure.Storage
public string ProviderCode => "Local";
/// <summary>
/// 单次直传:直接写入 LocalRootPath/{bucket}/{objectKey}。
/// bucket 传公开桶名即落到公开目录,可被静态托管直链访问。
/// </summary>
public async Task<StorageLocation> PutObjectAsync(PutObjectCommand command, CancellationToken token)
{
var fullPath = Path.Combine(providerOptions.LocalRootPath!, command.Bucket, command.ObjectKey);
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);
await using (var fs = new FileStream(fullPath, FileMode.Create))
{
await command.Content.CopyToAsync(fs, token);
await fs.FlushAsync(token);
}
return new StorageLocation(
storageProvider: ProviderCode,
bucket: command.Bucket,
objectKey: command.ObjectKey,
region: providerOptions.Region);
}
/// <summary>
/// 公开桶文件返回静态托管直链;私有文件返回 null。
/// </summary>
public string? GetPublicUrl(StorageLocation location)
{
if (string.IsNullOrEmpty(providerOptions.PublicBucket) ||
!string.Equals(location.Bucket, providerOptions.PublicBucket, StringComparison.OrdinalIgnoreCase))
{
return null;
}
var baseUrl = (providerOptions.PublicBaseUrl ?? providerOptions.LocalUploadApiBaseUrl ?? string.Empty)
.TrimEnd('/');
var key = location.ObjectKey.Replace('\\', '/').TrimStart('/');
return $"{baseUrl}/static/{key}";
}
/// <summary>
/// 打开本地文件读取流:LocalRootPath/{bucket}/{objectKey}。
/// </summary>
public Task<Stream> OpenReadAsync(StorageLocation location, CancellationToken token)
{
var fullPath = Path.Combine(providerOptions.LocalRootPath!, location.Bucket, location.ObjectKey);
if (!File.Exists(fullPath))
{
throw new FileNotFoundException(fullPath);
}
Stream stream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read);
return Task.FromResult(stream);
}
public async Task<CompleteUploadResult> CompleteUploadAsync(CompleteUploadCommand command, CancellationToken token)
{
var res = await MergeAsync(command.UploadSessionId, command.ObjectKey, command.Parts);