/// <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); } }
/// <inheritdoc/> public async Task DeleteCompletionAsync(IOperationContext operationContext, IResultContext resultContext) { // On Windows, for move with overwrite on folders to function correctly, // the deletion of the folder in the remote storage must be done in DeleteCompletionAsync() // Otherwise the folder will be deleted before files in it can be moved. Logger.LogMessage($"{nameof(IFileSystemItem)}.{nameof(DeleteCompletionAsync)}()", UserFileSystemPath); FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(RemoteStoragePath); if (remoteStorageItem != null) { if (remoteStorageItem is FileInfo) { remoteStorageItem.Delete(); } else { (remoteStorageItem as DirectoryInfo).Delete(true); } Logger.LogMessage("Deleted item in remote storage succesefully", UserFileSystemPath); } Engine.CustomDataManager(UserFileSystemPath, Logger).Delete(); }
/// <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> /// 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); } }
///<inheritdoc> public async Task MoveToAsync(string userFileSystemNewPath, byte[] targetParentItemId, IOperationContext operationContext, IConfirmationResultContext resultContext) { string userFileSystemOldPath = this.UserFileSystemPath; Logger.LogMessage($"{nameof(IFileSystemItem)}.{nameof(MoveToAsync)}()", userFileSystemOldPath, userFileSystemNewPath); string remoteStorageOldPath = RemoteStoragePath; string remoteStorageNewPath = Mapping.MapPath(userFileSystemNewPath); FileSystemInfo remoteStorageOldItem = FsPath.GetFileSystemItem(remoteStorageOldPath); if (remoteStorageOldItem != null) { if (remoteStorageOldItem is FileInfo) { (remoteStorageOldItem as FileInfo).MoveTo(remoteStorageNewPath, true); } else { (remoteStorageOldItem as DirectoryInfo).MoveTo(remoteStorageNewPath); } Logger.LogMessage("Moved item in remote storage succesefully", userFileSystemOldPath, userFileSystemNewPath); } await Engine.CustomDataManager(userFileSystemOldPath, Logger).MoveToAsync(userFileSystemNewPath); }
/// <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 the 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); // 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. if (!FsPath.Exists(userFileSystemPath)) { 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. FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath); if (remoteStorageItem != null) { IFileSystemItemMetadata newItemInfo = Mapping.GetUserFileSysteItemMetadata(remoteStorageItem); if (await engine.ServerNotifications(userFileSystemParentPath).CreateAsync(new[] { newItemInfo }) > 0) { LogMessage($"Created succesefully", userFileSystemPath); engine.CustomDataManager(userFileSystemPath, this).IsNew = false; } } } } catch (Exception ex) { LogError($"{e.ChangeType} failed", remoteStoragePath, null, 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; string userFileSystemPath = null; try { userFileSystemPath = Mapping.ReverseMapPath(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. if (IsModified(userFileSystemPath, remoteStoragePath)) { FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath); IFileSystemItemMetadata itemInfo = Mapping.GetUserFileSysteItemMetadata(remoteStorageItem); // Because of the on-demand population the file or folder placeholder may not exist in the user file system. if (await engine.ServerNotifications(userFileSystemPath).UpdateAsync(itemInfo)) { LogMessage("Updated succesefully", userFileSystemPath); } } } catch (IOException ex) { // The file is blocked in the user file system. This is a normal behaviour. LogMessage(ex.Message); } catch (Exception ex) { LogError($"{e.ChangeType} failed", remoteStoragePath, null, ex); } }
/// <summary> /// Updates information about the file or folder placeholder in the user file system. /// This method automatically hydrates and dehydrate files. /// </summary> /// <remarks>This method failes if the file or folder in user file system is modified (not in-sync with the remote storage).</remarks> /// <param name="itemInfo">New file or folder info.</param> /// <returns>True if the file was updated. False - otherwise.</returns> public async Task <bool> UpdateAsync(IFileSystemItemMetadata itemInfo) { FileSystemItemMetadataExt itemInfoExt = itemInfo as FileSystemItemMetadataExt ?? throw new NotImplementedException($"{nameof(FileSystemItemMetadataExt)}"); try { // Because of the on-demand population the file or folder placeholder may not exist in the user file system. if (FsPath.Exists(userFileSystemPath)) { PlaceholderItem placeholderItem = PlaceholderItem.GetItem(userFileSystemPath); // To be able to update the item we need to remove the read-only attribute. if ((FsPath.GetFileSystemItem(userFileSystemPath).Attributes | System.IO.FileAttributes.ReadOnly) != 0) { FsPath.GetFileSystemItem(userFileSystemPath).Attributes &= ~System.IO.FileAttributes.ReadOnly; } // Dehydrate/hydrate the file, update file size, custom data, creation date, modification date, attributes. await virtualDrive.Engine.ServerNotifications(userFileSystemPath).UpdateAsync(itemInfoExt); // Set ETag. await eTagManager.SetETagAsync(itemInfoExt.ETag); // Clear icon. //await ClearStateAsync(); // Set the read-only attribute and all custom columns data. await SetLockedByAnotherUserAsync(itemInfoExt.LockedByAnotherUser); await SetCustomColumnsDataAsync(itemInfoExt.CustomProperties); return(true); } } catch (Exception ex) { await SetDownloadErrorStateAsync(ex); // Rethrow the exception preserving stack trace of the original exception. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw(); } return(false); }
//$> //$<PlaceholderItem.SetItemInfo /// <summary> /// Updates information about the file or folder placeholder in the user file system. /// This method automatically hydrates and dehydrate files. /// </summary> /// <remarks>This method failes if the file or folder in user file system is modified (not in-sync with the remote storage).</remarks> /// <param name="itemInfo">New file or folder info.</param> /// <returns>True if the file was updated. False - otherwise.</returns> public async Task <bool> UpdateAsync(FileSystemItemBasicInfo itemInfo) { try { // Because of the on-demand population the file or folder placeholder may not exist in the user file system. if (FsPath.Exists(userFileSystemPath)) { PlaceholderItem placeholderItem = PlaceholderItem.GetItem(userFileSystemPath); // To be able to update the item we need to remove the read-only attribute. if ((FsPath.GetFileSystemItem(userFileSystemPath).Attributes | System.IO.FileAttributes.ReadOnly) != 0) { FsPath.GetFileSystemItem(userFileSystemPath).Attributes &= ~System.IO.FileAttributes.ReadOnly; } // Dehydrate/hydrate the file, update file size, custom data, creation date, modification date, attributes. placeholderItem.SetItemInfo(itemInfo); // Set ETag. await ETag.SetETagAsync(userFileSystemPath, itemInfo.ETag); // Clear icon. //await ClearStateAsync(); // Set the "locked by another user" icon and all custom columns data. await SetLockedByAnotherUserAsync(itemInfo.LockedByAnotherUser); await SetCustomColumnsDataAsync(itemInfo.CustomProperties); return(true); } } catch (Exception ex) { await SetDownloadErrorStateAsync(ex); // Rethrow the exception preserving stack trace of the original exception. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw(); } 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> /// Creates or updates the item in the remote storage. /// </summary> /// <param name="mode"> /// Indicates if the file should created or updated. /// Supported modes are <see cref="FileMode.CreateNew"/> and <see cref="FileMode.Open"/> /// </param> /// <param name="lockInfo">Information about the lock. Pass null if the item is not locked.</param> private async Task CreateOrUpdateAsync(FileMode mode, ServerLockInfo lockInfo = null) { if ((mode != FileMode.CreateNew) && (mode != FileMode.Open)) { throw new ArgumentOutOfRangeException("mode", $"Must be {FileMode.CreateNew} or {FileMode.Open}"); } FileSystemInfo userFileSystemItem = FsPath.GetFileSystemItem(userFileSystemPath); using (WindowsFileSystemItem userFileSystemWinItem = WindowsFileSystemItem.OpenReadAttributes(userFileSystemPath, FileMode.Open, FileShare.Read)) //await using (FileStream userFileSystemStream = userFileSystemFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read)) { // Create the new file/folder in the remote storage only if the file/folder in the user file system was not moved. // If the file is moved in user file system, move must first be syched to remote storage. if ((mode == FileMode.CreateNew) && PlaceholderItem.GetItem(userFileSystemPath).IsMoved()) { string originalPath = PlaceholderItem.GetItem(userFileSystemPath).GetOriginalPath(); throw new ConflictException(Modified.Client, $"The item was moved. Original path: {originalPath}"); } // Ensures LastWriteTimeUtc is in sync with file content after Open() was called. userFileSystemItem.Refresh(); IFileSystemItemBasicInfo info = GetBasicInfo(userFileSystemItem); // Update remote storage file. FileStream userFileSystemStream = null; try { string eTag = null; if (FsPath.IsFile(userFileSystemPath)) { // File is marked as not in-sync when updated OR moved. // Opening a file for reading triggers hydration, make sure to open only if content is modified. if (PlaceholderFile.GetFileDataSizeInfo(userFileSystemWinItem.SafeHandle).ModifiedDataSize > 0) { //userFileSystemStream = new FileStream(userFileSystemWinItem.SafeHandle, FileAccess.Read); userFileSystemStream = ((FileInfo)userFileSystemItem).Open(FileMode.Open, FileAccess.Read, FileShare.Read); } if (mode == FileMode.CreateNew) { string userFileSystemParentPath = Path.GetDirectoryName(userFileSystemPath); IUserFolder userFolder = await virtualDrive.GetItemAsync <IUserFolder>(userFileSystemParentPath); eTag = await userFolder.CreateFileAsync((IFileBasicInfo)info, userFileSystemStream); } else { IUserFile userFile = await virtualDrive.GetItemAsync <IUserFile>(userFileSystemPath); eTag = await userFile.UpdateAsync((IFileBasicInfo)info, userFileSystemStream, lockInfo); } } else { if (mode == FileMode.CreateNew) { string userFileSystemParentPath = Path.GetDirectoryName(userFileSystemPath); IUserFolder userFolder = await virtualDrive.GetItemAsync <IUserFolder>(userFileSystemParentPath); eTag = await userFolder.CreateFolderAsync((IFolderBasicInfo)info); } else { IUserFolder userFolder = await virtualDrive.GetItemAsync <IUserFolder>(userFileSystemPath); eTag = await userFolder.UpdateAsync((IFolderBasicInfo)info, lockInfo); } } await ETag.SetETagAsync(userFileSystemPath, eTag); if (mode == FileMode.CreateNew) { PlaceholderItem.GetItem(userFileSystemPath).SetOriginalPath(userFileSystemPath); } } finally { if (userFileSystemStream != null) { userFileSystemStream.Close(); } } PlaceholderItem.SetInSync(userFileSystemWinItem.SafeHandle, true); } }