//deleteObjectFromLocal()\r
using (\r
StatusNotification.GetNotifier("Deleting local {0}", "Deleted local {0}",\r
- Path.GetFileName(localFilePath)))\r
+ localInfo.Name))\r
{\r
- StatusKeeper.SetFileState(localFilePath, FileStatus.Deleted,\r
- FileOverlayStatus.Deleted, "");\r
- using (NetworkGate.Acquire(localFilePath, NetworkOperation.Deleting))\r
- {\r
- agent.Delete(localFilePath);\r
- }\r
- //updateRecord(Remove C, L)\r
- StatusKeeper.ClearFileStatus(localFilePath);\r
+ DeleteLocalFile(agent, localFilePath);\r
}\r
}\r
else\r
//If the file has moved on the server, move it locally before downloading\r
using (\r
StatusNotification.GetNotifier("Downloading {0}", "Downloaded {0}",\r
- Path.GetFileName(localFilePath)))\r
+ localInfo.Name))\r
{\r
var targetPath = MoveForServerMove(accountInfo, tuple);\r
\r
- StatusKeeper.SetFileState(targetPath, FileStatus.Modified, FileOverlayStatus.Modified,\r
- "");\r
-\r
- await\r
- NetworkAgent.Downloader.DownloadCloudFile(accountInfo, tuple.ObjectInfo, targetPath,\r
- token)\r
- .ConfigureAwait(false);\r
- //updateRecord( L = S )\r
- StatusKeeper.UpdateFileChecksum(targetPath, tuple.ObjectInfo.ETag,\r
- tuple.ObjectInfo.X_Object_Hash);\r
-\r
- StatusKeeper.StoreInfo(targetPath, tuple.ObjectInfo);\r
+ await DownloadCloudFile(accountInfo, tuple, token, targetPath).ConfigureAwait(false);\r
\r
AddOwnFolderToSelectives(accountInfo, tuple, targetPath);\r
}\r
var progress = new Progress<double>(d =>\r
StatusNotification.Notify(new StatusNotification(String.Format("Merkle Hashing for Upload {0:p} of {1}", d, localInfo.Name))));\r
\r
- //Debug.Assert(tuple.FileState !=null);\r
- var action = new CloudUploadAction(accountInfo, localInfo, tuple.FileState,\r
- accountInfo.BlockSize, accountInfo.BlockHash,\r
- "Poll", isUnselectedRootFolder,token,progress);\r
- using (\r
- StatusNotification.GetNotifier("Uploading {0}", "Uploaded {0}",\r
- Path.GetFileName(localFilePath)))\r
+ //Is this a result of a FILE move with no modifications? Then try to move it,\r
+ //to avoid an expensive hash\r
+ if (!MoveForLocalMove(accountInfo, tuple))\r
{\r
- await NetworkAgent.Uploader.UploadCloudFile(action, token).ConfigureAwait(false);\r
+ await UploadLocalFile(accountInfo, tuple, token, isUnselectedRootFolder, localInfo, progress).ConfigureAwait(false);\r
}\r
\r
//updateRecord( S = C )\r
}\r
}\r
\r
+ private void DeleteLocalFile(FileAgent agent, string localFilePath)\r
+ {\r
+ StatusKeeper.SetFileState(localFilePath, FileStatus.Deleted,\r
+ FileOverlayStatus.Deleted, "");\r
+ using (NetworkGate.Acquire(localFilePath, NetworkOperation.Deleting))\r
+ {\r
+ agent.Delete(localFilePath);\r
+ }\r
+ //updateRecord(Remove C, L)\r
+ StatusKeeper.ClearFileStatus(localFilePath);\r
+ }\r
+\r
+ private async Task DownloadCloudFile(AccountInfo accountInfo, StateTuple tuple, CancellationToken token, string targetPath)\r
+ {\r
+ StatusKeeper.SetFileState(targetPath, FileStatus.Modified, FileOverlayStatus.Modified,\r
+ "");\r
+\r
+ await\r
+ NetworkAgent.Downloader.DownloadCloudFile(accountInfo, tuple.ObjectInfo, targetPath,\r
+ token)\r
+ .ConfigureAwait(false);\r
+ //updateRecord( L = S )\r
+ StatusKeeper.UpdateFileChecksum(targetPath, tuple.ObjectInfo.ETag,\r
+ tuple.ObjectInfo.X_Object_Hash);\r
+\r
+ StatusKeeper.StoreInfo(targetPath, tuple.ObjectInfo);\r
+ }\r
+\r
+ private async Task UploadLocalFile(AccountInfo accountInfo, StateTuple tuple, CancellationToken token,\r
+ bool isUnselectedRootFolder, FileSystemInfo localInfo, Progress<double> progress)\r
+ {\r
+ var action = new CloudUploadAction(accountInfo, localInfo, tuple.FileState,\r
+ accountInfo.BlockSize, accountInfo.BlockHash,\r
+ "Poll", isUnselectedRootFolder, token, progress);\r
+ using (StatusNotification.GetNotifier("Uploading {0}", "Uploaded {0}",\r
+ localInfo.Name))\r
+ {\r
+ await NetworkAgent.Uploader.UploadCloudFile(action, token).ConfigureAwait(false);\r
+ }\r
+ }\r
+\r
+ private bool MoveForLocalMove(AccountInfo accountInfo, StateTuple tuple)\r
+ {\r
+ if (!(tuple.FileInfo is FileInfo) || String.IsNullOrWhiteSpace(tuple.OldFullPath) || tuple.OldMD5 != tuple.C)\r
+ return false;\r
+\r
+ try\r
+ {\r
+\r
+ var client = new CloudFilesClient(accountInfo);\r
+ var objectInfo = CloudAction.CreateObjectInfoFor(accountInfo, tuple.FileInfo);\r
+ var containerPath = Path.Combine(accountInfo.AccountPath, objectInfo.Container);\r
+ var oldName = tuple.OldFullPath.AsRelativeTo(containerPath);\r
+ //Then execute a move instead of an upload\r
+ using (StatusNotification.GetNotifier("Moving {0}", "Moved {0}", tuple.FileInfo.Name))\r
+ {\r
+ client.MoveObject(objectInfo.Account, objectInfo.Container, oldName,\r
+ objectInfo.Container, objectInfo.Name);\r
+ }\r
+ return true;\r
+ }\r
+ catch (Exception exc)\r
+ {\r
+ Log.ErrorFormat("[MOVE] Failed for [{0}],:\r\n{1}", tuple.FilePath, exc);\r
+ //Return false to force an upload of the file\r
+ return false;\r
+ }\r
+\r
+ }\r
+\r
private void AddOwnFolderToSelectives(AccountInfo accountInfo, StateTuple tuple, string targetPath)\r
{\r
//Not for shared folders\r
return results;\r
\r
*/\r
- private IEnumerable<StateTuple> MergeSources(IEnumerable<Tuple<string, ObjectInfo>> infos, IEnumerable<FileSystemInfo> files, IEnumerable<FileState> states, ConcurrentDictionary<string, MovedEventArgs> moves)\r
+ private IEnumerable<StateTuple> MergeSources(IEnumerable<Tuple<string, ObjectInfo>> infos, IEnumerable<FileSystemInfo> files, List<FileState> states, ConcurrentDictionary<string, MovedEventArgs> moves)\r
{\r
- var tuplesByPath = files.ToDictionary(f => f.FullName, f => new StateTuple {FileInfo = f}); new Dictionary<string, StateTuple>();\r
+ var tuplesByPath = new Dictionary<string, StateTuple>();\r
+ foreach (var info in files)\r
+ {\r
+ var tuple = new StateTuple(info);\r
+ //Is this the target of a move event?\r
+ var moveArg = moves.Values.FirstOrDefault(arg => info.FullName.Equals(arg.FullPath, StringComparison.InvariantCultureIgnoreCase) \r
+ || info.FullName.IsAtOrBelow(arg.FullPath));\r
+ if (moveArg != null)\r
+ {\r
+ tuple.NewFullPath = info.FullName;\r
+ var relativePath = info.AsRelativeTo(moveArg.FullPath); \r
+ tuple.OldFullPath = Path.Combine(moveArg.OldFullPath, relativePath);\r
+ tuple.OldMD5 = states.FirstOrDefault(st => st.FilePath.Equals(tuple.OldFullPath, StringComparison.InvariantCultureIgnoreCase))\r
+ .NullSafe(st => st.LastMD5);\r
+ }\r
+\r
+ tuplesByPath[tuple.FilePath] = tuple;\r
+ }\r
+ \r
+\r
+ \r
\r
//For files that have state\r
foreach (var state in states)\r
{\r
StateTuple hashTuple;\r
\r
+ \r
if (tuplesByPath.TryGetValue(state.FilePath, out hashTuple))\r
{\r
hashTuple.FileState = state;\r
{\r
var fsInfo = FileInfoExtensions.FromPath(state.FilePath);\r
hashTuple = new StateTuple {FileInfo = fsInfo, FileState = state};\r
+\r
+ //Is the source of a moved item?\r
+ var moveArg = moves.Values.FirstOrDefault(arg => state.FilePath.Equals(arg.OldFullPath,StringComparison.InvariantCultureIgnoreCase) \r
+ || state.FilePath.IsAtOrBelow(arg.OldFullPath));\r
+ if (moveArg != null)\r
+ {\r
+ var relativePath = state.FilePath.AsRelativeTo(moveArg.OldFullPath);\r
+ hashTuple.NewFullPath = Path.Combine(moveArg.FullPath,relativePath);\r
+ hashTuple.OldFullPath = state.FilePath;\r
+ //Do we have the old MD5?\r
+ hashTuple.OldMD5 = state.LastMD5;\r
+ }\r
+\r
+\r
tuplesByPath[state.FilePath] = hashTuple;\r
}\r
}\r