mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-14 20:06:34 +08:00
实现了多画布下,节点的复制粘贴功能
This commit is contained in:
@@ -26,17 +26,21 @@ namespace Serein.Workbench.Services
|
||||
/// </summary>
|
||||
private readonly IFlowEnvironment flowEnvironment;
|
||||
private readonly IFlowEnvironmentEvent flowEnvironmentEvent;
|
||||
private readonly UIContextOperation uIContextOperation;
|
||||
|
||||
/// <summary>
|
||||
/// 转发流程运行环境各个事件的实现类
|
||||
/// </summary>
|
||||
/// <param name="flowEnvironment"></param>
|
||||
/// <param name="flowEnvironmentEvent"></param>
|
||||
/// <param name="uIContextOperation"></param>
|
||||
public FlowEEForwardingService(IFlowEnvironment flowEnvironment,
|
||||
IFlowEnvironmentEvent flowEnvironmentEvent)
|
||||
IFlowEnvironmentEvent flowEnvironmentEvent,
|
||||
UIContextOperation uIContextOperation)
|
||||
{
|
||||
this.flowEnvironment = flowEnvironment;
|
||||
this.flowEnvironmentEvent = flowEnvironmentEvent;
|
||||
this.uIContextOperation = uIContextOperation;
|
||||
InitFlowEnvironmentEvent();
|
||||
}
|
||||
|
||||
@@ -185,7 +189,10 @@ namespace Serein.Workbench.Services
|
||||
/// <param name="value"></param>
|
||||
private void FlowEnvironment_OnEnvOutEvent(InfoType type, string value)
|
||||
{
|
||||
//LogOutWindow.AppendText($"{DateTime.Now} [{type}] : {value}{Environment.NewLine}");
|
||||
uIContextOperation.Invoke(() =>
|
||||
{
|
||||
OnEnvOut?.Invoke(type, value);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Serein.Library;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Workbench.Api;
|
||||
using Serein.Workbench.Node;
|
||||
@@ -6,7 +8,10 @@ using Serein.Workbench.Node.View;
|
||||
using Serein.Workbench.Node.ViewModel;
|
||||
using Serein.Workbench.ViewModels;
|
||||
using Serein.Workbench.Views;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Serein.Workbench.Services
|
||||
{
|
||||
@@ -34,7 +39,6 @@ namespace Serein.Workbench.Services
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region 创建节点相关的属性
|
||||
/// <summary>
|
||||
/// 当前查看的画布
|
||||
@@ -51,7 +55,6 @@ namespace Serein.Workbench.Services
|
||||
/// </summary>
|
||||
public NodeControlType CurrentNodeControlType { get; set; } = NodeControlType.None;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 当前鼠标位置
|
||||
/// </summary>
|
||||
@@ -61,6 +64,12 @@ namespace Serein.Workbench.Services
|
||||
/// 当前选中的节点
|
||||
/// </summary>
|
||||
public NodeControlBase? CurrentSelectNodeControl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 连接数据
|
||||
/// </summary>
|
||||
public ConnectingData ConnectingData { get; } = new ConnectingData();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
@@ -72,6 +81,11 @@ namespace Serein.Workbench.Services
|
||||
/// </summary>
|
||||
public NodeControlBase? ConnectionEndNode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前所有画布
|
||||
/// </summary>
|
||||
public FlowCanvasView[] FlowCanvass => Canvass.Select(c => c.Value).ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// 记录流程画布
|
||||
/// </summary>
|
||||
@@ -134,7 +148,27 @@ namespace Serein.Workbench.Services
|
||||
flowEEForwardingService.OnNodeTakeOut += FlowEEForwardingService_OnNodeTakeOut; ; // 节点从容器中取出
|
||||
|
||||
flowEEForwardingService.OnNodeConnectChange += FlowEEForwardingService_OnNodeConnectChange; // 节点连接状态改变事件
|
||||
|
||||
|
||||
flowEEForwardingService.OnStartNodeChange += FlowEEForwardingService_OnStartNodeChange; // 画布起始节点改变
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void FlowEEForwardingService_OnStartNodeChange(StartNodeChangeEventArgs eventArgs)
|
||||
{
|
||||
string oldNodeGuid = eventArgs.OldNodeGuid;
|
||||
string newNodeGuid = eventArgs.NewNodeGuid;
|
||||
if (!TryGetControl(newNodeGuid, out var newStartNodeControl)) return;
|
||||
if (!string.IsNullOrEmpty(oldNodeGuid))
|
||||
{
|
||||
if (!TryGetControl(oldNodeGuid, out var oldStartNodeControl)) return;
|
||||
oldStartNodeControl.BorderBrush = Brushes.Black;
|
||||
oldStartNodeControl.BorderThickness = new Thickness(0);
|
||||
}
|
||||
|
||||
newStartNodeControl.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10"));
|
||||
newStartNodeControl.BorderThickness = new Thickness(2);
|
||||
var node = newStartNodeControl?.ViewModel?.NodeModel;
|
||||
}
|
||||
|
||||
private void FlowEEForwardingService_OnNodeConnectChange(NodeConnectChangeEventArgs e)
|
||||
@@ -387,7 +421,167 @@ namespace Serein.Workbench.Services
|
||||
return Canvass.TryGetValue(nodeGuid, out flowCanvas);
|
||||
}
|
||||
|
||||
#region 节点的复制与粘贴
|
||||
/// <summary>
|
||||
/// 从节点信息转换为Json文本数据
|
||||
/// </summary>
|
||||
public string CpoyNodeInfo(List<NodeModelBase> dictSelection)
|
||||
{
|
||||
|
||||
// 遍历当前已选节点
|
||||
foreach (var node in dictSelection.ToArray())
|
||||
{
|
||||
if (node.ChildrenNode.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// 遍历这些节点的子节点,添加过来
|
||||
foreach (var childNode in node.ChildrenNode)
|
||||
{
|
||||
dictSelection.Add(childNode);
|
||||
}
|
||||
}
|
||||
|
||||
var nodeInfos = dictSelection.Select(item => item.ToInfo());
|
||||
|
||||
JObject json = new JObject()
|
||||
{
|
||||
["nodes"] = JArray.FromObject(nodeInfos)
|
||||
};
|
||||
|
||||
var jsonText = json.ToString();
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"复制已选节点({dictSelection.Count}个)");
|
||||
return jsonText;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"复制失败:{ex.Message}");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从Json中加载节点
|
||||
/// </summary>
|
||||
/// <param name="canvasGuid">需要加载在哪个画布上</param>
|
||||
/// <param name="jsonText">文本内容</param>
|
||||
/// <param name="positionOfUI">需要加载的位置</param>
|
||||
public void PasteNodeInfo(string canvasGuid, string jsonText, PositionOfUI positionOfUI)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<NodeInfo>? nodes = JsonConvert.DeserializeObject<List<NodeInfo>>(jsonText);
|
||||
if (nodes is not null && nodes.Count != 0)
|
||||
{
|
||||
|
||||
}
|
||||
if (nodes is null || nodes.Count < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#region 节点去重
|
||||
Dictionary<string, string> guids = new Dictionary<string, string>(); // 记录 Guid
|
||||
|
||||
// 遍历当前节点
|
||||
foreach (var node in nodes.ToArray())
|
||||
{
|
||||
if (NodeControls.ContainsKey(node.Guid) && !guids.ContainsKey(node.Guid))
|
||||
{
|
||||
// 如果是没出现过、且在当前记录中重复的Guid,则记录并新增对应的映射。
|
||||
guids.TryAdd(node.Guid, Guid.NewGuid().ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
// 出现过的Guid,说明重复添加了。应该不会走到这。
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.ChildNodeGuids is null)
|
||||
{
|
||||
continue; // 跳过没有子节点的节点
|
||||
}
|
||||
|
||||
// 遍历这些节点的子节点,获得完整的已选节点信息
|
||||
foreach (var childNodeGuid in node.ChildNodeGuids)
|
||||
{
|
||||
if (NodeControls.ContainsKey(node.Guid) && !NodeControls.ContainsKey(node.Guid))
|
||||
{
|
||||
// 当前Guid并不重复,跳过替换
|
||||
continue;
|
||||
}
|
||||
if (!guids.ContainsKey(childNodeGuid))
|
||||
{
|
||||
// 如果是没出现过的Guid,则记录并新增对应的映射。
|
||||
guids.TryAdd(node.Guid, Guid.NewGuid().ToString());
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(childNodeGuid)
|
||||
&& NodeControls.TryGetValue(childNodeGuid, out var nodeControl))
|
||||
{
|
||||
|
||||
var newNodeInfo = nodeControl.ViewModel.NodeModel.ToInfo();
|
||||
nodes.Add(newNodeInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Guid去重
|
||||
StringBuilder sb = new StringBuilder(jsonText);
|
||||
foreach (var kv in guids)
|
||||
{
|
||||
sb.Replace(kv.Key, kv.Value);
|
||||
}
|
||||
string result = sb.ToString();
|
||||
|
||||
/*var replacer = new GuidReplacer();
|
||||
foreach (var kv in guids)
|
||||
{
|
||||
replacer.AddReplacement(kv.Key, kv.Value);
|
||||
}
|
||||
string result = replacer.Replace(jsonText);*/
|
||||
|
||||
|
||||
//SereinEnv.WriteLine(InfoType.ERROR, result);
|
||||
nodes = JsonConvert.DeserializeObject<List<NodeInfo>>(result);
|
||||
|
||||
if (nodes is null || nodes.Count < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endregion
|
||||
|
||||
// 获取第一个节点的原始位置
|
||||
var index0NodeX = nodes[0].Position.X;
|
||||
var index0NodeY = nodes[0].Position.Y;
|
||||
|
||||
// 计算所有节点相对于第一个节点的偏移量
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
node.CanvasGuid = canvasGuid; // 替换画布Guid
|
||||
|
||||
var offsetX = node.Position.X - index0NodeX;
|
||||
var offsetY = node.Position.Y - index0NodeY;
|
||||
|
||||
// 根据鼠标位置平移节点
|
||||
node.Position = new PositionOfUI(positionOfUI.X + offsetX, positionOfUI.Y + offsetY);
|
||||
}
|
||||
|
||||
_ = flowEnvironment.LoadNodeInfosAsync(nodes);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
//SereinEnv.WriteLine(InfoType.ERROR, $"粘贴节点时发生异常:{ex}");
|
||||
}
|
||||
// SereinEnv.WriteLine(InfoType.INFO, $"剪贴板文本内容: {clipboardText}");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 向运行环境发出请求
|
||||
|
||||
|
||||
@@ -6,19 +6,20 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using static System.Windows.Forms.AxHost;
|
||||
|
||||
namespace Serein.Workbench.Services
|
||||
{
|
||||
delegate void KeyDownEventHandler(Key key);
|
||||
delegate void KeyUpEventHandler(Key key);
|
||||
public delegate void KeyDownEventHandler(Key key);
|
||||
public delegate void KeyUpEventHandler(Key key);
|
||||
|
||||
/// <summary>
|
||||
/// 全局事件服务
|
||||
/// 全局按键事件服务
|
||||
/// </summary>
|
||||
internal interface IKeyEventService
|
||||
public interface IKeyEventService
|
||||
{
|
||||
event KeyDownEventHandler KeyDown;
|
||||
event KeyUpEventHandler KeyUp;
|
||||
event KeyDownEventHandler OnKeyDown;
|
||||
event KeyUpEventHandler OnKeyUp;
|
||||
|
||||
/// <summary>
|
||||
/// 获取某个按键状态
|
||||
@@ -26,27 +27,34 @@ namespace Serein.Workbench.Services
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
bool GetKeyState(Key key);
|
||||
|
||||
/// <summary>
|
||||
/// 设置某个按键的状态
|
||||
/// 按下了某个键
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="state"></param>
|
||||
void SetKeyState(Key key, bool statestate);
|
||||
void KeyDown(Key key);
|
||||
|
||||
/// <summary>
|
||||
/// 抬起了某个键
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
void KeyUp(Key key);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 管理按键状态
|
||||
/// </summary>
|
||||
internal class KeyEventService : IKeyEventService
|
||||
public class KeyEventService : IKeyEventService
|
||||
{
|
||||
/// <summary>
|
||||
/// 按键按下
|
||||
/// </summary>
|
||||
public event KeyDownEventHandler KeyDown;
|
||||
public event KeyDownEventHandler OnKeyDown;
|
||||
/// <summary>
|
||||
/// 按键松开
|
||||
/// </summary>
|
||||
public event KeyUpEventHandler KeyUp;
|
||||
public event KeyUpEventHandler OnKeyUp;
|
||||
|
||||
public KeyEventService()
|
||||
{
|
||||
@@ -62,18 +70,23 @@ namespace Serein.Workbench.Services
|
||||
{
|
||||
return KeysState[(int)key];
|
||||
}
|
||||
public void SetKeyState(Key key, bool state)
|
||||
|
||||
|
||||
|
||||
|
||||
public void KeyDown(Key key)
|
||||
{
|
||||
if (state)
|
||||
{
|
||||
KeyDown?.Invoke(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyUp?.Invoke(key);
|
||||
}
|
||||
//Debug.WriteLine($"按键事件:{key} - {state}");
|
||||
KeysState[(int)key] = state;
|
||||
KeysState[(int)key] = true;
|
||||
OnKeyDown?.Invoke(key);
|
||||
Debug.WriteLine($"按键按下事件:{key}");
|
||||
}
|
||||
|
||||
public void KeyUp(Key key)
|
||||
{
|
||||
KeysState[(int)key] = false;
|
||||
OnKeyUp?.Invoke(key);
|
||||
Debug.WriteLine($"按键抬起事件:{key}");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow;
|
||||
using Serein.NodeFlow.Env;
|
||||
using Serein.Workbench.Api;
|
||||
using Serein.Workbench.Avalonia.Api;
|
||||
using Serein.Workbench.Node;
|
||||
using Serein.Workbench.Node.View;
|
||||
using Serein.Workbench.Node.ViewModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
|
||||
|
||||
namespace Serein.Workbench.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 节点操作相关服务
|
||||
/// </summary>
|
||||
internal class NodeControlService
|
||||
{
|
||||
|
||||
public NodeControlService(IFlowEnvironment flowEnvironment,
|
||||
IFlowEEForwardingService feefService)
|
||||
{
|
||||
/* this.flowEnvironment = flowEnvironment;
|
||||
this.feefService = feefService;
|
||||
feefService.OnNodeCreate += FeefService_OnNodeCreate; // 订阅运行环境创建节点事件
|
||||
feefService.OnNodeConnectChange += FeefService_OnNodeConnectChange; // 订阅运行环境连接了节点事件
|
||||
// 手动加载项目
|
||||
_ = Task.Run(async delegate
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
var flowEnvironment = new FlowEnvironment();// App.GetService<IFlowEnvironment>();
|
||||
var filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\debug\net8.0\project.dnf";
|
||||
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
|
||||
var projectData = JsonConvert.DeserializeObject<SereinProjectData>(content);
|
||||
var projectDfilePath = System.IO.Path.GetDirectoryName(filePath)!;
|
||||
flowEnvironment.LoadProject(new FlowEnvInfo { Project = projectData }, projectDfilePath);
|
||||
}, CancellationToken.None);*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -71,9 +72,15 @@ namespace Serein.Workbench.Services
|
||||
private void InitEvents()
|
||||
{
|
||||
flowEEForwardingService.OnProjectSaving += SaveProjectToLocalFile;
|
||||
flowEEForwardingService.OnEnvOut += FlowEEForwardingService_OnEnvOut;
|
||||
}
|
||||
|
||||
|
||||
private void FlowEEForwardingService_OnEnvOut(InfoType type, string value)
|
||||
{
|
||||
LogWindow.Instance.AppendText($"{DateTime.Now} [{type}] : {value}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 预览了某个方法信息(待创建)
|
||||
@@ -103,6 +110,8 @@ namespace Serein.Workbench.Services
|
||||
private void SaveProjectToLocalFile(ProjectSavingEventArgs e)
|
||||
{
|
||||
var project = e.ProjectData;
|
||||
|
||||
#region 获取保存路径
|
||||
// 创建一个新的保存文件对话框
|
||||
SaveFileDialog saveFileDialog = new()
|
||||
{
|
||||
@@ -128,8 +137,10 @@ namespace Serein.Workbench.Services
|
||||
SereinEnv.WriteLine(InfoType.ERROR, "保存项目DLL时返回了意外的文件保存路径");
|
||||
return;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region 将Dll输出到指定路径
|
||||
Uri saveProjectFileUri = new Uri(savePath);
|
||||
SereinEnv.WriteLine(InfoType.INFO, "项目文件保存路径:" + savePath);
|
||||
for (int index = 0; index < project.Librarys.Length; index++)
|
||||
@@ -180,12 +191,104 @@ namespace Serein.Workbench.Services
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 输出项目保存文件
|
||||
JObject projectJsonData = JObject.FromObject(project);
|
||||
File.WriteAllText(savePath, projectJsonData.ToString());
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#region 抽取重复文件(例如net运行时)
|
||||
/*
|
||||
|
||||
1. 扫描目录并计算哈希
|
||||
string[] directories = new[] { "path1", "path2" };
|
||||
var fileHashMap = new Dictionary<string, List<string>>(); // hash -> List<full paths>
|
||||
|
||||
foreach (var dir in directories)
|
||||
{
|
||||
foreach (var file in Directory.EnumerateFiles(dir, "*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
using var stream = File.OpenRead(file);
|
||||
using var sha = SHA256.Create();
|
||||
var hash = Convert.ToHexString(sha.ComputeHash(stream));
|
||||
|
||||
if (!fileHashMap.ContainsKey(hash))
|
||||
fileHashMap[hash] = new List<string>();
|
||||
|
||||
fileHashMap[hash].Add(file);
|
||||
}
|
||||
}
|
||||
|
||||
2. 将重复文件压缩并保存
|
||||
string archiveDir = "compressed_output";
|
||||
Directory.CreateDirectory(archiveDir);
|
||||
|
||||
var manifest = new List<FileRecord>();
|
||||
|
||||
foreach (var kvp in fileHashMap.Where(kvp => kvp.Value.Count > 1))
|
||||
{
|
||||
var hash = kvp.Key;
|
||||
var originalFile = kvp.Value[0];
|
||||
var archivePath = Path.Combine(archiveDir, $"{hash}.gz");
|
||||
|
||||
using (var input = File.OpenRead(originalFile))
|
||||
using (var output = File.Create(archivePath))
|
||||
using (var gzip = new GZipStream(output, CompressionLevel.Optimal))
|
||||
{
|
||||
input.CopyTo(gzip);
|
||||
}
|
||||
|
||||
manifest.Add(new FileRecord
|
||||
{
|
||||
Hash = hash,
|
||||
ArchiveFile = $"{hash}.gz",
|
||||
OriginalPaths = kvp.Value
|
||||
});
|
||||
}
|
||||
|
||||
3. 生成清单文件(JSON)
|
||||
public class FileRecord
|
||||
{
|
||||
public string Hash { get; set; }
|
||||
public string ArchiveFile { get; set; }
|
||||
public List<string> OriginalPaths { get; set; }
|
||||
}
|
||||
|
||||
File.WriteAllText("manifest.json", JsonSerializer.Serialize(manifest, new JsonSerializerOptions { WriteIndented = true }));
|
||||
|
||||
|
||||
4. 根据清单还原原始文件结构
|
||||
var manifestJson = File.ReadAllText("manifest.json");
|
||||
var manifest = JsonSerializer.Deserialize<List<FileRecord>>(manifestJson);
|
||||
|
||||
foreach (var record in manifest)
|
||||
{
|
||||
var archivePath = Path.Combine("compressed_output", record.ArchiveFile);
|
||||
|
||||
foreach (var path in record.OriginalPaths)
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
||||
|
||||
using var input = File.OpenRead(archivePath);
|
||||
using var gzip = new GZipStream(input, CompressionMode.Decompress);
|
||||
using var output = File.Create(path);
|
||||
|
||||
gzip.CopyTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
#endregion
|
||||
|
||||
|
||||
Reference in New Issue
Block a user