/// <inheritdoc/> public async Task GetChildrenAsync(string pattern, IOperationContext operationContext, IFolderListingResultContext resultContext) { // This method has a 60 sec timeout. // To process longer requests and reset the timout timer call one of the following: // - IGetChildrenContext.ReturnChildren() method. // - IGetChildrenContext.ReportProgress() method. LogMessage($"IFolder.GetChildrenAsync({pattern})", this.FullPath); string remoreStoragePath = Mapping.MapPath(this.FullPath); IEnumerable <FileSystemInfo> remoteStorageChildren = new DirectoryInfo(remoreStoragePath).EnumerateFileSystemInfos(pattern); List <IFileSystemItemBasicInfo> userFileSystemChildren = new List <IFileSystemItemBasicInfo>(); foreach (FileSystemInfo remoteStorageItem in remoteStorageChildren) { string userFileSystemPath = Path.Combine(this.FullPath, Path.GetFileName(remoteStorageItem.Name)); if (!FsPath.Exists(userFileSystemPath)) { // Uncomment to simulate slow network access. //Thread.Sleep(10000); //resultContext.ReportProgress(remoteStorageChildren.Count(), userFileSystemChildren.Count()); LogMessage("Creating:", Path.GetFileName(remoteStorageItem.Name)); FileSystemItemBasicInfo userFileSystemItemInfo = Mapping.GetUserFileSysteItemInfo(remoteStorageItem); userFileSystemChildren.Add(userFileSystemItemInfo); } } // To signal that the children enumeration is completed // always call this method in GetChildrenAsync(), even if the folder is empty. resultContext.ReturnChildren(userFileSystemChildren.ToArray(), userFileSystemChildren.Count()); }
/// <summary> /// Returns true if the remote storage ETag and user file system ETags are equal. False - otherwise. /// </summary> /// <param name="userFileSystemPath">User file system item.</param> /// <param name="remoteStorageItem">Remote storage item info.</param> /// <remarks> /// ETag is updated on the server during every document update and is sent to client with a file. /// During client->server update it is sent back to the remote storage together with a modified content. /// This ensures the changes on the server are not overwritten if the document on the server is modified. /// </remarks> internal static async Task <bool> ETagEqualsAsync(string userFileSystemPath, FileSystemItemBasicInfo remoteStorageItem) { string remoteStorageETag = remoteStorageItem.ETag; string userFileSystemETag = await ETag.GetETagAsync(userFileSystemPath); if (string.IsNullOrEmpty(remoteStorageETag) && string.IsNullOrEmpty(userFileSystemETag)) { // We assume the remote storage is not using ETags or no ETag is ssociated with this file/folder. return(true); } return(remoteStorageETag == userFileSystemETag); }
/// <summary> /// Creates or updates folder in the remote storage. /// </summary> /// <param name="remoteStoragePath">Path of the folder to be created or updated in the remote storage.</param> /// <param name="newInfo">New information about the folder, such as modification date, attributes, custom data, etc.</param> /// <param name="mode">Specifies if a new folder should be created or existing folder should be updated.</param> /// <returns>New ETag returned from the remote storage.</returns> protected async Task <string> CreateOrUpdateFolderAsync(string remoteStoragePath, IFolderBasicInfo newInfo, FileMode mode) { // Get ETag and lock-token here and send it to the remote storage with the new item content/info if needed. if (mode == FileMode.Open) { // Get ETag. string eTag = await ETag.GetETagAsync(UserFileSystemPath); // Get lock-token. string lockToken = Lock?.LockToken; } DirectoryInfo remoteStorageItem = new DirectoryInfo(remoteStoragePath); try { Program.RemoteStorageMonitorInstance.Enabled = false; // Disable RemoteStorageMonitor to avoid circular calls. remoteStorageItem.Create(); string userFileSystemPath = Mapping.ReverseMapPath(remoteStoragePath); if (mode == FileMode.Open) { // Verify that the item in the remote storage is not modified since it was downloaded to the user file system. // In your real-life application you will send the ETag to the server as part of the update request. FileSystemItemBasicInfo itemInfo = Mapping.GetUserFileSysteItemBasicInfo(remoteStorageItem); if (!(await ETag.ETagEqualsAsync(userFileSystemPath, itemInfo))) { throw new ConflictException(Modified.Server, "Item is modified in the remote storage, ETags not equal."); } } // Update ETag/LastWriteTime in user file system, so the synchronyzation or remote storage monitor would not start the new update. // This is only required to avoid circular updates because of the simplicity of this sample. // In your real-life application you will receive a new ETag from server in the update response. string eTag = newInfo.LastWriteTime.ToBinary().ToString(); await ETag.SetETagAsync(userFileSystemPath, eTag); remoteStorageItem.Attributes = newInfo.Attributes; remoteStorageItem.CreationTimeUtc = newInfo.CreationTime; remoteStorageItem.LastWriteTimeUtc = newInfo.LastWriteTime; remoteStorageItem.LastAccessTimeUtc = newInfo.LastAccessTime; remoteStorageItem.LastWriteTimeUtc = newInfo.LastWriteTime; return(eTag); } finally { Program.RemoteStorageMonitorInstance.Enabled = true; } }
/// <summary> /// Returns true if the remote storage ETag and user file system ETags are equal. False - otherwise. /// </summary> /// <param name="userFileSystemPath">User file system item.</param> /// <param name="remoteStorageItem">Remote storage item info.</param> /// <remarks> /// ETag is updated on the server during every document update and is sent to client with a file. /// During client->server update it is sent back to the remote storage together with a modified content. /// This ensures the changes on the server are not overwritten if the document on the server is modified. /// </remarks> internal static async Task <bool> ETagEqualsAsync(string userFileSystemPath, FileSystemItemBasicInfo remoteStorageItem) { string remoteStorageETag = remoteStorageItem.ETag; // Intstead of the real ETag we store remote storage LastWriteTime when // creating and updating files/folders. string userFileSystemETag = await ETag.GetETagAsync(userFileSystemPath); if (string.IsNullOrEmpty(userFileSystemETag)) { // No ETag associated with the file. This is a new file created in user file system. return(false); } return(remoteStorageETag == userFileSystemETag); }
/// <summary> /// Gets list of files and folders in this folder in the remote storage. /// </summary> /// <param name="pattern">Search pattern.</param> /// <returns> /// List of files and folders located in this folder in the remote /// storage that correstonds with the provided search pattern. /// </returns> public async Task <IEnumerable <FileSystemItemBasicInfo> > EnumerateChildrenAsync(string pattern) { // This method has a 60 sec timeout. // To process longer requests modify the IFolder.GetChildrenAsync() implementation. IEnumerable <FileSystemInfo> remoteStorageChildren = new DirectoryInfo(RemoteStoragePath).EnumerateFileSystemInfos(pattern); List <FileSystemItemBasicInfo> userFileSystemChildren = new List <FileSystemItemBasicInfo>(); foreach (FileSystemInfo remoteStorageItem in remoteStorageChildren) { FileSystemItemBasicInfo itemInfo = Mapping.GetUserFileSysteItemBasicInfo(remoteStorageItem); userFileSystemChildren.Add(itemInfo); } return(userFileSystemChildren); }
/// <summary> /// Creates or updates file in the remote storage. /// </summary> /// <param name="remoteStoragePath">Path of the file to be created or updated in the remote storage.</param> /// <param name="newInfo">New information about the file, such as modification date, attributes, custom data, etc.</param> /// <param name="mode">Specifies if a new file should be created or existing file should be updated.</param> /// <param name="newContentStream">New file content or null if the file content is not modified.</param> /// <returns>New ETag returned from the remote storage.</returns> protected async Task <string> CreateOrUpdateFileAsync(string remoteStoragePath, IFileBasicInfo newInfo, FileMode mode, Stream newContentStream = null) { // Get ETag and lock-token here and send it to the remote storage with the new item content/info if needed. if (mode == FileMode.Open) { // Get ETag. string eTag = await ETag.GetETagAsync(UserFileSystemPath); // Get lock-token. string lockToken = Lock?.LockToken; } FileInfo remoteStorageItem = new FileInfo(remoteStoragePath); try { Program.RemoteStorageMonitorInstance.Enabled = false; // Disable RemoteStorageMonitor to avoid circular calls. // If another thread is trying to sync the same file, this call will fail in other threads. // In your implementation you must lock your remote storage file, or block it for reading and writing by other means. await using (FileStream remoteStorageStream = remoteStorageItem.Open(mode, FileAccess.Write, FileShare.None)) { string userFileSystemPath = Mapping.ReverseMapPath(remoteStoragePath); if (mode == FileMode.Open) { // Verify that the item in the remote storage is not modified since it was downloaded to the user file system. // In your real-life application you will send the ETag to the server as part of the update request. FileSystemItemBasicInfo itemInfo = Mapping.GetUserFileSysteItemBasicInfo(remoteStorageItem); if (!(await ETag.ETagEqualsAsync(userFileSystemPath, itemInfo))) { throw new ConflictException(Modified.Server, "Item is modified in the remote storage, ETags not equal."); } } // Update ETag/LastWriteTime in user file system, so the synchronyzation or remote storage monitor would not start the new update. // This is only required to avoid circular updates because of the simplicity of this sample. // In your real-life application you will receive a new ETag from the server in the update response // and return it from this method. string eTag = newInfo.LastWriteTime.ToBinary().ToString(); await ETag.SetETagAsync(userFileSystemPath, eTag); // Update remote storage file content. if (newContentStream != null) { await newContentStream.CopyToAsync(remoteStorageStream); remoteStorageStream.SetLength(newContentStream.Length); } // Update remote storage file basic info. WindowsFileSystemItem.SetFileInformation( remoteStorageStream.SafeFileHandle, newInfo.Attributes, newInfo.CreationTime, newInfo.LastWriteTime, newInfo.LastAccessTime, newInfo.LastWriteTime); return(eTag); } } finally { Program.RemoteStorageMonitorInstance.Enabled = true; } }