添加项目文件。
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="16.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FileService.Domain\FileService.Domain.csproj" />
|
||||
<ProjectReference Include="..\IM.Commons\IM.Commons.csproj" />
|
||||
<ProjectReference Include="..\IM.InitCommon\IM.InitCommon.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,14 @@
|
||||
using FileService.Application.UploadFile;
|
||||
using IM.Commons;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace FileService.Application
|
||||
{
|
||||
public class ModueInit : IModuleInitializer
|
||||
{
|
||||
public void Initialize(IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<UploadFileService>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using FileService.Application.StorageContracts;
|
||||
|
||||
namespace FileService.Application.Ports
|
||||
{
|
||||
public interface IObjectStoragePort
|
||||
{
|
||||
string ProviderCode { get; }
|
||||
public Task<InitiateUploadResult> InitUploadAsync(InitiateUploadCommand command,CancellationToken token);
|
||||
public Task<PresignedUrl> GenerateUploadUrlAsync(GenerateUploadUrlCommand command, CancellationToken token);
|
||||
public Task<CompleteUploadResult> CompleteUploadAsync(CompleteUploadCommand command, CancellationToken token);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FileService.Application.Ports
|
||||
{
|
||||
public interface IObjectStorageRouter
|
||||
{
|
||||
IObjectStoragePort Route(string providerCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using FileService.Application.StorageContracts;
|
||||
|
||||
namespace FileService.Application.Ports
|
||||
{
|
||||
public interface IStorageRedisCache
|
||||
{
|
||||
Task SetAsync(UploadRuntimeCache upload);
|
||||
Task DeleteAsync(string sessionId);
|
||||
Task DeleteByTaskIdAsync(string taskId);
|
||||
Task<UploadRuntimeCache?> GetAsync(string sessionId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
|
||||
using FileService.Domain.ValueObjects;
|
||||
|
||||
namespace FileService.Application.StorageContracts
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="ProviderCode">储存提供商编号</param>
|
||||
/// <param name="Bucket">储存桶名称</param>
|
||||
/// <param name="ObjectKey">鉴权key</param>
|
||||
/// <param name="ContentType"></param>
|
||||
/// <param name="ContentLength"></param>
|
||||
/// <param name="Metadata"></param>
|
||||
public sealed record InitiateUploadCommand(
|
||||
string ProviderCode,
|
||||
string Bucket,
|
||||
string ObjectKey,
|
||||
string ContentType,
|
||||
long ContentLength,
|
||||
IReadOnlyDictionary<string, string>? Metadata = null);
|
||||
|
||||
public sealed record InitiateUploadResult(
|
||||
string UploadSessionId,
|
||||
StorageLocation Location);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="ProviderCode"></param>
|
||||
/// <param name="Bucket"></param>
|
||||
/// <param name="ObjectKey"></param>
|
||||
/// <param name="UploadSessionId"></param>
|
||||
/// <param name="PartNumber"></param>
|
||||
/// <param name="ExpiresIn"></param>
|
||||
public sealed record GenerateUploadUrlCommand(
|
||||
string ProviderCode,
|
||||
string Bucket,
|
||||
string ObjectKey,
|
||||
string UploadSessionId,
|
||||
int? PartNumber,
|
||||
TimeSpan ExpiresIn);
|
||||
|
||||
public sealed record PresignedUrl(
|
||||
string Url,
|
||||
IReadOnlyDictionary<string, string> Headers,
|
||||
DateTimeOffset ExpiresAt);
|
||||
|
||||
public sealed record CompleteUploadCommand(
|
||||
string ProviderCode,
|
||||
string Bucket,
|
||||
string Region,
|
||||
string ObjectKey,
|
||||
string UploadSessionId,
|
||||
IReadOnlyList<UploadPart> Parts);
|
||||
|
||||
public sealed record UploadPart(
|
||||
int PartNumber,
|
||||
string ETag,
|
||||
long? Size = null,
|
||||
string? Checksum = null);
|
||||
|
||||
public sealed record CompleteUploadResult(
|
||||
StorageLocation Location,
|
||||
string? ETag,
|
||||
long Size,
|
||||
string? Checksum = null,
|
||||
string? VersionId = null);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FileService.Application.StorageContracts
|
||||
{
|
||||
public sealed class UploadRuntimeCache
|
||||
{
|
||||
public string TaskId { get; init; } = default!;
|
||||
|
||||
public string ProviderCode { get; init; } = default!;
|
||||
|
||||
public string UploadSessionId { get; init; } = default!;
|
||||
|
||||
public string Bucket { get; init; } = default!;
|
||||
|
||||
public string Region { get; init; } = default!;
|
||||
|
||||
public string ObjectKey { get; init; } = default!;
|
||||
|
||||
public long FileSize { get; init; }
|
||||
|
||||
public int TotalPartCount { get; init; }
|
||||
|
||||
public long UploadedBytes { get; private set; }
|
||||
|
||||
public DateTimeOffset CreatedAt { get; init; }
|
||||
|
||||
public DateTimeOffset ExpireAt { get; init; }
|
||||
|
||||
public Dictionary<int, UploadPart> Parts { get; init; } = new();
|
||||
|
||||
public void AddOrUpdatePart(UploadPart part)
|
||||
{
|
||||
Parts[part.PartNumber] = part;
|
||||
|
||||
UploadedBytes = Parts.Sum(x => x.Value.Size ?? 0);
|
||||
}
|
||||
|
||||
public bool IsCompleted()
|
||||
{
|
||||
return Parts.Count == TotalPartCount;
|
||||
}
|
||||
|
||||
public string ToJson()
|
||||
{
|
||||
return JsonSerializer.Serialize(this);
|
||||
}
|
||||
|
||||
public static UploadRuntimeCache? FromJson(string json)
|
||||
{
|
||||
return JsonSerializer.Deserialize<UploadRuntimeCache>(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using FileService.Domain;
|
||||
using FileService.Domain.ValueObjects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FileService.Application.UploadFile
|
||||
{
|
||||
public class FileResponse
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid OwnerId { get; set; }
|
||||
public FileName FileName { get; set; }
|
||||
public long FileSize { get; set; }
|
||||
public ContentType ContentType { get; set; }
|
||||
public FileState State { get; set; }
|
||||
public StorageLocation StorageLocation { get; set; }
|
||||
public CheckSum CheckSum { get; set; }
|
||||
public DateTimeOffset Created { get; set; }
|
||||
public DateTimeOffset Updated { get; set;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using AutoMapper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FileService.Application.UploadFile
|
||||
{
|
||||
public class UploadFileMapperConfig:Profile
|
||||
{
|
||||
public UploadFileMapperConfig()
|
||||
{
|
||||
CreateMap<Domain.Entities.UploadFile, FileResponse>()
|
||||
.ForMember(dest => dest.Created, opt => opt.MapFrom(src => src.CreationTime))
|
||||
.ForMember(dest => dest.Updated, opt => opt.MapFrom(src => src.ModificationTime))
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using AutoMapper;
|
||||
using FileService.Domain.IReposities;
|
||||
using IM.Commons;
|
||||
|
||||
namespace FileService.Application.UploadFile
|
||||
{
|
||||
public class UploadFileService
|
||||
{
|
||||
private readonly IUploadFileReposity reposity;
|
||||
private readonly IMapper mapper;
|
||||
|
||||
public UploadFileService(IUploadFileReposity reposity, IMapper mapper)
|
||||
{
|
||||
this.reposity = reposity;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<Result<FileResponse>> GetFileInfoAsync(Guid id)
|
||||
{
|
||||
var file = await reposity.FindByIdAsync(id);
|
||||
if (file == null)
|
||||
{
|
||||
return Result.Fail<FileResponse>(ResultCode.FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
return Result.Success(mapper.Map<FileResponse>(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using FileService.Domain.ValueObjects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FileService.Application.UploadFileTask
|
||||
{
|
||||
public class TaskInitResponse
|
||||
{
|
||||
public Guid TaskId { get; set; }
|
||||
public string UploadSessionId { get; init; }
|
||||
|
||||
public StorageLocation StorageLocation { get; init; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using AutoMapper;
|
||||
using FileService.Application.Ports;
|
||||
using FileService.Domain.Entities;
|
||||
using FileService.Domain.IReposities;
|
||||
using IM.Commons;
|
||||
using IM.InitCommon;
|
||||
using MassTransit.Internals;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FileService.Application.UploadFileTask
|
||||
{
|
||||
public class UploadFileTaskService
|
||||
{
|
||||
private readonly IUploadTaskReposity reposity;
|
||||
private readonly IMapper mapper;
|
||||
private readonly IObjectStorageRouter router;
|
||||
private readonly IOptions<StorageOptions> options;
|
||||
|
||||
public UploadFileTaskService(IUploadTaskReposity reposity, IMapper mapper, IObjectStorageRouter router, IOptions<StorageOptions> options)
|
||||
{
|
||||
this.reposity = reposity;
|
||||
this.mapper = mapper;
|
||||
this.router = router;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public async Task<Result<TaskInitResponse>> InitTaskAsync(UploadTaskInitCommand command)
|
||||
{
|
||||
CancellationToken cancellationToken = CancellationToken.None;
|
||||
var task = command.ToUploadTask();
|
||||
var storage = router.Route(options.Value.ProviderCode);
|
||||
var initRes = await storage.InitUploadAsync(new StorageContracts.InitiateUploadCommand(
|
||||
ProviderCode: options.Value.ProviderCode,
|
||||
Bucket: options.Value.Bucket,
|
||||
ObjectKey: options.Value.Endpoint,
|
||||
ContentType:task.ContentType.Value,
|
||||
ContentLength: command.FileSize,
|
||||
null
|
||||
), cancellationToken);
|
||||
|
||||
var res = mapper.Map<TaskInitResponse>(initRes);
|
||||
res.TaskId = task.Id;
|
||||
|
||||
return Result.Success(res);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FileService.Application.UploadFileTask
|
||||
{
|
||||
public record UploadTaskInitCommand(
|
||||
Guid UploaderId,
|
||||
Guid ConversationId, string FileName,
|
||||
long FileSize,string contentType,
|
||||
string checkSum
|
||||
)
|
||||
{
|
||||
public Domain.Entities.UploadTask ToUploadTask()
|
||||
{
|
||||
return new Domain.Entities.UploadTask(
|
||||
UploaderId,
|
||||
ConversationId, FileName, FileSize, contentType,
|
||||
null,new Domain.ValueObjects.CheckSum("md5", checkSum)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using AutoMapper;
|
||||
using FileService.Application.StorageContracts;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FileService.Application.UploadFileTask
|
||||
{
|
||||
public class UploadTaskMapperConfig:Profile
|
||||
{
|
||||
public UploadTaskMapperConfig()
|
||||
{
|
||||
CreateMap<InitiateUploadResult, TaskInitResponse>()
|
||||
.ForMember(dest => dest.StorageLocation, opt => opt.MapFrom(src => src.Location))
|
||||
.ForMember(dest => dest.UploadSessionId, opt => opt.MapFrom(src => src.UploadSessionId))
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user