26 changed files with 390 additions and 171 deletions
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,19 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.BlobStoring" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.OssManagement.HttpApi.Client\LINGYUN.Abp.OssManagement.HttpApi.Client.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,12 @@ |
|||||
|
using LINGYUN.Abp.OssManagement; |
||||
|
using Volo.Abp.BlobStoring; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BlobStoring.OssManagement |
||||
|
{ |
||||
|
[DependsOn(typeof(AbpBlobStoringModule))] |
||||
|
[DependsOn(typeof(AbpOssManagementHttpApiClientModule))] |
||||
|
public class AbpBlobStoringOssManagementModule : AbpModule |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.BlobStoring; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BlobStoring.OssManagement; |
||||
|
|
||||
|
public static class OssManagementBlobContainerConfigurationExtensions |
||||
|
{ |
||||
|
public static OssManagementBlobProviderConfiguration GetOssManagementConfiguration( |
||||
|
this BlobContainerConfiguration containerConfiguration) |
||||
|
{ |
||||
|
return new OssManagementBlobProviderConfiguration(containerConfiguration); |
||||
|
} |
||||
|
|
||||
|
public static BlobContainerConfiguration UseOssManagement( |
||||
|
this BlobContainerConfiguration containerConfiguration, |
||||
|
Action<OssManagementBlobProviderConfiguration> fileSystemConfigureAction) |
||||
|
{ |
||||
|
containerConfiguration.ProviderType = typeof(OssManagementBlobProvider); |
||||
|
containerConfiguration.NamingNormalizers.TryAdd<OssManagementBlobNamingNormalizer>(); |
||||
|
|
||||
|
fileSystemConfigureAction(new OssManagementBlobProviderConfiguration(containerConfiguration)); |
||||
|
|
||||
|
return containerConfiguration; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
using System; |
||||
|
using System.Text; |
||||
|
using System.Web; |
||||
|
using Volo.Abp.BlobStoring; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BlobStoring.OssManagement |
||||
|
{ |
||||
|
public class OssManagementBlobNamingNormalizer : IBlobNamingNormalizer, ITransientDependency |
||||
|
{ |
||||
|
public string NormalizeBlobName(string blobName) |
||||
|
{ |
||||
|
// 路径需要URL编码
|
||||
|
return HttpUtility.UrlEncode(blobName, Encoding.UTF8); |
||||
|
} |
||||
|
|
||||
|
public string NormalizeContainerName(string containerName) |
||||
|
{ |
||||
|
// 取消反斜杠开头
|
||||
|
return containerName.EnsureStartsWith('/'); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,92 @@ |
|||||
|
using LINGYUN.Abp.OssManagement; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using System; |
||||
|
using System.IO; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.BlobStoring; |
||||
|
using Volo.Abp.Content; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BlobStoring.OssManagement |
||||
|
{ |
||||
|
public class OssManagementBlobProvider : BlobProviderBase, ITransientDependency |
||||
|
{ |
||||
|
public ILogger<OssManagementBlobProvider> Logger { protected get; set; } |
||||
|
|
||||
|
private readonly IOssObjectAppService _ossObjectAppService; |
||||
|
public OssManagementBlobProvider( |
||||
|
IOssObjectAppService ossObjectAppService) |
||||
|
{ |
||||
|
_ossObjectAppService = ossObjectAppService; |
||||
|
|
||||
|
Logger = NullLogger<OssManagementBlobProvider>.Instance; |
||||
|
} |
||||
|
|
||||
|
public override async Task<bool> DeleteAsync(BlobProviderDeleteArgs args) |
||||
|
{ |
||||
|
var configuration = args.Configuration.GetOssManagementConfiguration(); |
||||
|
await _ossObjectAppService.DeleteAsync(new GetOssObjectInput |
||||
|
{ |
||||
|
Bucket = configuration.Bucket, |
||||
|
Object = args.BlobName |
||||
|
}); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public override async Task<bool> ExistsAsync(BlobProviderExistsArgs args) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var configuration = args.Configuration.GetOssManagementConfiguration(); |
||||
|
var oss = await _ossObjectAppService.GetAsync(new GetOssObjectInput |
||||
|
{ |
||||
|
Bucket = configuration.Bucket, |
||||
|
Object = args.BlobName |
||||
|
}); |
||||
|
return oss != null; |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
Logger.LogWarning("An error occurred while getting the OSS object, always returning that the object does not exist"); |
||||
|
Logger.LogWarning(ex.Message); |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override async Task<Stream> GetOrNullAsync(BlobProviderGetArgs args) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var configuration = args.Configuration.GetOssManagementConfiguration(); |
||||
|
var content = await _ossObjectAppService.GetContentAsync(new GetOssObjectInput |
||||
|
{ |
||||
|
Bucket = configuration.Bucket, |
||||
|
Object = args.BlobName |
||||
|
}); |
||||
|
|
||||
|
return content?.GetStream(); |
||||
|
} |
||||
|
catch(Exception ex) |
||||
|
{ |
||||
|
Logger.LogWarning("An error occurred while getting the OSS object and an empty data stream will be returned"); |
||||
|
Logger.LogWarning(ex.Message); |
||||
|
|
||||
|
return Stream.Null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override async Task SaveAsync(BlobProviderSaveArgs args) |
||||
|
{ |
||||
|
var configuration = args.Configuration.GetOssManagementConfiguration(); |
||||
|
await _ossObjectAppService.CreateAsync(new CreateOssObjectInput |
||||
|
{ |
||||
|
Bucket = configuration.Bucket, |
||||
|
Overwrite = true, |
||||
|
FileName = args.BlobName, |
||||
|
File = new RemoteStreamContent(args.BlobStream) |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.BlobStoring; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BlobStoring.OssManagement |
||||
|
{ |
||||
|
public class OssManagementBlobProviderConfiguration |
||||
|
{ |
||||
|
public string Bucket |
||||
|
{ |
||||
|
get => _containerConfiguration.GetConfiguration<string>(OssManagementBlobProviderConfigurationNames.Bucket); |
||||
|
set => _containerConfiguration.SetConfiguration(OssManagementBlobProviderConfigurationNames.Bucket, Check.NotNullOrWhiteSpace(value, nameof(value))); |
||||
|
} |
||||
|
|
||||
|
private readonly BlobContainerConfiguration _containerConfiguration; |
||||
|
|
||||
|
public OssManagementBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration) |
||||
|
{ |
||||
|
_containerConfiguration = containerConfiguration; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace LINGYUN.Abp.BlobStoring.OssManagement |
||||
|
{ |
||||
|
public class OssManagementBlobProviderConfigurationNames |
||||
|
{ |
||||
|
public const string Bucket = "OssManagement:Bucket"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -1,162 +0,0 @@ |
|||||
using Microsoft.Extensions.Options; |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Globalization; |
|
||||
using System.Linq; |
|
||||
using System.Net.Http; |
|
||||
using System.Net.Http.Headers; |
|
||||
using System.Reflection; |
|
||||
using System.Text; |
|
||||
using System.Text.Json; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
using Volo.Abp.Http.Client.DynamicProxying; |
|
||||
using Volo.Abp.Http.Modeling; |
|
||||
using Volo.Abp.MultiTenancy; |
|
||||
using Volo.Abp.Threading; |
|
||||
using Volo.Abp.Tracing; |
|
||||
|
|
||||
namespace LINGYUN.Abp.OssManagement |
|
||||
{ |
|
||||
public class LINGYUNApiDescriptionFinder : IApiDescriptionFinder, ITransientDependency |
|
||||
{ |
|
||||
public ICancellationTokenProvider CancellationTokenProvider { get; set; } |
|
||||
protected IApiDescriptionCache Cache { get; } |
|
||||
protected AbpCorrelationIdOptions AbpCorrelationIdOptions { get; } |
|
||||
protected ICorrelationIdProvider CorrelationIdProvider { get; } |
|
||||
protected ICurrentTenant CurrentTenant { get; } |
|
||||
|
|
||||
public LINGYUNApiDescriptionFinder( |
|
||||
IApiDescriptionCache cache, |
|
||||
IOptions<AbpCorrelationIdOptions> abpCorrelationIdOptions, |
|
||||
ICorrelationIdProvider correlationIdProvider, |
|
||||
ICurrentTenant currentTenant) |
|
||||
{ |
|
||||
Cache = cache; |
|
||||
AbpCorrelationIdOptions = abpCorrelationIdOptions.Value; |
|
||||
CorrelationIdProvider = correlationIdProvider; |
|
||||
CurrentTenant = currentTenant; |
|
||||
CancellationTokenProvider = NullCancellationTokenProvider.Instance; |
|
||||
} |
|
||||
|
|
||||
public async Task<ActionApiDescriptionModel> FindActionAsync(HttpClient client, string baseUrl, Type serviceType, MethodInfo method) |
|
||||
{ |
|
||||
var apiDescription = await GetApiDescriptionAsync(client, baseUrl); |
|
||||
|
|
||||
//TODO: Cache finding?
|
|
||||
|
|
||||
var methodParameters = method.GetParameters().ToArray(); |
|
||||
|
|
||||
foreach (var module in apiDescription.Modules.Values) |
|
||||
{ |
|
||||
foreach (var controller in module.Controllers.Values) |
|
||||
{ |
|
||||
if (!controller.Implements(serviceType)) |
|
||||
{ |
|
||||
continue; |
|
||||
} |
|
||||
|
|
||||
foreach (var action in controller.Actions.Values) |
|
||||
{ |
|
||||
if (action.Name == method.Name && action.ParametersOnMethod.Count == methodParameters.Length) |
|
||||
{ |
|
||||
var found = true; |
|
||||
|
|
||||
for (int i = 0; i < methodParameters.Length; i++) |
|
||||
{ |
|
||||
if (!TypeMatches(action.ParametersOnMethod[i], methodParameters[i])) |
|
||||
{ |
|
||||
found = false; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (found) |
|
||||
{ |
|
||||
return action; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
throw new AbpException($"Could not found remote action for method: {method} on the URL: {baseUrl}"); |
|
||||
} |
|
||||
|
|
||||
public virtual async Task<ApplicationApiDescriptionModel> GetApiDescriptionAsync(HttpClient client, string baseUrl) |
|
||||
{ |
|
||||
return await Cache.GetAsync(baseUrl, () => GetApiDescriptionFromServerAsync(client, baseUrl)); |
|
||||
} |
|
||||
|
|
||||
protected virtual async Task<ApplicationApiDescriptionModel> GetApiDescriptionFromServerAsync( |
|
||||
HttpClient client, |
|
||||
string baseUrl) |
|
||||
{ |
|
||||
var requestMessage = new HttpRequestMessage( |
|
||||
HttpMethod.Get, |
|
||||
baseUrl.EnsureEndsWith('/') + "api/abp/api-definition" |
|
||||
); |
|
||||
|
|
||||
AddHeaders(requestMessage); |
|
||||
|
|
||||
var response = await client.SendAsync( |
|
||||
requestMessage, |
|
||||
CancellationTokenProvider.Token |
|
||||
); |
|
||||
|
|
||||
if (!response.IsSuccessStatusCode) |
|
||||
{ |
|
||||
throw new AbpException("Remote service returns error! StatusCode = " + response.StatusCode); |
|
||||
} |
|
||||
|
|
||||
var content = await response.Content.ReadAsStringAsync(); |
|
||||
|
|
||||
var result = JsonSerializer.Deserialize<ApplicationApiDescriptionModel>(content, new JsonSerializerOptions |
|
||||
{ |
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase |
|
||||
}); |
|
||||
|
|
||||
return (ApplicationApiDescriptionModel)result; |
|
||||
} |
|
||||
|
|
||||
protected virtual void AddHeaders(HttpRequestMessage requestMessage) |
|
||||
{ |
|
||||
//CorrelationId
|
|
||||
requestMessage.Headers.Add(AbpCorrelationIdOptions.HttpHeaderName, CorrelationIdProvider.Get()); |
|
||||
|
|
||||
//TenantId
|
|
||||
if (CurrentTenant.Id.HasValue) |
|
||||
{ |
|
||||
//TODO: Use AbpAspNetCoreMultiTenancyOptions to get the key
|
|
||||
requestMessage.Headers.Add(TenantResolverConsts.DefaultTenantKey, CurrentTenant.Id.Value.ToString()); |
|
||||
} |
|
||||
|
|
||||
//Culture
|
|
||||
//TODO: Is that the way we want? Couldn't send the culture (not ui culture)
|
|
||||
var currentCulture = CultureInfo.CurrentUICulture.Name ?? CultureInfo.CurrentCulture.Name; |
|
||||
if (!currentCulture.IsNullOrEmpty()) |
|
||||
{ |
|
||||
requestMessage.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(currentCulture)); |
|
||||
} |
|
||||
|
|
||||
//X-Requested-With
|
|
||||
requestMessage.Headers.Add("X-Requested-With", "XMLHttpRequest"); |
|
||||
} |
|
||||
|
|
||||
protected virtual bool TypeMatches(MethodParameterApiDescriptionModel actionParameter, ParameterInfo methodParameter) |
|
||||
{ |
|
||||
return NormalizeTypeName(actionParameter.TypeAsString) == |
|
||||
NormalizeTypeName(methodParameter.ParameterType.GetFullNameWithAssemblyName()); |
|
||||
} |
|
||||
|
|
||||
protected virtual string NormalizeTypeName(string typeName) |
|
||||
{ |
|
||||
const string placeholder = "%COREFX%"; |
|
||||
const string netCoreLib = "System.Private.CoreLib"; |
|
||||
const string netFxLib = "mscorlib"; |
|
||||
|
|
||||
return typeName.Replace(netCoreLib, placeholder).Replace(netFxLib, placeholder); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,10 @@ |
|||||
|
using Volo.Abp.BlobStoring; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowCore.Components |
||||
|
{ |
||||
|
[BlobContainerName(Name)] |
||||
|
public class WorkflowContainer |
||||
|
{ |
||||
|
public const string Name = "workflow"; |
||||
|
} |
||||
|
} |
||||
@ -1 +1,2 @@ |
|||||
Modules |
Modules |
||||
|
file-blob-storing |
||||
Loading…
Reference in new issue