/// <summary> /// Called when a file or folder attributes changed in the user file system. /// </summary> /// <remarks> /// Here we monitor pinned and unpinned attributes and hydrate/dehydrate files. /// </remarks> private async void ChangedAsync(object sender, FileSystemEventArgs e) { LogMessage($"{e.ChangeType}", e.FullPath); try { string userFileSystemPath = e.FullPath; if (FsPath.Exists(userFileSystemPath) && !FsPath.AvoidSync(userFileSystemPath)) { // Hydrate / dehydrate. if (new UserFileSystemRawItem(userFileSystemPath).HydrationRequired()) { LogMessage("Hydrating", userFileSystemPath); new PlaceholderFile(userFileSystemPath).Hydrate(0, -1); LogMessage("Hydrated succesefully", userFileSystemPath); } else if (new UserFileSystemRawItem(userFileSystemPath).DehydrationRequired()) { LogMessage("Dehydrating", userFileSystemPath); new PlaceholderFile(userFileSystemPath).Dehydrate(0, -1); LogMessage("Dehydrated succesefully", userFileSystemPath); } } } catch (Exception ex) { LogError("Hydration/dehydration failed", e.FullPath, null, ex); } }
/// <summary> /// Called when a file or folder is created in the user file system. /// </summary> /// <remarks> /// This method is also called when a file is being moved in user file system, after the IFileSystem.MoveToAsync() call. /// </remarks> private async void CreatedAsync(object sender, FileSystemEventArgs e) { LogMessage($"{e.ChangeType}", e.FullPath); string userFileSystemPath = e.FullPath; try { // When a file/folder is moved this method is also called. The file/folder move is already processed in IFileSystem.MoveToAsync(). if (FsPath.Exists(userFileSystemPath) && !PlaceholderItem.IsPlaceholder(userFileSystemPath)) { // When a new file or folder is created under sync root it is // created as a regular file or folder. Converting to placeholder. PlaceholderItem.ConvertToPlaceholder(userFileSystemPath, false); LogMessage("Converted to placeholder", userFileSystemPath); // Do not create temp MS Office, temp and hidden files in remote storage. if (!FsPath.AvoidSync(userFileSystemPath)) { // Create the file/folder in the remote storage. try { await RemoteStorageRawItem.CreateAsync(userFileSystemPath, this); } catch (IOException ex) { LogError("Creation in remote storage failed. Possibly in use by an application", userFileSystemPath, null, ex); } } } } catch (Exception ex) { LogError($"{e.ChangeType} failed", userFileSystemPath, null, ex); } }
/// <summary> /// Called when a file or folder is renamed in the remote storage. /// </summary> /// <remarks>In this method we rename corresponding file/folder in user file system.</remarks> private async void RenamedAsync(object sender, RenamedEventArgs e) { LogMessage("Renamed:", e.OldFullPath, e.FullPath); string remoteStorageOldPath = e.OldFullPath; string remoteStorageNewPath = e.FullPath; try { string userFileSystemOldPath = Mapping.ReverseMapPath(remoteStorageOldPath); // Because of the on-demand population the file or folder placeholder may not exist in the user file system. if (FsPath.Exists(userFileSystemOldPath)) { if (!FsPath.AvoidSync(remoteStorageOldPath) && !FsPath.AvoidSync(remoteStorageNewPath)) { string userFileSystemNewPath = Mapping.ReverseMapPath(remoteStorageNewPath); await new UserFileSystemItem(userFileSystemOldPath).MoveAsync(userFileSystemNewPath); LogMessage("Renamed succesefully:", userFileSystemOldPath, userFileSystemNewPath); } } } catch (Exception ex) { LogError($"{e.ChangeType} failed:", $"From:{remoteStorageOldPath} To:{remoteStorageNewPath}", ex); } }
/// <summary> /// Called when a file or folder is created in the remote storage. /// </summary> /// <remarks>In this method we create a new file/folder in user file system.</remarks> private async void CreatedAsync(object sender, FileSystemEventArgs e) { LogMessage(e.ChangeType.ToString(), e.FullPath); string remoteStoragePath = e.FullPath; try { // We do not want to sync MS Office temp files from remote storage. if (!FsPath.AvoidSync(remoteStoragePath)) { string userFileSystemPath = Mapping.ReverseMapPath(remoteStoragePath); string userFileSystemParentPath = Path.GetDirectoryName(userFileSystemPath); // Because of the on-demand population the file or folder placeholder may not exist in the user file system // or the folder may be offline. if (Directory.Exists(userFileSystemParentPath) && !new DirectoryInfo(userFileSystemParentPath).Attributes.HasFlag(System.IO.FileAttributes.Offline)) { FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath); FileSystemItemBasicInfo newItemInfo = Mapping.GetUserFileSysteItemBasicInfo(remoteStorageItem); await UserFileSystemRawItem.CreateAsync(userFileSystemParentPath, new[] { newItemInfo }); LogMessage($"Created succesefully", userFileSystemPath); } } } catch (Exception ex) { LogError($"{e.ChangeType} failed", remoteStoragePath, null, ex); } }
/// <summary> /// Called when a file or folder is created in the remote storage. /// </summary> /// <remarks>In this method we create a new file/folder in user file system.</remarks> private async void CreatedAsync(object sender, FileSystemEventArgs e) { LogMessage(e.ChangeType.ToString(), e.FullPath); string remoteStoragePath = e.FullPath; try { string userFileSystemPath = Mapping.ReverseMapPath(remoteStoragePath); string userFileSystemParentPath = Path.GetDirectoryName(userFileSystemPath); // Because of the on-demand population the file or folder placeholder may not exist in the user file system. // Here we also check that the folder content was loaded into user file system (the folder is not offline). if (Directory.Exists(userFileSystemParentPath) && !new DirectoryInfo(userFileSystemParentPath).Attributes.HasFlag(System.IO.FileAttributes.Offline)) { if (!FsPath.AvoidSync(remoteStoragePath)) { FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath); await UserFileSystemItem.CreateAsync(userFileSystemParentPath, remoteStorageItem); LogMessage("Created succesefully:", userFileSystemPath); } } } catch (Exception ex) { LogError($"{e.ChangeType} failed:", remoteStoragePath, ex); } }
/// <summary> /// Moves the item in the remote storage. This method is called by the platform. /// To move item manually use the <see cref="MoveToAsync(string)"/> method instead. /// </summary> /// <param name="userFileSystemNewPath">Target path in user file system.</param> /// <param name="resultContext">Confirms move competeion. Passed by the platform only.</param> internal async Task MoveToAsync(string userFileSystemNewPath, IConfirmationResultContext resultContext) { // In this method you must either confirm or reject the move by calling call either // IConfirmationResultContext.ReturnConfirmationResult() or IConfirmationResultContext.ReturnErrorResult(). string userFileSystemOldPath = userFileSystemPath; try { if (!FsPath.IsRecycleBin(userFileSystemNewPath) && // When a file is deleted, it is moved to a Recycle Bin. !FsPath.AvoidSync(userFileSystemOldPath) && !FsPath.AvoidSync(userFileSystemNewPath)) { logger.LogMessage("Moving item in remote storage", userFileSystemOldPath, userFileSystemNewPath); /* * // Read In-Sync state before move and set after move. * if (FsPath.Exists(userFileSystemOldPath)) * { * inSync = PlaceholderItem.GetItem(userFileSystemOldPath).GetInSync(); * } */ IVirtualFileSystemItem userFileSystemItemOld = await GetItemAsync(userFileSystemOldPath); await userFileSystemItemOld.MoveToAsync(userFileSystemNewPath); //updateTargetOnSuccess = true; logger.LogMessage("Moved succesefully in remote storage", userFileSystemOldPath, userFileSystemNewPath); ETagManager eTagManager = virtualDrive.GetETagManager(userFileSystemOldPath); if (FsPath.Exists(eTagManager.ETagFilePath)) { await eTagManager.MoveToAsync(userFileSystemNewPath); logger.LogMessage("Moved ETag succesefully", userFileSystemOldPath, userFileSystemNewPath); } } // Confirm move. if (resultContext != null) { // Calling ReturnConfirmationResult() moves file in the user file system. logger.LogMessage("Confirming move in user file system", userFileSystemOldPath, userFileSystemNewPath); resultContext.ReturnConfirmationResult(); // After ReturnConfirmationResult() call the MoveToCompletionAsync() method is called. // After that, in case of a file, the file handle is closed, triggering IFile.CloseAsync() call // and Windows File Manager move progress window is closed. // In addition to thos, in case the target is the offline folder, the IFolder.GetChildrenAsync() method is called. } } catch (Exception ex) { logger.LogError("Failed to move item", userFileSystemOldPath, userFileSystemNewPath, ex); resultContext.ReturnErrorResult(); //string userFileSystemExPath = FsPath.Exists(userFileSystemNewPath) ? userFileSystemNewPath : userFileSystemOldPath; //await virtualDrive.GetUserFileSystemRawItem(userFileSystemExPath, logger).SetUploadErrorStateAsync(ex); } }
/// <summary> /// Called when a file content changed or file/folder attributes changed in the remote storage. /// </summary> /// <remarks> /// In this method we update corresponding file/folder information in user file system. /// We also dehydrate the file if it is not blocked. /// </remarks> private async void ChangedAsync(object sender, FileSystemEventArgs e) { LogMessage(e.ChangeType.ToString(), e.FullPath); string remoteStoragePath = e.FullPath; try { string userFileSystemPath = Mapping.ReverseMapPath(remoteStoragePath); // Because of the on-demand population the file or folder placeholder may not exist in the user file system. if (FsPath.Exists(userFileSystemPath)) { FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath); if (!FsPath.AvoidSync(remoteStoragePath)) { // This check is only required because we can not prevent circular calls because of the simplicity of this example. if (!await new UserFileSystemItem(userFileSystemPath).EqualsAsync(remoteStorageItem)) { LogMessage("Item modified:", remoteStoragePath); await new UserFileSystemItem(userFileSystemPath).UpdateAsync(remoteStorageItem); LogMessage("Updated succesefully:", userFileSystemPath); } } } } catch (Exception ex) { LogError($"{e.ChangeType} failed:", remoteStoragePath, ex); } }
/// <summary> /// Recursively updates and creates files and folders in remote storage. /// Synchronizes only folders already loaded into the user file system. /// </summary> /// <param name="userFileSystemFolderPath">Folder path in user file system.</param> internal async Task SyncronizeFolderAsync(string userFileSystemFolderPath) { // In case of on-demand loading the user file system contains only a subset of the server files and folders. // Here we sync folder only if its content already loaded into user file system (folder is not offline). // The folder content is loaded inside IFolder.GetChildrenAsync() method. if (new DirectoryInfo(userFileSystemFolderPath).Attributes.HasFlag(System.IO.FileAttributes.Offline)) { // LogMessage("Folder offline, skipping:", userFileSystemFolderPath); return; } IEnumerable <string> userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*"); // LogMessage("Synchronizing:", userFileSystemFolderPath); // Update and create files/folders in remote storage. userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*"); foreach (string userFileSystemPath in userFileSystemChildren) { try { if (!FsPath.AvoidSync(userFileSystemPath)) { if (PlaceholderItem.GetItem(userFileSystemPath).IsNew(virtualDrive)) { // Create a file/folder in the remote storage. FileSystemItemTypeEnum itemType = FsPath.GetItemType(userFileSystemPath); await virtualDrive.GetRemoteStorageRawItem(userFileSystemPath, itemType, this).CreateAsync(); } else if (!PlaceholderItem.GetItem(userFileSystemPath).IsMoved()) { // Update file/folder in the remote storage. Unlock if auto-locked. FileSystemItemTypeEnum itemType = FsPath.GetItemType(userFileSystemPath); await virtualDrive.GetRemoteStorageRawItem(userFileSystemPath, itemType, this).UpdateAsync(); } } } catch (Exception ex) { LogError("Update failed", userFileSystemPath, null, ex); } // Synchronize subfolders. try { if (Directory.Exists(userFileSystemPath)) { await SyncronizeFolderAsync(userFileSystemPath); } } catch (Exception ex) { LogError("Folder sync failed:", userFileSystemPath, null, ex); } } }
/// <summary> /// Deletes file or folder in the remote storage. /// </summary> internal async Task <bool> DeleteAsync() { if (!FsPath.AvoidSync(userFileSystemPath)) { await new VirtualFileSystem.UserFileSystemItem(userFileSystemPath).DeleteAsync(); ETag.DeleteETag(userFileSystemPath); return(true); } return(false); }
/// <summary> /// Deletes file or folder in the remote storage. /// </summary> internal async Task <bool> DeleteAsync() { if (!FsPath.AvoidSync(userFileSystemPath)) { IUserFileSystemItem userFileSystemItem = await virtualDrive.GetItemAsync <IUserFileSystemItem>(userFileSystemPath); await userFileSystemItem.DeleteAsync(); ETag.DeleteETag(userFileSystemPath); return(true); } return(false); }
/// <inheritdoc/> public async Task <bool> DeleteAsync() { if (!FsPath.AvoidSync(userFileSystemPath)) { IVirtualFileSystemItem userFileSystemItem = await GetItemAsync(userFileSystemPath); await userFileSystemItem.DeleteAsync(); virtualDrive.GetETagManager(userFileSystemPath).DeleteETag(); return(true); } return(false); }
/// <summary> /// Called when a file content changed or file/folder attributes changed in the remote storage. /// </summary> /// <remarks> /// In this method we update corresponding file/folder information in user file system. /// We also dehydrate the file if it is not blocked. /// </remarks> private async void ChangedAsync(object sender, FileSystemEventArgs e) { LogMessage(e.ChangeType.ToString(), e.FullPath); string remoteStoragePath = e.FullPath; try { // We do not want to sync MS Office temp files, etc. from remote storage. if (!FsPath.AvoidSync(remoteStoragePath)) { string userFileSystemPath = Mapping.ReverseMapPath(remoteStoragePath); // Because of the on-demand population the file or folder placeholder may not exist in the user file system. if (FsPath.Exists(userFileSystemPath)) { FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath); // This check is only required because we can not prevent circular calls because of the simplicity of this example. // In your real-life application you will not sent updates from server back to client that issued the update. FileSystemItemBasicInfo itemInfo = Mapping.GetUserFileSysteItemBasicInfo(remoteStorageItem); if (!await ETag.ETagEqualsAsync(userFileSystemPath, itemInfo)) { await new UserFileSystemRawItem(userFileSystemPath).UpdateAsync(itemInfo); LogMessage("Updated succesefully", userFileSystemPath); } // Update "locked by another user" icon. await new UserFileSystemRawItem(userFileSystemPath).SetLockedByAnotherUserAsync(itemInfo.LockedByAnotherUser); } } } catch (Exception ex) { LogError($"{e.ChangeType} failed", remoteStoragePath, null, ex); } }
/// <summary> /// Called when a file or folder is deleted in the remote storage. /// </summary> /// <remarks>In this method we delete corresponding file/folder in user file system.</remarks> private async void DeletedAsync(object sender, FileSystemEventArgs e) { LogMessage(e.ChangeType.ToString(), e.FullPath); string remoteStoragePath = e.FullPath; try { string userFileSystemPath = Mapping.ReverseMapPath(remoteStoragePath); // Because of the on-demand population the file or folder placeholder may not exist in the user file system. if (FsPath.Exists(userFileSystemPath)) { if (!FsPath.AvoidSync(remoteStoragePath)) { await new UserFileSystemItem(userFileSystemPath).DeleteAsync(); LogMessage("Deleted succesefully:", userFileSystemPath); } } } catch (Exception ex) { LogError($"{e.ChangeType} failed:", remoteStoragePath, ex); } }
/// <summary> /// Recursively synchronizes all files and folders from server to client. /// Synchronizes only folders already loaded into the user file system. /// </summary> /// <param name="userFileSystemFolderPath">Folder path in user file system.</param> internal async Task SyncronizeFolderAsync(string userFileSystemFolderPath) { // In case of on-demand loading the user file system contains only a subset of the server files and folders. // Here we sync folder only if its content already loaded into user file system (folder is not offline). // The folder content is loaded inside IFolder.GetChildrenAsync() method. if (new DirectoryInfo(userFileSystemFolderPath).Attributes.HasFlag(System.IO.FileAttributes.Offline)) { // LogMessage("Folder offline, skipping:", userFileSystemFolderPath); return; } IEnumerable <string> userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*"); //LogMessage("Synchronizing:", userFileSystemFolderPath); IVirtualFolder userFolder = await virtualDrive.GetItemAsync <IVirtualFolder>(userFileSystemFolderPath, this); IEnumerable <FileSystemItemMetadataExt> remoteStorageChildrenItems = await userFolder.EnumerateChildrenAsync("*"); // Create new files/folders in the user file system. foreach (FileSystemItemMetadataExt remoteStorageItem in remoteStorageChildrenItems) { string userFileSystemPath = Path.Combine(userFileSystemFolderPath, remoteStorageItem.Name); try { // We do not want to sync MS Office temp files, etc. from remote storage. // We also do not want to create MS Office files during transactional save in user file system. if (!FsPath.AvoidSync(remoteStorageItem.Name) && !FsPath.AvoidSync(userFileSystemPath)) { if (!FsPath.Exists(userFileSystemPath)) { LogMessage($"Creating", userFileSystemPath); await virtualDrive.GetUserFileSystemRawItem(userFileSystemFolderPath, this).CreateAsync(new[] { remoteStorageItem }); LogMessage($"Created succesefully", userFileSystemPath); } } } catch (Exception ex) { LogError("Creation failed", userFileSystemPath, null, ex); } } // Update files/folders in user file system and sync subfolders. userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*"); foreach (string userFileSystemPath in userFileSystemChildren) { try { string itemName = Path.GetFileName(userFileSystemPath); FileSystemItemMetadataExt remoteStorageItem = remoteStorageChildrenItems.FirstOrDefault(x => x.Name.Equals(itemName, StringComparison.InvariantCultureIgnoreCase)); if (!FsPath.AvoidSync(userFileSystemPath)) { UserFileSystemRawItem userFileSystemRawItem = virtualDrive.GetUserFileSystemRawItem(userFileSystemPath, this); if (remoteStorageItem == null) { if (PlaceholderItem.GetItem(userFileSystemPath).GetInSync() //&& !PlaceholderItem.GetItem(userFileSystemPath).IsNew(virtualDrive) // Extra check to protect from deletion files that were deleted becuse of incorrect folder merging operation. ) { // Delete the file/folder in user file system. LogMessage("Deleting item", userFileSystemPath); await userFileSystemRawItem.DeleteAsync(); LogMessage("Deleted succesefully", userFileSystemPath); } } else { if (PlaceholderItem.GetItem(userFileSystemPath).GetInSync() && !await virtualDrive.GetETagManager(userFileSystemPath).ETagEqualsAsync(remoteStorageItem)) { // User file system <- remote storage update. LogMessage("Remote item modified", userFileSystemPath); await userFileSystemRawItem.UpdateAsync(remoteStorageItem); LogMessage("Updated succesefully", userFileSystemPath); } // Set the read-only attribute and all custom columns data. if (PlaceholderItem.GetItem(userFileSystemPath).GetInSync()) { await userFileSystemRawItem.SetLockedByAnotherUserAsync(remoteStorageItem.LockedByAnotherUser); await userFileSystemRawItem.SetCustomColumnsDataAsync(remoteStorageItem.CustomProperties); } // Hydrate / dehydrate the file. if (userFileSystemRawItem.HydrationRequired()) { LogMessage("Hydrating", userFileSystemPath); try { new PlaceholderFile(userFileSystemPath).Hydrate(0, -1); LogMessage("Hydrated succesefully", userFileSystemPath); } catch (FileLoadException) { // Typically this happens if another thread already hydrating the file. LogMessage("Failed to hydrate. The file is blocked.", userFileSystemPath); } } else if (userFileSystemRawItem.DehydrationRequired()) { LogMessage("Dehydrating", userFileSystemPath); new PlaceholderFile(userFileSystemPath).Dehydrate(0, -1); LogMessage("Dehydrated succesefully", userFileSystemPath); } } } } catch (Exception ex) { LogError("Update failed", userFileSystemPath, null, ex); } // Synchronize subfolders. try { if (Directory.Exists(userFileSystemPath)) { await SyncronizeFolderAsync(userFileSystemPath); } } catch (Exception ex) { LogError("Folder sync failed:", userFileSystemPath, null, ex); } } }
internal async Task MoveToAsync(string userFileSystemNewPath, IConfirmationResultContext resultContext = null) { string userFileSystemOldPath = userFileSystemPath; try { bool? inSync = null; bool updateTargetOnSuccess = false; string eTag = null; try { if (!FsPath.IsRecycleBin(userFileSystemNewPath) && // When a file is deleted, it is moved to a Recycle Bin. !FsPath.AvoidSync(userFileSystemOldPath) && !FsPath.AvoidSync(userFileSystemNewPath)) { logger.LogMessage("Moving item in remote storage", userFileSystemOldPath, userFileSystemNewPath); // Read In-Sync state before move and set after move if (FsPath.Exists(userFileSystemOldPath)) { inSync = PlaceholderItem.GetItem(userFileSystemOldPath).GetInSync(); } eTag = await ETag.GetETagAsync(userFileSystemOldPath); ETag.DeleteETag(userFileSystemOldPath); IUserFileSystemItem userFileSystemItemOld = await virtualDrive.GetItemAsync <IUserFileSystemItem>(userFileSystemOldPath); await userFileSystemItemOld.MoveToAsync(userFileSystemNewPath); updateTargetOnSuccess = true; logger.LogMessage("Moved succesefully in remote storage", userFileSystemOldPath, userFileSystemNewPath); } } finally { if (resultContext != null) { resultContext.ReturnConfirmationResult(); } // This check is just to avoid extra error in the log. if (FsPath.Exists(userFileSystemNewPath)) { // Open file to preven reads and changes between GetFileDataSizeInfo() call and SetInSync() call. using (WindowsFileSystemItem userFileSystemWinItem = WindowsFileSystemItem.OpenReadAttributes(userFileSystemNewPath, FileMode.Open, FileShare.None)) { if ((eTag != null) && PlaceholderItem.IsPlaceholder(userFileSystemNewPath)) { await ETag.SetETagAsync(userFileSystemNewPath, eTag); } // If a file with content is deleted it is moved to a recycle bin and converted // to a regular file, so placeholder features are not available on it, checking if a file is a placeholder. if (updateTargetOnSuccess && PlaceholderItem.IsPlaceholder(userFileSystemNewPath)) { PlaceholderItem placeholderNew = PlaceholderItem.GetItem(userFileSystemNewPath); // Update OriginalPath, so the item does not appear as moved. placeholderNew.SetOriginalPath(userFileSystemNewPath); if (inSync != null) { placeholderNew.SetInSync(inSync.Value); } else if ((placeholderNew is PlaceholderFile) && ((PlaceholderFile)placeholderNew).GetFileDataSizeInfo().ModifiedDataSize == 0) { placeholderNew.SetInSync(true); } await new UserFileSystemRawItem(userFileSystemNewPath).ClearStateAsync(); } } } } } catch (Exception ex) { string userFileSystemPath = FsPath.Exists(userFileSystemNewPath) ? userFileSystemNewPath : userFileSystemOldPath; await new UserFileSystemRawItem(userFileSystemPath).SetUploadErrorStateAsync(ex); // Rethrow the exception preserving stack trace of the original exception. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw(); } }
/// <summary> /// Recursively synchronizes all files and folders from server to client. /// Synchronizes only folders already loaded into the user file system. /// </summary> /// <param name="userFileSystemFolderPath">Folder path in user file system.</param> internal async Task SyncronizeFolderAsync(string userFileSystemFolderPath) { // In case of on-demand loading the user file system contains only a subset of the server files and folders. // Here we sync folder only if its content already loaded into user file system (folder is not offline). // The folder content is loaded inside IFolder.GetChildrenAsync() method. if (new DirectoryInfo(userFileSystemFolderPath).Attributes.HasFlag(System.IO.FileAttributes.Offline)) { // LogMessage("Folder offline, skipping:", userFileSystemFolderPath); return; } IEnumerable <string> userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*"); //LogMessage("Synchronizing:", userFileSystemFolderPath); IEnumerable <FileSystemItemBasicInfo> remoteStorageChildrenItems = await new UserFolder(userFileSystemFolderPath).EnumerateChildrenAsync("*"); // Create new files/folders in the user file system. string remoteStorageFolderPath = Mapping.MapPath(userFileSystemFolderPath); foreach (FileSystemItemBasicInfo remoteStorageItem in remoteStorageChildrenItems) { string userFileSystemPath = Path.Combine(userFileSystemFolderPath, remoteStorageItem.Name); try { // We do not want to sync MS Office temp files, etc. from remote storage. // We also do not want to create MS Office files during transactional save in user file system. string remoteStorageItemFullPath = Path.Combine(remoteStorageFolderPath, remoteStorageItem.Name); if (!FsPath.AvoidSync(remoteStorageItemFullPath) && !FsPath.AvoidSync(userFileSystemPath)) { if (!FsPath.Exists(userFileSystemPath)) { LogMessage($"Creating", userFileSystemPath); await UserFileSystemRawItem.CreateAsync(userFileSystemFolderPath, new[] { remoteStorageItem }); LogMessage($"Created succesefully", userFileSystemPath); } } } catch (Exception ex) { LogError("Creation failed", userFileSystemPath, null, ex); } } // Update files/folders in user file system and sync subfolders. userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*"); foreach (string userFileSystemPath in userFileSystemChildren) { try { string itemName = Path.GetFileName(userFileSystemPath); string remoteStoragePath = Mapping.MapPath(userFileSystemPath); FileSystemItemBasicInfo remoteStorageItem = remoteStorageChildrenItems.FirstOrDefault(x => x.Name.Equals(itemName, StringComparison.InvariantCultureIgnoreCase)); if (!FsPath.AvoidSync(userFileSystemPath) && !FsPath.AvoidSync(remoteStoragePath)) { if (remoteStorageItem == null) { if (PlaceholderItem.GetItem(userFileSystemPath).GetInSync()) { // Delete the file/folder in user file system. LogMessage("Deleting item", userFileSystemPath); await new UserFileSystemRawItem(userFileSystemPath).DeleteAsync(); LogMessage("Deleted succesefully", userFileSystemPath); } } else { if (PlaceholderItem.GetItem(userFileSystemPath).GetInSync() && !await ETag.ETagEqualsAsync(userFileSystemPath, remoteStorageItem)) { // User file system <- remote storage update. LogMessage("Item modified", remoteStoragePath); await new UserFileSystemRawItem(userFileSystemPath).UpdateAsync(remoteStorageItem); LogMessage("Updated succesefully", userFileSystemPath); } // Update "locked by another user" icon. if (PlaceholderItem.GetItem(userFileSystemPath).GetInSync()) { await new UserFileSystemRawItem(userFileSystemPath).SetLockedByAnotherUserAsync(remoteStorageItem.LockedByAnotherUser); } // Hydrate / dehydrate the file. if (new UserFileSystemRawItem(userFileSystemPath).HydrationRequired()) { LogMessage("Hydrating", userFileSystemPath); new PlaceholderFile(userFileSystemPath).Hydrate(0, -1); LogMessage("Hydrated succesefully", userFileSystemPath); } else if (new UserFileSystemRawItem(userFileSystemPath).DehydrationRequired()) { LogMessage("Dehydrating", userFileSystemPath); new PlaceholderFile(userFileSystemPath).Dehydrate(0, -1); LogMessage("Dehydrated succesefully", userFileSystemPath); } } } } catch (Exception ex) { LogError("Update failed", userFileSystemPath, null, ex); } // Synchronize subfolders. try { if (Directory.Exists(userFileSystemPath)) { await SyncronizeFolderAsync(userFileSystemPath); } } catch (Exception ex) { LogError("Folder sync failed:", userFileSystemPath, null, ex); } } }
/// <summary> /// Recursively synchronizes all files and folders moved in user file system with the remote storage. /// Synchronizes only folders already loaded into the user file system. /// </summary> /// <param name="userFileSystemFolderPath">Folder path in user file system.</param> internal async Task SyncronizeMovedAsync(string userFileSystemFolderPath) { // In case of on-demand loading the user file system contains only a subset of the server files and folders. // Here we sync folder only if its content already loaded into user file system (folder is not offline). // The folder content is loaded inside IFolder.GetChildrenAsync() method. if (new DirectoryInfo(userFileSystemFolderPath).Attributes.HasFlag(System.IO.FileAttributes.Offline)) { //LogMessage("Folder offline, skipping:", userFileSystemFolderPath); return; } IEnumerable <string> userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*"); //LogMessage("Synchronizing:", userFileSystemFolderPath); foreach (string userFileSystemPath in userFileSystemChildren) { string userFileSystemOldPath = null; try { //$<PlaceholderItem.IsPlaceholder if (!PlaceholderItem.IsPlaceholder(userFileSystemPath)) { // Convert regular file/folder to placeholder. // The file/folder was created or overwritten. PlaceholderItem.ConvertToPlaceholder(userFileSystemPath, false); LogMessage("Converted to placeholder", userFileSystemPath); } //$> if (!FsPath.AvoidSync(userFileSystemPath)) { PlaceholderItem userFileSystemItem = PlaceholderItem.GetItem(userFileSystemPath); if (userFileSystemItem.IsMoved()) { // Process items moved in user file system. userFileSystemOldPath = userFileSystemItem.GetOriginalPath(); await new RemoteStorageRawItem(userFileSystemOldPath, this).MoveToAsync(userFileSystemPath); } else { // Restore Original Path and 'locked' icon that are lost during MS Office transactional save. // We keep Original Path to process moved files when app was not running. userFileSystemOldPath = userFileSystemItem.GetOriginalPath(); if (!userFileSystemItem.IsNew() && string.IsNullOrEmpty(userFileSystemOldPath)) { // Restore Original Path. LogMessage("Saving Original Path", userFileSystemItem.Path); userFileSystemItem.SetOriginalPath(userFileSystemItem.Path); // Restore the 'locked' icon. bool isLocked = await Lock.IsLockedAsync(userFileSystemPath); await new UserFileSystemRawItem(userFileSystemPath).SetLockIconAsync(isLocked); } } } } catch (Exception ex) { LogError("Move in remote storage failed", userFileSystemOldPath, userFileSystemPath, ex); } // Synchronize subfolders. try { if (FsPath.IsFolder(userFileSystemPath)) { await SyncronizeMovedAsync(userFileSystemPath); } } catch (Exception ex) { LogError("Folder move sync failed", userFileSystemPath, null, ex); } } }