/// <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 { if (!FsPath.AvoidSync(remoteStorageOldPath) && !FsPath.AvoidSync(remoteStorageNewPath)) { string userFileSystemOldPath = Mapping.ReverseMapPath(remoteStorageOldPath); string userFileSystemNewPath = Mapping.ReverseMapPath(remoteStorageNewPath); // Because of the on-demand population the file or folder placeholder may not exist in the user file system. if (await virtualDrive.ServerNotifications(userFileSystemOldPath, this).MoveToAsync(userFileSystemNewPath)) { LogMessage("Renamed succesefully:", userFileSystemOldPath, userFileSystemNewPath); } } } catch (Exception ex) { LogError($"{e.ChangeType} failed", remoteStorageOldPath, remoteStorageNewPath, 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 { // 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. FileSystemItemMetadataExt itemInfo = Mapping.GetUserFileSysteItemMetadata(remoteStorageItem); if (!await virtualDrive.GetETagManager(userFileSystemPath, this).ETagEqualsAsync(itemInfo)) { await virtualDrive.ServerNotifications(userFileSystemPath, this).UpdateAsync(itemInfo); LogMessage("Updated 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 { // 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. FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath); FileSystemItemMetadataExt newItemInfo = Mapping.GetUserFileSysteItemMetadata(remoteStorageItem); if (await virtualDrive.ServerNotifications(userFileSystemParentPath, this).CreateAsync(new[] { newItemInfo }) > 0) { LogMessage($"Created succesefully", userFileSystemPath); } } } catch (Exception ex) { LogError($"{e.ChangeType} failed", remoteStoragePath, null, ex); } }
//$> //$<IFileSystemItem.DeleteAsync ///<inheritdoc> public async Task DeleteAsync(IOperationContext operationContext, IConfirmationResultContext resultContext) { Logger.LogMessage("IFileSystemItem.DeleteAsync()", this.UserFileSystemPath); string userFileSystemPath = this.UserFileSystemPath; string remoteStoragePath = null; try { if (Engine.ChangesProcessingEnabled && !FsPath.AvoidSync(userFileSystemPath)) { await new RemoteStorageRawItem(userFileSystemPath, Logger).DeleteAsync(); Logger.LogMessage("Deleted item in remote storage succesefully", userFileSystemPath); } } catch (Exception ex) { Logger.LogError("Delete failed", remoteStoragePath, null, ex); } finally { resultContext.ReturnConfirmationResult(); } }
///<inheritdoc> public async Task MoveToAsync(string userFileSystemNewPath, IOperationContext operationContext, IConfirmationResultContext resultContext) { // Here we will simply move the file in remote storage and confirm the operation. // In your implementation you may implement a more complex scenario with offline operations support. LogMessage("IFileSystemItem.MoveToAsync()", this.FullPath, userFileSystemNewPath); string userFileSystemOldPath = this.FullPath; try { bool?inSync = null; try { Program.RemoteStorageMonitorInstance.Enabled = false; // Disable RemoteStorageMonitor to avoid circular calls. // When a file is deleted, it is moved to a Recycle Bin, that is why we check for recycle bin here. if (FsPath.Exists(userFileSystemOldPath) && !FsPath.IsRecycleBin(userFileSystemNewPath) && !FsPath.AvoidSync(userFileSystemOldPath) && !FsPath.AvoidSync(userFileSystemNewPath)) { inSync = PlaceholderItem.GetItem(userFileSystemOldPath).GetInSync(); string remoteStorageOldPath = Mapping.MapPath(userFileSystemOldPath); string remoteStorageNewPath = Mapping.MapPath(userFileSystemNewPath); FileSystemInfo remoteStorageOldItem = FsPath.GetFileSystemItem(remoteStorageOldPath); if (remoteStorageOldItem is FileInfo) { (remoteStorageOldItem as FileInfo).MoveTo(remoteStorageNewPath); } else { (remoteStorageOldItem as DirectoryInfo).MoveTo(remoteStorageNewPath); } LogMessage("Moved succesefully:", remoteStorageOldPath, remoteStorageNewPath); } } finally { resultContext.ReturnConfirmationResult(); // 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. if ((inSync != null) && PlaceholderItem.IsPlaceholder(userFileSystemNewPath)) { PlaceholderItem.GetItem(userFileSystemNewPath).SetInSync(inSync.Value); } } } catch (Exception ex) { // remove try-catch when error processing inside CloudProvider is fixed. LogError("Move failed:", $"From: {this.FullPath} to:{userFileSystemNewPath}", ex); } finally { Program.RemoteStorageMonitorInstance.Enabled = true; } }
//$<IFolder.CloseAsync /// <inheritdoc/> public async Task CloseAsync(IOperationContext operationContext, IResultContext context) { // Here, if the file in the user file system is modified (not in-sync), you will send the file content, // creation time, modification time and attributes to the remote storage. // We also send ETag, to make sure the changes on the server, if any, are not overwritten. Logger.LogMessage("IFile.CloseAsync()", UserFileSystemPath); string userFileSystemFilePath = UserFileSystemPath; // In case the file is moved it does not exist in user file system when CloseAsync() is called. if (Engine.ChangesProcessingEnabled && FsPath.Exists(userFileSystemFilePath) && !FsPath.AvoidSync(userFileSystemFilePath)) { // In case the file is overwritten it is converted to a regular file prior to CloseAsync(). // we need to convert it back into file/folder placeholder. if (!PlaceholderItem.IsPlaceholder(userFileSystemFilePath)) { PlaceholderItem.ConvertToPlaceholder(userFileSystemFilePath, false); Logger.LogMessage("Converted to placeholder", userFileSystemFilePath); } try { if (PlaceholderItem.GetItem(userFileSystemFilePath).IsNew()) { // Create new file in the remote storage. await RemoteStorageRawItem.CreateAsync(userFileSystemFilePath, Logger); } else { // Send content to remote storage. Unlock if auto-locked. await new RemoteStorageRawItem(userFileSystemFilePath, Logger).UpdateAsync(); } } catch (IOException ex) { // Either the file is already being synced in another thread or client or server file is blocked by concurrent process. // This is a normal behaviour. // The file must be synched by your synchronyzation service at a later time, when the file becomes available. Logger.LogMessage("Failed to upload file. Possibly in use by an application or blocked for synchronization in another thread:", ex.Message); } } }
/// <inheritdoc/> public async Task CloseAsync(IOperationContext operationContext, IResultContext context) { // Here, if the file in user file system is modified (not in-sync), we send file content, // creation time, modification time and attributes to remote storage. // We also create new ETag, associate it with a file in user file system and send it to the server. LogMessage("IFile.CloseAsync()", this.FullPath); string userFileSystemFilePath = this.FullPath; // In case the file is moved it does not exist in user file system when Close() is called. if (!FsPath.Exists(userFileSystemFilePath) || FsPath.AvoidSync(userFileSystemFilePath)) { return; } // In case the file is overwritten it is converted to a regular file prior to Close(). // we need to convert it back into file/folder placeholder. if (!PlaceholderItem.IsPlaceholder(userFileSystemFilePath)) { PlaceholderItem.ConvertToPlaceholder(userFileSystemFilePath, false); LogMessage("Converted to placeholder:", userFileSystemFilePath); } if (!PlaceholderItem.GetItem(userFileSystemFilePath).GetInSync()) { LogMessage("Changed:", userFileSystemFilePath); string remoteStorageFilePath = Mapping.MapPath(userFileSystemFilePath); try { await new RemoteStorageItem(remoteStorageFilePath).UpdateAsync(userFileSystemFilePath); LogMessage("Updated succesefully:", remoteStorageFilePath); } catch (IOException ex) { // Either the file is already being synced in another thread or client or server file is blocked by concurrent process. // This is a normal behaviour. // The file must be synched by your synchronyzation service at a later time, when the file becomes available. LogMessage("Failed to upload file. Possibly in use by an application or blocked for synchronization in another thread:", ex.Message); } } }
///<inheritdoc> public async Task DeleteAsync(IOperationContext operationContext, IConfirmationResultContext resultContext) { // Here we will simply delete the file in remote storage and confirm the operation. // In your implementation you may implement a more complex scenario with offline operations support. LogMessage("IFileSystemItem.DeleteAsync()", this.FullPath); string userFileSystemPath = this.FullPath; string remoteStoragePath = null; try { try { Program.RemoteStorageMonitorInstance.Enabled = false; // Disable RemoteStorageMonitor to avoid circular calls. remoteStoragePath = Mapping.MapPath(userFileSystemPath); if (FsPath.Exists(remoteStoragePath) && !FsPath.AvoidSync(userFileSystemPath)) { FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath); remoteStorageItem.Delete(); LogMessage("Deleted succesefully:", remoteStoragePath); } } finally { resultContext.ReturnConfirmationResult(); } } catch (Exception ex) { // remove try-catch when error processing inside CloudProvider is fixed. LogError("Delete failed:", remoteStoragePath, ex); } finally { Program.RemoteStorageMonitorInstance.Enabled = true; } }
/// <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 { 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 (await virtualDrive.ServerNotifications(userFileSystemPath, this).DeleteAsync()) { LogMessage("Deleted succesefully", userFileSystemPath); } } } catch (Exception ex) { LogError($"{e.ChangeType} failed", remoteStoragePath, null, ex); } }