You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1220 lines
54 KiB
1220 lines
54 KiB
//------------------------------------------------------------------------------ |
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
|
// Github源代码仓库:https://github.com/RRQM |
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
|
// 交流QQ群:234762506 |
|
// 感谢您的下载和使用 |
|
//------------------------------------------------------------------------------ |
|
//------------------------------------------------------------------------------ |
|
using System; |
|
using System.Collections.Concurrent; |
|
using System.IO; |
|
using System.Threading; |
|
using System.Threading.Tasks; |
|
using TouchSocket.Core; |
|
using TouchSocket.Resources; |
|
|
|
namespace TouchSocket.Rpc.TouchRpc |
|
{ |
|
public partial class RpcActor |
|
{ |
|
private readonly ConcurrentDictionary<int, object> m_eventArgs; |
|
private string m_rootPath = string.Empty; |
|
|
|
/// <inheritdoc/> |
|
public string RootPath |
|
{ |
|
get => m_rootPath; |
|
set |
|
{ |
|
value ??= string.Empty; |
|
m_rootPath = value; |
|
} |
|
} |
|
|
|
#region Pull |
|
|
|
/// <inheritdoc/> |
|
public Result PullFile(FileOperator fileOperator) |
|
{ |
|
return this.PrivatePullFile(default, fileOperator); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public Result PullFile(string targetId, FileOperator fileOperator) |
|
{ |
|
if (IsService) |
|
{ |
|
if (this.TryFindRpcActor(targetId, out RpcActor rpcActor)) |
|
{ |
|
return rpcActor.PullFile(fileOperator); |
|
} |
|
return new Result(ResultCode.Error, TouchSocketStatus.ClientNotFind.GetDescription()); |
|
} |
|
|
|
return this.PrivatePullFile(targetId, fileOperator); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public Task<Result> PullFileAsync(string targetId, FileOperator fileOperator) |
|
{ |
|
return EasyTask.Run(() => |
|
{ |
|
return PullFile(targetId, fileOperator); |
|
}); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public Task<Result> PullFileAsync(FileOperator fileOperator) |
|
{ |
|
return EasyTask.Run(() => |
|
{ |
|
return PullFile(fileOperator); |
|
}); |
|
} |
|
|
|
private Result PreviewPullFile(string targetId, FileOperator fileOperator, WaitFileInfoPackage waitFileInfoPackage) |
|
{ |
|
TouchRpcFileInfo fileInfo = waitFileInfoPackage.FileInfo; |
|
fileOperator.SavePath = FileController.GetFullPath(m_rootPath, fileOperator.SavePath); |
|
TouchRpcFileStream stream; |
|
try |
|
{ |
|
FileController.TryReadTempInfo(fileOperator.SavePath, fileOperator.Flags, ref fileInfo); |
|
stream = TouchRpcFileStream.Create(fileOperator.SavePath, ref fileInfo, fileOperator.Flags.HasFlag(TransferFlags.BreakpointResume)); |
|
} |
|
catch (Exception ex) |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Error, ex.Message)); |
|
} |
|
|
|
WaitTransferPackage waitTransfer = new WaitTransferPackage() |
|
{ |
|
EventHashCode = waitFileInfoPackage.EventHashCode, |
|
Path = fileInfo.FullName, |
|
Position = fileInfo.Position, |
|
SourceId = this.ID, |
|
TargetId = targetId, |
|
Route = targetId.HasValue() |
|
}; |
|
|
|
WaitData<IWaitResult> waitData = WaitHandlePool.GetWaitData(waitTransfer); |
|
ByteBlock byteBlock = new ByteBlock(); |
|
Channel channel = default; |
|
try |
|
{ |
|
if (targetId == null) |
|
{ |
|
channel = CreateChannel(); |
|
} |
|
else |
|
{ |
|
channel = CreateChannel(targetId); |
|
} |
|
waitTransfer.ChannelID = channel.ID; |
|
waitTransfer.Package(byteBlock); |
|
Send(TouchRpcUtility.P_501_BeginPullFile_Request, byteBlock); |
|
|
|
if (fileOperator.Token.IsCancellationRequested) |
|
{ |
|
waitData.SetCancellationToken(fileOperator.Token); |
|
} |
|
|
|
switch (waitData.Wait(fileOperator.Timeout)) |
|
{ |
|
case WaitDataStatus.SetRunning: |
|
{ |
|
WaitTransferPackage waitTransferResult = (WaitTransferPackage)waitData.WaitResult; |
|
switch (waitTransferResult.Status.ToStatus()) |
|
{ |
|
case TouchSocketStatus.Success: |
|
{ |
|
fileOperator.SetFileCompletedLength(fileInfo.Position); |
|
fileOperator.SetLength(fileInfo.Length); |
|
channel.Timeout = fileOperator.Timeout; |
|
while (channel.MoveNext()) |
|
{ |
|
if (fileOperator.Token.IsCancellationRequested) |
|
{ |
|
channel.Cancel(); |
|
fileOperator.SetResult(new Result(ResultCode.Canceled)); |
|
break; |
|
} |
|
byte[] data = channel.GetCurrent(); |
|
if (data != null) |
|
{ |
|
stream.Write(data, 0, data.Length); |
|
fileInfo.Position = stream.FileWriter.Position; |
|
fileOperator.AddFlow(data.Length); |
|
} |
|
} |
|
|
|
if (channel.Status == ChannelStatus.Completed) |
|
{ |
|
stream.FinishStream(); |
|
return fileOperator.SetResult(new Result(ResultCode.Success)); |
|
} |
|
else |
|
{ |
|
return fileOperator.SetResult(new Result(channel.Status.ToResultCode())); |
|
} |
|
} |
|
case TouchSocketStatus.LoadStreamFail: |
|
return fileOperator.SetResult(new Result(ResultCode.Fail, TouchSocketStatus.LoadStreamFail.GetDescription(fileInfo.FullName))); |
|
|
|
default: |
|
return fileOperator.SetResult(new Result(ResultCode.Error, waitTransferResult.Status.ToStatus().GetDescription(waitTransferResult.Message))); |
|
} |
|
} |
|
case WaitDataStatus.Overtime: |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Overtime)); |
|
} |
|
case WaitDataStatus.Canceled: |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Canceled)); |
|
} |
|
case WaitDataStatus.Disposed: |
|
default: |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Error)); |
|
} |
|
} |
|
} |
|
catch (Exception ex) |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Error, ex.Message)); |
|
} |
|
finally |
|
{ |
|
WaitHandlePool.Destroy(waitData); |
|
stream.SafeDispose(); |
|
byteBlock.Dispose(); |
|
channel.SafeDispose(); |
|
} |
|
} |
|
|
|
private Result PrivatePullFile(string targetId, FileOperator fileOperator) |
|
{ |
|
if (fileOperator is null) |
|
{ |
|
return new Result(ResultCode.Error, TouchSocketStatus.ArgumentNull.GetDescription(nameof(fileOperator))); |
|
} |
|
|
|
WaitFileInfoPackage waitFileInfo = new WaitFileInfoPackage |
|
{ |
|
SourceId = this.ID, |
|
TargetId = targetId, |
|
Route = targetId.HasValue(), |
|
Metadata = fileOperator.Metadata, |
|
SavePath = this.FileController.GetFullPath(this.m_rootPath, fileOperator.SavePath), |
|
Flags = fileOperator.Flags, |
|
ResourcePath = fileOperator.ResourcePath |
|
}; |
|
|
|
WaitData<IWaitResult> waitData = WaitHandlePool.GetWaitData(waitFileInfo); |
|
|
|
ByteBlock byteBlock = new ByteBlock(); |
|
try |
|
{ |
|
waitFileInfo.Package(byteBlock); |
|
Send(TouchRpcUtility.P_500_PullFile_Request, byteBlock); |
|
|
|
if (fileOperator.Token.IsCancellationRequested) |
|
{ |
|
waitData.SetCancellationToken(fileOperator.Token); |
|
} |
|
|
|
waitData.Wait(fileOperator.Timeout); |
|
|
|
switch (waitData.Status) |
|
{ |
|
case WaitDataStatus.SetRunning: |
|
{ |
|
WaitFileInfoPackage waitFileResult = (WaitFileInfoPackage)waitData.WaitResult; |
|
switch (waitFileResult.Status.ToStatus()) |
|
{ |
|
case TouchSocketStatus.Success: |
|
return PreviewPullFile(targetId, fileOperator, waitFileResult); |
|
|
|
case TouchSocketStatus.FileNotExists: |
|
return fileOperator.SetResult(new Result(ResultCode.Error, TouchSocketStatus.FileNotExists.GetDescription(waitFileResult.ResourcePath))); |
|
|
|
case TouchSocketStatus.RemoteRefuse: |
|
default: |
|
return fileOperator.SetResult(new Result(ResultCode.Fail, waitFileResult.Status.ToStatus().GetDescription(waitFileResult.Message))); |
|
} |
|
} |
|
case WaitDataStatus.Overtime: |
|
{ |
|
return fileOperator.SetResult(Result.Overtime); |
|
} |
|
case WaitDataStatus.Canceled: |
|
{ |
|
return fileOperator.SetResult(Result.Canceled); |
|
} |
|
case WaitDataStatus.Disposed: |
|
default: |
|
{ |
|
return fileOperator.SetResult(Result.UnknownFail); |
|
} |
|
} |
|
} |
|
catch (Exception ex) |
|
{ |
|
return fileOperator.SetResult(new Result(ex)); |
|
} |
|
finally |
|
{ |
|
WaitHandlePool.Destroy(waitData); |
|
byteBlock.Dispose(); |
|
} |
|
} |
|
|
|
#endregion Pull |
|
|
|
#region Push |
|
|
|
/// <inheritdoc/> |
|
public Result PushFile(FileOperator fileOperator) |
|
{ |
|
return PrivatePushFile(default, fileOperator); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public Result PushFile(string targetId, FileOperator fileOperator) |
|
{ |
|
if (IsService) |
|
{ |
|
if (this.TryFindRpcActor(targetId, out RpcActor rpcActor)) |
|
{ |
|
return rpcActor.PushFile(fileOperator); |
|
} |
|
return new Result(ResultCode.Error, TouchSocketStatus.ClientNotFind.GetDescription()); |
|
} |
|
return this.PrivatePushFile(default, fileOperator); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public Task<Result> PushFileAsync(string targetId, FileOperator fileOperator) |
|
{ |
|
return EasyTask.Run(() => |
|
{ |
|
return PushFile(targetId, fileOperator); |
|
}); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public Task<Result> PushFileAsync(FileOperator fileOperator) |
|
{ |
|
return EasyTask.Run(() => |
|
{ |
|
return PushFile(fileOperator); |
|
}); |
|
} |
|
|
|
private Result PreviewPushFile(FileOperator fileOperator, WaitTransferPackage waitTransfer) |
|
{ |
|
try |
|
{ |
|
using FileStorageReader reader = FilePool.GetReader(waitTransfer.Path); |
|
fileOperator.SetLength(reader.FileStorage.FileInfo.Length); |
|
if (TrySubscribeChannel(waitTransfer.ChannelID, out Channel channel)) |
|
{ |
|
ByteBlock byteBlock = BytePool.Default.GetByteBlock(TouchRpcUtility.TransferPackage); |
|
try |
|
{ |
|
long position = waitTransfer.Position; |
|
reader.Position = position; |
|
fileOperator.SetFileCompletedLength(waitTransfer.Position); |
|
|
|
channel.Timeout = fileOperator.Timeout; |
|
while (true) |
|
{ |
|
if (fileOperator.Token.IsCancellationRequested) |
|
{ |
|
channel.Cancel("主动取消"); |
|
fileOperator.SetResult(new Result(ResultCode.Canceled)); |
|
break; |
|
} |
|
int r = reader.Read(byteBlock.Buffer, 0, (int)Math.Min(TouchRpcUtility.TransferPackage, fileOperator.MaxSpeed / 10.0)); |
|
if (r == 0) |
|
{ |
|
channel.Complete(); |
|
WaitPushFileAckPackage waitResult = null; |
|
if (SpinWait.SpinUntil(() => |
|
{ |
|
if (m_eventArgs.TryRemove(waitTransfer.EventHashCode, out object obj)) |
|
{ |
|
waitResult = (WaitPushFileAckPackage)obj; |
|
return true; |
|
} |
|
return false; |
|
}, fileOperator.Timeout)) |
|
{ |
|
if (waitResult.Status.ToStatus() == TouchSocketStatus.Success) |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Success)); |
|
} |
|
else |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Error, waitResult.Message)); |
|
} |
|
} |
|
else |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Overtime, "等待最后状态确认超时。")); |
|
} |
|
} |
|
else |
|
{ |
|
position += r; |
|
if (channel.TryWrite(byteBlock.Buffer, 0, r)) |
|
{ |
|
fileOperator.AddFlow(r); |
|
} |
|
else |
|
{ |
|
break; |
|
} |
|
} |
|
} |
|
if (channel.Status == ChannelStatus.Cancel && !string.IsNullOrEmpty(channel.LastOperationMes)) |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Canceled, channel.LastOperationMes)); |
|
} |
|
else |
|
{ |
|
return fileOperator.SetResult(new Result(channel.Status.ToResultCode())); |
|
} |
|
} |
|
catch (Exception ex) |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Error, ex.Message)); |
|
} |
|
finally |
|
{ |
|
byteBlock.Dispose(); |
|
} |
|
} |
|
else |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Error, TouchSocketStatus.SetChannelFail.GetDescription())); |
|
} |
|
} |
|
catch (Exception ex) |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Error, TouchSocketStatus.LoadStreamFail.GetDescription(waitTransfer.Path, ex.Message))); |
|
} |
|
} |
|
|
|
private Result PrivatePushFile(string targetId, FileOperator fileOperator) |
|
{ |
|
if (fileOperator is null) |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Error, TouchSocketStatus.ArgumentNull.GetDescription(nameof(fileOperator)))); |
|
} |
|
|
|
string fullPath = FileController.GetFullPath(m_rootPath, fileOperator.ResourcePath); |
|
|
|
if (!File.Exists(fullPath)) |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Error, TouchSocketStatus.FileNotExists.GetDescription(fullPath))); |
|
} |
|
|
|
TouchRpcFileInfo fileInfo = new TouchRpcFileInfo(); |
|
try |
|
{ |
|
FileController.GetFileInfo(fullPath, fileOperator.Flags.HasFlag(TransferFlags.MD5Verify), ref fileInfo); |
|
} |
|
catch (Exception ex) |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Error, ex.Message)); |
|
} |
|
|
|
WaitFileInfoPackage waitFileInfoPackage = new WaitFileInfoPackage() |
|
{ |
|
FileInfo = fileInfo, |
|
Metadata = fileOperator.Metadata, |
|
ResourcePath = this.FileController.GetFullPath(this.m_rootPath, fileOperator.ResourcePath), |
|
SavePath = fileOperator.SavePath, |
|
Flags = fileOperator.Flags, |
|
TargetId = targetId, |
|
SourceId = this.ID, |
|
Route = targetId.HasValue() |
|
}; |
|
WaitData<IWaitResult> waitData = WaitHandlePool.GetWaitData(waitFileInfoPackage); |
|
|
|
try |
|
{ |
|
SendPackage(TouchRpcUtility.P_502_PushFile_Request, waitFileInfoPackage); |
|
waitData.SetCancellationToken(fileOperator.Token); |
|
|
|
waitData.Wait(fileOperator.Timeout); |
|
|
|
switch (waitData.Status) |
|
{ |
|
case WaitDataStatus.SetRunning: |
|
{ |
|
WaitTransferPackage waitResult = (WaitTransferPackage)waitData.WaitResult; |
|
switch (waitResult.Status.ToStatus()) |
|
{ |
|
case TouchSocketStatus.Success: |
|
return PreviewPushFile(fileOperator, waitResult); |
|
|
|
case TouchSocketStatus.LoadStreamFail: |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Error, TouchSocketStatus.LoadStreamFail.GetDescription(waitResult.Path, waitResult.Message))); |
|
} |
|
case TouchSocketStatus.ClientNotFind: |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Error, TouchSocketStatus.ClientNotFind.GetDescription(targetId))); |
|
} |
|
case TouchSocketStatus.DirectoryNotExists: |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Error, TouchSocketStatus.DirectoryNotExists.GetDescription(waitResult.Path))); |
|
} |
|
case TouchSocketStatus.RemoteRefuse: |
|
default: |
|
return fileOperator.SetResult(new Result(ResultCode.Error, waitResult.Status.ToStatus().GetDescription(waitResult.Message))); |
|
} |
|
} |
|
case WaitDataStatus.Overtime: |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Overtime)); |
|
} |
|
case WaitDataStatus.Canceled: |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Canceled)); |
|
} |
|
case WaitDataStatus.Disposed: |
|
default: |
|
{ |
|
return fileOperator.SetResult(new Result(ResultCode.Error)); |
|
} |
|
} |
|
} |
|
catch (Exception ex) |
|
{ |
|
return fileOperator.SetResult(new Result(ex)); |
|
} |
|
finally |
|
{ |
|
WaitHandlePool.Destroy(waitData); |
|
} |
|
} |
|
|
|
#endregion Push |
|
|
|
#region 小文件传输 |
|
|
|
/// <summary> |
|
/// 允许传输的小文件的最大长度。默认1024*1024字节。 |
|
/// <para>注意,当调整该值时,应该和对端保持一致。</para> |
|
/// </summary> |
|
public int MaxSmallFileLength { get; set; } = 1024 * 1024; |
|
|
|
/// <inheritdoc/> |
|
public PullSmallFileResult PullSmallFile(string targetId, string path, Metadata metadata = null, int timeout = 5000, CancellationToken token = default) |
|
{ |
|
if (IsService) |
|
{ |
|
if (this.TryFindRpcActor(targetId, out RpcActor actor)) |
|
{ |
|
return actor.PullSmallFile(path, metadata, timeout, token); |
|
} |
|
return new PullSmallFileResult(ResultCode.Error, TouchSocketStatus.ClientNotFind.GetDescription()); |
|
} |
|
|
|
return PrivatePullSmallFile(targetId, path, metadata, timeout, token); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public PullSmallFileResult PullSmallFile(string path, Metadata metadata = null, int timeout = 5000, CancellationToken token = default) |
|
{ |
|
return PrivatePullSmallFile(default, path, metadata, timeout, token); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public Task<PullSmallFileResult> PullSmallFileAsync(string targetId, string path, Metadata metadata = null, int timeout = 5000, CancellationToken token = default) |
|
{ |
|
return Task.Run(() => |
|
{ |
|
return PullSmallFile(targetId, path, metadata, timeout, token); |
|
}); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public Task<PullSmallFileResult> PullSmallFileAsync(string path, Metadata metadata = null, int timeout = 5000, CancellationToken token = default) |
|
{ |
|
return Task.Run(() => |
|
{ |
|
return PullSmallFile(path, metadata, timeout, token); |
|
}); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public Result PushSmallFile(string targetId, string savePath, FileInfo fileInfo, Metadata metadata = null, int timeout = 5000, CancellationToken token = default) |
|
{ |
|
if (IsService) |
|
{ |
|
if (this.TryFindRpcActor(targetId, out RpcActor rpcActor)) |
|
{ |
|
return rpcActor.PushSmallFile(savePath, fileInfo, metadata, timeout, token); |
|
} |
|
return new Result(ResultCode.Error, TouchSocketStatus.ClientNotFind.GetDescription()); |
|
} |
|
return PrivatePushSmallFile(targetId, savePath, fileInfo, metadata, timeout, token); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public Result PushSmallFile(string savePath, FileInfo fileInfo, Metadata metadata = null, int timeout = 5000, CancellationToken token = default) |
|
{ |
|
return PrivatePushSmallFile(default, savePath, fileInfo, metadata, timeout, token); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public Task<Result> PushSmallFileAsync(string targetId, string savePath, FileInfo fileInfo, Metadata metadata = null, int timeout = 5000, CancellationToken token = default) |
|
{ |
|
return Task.Run(() => |
|
{ |
|
return PushSmallFile(targetId, savePath, fileInfo, metadata, timeout, token); |
|
}); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public Task<Result> PushSmallFileAsync(string savePath, FileInfo fileInfo, Metadata metadata = null, int timeout = 5000, CancellationToken token = default) |
|
{ |
|
return Task.Run(() => |
|
{ |
|
return PushSmallFile(savePath, fileInfo, metadata, timeout, token); |
|
}); |
|
} |
|
|
|
private PullSmallFileResult PrivatePullSmallFile(string targetId, string path, Metadata metadata = null, int timeout = 5000, CancellationToken token = default) |
|
{ |
|
WaitSmallFilePackage waitSmallFilePackage = new WaitSmallFilePackage() |
|
{ |
|
Path = path, |
|
SourceId = ID, |
|
TargetId = targetId, |
|
Route = targetId.HasValue(), |
|
Metadata = metadata |
|
}; |
|
|
|
WaitData<IWaitResult> waitData = WaitHandlePool.GetWaitData(waitSmallFilePackage); |
|
|
|
ByteBlock byteBlock = new ByteBlock(); |
|
try |
|
{ |
|
waitSmallFilePackage.Package(byteBlock); |
|
Send(TouchRpcUtility.P_517_PullSmallFile_Request, byteBlock); |
|
waitData.SetCancellationToken(token); |
|
|
|
waitData.Wait(timeout); |
|
|
|
switch (waitData.Status) |
|
{ |
|
case WaitDataStatus.SetRunning: |
|
{ |
|
WaitSmallFilePackage waitFile = (WaitSmallFilePackage)waitData.WaitResult; |
|
switch (waitFile.Status.ToStatus()) |
|
{ |
|
case TouchSocketStatus.Success: |
|
{ |
|
return new PullSmallFileResult(waitFile.Data); |
|
} |
|
case TouchSocketStatus.FileNotExists: |
|
{ |
|
return new PullSmallFileResult(ResultCode.Error, TouchSocketStatus.FileNotExists.GetDescription(waitFile.Path)); |
|
} |
|
case TouchSocketStatus.RemoteRefuse: |
|
case TouchSocketStatus.LengthErrorWhenRead: |
|
case TouchSocketStatus.FileLengthTooLong: |
|
case TouchSocketStatus.ClientNotFind: |
|
default: |
|
{ |
|
return new PullSmallFileResult(ResultCode.Error, TouchSocketStatus.RemoteRefuse.GetDescription(waitFile.Message)); |
|
} |
|
} |
|
} |
|
case WaitDataStatus.Overtime: |
|
{ |
|
return new PullSmallFileResult(ResultCode.Overtime); |
|
} |
|
case WaitDataStatus.Canceled: |
|
{ |
|
return new PullSmallFileResult(ResultCode.Canceled); |
|
} |
|
case WaitDataStatus.Disposed: |
|
default: |
|
{ |
|
return new PullSmallFileResult(ResultCode.Error, TouchSocketStatus.UnknownError.GetDescription()); |
|
} |
|
} |
|
} |
|
finally |
|
{ |
|
WaitHandlePool.Destroy(waitData); |
|
byteBlock.Dispose(); |
|
} |
|
} |
|
|
|
private Result PrivatePushSmallFile(string targetId, string savePath, FileInfo fileInfo, Metadata metadata = null, int timeout = 5000, CancellationToken token = default) |
|
{ |
|
if (!File.Exists(fileInfo.FullName)) |
|
{ |
|
return new Result(ResultCode.Error, TouchSocketStatus.FileNotExists.GetDescription(fileInfo.FullName)); |
|
} |
|
if (fileInfo.Length > MaxSmallFileLength) |
|
{ |
|
return new Result(ResultCode.Error, TouchSocketStatus.FileLengthTooLong.GetDescription()); |
|
} |
|
WaitSmallFilePackage waitSmallFilePackage = new WaitSmallFilePackage() |
|
{ |
|
Path = savePath, |
|
SourceId = ID, |
|
Metadata = metadata, |
|
Route = targetId.HasValue(), |
|
TargetId = targetId, |
|
FileInfo = new RemoteFileInfo(fileInfo) |
|
}; |
|
|
|
WaitData<IWaitResult> waitData = WaitHandlePool.GetWaitData(waitSmallFilePackage); |
|
|
|
ByteBlock byteBlock = new ByteBlock(); |
|
byte[] buffer = BytePool.Default.GetByteCore((int)fileInfo.Length); |
|
try |
|
{ |
|
int r = FileController.ReadAllBytes(fileInfo, buffer); |
|
if (r <= 0) |
|
{ |
|
return new Result(ResultCode.Error, TouchSocketStatus.LengthErrorWhenRead.GetDescription()); |
|
} |
|
waitSmallFilePackage.Data = buffer; |
|
waitSmallFilePackage.Len = r; |
|
|
|
waitSmallFilePackage.Package(byteBlock); |
|
Send(TouchRpcUtility.P_518_PushSmallFile_Request, byteBlock); |
|
waitData.SetCancellationToken(token); |
|
|
|
waitData.Wait(timeout); |
|
|
|
switch (waitData.Status) |
|
{ |
|
case WaitDataStatus.SetRunning: |
|
{ |
|
WaitSmallFilePackage waitFile = (WaitSmallFilePackage)waitData.WaitResult; |
|
switch (waitFile.Status.ToStatus()) |
|
{ |
|
case TouchSocketStatus.Success: |
|
{ |
|
return Result.Success; |
|
} |
|
case TouchSocketStatus.RemoteRefuse: |
|
{ |
|
return new Result(ResultCode.Error, TouchSocketStatus.RemoteRefuse.GetDescription(waitFile.Message)); |
|
} |
|
case TouchSocketStatus.ClientNotFind: |
|
{ |
|
return new Result(ResultCode.Error, TouchSocketStatus.ClientNotFind.GetDescription()); |
|
} |
|
default: |
|
return new Result(ResultCode.Exception, waitFile.Message); |
|
} |
|
} |
|
case WaitDataStatus.Overtime: |
|
{ |
|
return Result.Overtime; |
|
} |
|
case WaitDataStatus.Canceled: |
|
{ |
|
return Result.Canceled; |
|
} |
|
case WaitDataStatus.Disposed: |
|
default: |
|
{ |
|
return Result.UnknownFail; |
|
} |
|
} |
|
} |
|
finally |
|
{ |
|
WaitHandlePool.Destroy(waitData); |
|
byteBlock.Dispose(); |
|
BytePool.Default.Recycle(buffer); |
|
} |
|
} |
|
|
|
private void RequestPullSmallFile(object o) |
|
{ |
|
//2.不响应 |
|
//3.不允许 |
|
//4.不存在 |
|
//5.读取文件长度异常 |
|
|
|
byte[] buffer = BytePool.Default.GetByteCore(MaxSmallFileLength); |
|
try |
|
{ |
|
WaitSmallFilePackage waitSmallFilePackage = (WaitSmallFilePackage)o; |
|
Result resultThis = Result.Success; |
|
try |
|
{ |
|
FileOperationEventArgs args = new FileOperationEventArgs(TransferType.SmallPull, |
|
waitSmallFilePackage.Metadata, default) |
|
{ |
|
ResourcePath = waitSmallFilePackage.Path |
|
}; |
|
|
|
OnFileTransfering?.Invoke(this, args); |
|
|
|
string fullPath = FileController.GetFullPath(RootPath, args.ResourcePath); |
|
waitSmallFilePackage.Path = fullPath; |
|
if (args.IsPermitOperation) |
|
{ |
|
if (File.Exists(fullPath)) |
|
{ |
|
FileInfo fileInfo = new FileInfo(fullPath); |
|
if (fileInfo.Length > MaxSmallFileLength) |
|
{ |
|
waitSmallFilePackage.Status = TouchSocketStatus.FileLengthTooLong.ToValue(); |
|
resultThis = new Result(ResultCode.Error, TouchSocketStatus.FileLengthTooLong.GetDescription()); |
|
} |
|
else |
|
{ |
|
int r = FileController.ReadAllBytes(fileInfo, buffer); |
|
if (r > 0) |
|
{ |
|
waitSmallFilePackage.Data = buffer; |
|
waitSmallFilePackage.Len = r; |
|
waitSmallFilePackage.FileInfo = new RemoteFileInfo(fileInfo); |
|
waitSmallFilePackage.Status = TouchSocketStatus.Success.ToValue(); |
|
} |
|
else |
|
{ |
|
waitSmallFilePackage.Status = TouchSocketStatus.LengthErrorWhenRead.ToValue(); |
|
resultThis = new Result(ResultCode.Error, TouchSocketStatus.LengthErrorWhenRead.GetDescription()); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
waitSmallFilePackage.Status = TouchSocketStatus.FileNotExists.ToValue(); |
|
resultThis = new Result(ResultCode.Error, TouchSocketStatus.FileNotExists.GetDescription(fullPath)); |
|
} |
|
} |
|
else |
|
{ |
|
waitSmallFilePackage.Status = TouchSocketStatus.RemoteRefuse.ToValue(); |
|
waitSmallFilePackage.Message = args.Message; |
|
resultThis = new Result(ResultCode.Error, TouchSocketStatus.RemoteRefuse.GetDescription(args.Message)); |
|
} |
|
} |
|
catch (Exception ex) |
|
{ |
|
waitSmallFilePackage.Status = TouchSocketStatus.Exception.ToValue(); |
|
waitSmallFilePackage.Message = ex.Message; |
|
} |
|
|
|
using (ByteBlock byteBlock = new ByteBlock()) |
|
{ |
|
waitSmallFilePackage.SwitchId(); |
|
waitSmallFilePackage.Package(byteBlock); |
|
Send(TouchRpcUtility.P_1517_PullSmallFile_Response, byteBlock); |
|
} |
|
|
|
FileTransferStatusEventArgs resultArgs = new FileTransferStatusEventArgs( |
|
TransferType.SmallPull, waitSmallFilePackage.Metadata, waitSmallFilePackage.FileInfo, resultThis) |
|
{ |
|
ResourcePath = waitSmallFilePackage.Path |
|
}; |
|
OnFileTransfered?.Invoke(this, resultArgs); |
|
} |
|
catch |
|
{ |
|
} |
|
finally |
|
{ |
|
BytePool.Default.Recycle(buffer); |
|
} |
|
} |
|
|
|
private void RequestPushSmallFile(object o) |
|
{ |
|
//2.不响应 |
|
//3.不允许 |
|
|
|
try |
|
{ |
|
WaitSmallFilePackage waitSmallFilePackage = (WaitSmallFilePackage)o; |
|
Result resultThis = Result.Success; |
|
try |
|
{ |
|
FileOperationEventArgs args = new FileOperationEventArgs(TransferType.SmallPush, |
|
waitSmallFilePackage.Metadata, waitSmallFilePackage.FileInfo) |
|
{ |
|
SavePath = waitSmallFilePackage.Path |
|
}; |
|
|
|
OnFileTransfering?.Invoke(this, args); |
|
|
|
string fullPath = FileController.GetFullPath(RootPath, args.SavePath); |
|
waitSmallFilePackage.Path = fullPath; |
|
if (args.IsPermitOperation) |
|
{ |
|
FileInfo fileInfo = new FileInfo(fullPath); |
|
FileController.WriteAllBytes(fileInfo, waitSmallFilePackage.Data, 0, waitSmallFilePackage.Data.Length); |
|
waitSmallFilePackage.Status = TouchSocketStatus.Success.ToValue(); |
|
} |
|
else |
|
{ |
|
waitSmallFilePackage.Status = TouchSocketStatus.RemoteRefuse.ToValue(); |
|
waitSmallFilePackage.Message = args.Message; |
|
resultThis = new Result(ResultCode.Error, TouchSocketStatus.RemoteRefuse.GetDescription(args.Message)); |
|
} |
|
} |
|
catch (Exception ex) |
|
{ |
|
waitSmallFilePackage.Status = TouchSocketStatus.Exception.ToValue(); |
|
waitSmallFilePackage.Message = ex.Message; |
|
} |
|
waitSmallFilePackage.FileInfo = default; |
|
waitSmallFilePackage.Data = default; |
|
waitSmallFilePackage.SwitchId(); |
|
using (ByteBlock byteBlock = new ByteBlock()) |
|
{ |
|
waitSmallFilePackage.Package(byteBlock); |
|
Send(TouchRpcUtility.P_1518_PushSmallFile_Response, byteBlock); |
|
} |
|
|
|
FileTransferStatusEventArgs resultArgs = new FileTransferStatusEventArgs( |
|
TransferType.SmallPush, waitSmallFilePackage.Metadata, waitSmallFilePackage.FileInfo, resultThis) |
|
{ |
|
SavePath = waitSmallFilePackage.Path |
|
}; |
|
OnFileTransfered?.Invoke(this, resultArgs); |
|
} |
|
catch |
|
{ |
|
} |
|
} |
|
|
|
#endregion 小文件传输 |
|
|
|
private void BeginPullFile(object o) |
|
{ |
|
try |
|
{ |
|
WaitTransferPackage waitTransferPackage = (WaitTransferPackage)o; |
|
FileTransferStatusEventArgs e; |
|
if (m_eventArgs.TryRemove(waitTransferPackage.EventHashCode, out object obj) && obj is FileOperationEventArgs args) |
|
{ |
|
try |
|
{ |
|
using (FileStorageReader reader = FilePool.GetReader(waitTransferPackage.Path)) |
|
{ |
|
if (TrySubscribeChannel(waitTransferPackage.ChannelID, out Channel channel)) |
|
{ |
|
ByteBlock byteBlock = BytePool.Default.GetByteBlock(TouchRpcUtility.TransferPackage); |
|
FileOperator fileOperator = args.FileOperator; |
|
fileOperator.SetLength(reader.FileStorage.FileInfo.Length); |
|
try |
|
{ |
|
waitTransferPackage.Status = TouchSocketStatus.Success.ToValue(); |
|
waitTransferPackage.SwitchId(); |
|
SendPackage(TouchRpcUtility.P_1501_BeginPullFile_Response, waitTransferPackage); |
|
long position = waitTransferPackage.Position; |
|
reader.Position = position; |
|
fileOperator.SetFileCompletedLength(position); |
|
while (true) |
|
{ |
|
if (fileOperator.Token.IsCancellationRequested) |
|
{ |
|
channel.Cancel("主动取消"); |
|
fileOperator.SetResult(new Result(ResultCode.Canceled)); |
|
break; |
|
} |
|
int r = reader.Read(byteBlock.Buffer, 0, |
|
(int)Math.Min(TouchRpcUtility.TransferPackage, fileOperator.MaxSpeed / 10.0)); |
|
if (r == 0) |
|
{ |
|
channel.Complete(); |
|
break; |
|
} |
|
else |
|
{ |
|
position += r; |
|
if (channel.TryWrite(byteBlock.Buffer, 0, r)) |
|
{ |
|
fileOperator.AddFlow(r); |
|
} |
|
else |
|
{ |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
catch |
|
{ |
|
} |
|
finally |
|
{ |
|
byteBlock.Dispose(); |
|
|
|
e = new FileTransferStatusEventArgs( |
|
fileOperator.SetResult(new Result(channel.Status.ToResultCode())), args); |
|
OnFileTransfered?.Invoke(this, e); |
|
} |
|
|
|
return; |
|
} |
|
else |
|
{ |
|
waitTransferPackage.Status = TouchSocketStatus.SetChannelFail.ToValue(); |
|
e = new FileTransferStatusEventArgs(new Result(ResultCode.Error, StringResStore.GetDescription(TouchSocketStatus.SetChannelFail)), args); |
|
} |
|
} |
|
} |
|
catch (Exception ex) |
|
{ |
|
waitTransferPackage.Status = TouchSocketStatus.Exception.ToValue(); |
|
waitTransferPackage.Message = ex.Message; |
|
e = new FileTransferStatusEventArgs(new Result(ResultCode.Error, StringResStore.GetDescription(TouchSocketStatus.LoadStreamFail)), args); |
|
} |
|
} |
|
else |
|
{ |
|
e = new FileTransferStatusEventArgs(TransferType.Pull, default, default, new Result(ResultCode.Overtime, StringResStore.GetDescription(TouchSocketStatus.GetEventArgsFail))); |
|
waitTransferPackage.Status = TouchSocketStatus.GetEventArgsFail.ToValue(); |
|
} |
|
|
|
waitTransferPackage.SwitchId(); |
|
SendPackage(TouchRpcUtility.P_1501_BeginPullFile_Response, waitTransferPackage); |
|
|
|
OnFileTransfered?.Invoke(this, e); |
|
} |
|
catch |
|
{ |
|
} |
|
} |
|
|
|
private void RequestPullFile(object o) |
|
{ |
|
try |
|
{ |
|
WaitFileInfoPackage waitFileInfoPackage = (WaitFileInfoPackage)o; |
|
try |
|
{ |
|
FileOperator fileOperator = new FileOperator() |
|
{ |
|
Flags = waitFileInfoPackage.Flags, |
|
ResourcePath = waitFileInfoPackage.ResourcePath, |
|
SavePath = waitFileInfoPackage.SavePath, |
|
Metadata = waitFileInfoPackage.Metadata |
|
}; |
|
|
|
FileOperationEventArgs args = new FileOperationEventArgs(TransferType.Pull, |
|
fileOperator, null); |
|
|
|
OnFileTransfering?.Invoke(this, args); |
|
|
|
args.ResourcePath = FileController.GetFullPath(m_rootPath, args.ResourcePath); |
|
|
|
if (args.IsPermitOperation) |
|
{ |
|
if (File.Exists(args.ResourcePath)) |
|
{ |
|
//合法传输 |
|
TouchRpcFileInfo touchRpcFileInfo = new TouchRpcFileInfo(); |
|
FileController.GetFileInfo(args.ResourcePath, args.Flags.HasFlag(TransferFlags.MD5Verify), ref touchRpcFileInfo); |
|
waitFileInfoPackage.FileInfo = touchRpcFileInfo; |
|
waitFileInfoPackage.Status = TouchSocketStatus.Success.ToValue(); |
|
waitFileInfoPackage.EventHashCode = args.GetHashCode(); |
|
if (m_eventArgs.TryAdd(args.GetHashCode(), args)) |
|
{ |
|
EasyTask.DelayRun(1000 * 60, args, (a) => |
|
{ |
|
if (m_eventArgs.TryRemove(a.GetHashCode(), out object obj) && obj is FileOperationEventArgs eventArgs) |
|
{ |
|
FileTransferStatusEventArgs e = new FileTransferStatusEventArgs( |
|
new Result(ResultCode.Overtime, StringResStore.GetDescription(TouchSocketStatus.Overtime)), eventArgs); |
|
OnFileTransfered?.Invoke(this, e); |
|
} |
|
}); |
|
} |
|
else |
|
{ |
|
waitFileInfoPackage.Status = TouchSocketStatus.UnknownError.ToValue(); |
|
} |
|
} |
|
else |
|
{ |
|
waitFileInfoPackage.Status = TouchSocketStatus.FileNotExists.ToValue(); |
|
} |
|
} |
|
else |
|
{ |
|
waitFileInfoPackage.Message = args.Message; |
|
waitFileInfoPackage.Status = TouchSocketStatus.RemoteRefuse.ToValue(); |
|
} |
|
} |
|
catch (Exception ex) |
|
{ |
|
waitFileInfoPackage.Status = TouchSocketStatus.Exception.ToValue(); |
|
waitFileInfoPackage.Message = ex.Message; |
|
} |
|
waitFileInfoPackage.SwitchId(); |
|
this.SendPackage(TouchRpcUtility.P_1500_PullFile_Response, waitFileInfoPackage); |
|
} |
|
catch |
|
{ |
|
} |
|
} |
|
|
|
private void RequestPushFile(object o) |
|
{ |
|
try |
|
{ |
|
WaitFileInfoPackage waitFileInfoPackage = (WaitFileInfoPackage)o; |
|
TouchRpcFileInfo fileInfo = waitFileInfoPackage.FileInfo; |
|
FileOperator fileOperator = new FileOperator() |
|
{ |
|
ResourcePath = waitFileInfoPackage.ResourcePath, |
|
SavePath = waitFileInfoPackage.SavePath, |
|
Flags = waitFileInfoPackage.Flags, |
|
Metadata = waitFileInfoPackage.Metadata, |
|
}; |
|
WaitTransferPackage waitTransferPackage = new WaitTransferPackage() |
|
{ |
|
Sign = waitFileInfoPackage.Sign, |
|
TargetId = waitFileInfoPackage.SourceId, |
|
SourceId = waitFileInfoPackage.TargetId, |
|
Route = waitFileInfoPackage.Route, |
|
}; |
|
|
|
FileOperationEventArgs args = new FileOperationEventArgs(TransferType.Push, fileOperator, fileInfo); |
|
OnFileTransfering?.Invoke(this, args); |
|
|
|
waitTransferPackage.Path = args.ResourcePath; |
|
waitTransferPackage.EventHashCode = args.GetHashCode(); |
|
|
|
if (!args.IsPermitOperation) |
|
{ |
|
fileOperator.SetResult(new Result(ResultCode.Fail, TouchSocketStatus.RemoteRefuse.GetDescription(args.Message))); |
|
waitTransferPackage.Message = args.Message; |
|
waitTransferPackage.Status = TouchSocketStatus.RemoteRefuse.ToValue(); |
|
OnFileTransfered?.Invoke(this, new FileTransferStatusEventArgs(fileOperator.Result, args)); |
|
SendPackage(TouchRpcUtility.P_1502_PushFile_Response, waitTransferPackage); |
|
return; |
|
} |
|
|
|
string saveFullPath = FileController.GetFullPath(m_rootPath, args.SavePath); |
|
|
|
if (!Directory.Exists(Path.GetDirectoryName(saveFullPath))) |
|
{ |
|
fileOperator.SetResult(new Result(ResultCode.Fail, TouchSocketStatus.DirectoryNotExists.GetDescription(Path.GetDirectoryName(saveFullPath)))); |
|
waitTransferPackage.Message = args.Message; |
|
waitTransferPackage.Status = TouchSocketStatus.DirectoryNotExists.ToValue(); |
|
waitTransferPackage.Path = Path.GetDirectoryName(saveFullPath); |
|
OnFileTransfered?.Invoke(this, new FileTransferStatusEventArgs(fileOperator.Result, args)); |
|
SendPackage(TouchRpcUtility.P_1502_PushFile_Response, waitTransferPackage); |
|
return; |
|
} |
|
Channel channel = null; |
|
try |
|
{ |
|
FileController.TryReadTempInfo(saveFullPath, args.Flags, ref fileInfo); |
|
using (TouchRpcFileStream stream = TouchRpcFileStream.Create(saveFullPath, ref fileInfo, args.Flags.HasFlag(TransferFlags.MD5Verify))) |
|
{ |
|
if (waitFileInfoPackage.Route) |
|
{ |
|
channel = CreateChannel(waitTransferPackage.SourceId); |
|
} |
|
else |
|
{ |
|
channel = CreateChannel(); |
|
} |
|
|
|
waitTransferPackage.ChannelID = channel.ID; |
|
waitTransferPackage.Position = fileInfo.Position; |
|
waitTransferPackage.Status = TouchSocketStatus.Success.ToValue(); |
|
SendPackage(TouchRpcUtility.P_1502_PushFile_Response, waitTransferPackage); |
|
|
|
fileOperator.SetFileCompletedLength(fileInfo.Position); |
|
fileOperator.SetLength(fileInfo.Length); |
|
while (channel.MoveNext()) |
|
{ |
|
if (fileOperator.Token.IsCancellationRequested) |
|
{ |
|
channel.Cancel(); |
|
fileOperator.SetResult(new Result(ResultCode.Canceled)); |
|
break; |
|
} |
|
|
|
byte[] data = channel.GetCurrent(); |
|
if (data != null) |
|
{ |
|
stream.Write(data, 0, data.Length); |
|
fileInfo.Position = stream.FileWriter.Position; |
|
fileOperator.AddFlow(data.Length); |
|
} |
|
} |
|
|
|
if (channel.Status == ChannelStatus.Completed) |
|
{ |
|
stream.FinishStream(); |
|
fileOperator.SetResult(new Result(ResultCode.Success)); |
|
} |
|
else |
|
{ |
|
fileOperator.SetResult(new Result(channel.Status.ToResultCode())); |
|
} |
|
} |
|
} |
|
catch (Exception ex) |
|
{ |
|
fileOperator.SetResult(new Result(ResultCode.Error, ex.Message)); |
|
channel?.Cancel(ex.Message); |
|
} |
|
finally |
|
{ |
|
channel.SafeDispose(); |
|
} |
|
|
|
OnFileTransfered?.Invoke(this, new FileTransferStatusEventArgs(fileOperator.Result, args)); |
|
if (channel?.Status == ChannelStatus.Completed && fileOperator.Result.IsSuccess()) |
|
{ |
|
SendPackage(TouchRpcUtility.P_509_PushFileAck_Request, new WaitPushFileAckPackage() |
|
{ |
|
TargetId = waitFileInfoPackage.SourceId, |
|
SourceId = waitFileInfoPackage.TargetId, |
|
Route = waitFileInfoPackage.Route, |
|
Sign = args.GetHashCode(), |
|
Status = TouchSocketStatus.Success.ToValue(), |
|
Message = fileOperator.Result.Message |
|
}); |
|
} |
|
else |
|
{ |
|
SendPackage(TouchRpcUtility.P_509_PushFileAck_Request, new WaitPushFileAckPackage() |
|
{ |
|
TargetId = waitFileInfoPackage.SourceId, |
|
SourceId = waitFileInfoPackage.TargetId, |
|
Route = waitFileInfoPackage.Route, |
|
Sign = args.GetHashCode(), |
|
Status = TouchSocketStatus.Exception.ToValue(), |
|
Message = fileOperator.Result.Message |
|
}); |
|
} |
|
} |
|
catch |
|
{ |
|
} |
|
} |
|
} |
|
} |