Browse Source

feat: Add BlobStoring Activities Module

pull/1568/head
colin 3 weeks ago
parent
commit
298f957e2c
  1. 2
      aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Activities.BlobStoring/LINGYUN/Abp/Elsa/Activities/BlobStoring/ElsaBlobContainer.cs
  2. 3
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/FodyWeavers.xml
  3. 30
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/FodyWeavers.xsd
  4. 29
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN.Abp.ElsaNext.BlobStoring.csproj
  5. 38
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/AbpElsaNextBlobStoringModule.cs
  6. 39
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Activities/BlobActivity.cs
  7. 20
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Activities/DeleteBlob.cs
  8. 40
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Activities/ReadBlob.cs
  9. 99
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Activities/SaveBlob.cs
  10. 8
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/ElsaBlobContainer.cs
  11. 14
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Extensions/ModuleExtensions.cs
  12. 17
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Features/BlobStoringFeature.cs
  13. 12
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Localization/Resources/en.json
  14. 12
      aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Localization/Resources/zh-Hans.json

2
aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Activities.BlobStoring/LINGYUN/Abp/Elsa/Activities/BlobStoring/ElsaBlobContainer.cs

@ -2,7 +2,7 @@
namespace LINGYUN.Abp.Elsa.Activities.BlobStoring;
[BlobContainerName("elsa")]
[BlobContainerName("workflow")]
public class ElsaBlobContainer
{
}

3
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/FodyWeavers.xsd

@ -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>

29
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN.Abp.ElsaNext.BlobStoring.csproj

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\configureawait.props" />
<Import Project="..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<AssemblyName>LINGYUN.Abp.ElsaNext.BlobStoring</AssemblyName>
<PackageId>LINGYUN.Abp.ElsaNext.BlobStoring</PackageId>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<Content Remove="LINGYUN\Abp\ElsaNext\BlobStoring\Localization\**\*.json" />
<EmbeddedResource Include="LINGYUN\Abp\ElsaNext\BlobStoring\Localization\**\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.BlobStoring" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.ElsaNext\LINGYUN.Abp.ElsaNext.csproj" />
</ItemGroup>
</Project>

38
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/AbpElsaNextBlobStoringModule.cs

@ -0,0 +1,38 @@
using Elsa.Features.Services;
using LINGYUN.Abp.ElsaNext.BlobStoring.Extensions;
using LINGYUN.Abp.ElsaNext.Localization;
using Volo.Abp.BlobStoring;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.ElsaNext.BlobStoring;
[DependsOn(
typeof(AbpBlobStoringModule),
typeof(AbpElsaNextModule))]
public class AbpElsaNextBlobStoringModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<IModule>(elsa =>
{
elsa.UseAbpBlobStoring();
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpElsaNextBlobStoringModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<ElsaNextResource>()
.AddVirtualJson("/LINGYUN/Abp/ElsaNext/BlobStoring/Localization/Resources");
});
}
}

39
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Activities/BlobActivity.cs

@ -0,0 +1,39 @@
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Models;
using System.Threading.Tasks;
using Volo.Abp.BlobStoring;
namespace LINGYUN.Abp.ElsaNext.BlobStoring.Activities;
public abstract class BlobActivity : CodeActivity
{
[Input(Description = "Path of the blob.")]
public Input<string> Path { get; set; } = default!;
protected virtual IBlobContainer<ElsaBlobContainer> GetBlobContainer(ActivityExecutionContext context)
{
return context.GetRequiredService<IBlobContainer<ElsaBlobContainer>>();
}
protected async virtual ValueTask OnErrorCompletedAsync(ActivityCompletedContext context)
{
await context.TargetContext.CompleteActivityAsync();
}
}
public abstract class BlobActivity<TResult> : CodeActivity<TResult>
{
[Input(Description = "Path of the blob.")]
public Input<string> Path { get; set; } = default!;
protected virtual IBlobContainer<ElsaBlobContainer> GetBlobContainer(ActivityExecutionContext context)
{
return context.GetRequiredService<IBlobContainer<ElsaBlobContainer>>();
}
protected async virtual ValueTask OnErrorCompletedAsync(ActivityCompletedContext context)
{
await context.TargetContext.CompleteActivityAsync();
}
}

20
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Activities/DeleteBlob.cs

@ -0,0 +1,20 @@
using Elsa.Extensions;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
using System.Threading.Tasks;
namespace LINGYUN.Abp.ElsaNext.BlobStoring.Activities;
[Activity("Elsa", "BlobStoring", "Delete a blob.", Kind = ActivityKind.Task)]
public class DeleteBlob : BlobActivity<bool>
{
protected async override ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var cancellationToken = context.CancellationToken;
var path = Path.Get(context);
var blobContainer = GetBlobContainer(context);
var result = await blobContainer.DeleteAsync(path, cancellationToken);
Result.Set(context, result);
}
}

40
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Activities/ReadBlob.cs

@ -0,0 +1,40 @@
using Elsa.Extensions;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Threading.Tasks;
namespace LINGYUN.Abp.ElsaNext.BlobStoring.Activities;
[Activity("Elsa", "BlobStoring", "Open a blob.", Kind = ActivityKind.Task)]
public class ReadBlob : BlobActivity<Stream>
{
[Port] public IActivity? Error { get; set; }
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var cancellationToken = context.CancellationToken;
var path = Path.Get(context);
var blobContainer = GetBlobContainer(context);
var logger = context.GetRequiredService<ILogger<ReadBlob>>();
try
{
var data = await blobContainer.GetAsync(path, cancellationToken);
Result.Set(context, data);
await context.CompleteActivityAsync();
}
catch (Exception e)
{
logger.LogWarning(e, "Error while open a blob.");
context.AddExecutionLogEntry("Error", e.Message, payload: new
{
e.StackTrace
});
await context.ScheduleActivityAsync(Error, OnErrorCompletedAsync);
}
}
}

99
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Activities/SaveBlob.cs

@ -0,0 +1,99 @@
using Elsa.Extensions;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Collections;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.BlobStoring;
namespace LINGYUN.Abp.ElsaNext.BlobStoring.Activities;
[Activity("Elsa", "BlobStoring", "Save a blob.", Kind = ActivityKind.Task)]
public class SaveBlob : BlobActivity
{
[Input(Description = "Blob exists whether overwrite.")]
public Input<bool> Overwrite { get; set; } = null!;
[Input(Description = "The file data to save. This can be a stream, binary data, a string, a form file or a collection of files.")]
public Input<object> Data { get; set; } = null!;
[Port] public IActivity? Error { get; set; }
protected async override ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var cancellationToken = context.CancellationToken;
var data = await ResolveAsStreamAsync(Data.Get(context), cancellationToken);
var overwrite = Overwrite.GetOrDefault(context);
var path = Path.Get(context);
var blobContainer = GetBlobContainer(context);
var logger = context.GetRequiredService<ILogger<SaveBlob>>();
try
{
await blobContainer.SaveAsync(path, data, overwrite, cancellationToken);
await context.CompleteActivityAsync();
}
catch (Exception e)
{
logger.LogWarning(e, "Error while save a blob.");
context.AddExecutionLogEntry("Error", e.Message, payload: new
{
e.StackTrace
});
await context.ScheduleActivityAsync(Error, OnErrorCompletedAsync);
}
}
private async Task<Stream> ResolveAsStreamAsync(object data, CancellationToken cancellationToken)
{
if (data is Stream stream)
return stream;
if (data is byte[] bytes)
return new MemoryStream(bytes);
if (data is IFormFile formFile)
return formFile.OpenReadStream();
if (data is string stringData)
return new MemoryStream(Encoding.UTF8.GetBytes(stringData));
if (data is IEnumerable enumerable)
{
var files = enumerable.Cast<object>().ToList();
return files.Count == 1 ? await ResolveAsStreamAsync(files[0], cancellationToken) : await CreateZipArchiveAsync(files, cancellationToken);
}
throw new NotSupportedException($"The provided data type is not supported: {data.GetType().Name}");
}
private async Task<Stream> CreateZipArchiveAsync(IEnumerable files, CancellationToken cancellationToken = default)
{
var currentFileIndex = 0;
var zipStream = new MemoryStream();
var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Create, true);
foreach (var file in files)
{
var entryName = $"file-{currentFileIndex}.bin";
var entry = zipArchive.CreateEntry(entryName);
var fileStream = await ResolveAsStreamAsync(file, cancellationToken);
await using var entryStream = entry.Open();
await fileStream.CopyToAsync(entryStream, cancellationToken);
await entryStream.FlushAsync(cancellationToken);
entryStream.Close();
currentFileIndex++;
}
zipStream.Seek(0, SeekOrigin.Begin);
return zipStream;
}
}

8
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/ElsaBlobContainer.cs

@ -0,0 +1,8 @@
using Volo.Abp.BlobStoring;
namespace LINGYUN.Abp.ElsaNext.BlobStoring;
[BlobContainerName("workflow")]
public class ElsaBlobContainer
{
}

14
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Extensions/ModuleExtensions.cs

@ -0,0 +1,14 @@
using Elsa.Features.Services;
using LINGYUN.Abp.ElsaNext.BlobStoring.Features;
using System;
namespace LINGYUN.Abp.ElsaNext.BlobStoring.Extensions;
public static class ModuleExtensions
{
public static IModule UseAbpBlobStoring(this IModule configuration, Action<BlobStoringFeature>? configure = null)
{
configuration.Configure(configure);
return configuration;
}
}

17
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Features/BlobStoringFeature.cs

@ -0,0 +1,17 @@
using Elsa.Extensions;
using Elsa.Features.Abstractions;
using Elsa.Features.Services;
namespace LINGYUN.Abp.ElsaNext.BlobStoring.Features;
public class BlobStoringFeature : FeatureBase
{
public BlobStoringFeature(IModule module) : base(module)
{
}
public override void Configure()
{
Module.AddActivitiesFrom<BlobStoringFeature>();
}
}

12
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Localization/Resources/en.json

@ -0,0 +1,12 @@
{
"culture": "en",
"texts": {
"Path of the blob.": "Path of the blob.",
"Blob exists whether overwrite.": "Blob exists whether overwrite.",
"The file data to save. This can be a stream, binary data, a string, a form file or a collection of files.": "The file data to save. This can be a stream, binary data, a string, a form file or a collection of files.",
"BlobStoring": "Blob Storing",
"Delete Blob": "Delete a blob.",
"Read Blob.": "Read a blob.",
"Save Blob.": "Save a blob."
}
}

12
aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.BlobStoring/LINGYUN/Abp/ElsaNext/BlobStoring/Localization/Resources/zh-Hans.json

@ -0,0 +1,12 @@
{
"culture": "zh-Hans",
"texts": {
"Path of the blob.": "文件存储路径。",
"Blob exists whether overwrite.": "覆盖已有文件。",
"The file data to save. This can be a stream, binary data, a string, a form file or a collection of files.": "要保存的文件数据。可以是流、二进制数据、字符串、表单文件或一组文件。",
"BlobStoring": "对象存储",
"Delete Blob": "删除文件",
"Read Blob": "打开文件",
"Save Blob": "保存文件"
}
}
Loading…
Cancel
Save