1 Star 0 Fork 8

zilin_zhang/fnsync

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
FileTransmissionBase.cs 23.11 KB
一键复制 编辑 原始数据 按行查看 历史
holmium 提交于 2021-04-20 21:25 . Minor Improvements
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace FnSync
{
namespace FileTransmission
{
public enum ListModeClass
{
DEEP,
PLAIN_WITHOUT_FOLDER_LENGTH,
PLAIN_WITH_FOLDER_LENGTH,
}
public enum OperationClass
{
CUT,
COPY
}
public enum DirectionClass
{
INSIDE_PHONE,
PHONE_TO_PC,
PC_TO_PHONE,
}
public class BaseEntry
{
private static async Task<IList<ControlFolderListItemViewBase.UiItem>> ListDirFiles(PhoneClient Client, string FullPath)
{
JObject List = await PhoneMessageCenter.Singleton.OneShotMsgPart(
Client,
new JObject()
{
["path"] = FullPath,
["recursive"] = true
},
ControlFolderListPhoneRootItem.MSG_TYPE_LIST_FOLDER,
ControlFolderListPhoneRootItem.MSG_TYPE_FOLDER_CONTENT,
60000
);
JArray ListPart = (JArray)List["files"];
List<ControlFolderListItemViewBase.UiItem> Files = ListPart.ToObject<List<ControlFolderListItemViewBase.UiItem>>();
return Files;
}
public static async Task<T[]> ConvertFromUiItems<T>(IEnumerable<ControlFolderListItemViewBase.UiItem> UiItems, String RootOnPhone, PhoneClient Client, ListModeClass ListMode = ListModeClass.DEEP) where T : BaseEntry, new()
{
List<T> ResultEntries = new List<T>();
foreach (ControlFolderListItemViewBase.UiItem item in UiItems)
{
if (item.type == "dir")
{
string FolderPathOnPhone = item.path.AppendIfNotEnding("/");
if (ListMode == ListModeClass.DEEP)
{
ResultEntries.Add(new T()
{
key = null,
length = 0,
mime = null,
name = item.name,
path = FolderPathOnPhone,
last = item.last
});
IList<ControlFolderListItemViewBase.UiItem> Files = await ListDirFiles(Client, RootOnPhone + item.path);
foreach (ControlFolderListItemViewBase.UiItem i in Files)
{
if (i.type == "dir")
{
ResultEntries.Add(new T()
{
key = null,
length = 0,
mime = null,
name = i.name,
path = FolderPathOnPhone + i.path.AppendIfNotEnding("/"),
last = item.last
});
}
else if (i.type == "file")
{
ResultEntries.Add(new T()
{
key = null,
length = i.size,
mime = null,
name = i.name,
path = FolderPathOnPhone + i.path,
last = item.last
});
}
}
}
else if (ListMode == ListModeClass.PLAIN_WITH_FOLDER_LENGTH)
{
IList<ControlFolderListItemViewBase.UiItem> Files = await ListDirFiles(Client, RootOnPhone + item.path);
long DirLength = 0;
foreach (ControlFolderListItemViewBase.UiItem i in Files)
{
DirLength += i.size;
}
ResultEntries.Add(new T()
{
key = null,
length = DirLength,
mime = null,
name = item.name,
path = FolderPathOnPhone,
last = item.last
});
}
else if (ListMode == ListModeClass.PLAIN_WITHOUT_FOLDER_LENGTH)
{
ResultEntries.Add(new T()
{
key = null,
length = 0,
mime = null,
name = item.name,
path = FolderPathOnPhone,
last = item.last
});
}
}
else if (item.type == "file")
{
ResultEntries.Add(new T()
{
key = null,
length = item.size,
mime = null,
name = item.name,
path = item.path,
last = item.last
});
}
}
return ResultEntries.ToArray();
}
public static long SizeOfAllFiles(IEnumerable<BaseEntry> entries)
{
long ret = 0;
foreach (BaseEntry entry in entries)
{
ret += entry.length;
}
return ret;
}
////////////////////////////////////////////////////////
public virtual string mime { get; set; }
public virtual string name { get; set; }
public virtual string ConvertedName
{
get => name;
set { }
}
public virtual long length { get; set; }
public virtual string key { get; set; }
public virtual long last { get; set; } = -1;
//
// Summary:
// File path under a specific folder on source. It's a relative path including the filename
public virtual string path { get; set; }
public virtual string ConvertedPath
{
get => path;
protected set { }
}
public virtual bool IsFolder => path != null && path.EndsWith("/");
}
public enum TransmissionStatus
{
INITIAL = -1,
SUCCESSFUL = 0,
FAILED_CONTINUE,
FAILED_ABORT,
SKIPPED,
RESET_CURRENT,
}
public class ProgressChangedEventArgs : EventArgs
{
public readonly long Received, Size;
public readonly long TotalReceived, TotalSize;
public readonly float Percent, TotalPercent;
public readonly double BytesPerSec;
public ProgressChangedEventArgs(
long Received, long Size,
long TotalReceived, long TotalSize,
double BytesPerSec)
{
this.Received = Received;
this.Size = Size;
this.TotalReceived = TotalReceived;
this.TotalSize = TotalSize;
if (Size != 0)
{
Percent = (float)((double)Received / (double)Size) * 100f;
}
else
{
Percent = 100;
}
if (TotalSize != 0)
{
TotalPercent = (float)((double)TotalReceived / (double)TotalSize) * 100f;
}
else
{
TotalPercent = 100;
}
this.BytesPerSec = BytesPerSec;
}
}
public delegate void PercentageChangedEventHandler(object sender, ProgressChangedEventArgs e);
public class NextFileEventArgs : EventArgs
{
public readonly int Current, Count;
public readonly BaseEntry entry, last;
public readonly TransmissionStatus lastStatus;
public NextFileEventArgs(int Current, int Count, BaseEntry entry, BaseEntry last, TransmissionStatus lastStatus)
{
this.Current = Current;
this.Count = Count;
this.entry = entry;
this.last = last;
this.lastStatus = lastStatus;
}
}
public delegate void NextFileEventHandler(object sender, NextFileEventArgs e);
public class FileAlreadyExistEventArgs : EventArgs
{
public enum Measure
{
NONE, SKIP, OVERWRITE, RENAME
}
public readonly BaseEntry entry;
public Measure Action;
public FileAlreadyExistEventArgs(BaseEntry entry)
{
this.entry = entry;
Action = Measure.SKIP;
}
}
public delegate void FileAlreadyExistEventHandler(object sender, FileAlreadyExistEventArgs e);
public interface IBase : IDisposable
{
OperationClass Operation { get; }
DirectionClass Direction { get; }
bool IsInitialzed { get; }
event PercentageChangedEventHandler ProgressChangedEvent;
event NextFileEventHandler OnNextFileEvent;
event FileAlreadyExistEventHandler FileAlreadyExistEvent;
event EventHandler OnErrorEvent;
event EventHandler OnExitEvent;
int FileCount { get; }
string FirstName { get; set; }
string DestinationFolder { get; set; }
void StartTransmittion();
Task Init(PhoneClient client, IEnumerable<ControlFolderListItemViewBase.UiItem> UiItems, long TotalSize = -1, string FileRootOnPhone = null);
void Init(PhoneClient client, BaseEntry[] Entries, long TotalSize = -1, string FileRootOnPhone = null);
}
public abstract class BaseModule<E> : IBase where E : BaseEntry, new()
{
public OperationClass Operation { get; set; } = OperationClass.COPY;
public DirectionClass Direction { get; protected set; } = DirectionClass.INSIDE_PHONE;
public int CurrentFileIndex { get; private set; } = -1;
protected E[] EntryList { get; set; } = null;
public bool IsInitialzed => EntryList != null;
protected PhoneClient Client;
protected string FileRootOnSource;
public string FirstName
{
get => EntryList[0].ConvertedName;
set
{
EntryList[0].ConvertedName = value;
}
}
public int FileCount => EntryList.Length;
protected E CurrentEntry { get; private set; } = null;
public long CurrnetReceived { get; private set; } = 0;
public long TotalReceived { get; private set; } = 0;
public long TotalSize { get; protected set; }
protected ListModeClass ListMode = ListModeClass.DEEP;
public abstract string DestinationFolder { get; set; }
public event NextFileEventHandler OnNextFileEvent;
private SpeedWatch Speeder = null;
public virtual void StartTransmittion()
{
Speeder = new SpeedWatch();
StartNext(TransmissionStatus.INITIAL);
}
protected abstract void ResetCurrentFileTransmisionAction(E entry);
protected abstract void FileTransmitSuccessAction(E entry);
protected abstract void FileFailedCleanUpAction(E entry);
protected class ExitLoop : Exception { }
protected class ContinueLoop : Exception
{
public readonly TransmissionStatus LastStatus;
public ContinueLoop(TransmissionStatus LastStatus)
{
this.LastStatus = LastStatus;
}
}
protected class TransmissionStatusReport : ContinueLoop
{
public TransmissionStatusReport(TransmissionStatus LastStatus) : base(LastStatus)
{
}
}
public event PercentageChangedEventHandler ProgressChangedEvent;
protected void FireProgressChangedEvent(double speed)
{
ProgressChangedEvent?.Invoke(
this,
new ProgressChangedEventArgs(
CurrnetReceived, CurrentEntry.length,
TotalReceived, TotalSize,
speed
)
);
}
public abstract event EventHandler OnErrorEvent;
public event EventHandler OnExitEvent;
protected virtual void AddTransmitLength(long len)
{
if (len < 0)
throw new ArgumentOutOfRangeException("len");
CurrnetReceived += len;
TotalReceived += len;
Speeder.Add(len);
double BytesPerSec = Speeder.BytesPerSec(250);
if (BytesPerSec >= 0)
{
FireProgressChangedEvent(BytesPerSec);
Speeder.Reset();
}
}
public async Task Init(PhoneClient client, IEnumerable<ControlFolderListItemViewBase.UiItem> UiItems, long TotalSize = -1, string FileRootOnPhone = null)
{
if (EntryList != null)
{
throw new InvalidOperationException("Already Initialized");
}
E[] Entries = await BaseEntry.ConvertFromUiItems<E>(UiItems, FileRootOnPhone, client, ListMode);
this.Init(client, Entries, TotalSize, FileRootOnPhone);
}
public virtual void Init(PhoneClient client, BaseEntry[] Entries, long TotalSize = -1, string FileRootOnPhone = null)
{
if (EntryList != null)
{
throw new InvalidOperationException("Already Initialized");
}
Client = client;
this.FileRootOnSource = FileRootOnPhone.AppendIfNotEnding("/");
EntryList = (E[])Entries;
if (TotalSize < 0)
{
this.TotalSize = BaseEntry.SizeOfAllFiles(Entries);
}
else
{
this.TotalSize = TotalSize;
}
PhoneMessageCenter.Singleton.Register(
Client.Id,
PhoneMessageCenter.MSG_FAKE_TYPE_ON_CONNECTED,
OnReconnectedInner,
false
);
PhoneMessageCenter.Singleton.Register(
Client.Id,
PhoneMessageCenter.MSG_FAKE_TYPE_ON_DISCONNECTED,
OnDisconnectedInner,
false
);
}
protected abstract Task OnReconnected();
private async void OnReconnectedInner(string id, string msgType, object msgObj, PhoneClient client)
{
Client = client;
if (CurrentEntry != null)
{
await Task.Delay(1000);
if (Client != client)
{
// Phones may reconnect multiple times within this 1 second delayed above.
return;
}
await OnReconnected();
}
}
protected abstract Task OnDisconnected();
private async void OnDisconnectedInner(string id, string msgType, object msgObj, PhoneClient client)
{
if (CurrentEntry != null)
{
await OnDisconnected();
}
}
protected abstract Task Transmit(E entry, FileAlreadyExistEventArgs.Measure Measure);
protected abstract Task<bool> DetermineFileExistence(E entry);
public event FileAlreadyExistEventHandler FileAlreadyExistEvent;
private async Task StartNextInner(TransmissionStatus LastStatus)
{
E LastEntry = CurrentEntry;
switch (LastStatus)
{
case TransmissionStatus.SUCCESSFUL:
FileTransmitSuccessAction(CurrentEntry);
goto case TransmissionStatus.INITIAL;
case TransmissionStatus.RESET_CURRENT:
TotalReceived -= CurrnetReceived;
CurrnetReceived = 0;
ResetCurrentFileTransmisionAction(LastEntry);
--CurrentFileIndex;
goto case TransmissionStatus.INITIAL;
case TransmissionStatus.FAILED_CONTINUE:
FileFailedCleanUpAction(LastEntry);
goto case TransmissionStatus.INITIAL;
case TransmissionStatus.FAILED_ABORT:
FileFailedCleanUpAction(LastEntry);
throw new ExitLoop();
case TransmissionStatus.SKIPPED:
goto case TransmissionStatus.INITIAL;
case TransmissionStatus.INITIAL:
++CurrentFileIndex;
break;
}
if (CurrentFileIndex >= EntryList.Length)
{
StopWatchJob();
OnNextFileEvent?.Invoke(
this,
new NextFileEventArgs(
CurrentFileIndex,
-1,
null,
LastEntry,
LastStatus)
);
OnExitEvent?.Invoke(this, null);
throw new ExitLoop();
}
CurrnetReceived = 0;
CurrentEntry = EntryList[CurrentFileIndex];
OnNextFileEvent?.Invoke(
this,
new NextFileEventArgs(
CurrentFileIndex,
EntryList.Length,
CurrentEntry,
LastEntry,
LastStatus)
);
FileAlreadyExistEventArgs args = null;
if (await DetermineFileExistence(CurrentEntry))
{
args = new FileAlreadyExistEventArgs(CurrentEntry);
FileAlreadyExistEvent?.Invoke(this, args);
switch (args.Action)
{
case FileAlreadyExistEventArgs.Measure.SKIP:
throw new ContinueLoop(TransmissionStatus.SKIPPED);
case FileAlreadyExistEventArgs.Measure.OVERWRITE:
break;
case FileAlreadyExistEventArgs.Measure.RENAME:
break;
}
}
await Transmit(CurrentEntry, args?.Action ?? FileAlreadyExistEventArgs.Measure.NONE);
}
public async void StartNext(TransmissionStatus LastStatus)
{
TransmissionStatus status = LastStatus;
while (true)
{
try
{
await StartNextInner(status);
break;
}
catch (ContinueLoop e)
{
status = e.LastStatus;
continue;
}
catch (ExitLoop e)
{
break;
}
catch (Exception e)
{
status = TransmissionStatus.FAILED_CONTINUE;
break;
}
}
}
private object WatchJobToken = null;
private long LastReceivedTotal = long.MinValue;
protected virtual void WatchJob()
{
if (LastReceivedTotal == TotalReceived)
{
Client?.SendMsgNoThrow(PhoneClient.MSG_TYPE_HELLO);
FireProgressChangedEvent(0);
}
else
{
LastReceivedTotal = TotalReceived;
}
}
protected virtual async void StartWatchJob(int IntervalMills = 200)
{
object token = new object();
WatchJobToken = token;
while (WatchJobToken == token)
{
await Task.Delay(IntervalMills);
WatchJob();
}
}
protected virtual void StopWatchJob()
{
WatchJob();
WatchJobToken = null;
}
public virtual void Dispose()
{
StopWatchJob();
if (Client != null)
{
PhoneMessageCenter.Singleton.Unregister(
Client.Id,
PhoneMessageCenter.MSG_FAKE_TYPE_ON_CONNECTED,
OnReconnectedInner
);
PhoneMessageCenter.Singleton.Unregister(
Client.Id,
PhoneMessageCenter.MSG_FAKE_TYPE_ON_DISCONNECTED,
OnDisconnectedInner
);
}
}
}
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C#
1
https://gitee.com/zilin__zhang/fnsync.git
git@gitee.com:zilin__zhang/fnsync.git
zilin__zhang
fnsync
fnsync
master

搜索帮助