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