Пример #1
0
        /// <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);

                    if (await engine.ServerNotifications(userFileSystemPath).UpdateAsync(itemInfo))
                    {
                        // Because of the on-demand population the file or folder placeholder may not exist in the user file system.
                        // In this case the IServerNotifications.UpdateAsync() call is ignored.
                        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);
            }
        }
Пример #2
0
        /// <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);

                    FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath);
                    if (remoteStorageItem != null)
                    {
                        IFileSystemItemMetadata newItemInfo = Mapping.GetUserFileSysteItemMetadata(remoteStorageItem);
                        if (await engine.ServerNotifications(userFileSystemParentPath).CreateAsync(new[] { newItemInfo }) > 0)
                        {
                            // Because of the on-demand population, the parent folder placeholder may not exist in the user file system
                            // or the folder may be offline. In this case the IServerNotifications.CreateAsync() call is ignored.
                            LogMessage($"Created succesefully", userFileSystemPath);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogError($"{e.ChangeType} failed", remoteStoragePath, null, ex);
            }
        }
Пример #3
0
        /// <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:
            // - resultContext.ReturnChildren() method.
            // - resultContext.ReportProgress() method.

            Logger.LogMessage($"{nameof(IFolder)}.{nameof(GetChildrenAsync)}({pattern})", UserFileSystemPath);

            IEnumerable <FileSystemInfo> remoteStorageChildren = new DirectoryInfo(RemoteStoragePath).EnumerateFileSystemInfos(pattern);

            List <IFileSystemItemMetadata> userFileSystemChildren = new List <IFileSystemItemMetadata>();

            foreach (FileSystemInfo remoteStorageItem in remoteStorageChildren)
            {
                IFileSystemItemMetadata itemInfo = Mapping.GetUserFileSysteItemMetadata(remoteStorageItem);

                string userFileSystemItemPath = Path.Combine(UserFileSystemPath, itemInfo.Name);

                // Filtering existing files/folders. This is only required to avoid extra errors in the log.
                if (!FsPath.Exists(userFileSystemItemPath))
                {
                    Logger.LogMessage("Creating", userFileSystemItemPath);
                    userFileSystemChildren.Add(itemInfo);
                }
            }

            // To signal that the children enumeration is completed
            // always call ReturnChildren(), even if the folder is empty.
            resultContext.ReturnChildren(userFileSystemChildren.ToArray(), userFileSystemChildren.Count());
        }
Пример #4
0
        /// <summary>
        /// Creates or updates this item in the remote storage.
        /// </summary>
        /// <param name="remoteStorageUri">Uri of the file to be created or updated in the remote storage.</param>
        /// <param name="newInfo">New information about the file or folder, 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="content">New file content or null if the file content is not modified.</param>
        /// <param name="eTagOld">The ETag to be sent to the remote storage as part of the update request to make sure the content is not overwritten.</param>
        /// <param name="lockInfo">Information about the lock. Caller passes null if the item is not locked.</param>
        /// <returns>The new ETag returned from the remote storage.</returns>
        protected static async Task <string> CreateOrUpdateFileAsync(
            Uri remoteStorageUri, IFileSystemItemMetadata newInfo, FileMode mode, Stream content = null, string eTagOld = null, ServerLockInfo lockInfo = null)
        {
            string eTagNew = null;

            if (content != null || mode == FileMode.CreateNew)
            {
                long contentLength = content != null ? content.Length : 0;

                IWebRequestAsync request = await Program.DavClient.GetFileWriteRequestAsync(
                    remoteStorageUri, null, contentLength, 0, -1, lockInfo?.LockToken, eTagOld);

                // Update remote storage file content.
                using (Stream davContentStream = await request.GetRequestStreamAsync())
                {
                    if (content != null)
                    {
                        await content.CopyToAsync(davContentStream);
                    }

                    // Get the new ETag returned by the server (if any).
                    // We return the new ETag to the Engine to be stored with the file unlil the next update.
                    IWebResponseAsync response = await request.GetResponseAsync();

                    eTagNew = response.Headers["ETag"];
                    response.Close();
                }
            }
            return(eTagNew);
        }
Пример #5
0
        /// <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:
            // - resultContext.ReturnChildren() method.
            // - resultContext.ReportProgress() method.

            Logger.LogMessage($"{nameof(IFolder)}.{nameof(GetChildrenAsync)}({pattern})", UserFileSystemPath);

            IEnumerable <FileSystemInfo> remoteStorageChildren = new DirectoryInfo(RemoteStoragePath).EnumerateFileSystemInfos(pattern);

            List <IFileSystemItemMetadata> userFileSystemChildren = new List <IFileSystemItemMetadata>();

            foreach (FileSystemInfo remoteStorageItem in remoteStorageChildren)
            {
                IFileSystemItemMetadata itemInfo = Mapping.GetUserFileSysteItemMetadata(remoteStorageItem);

                string userFileSystemItemPath = Path.Combine(UserFileSystemPath, itemInfo.Name);

                // Filtering existing files/folders. This is only required to avoid extra errors in the log.
                if (!FsPath.Exists(userFileSystemItemPath))
                {
                    Logger.LogMessage("Creating", userFileSystemItemPath);
                    userFileSystemChildren.Add(itemInfo);
                }

                ExternalDataManager customDataManager = Engine.CustomDataManager(userFileSystemItemPath);

                // Mark this item as not new, which is required for correct MS Office saving opertions.
                customDataManager.IsNew = false;

                // Save ETag on the client side, to be sent to the remote storage as part of the update.
                await customDataManager.ETagManager.SetETagAsync("1234567890");
            }

            // To signal that the children enumeration is completed
            // always call ReturnChildren(), even if the folder is empty.
            resultContext.ReturnChildren(userFileSystemChildren.ToArray(), userFileSystemChildren.Count());

            // Show some custom column in file manager for demo purposes.
            foreach (IFileSystemItemMetadata itemInfo in userFileSystemChildren)
            {
                string userFileSystemItemPath = Path.Combine(UserFileSystemPath, itemInfo.Name);

                FileSystemItemPropertyData eTagColumn = new FileSystemItemPropertyData((int)CustomColumnIds.ETag, "1234567890");
                await Engine.CustomDataManager(userFileSystemItemPath).SetCustomColumnsAsync(new [] { eTagColumn });
            }
        }
Пример #6
0
        /// <inheritdoc/>
        public async Task GetChildrenAsync(string pattern, IOperationContext operationContext, IFolderListingResultContext resultContext)
        {
            Logger.LogMessage($"{nameof(IFolder)}.{nameof(GetChildrenAsync)}({pattern})", UserFileSystemPath);

            IEnumerable <FileSystemInfo> remoteStorageChildren = new DirectoryInfo(RemoteStoragePath).EnumerateFileSystemInfos(pattern);

            List <IFileSystemItemMetadata> userFileSystemChildren = new List <IFileSystemItemMetadata>();

            foreach (FileSystemInfo remoteStorageItem in remoteStorageChildren)
            {
                IFileSystemItemMetadata itemInfo = Mapping.GetUserFileSysteItemMetadata(remoteStorageItem);
                userFileSystemChildren.Add(itemInfo);
            }

            // To signal that the children enumeration is completed
            // always call ReturnChildren(), even if the folder is empty.
            resultContext.ReturnChildren(userFileSystemChildren.ToArray(), userFileSystemChildren.Count);
        }
Пример #7
0
        /// <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);
        }
Пример #8
0
        /// <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();

                IFileSystemItemMetadata info = GetMetadata(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);
                            IVirtualFolder userFolder = await virtualDrive.GetItemAsync <IVirtualFolder>(userFileSystemParentPath, logger);

                            eTag = await userFolder.CreateFileAsync((IFileMetadata)info, userFileSystemStream);
                        }
                        else
                        {
                            IVirtualFile userFile = await GetItemAsync(userFileSystemPath) as IVirtualFile;

                            eTag = await virtualDrive.GetETagManager(userFileSystemPath).GetETagAsync();

                            eTag = await userFile.UpdateAsync((IFileMetadata)info, userFileSystemStream, eTag, lockInfo);
                        }
                    }
                    else
                    {
                        if (mode == FileMode.CreateNew)
                        {
                            string         userFileSystemParentPath = Path.GetDirectoryName(userFileSystemPath);
                            IVirtualFolder userFolder = await virtualDrive.GetItemAsync <IVirtualFolder>(userFileSystemParentPath, logger);

                            eTag = await userFolder.CreateFolderAsync((IFolderMetadata)info);
                        }
                        else
                        {
                            IVirtualFolder userFolder = await GetItemAsync(userFileSystemPath) as IVirtualFolder;

                            eTag = await virtualDrive.GetETagManager(userFileSystemPath).GetETagAsync();

                            eTag = await userFolder.UpdateAsync((IFolderMetadata)info, eTag, lockInfo);
                        }
                    }
                    await virtualDrive.GetETagManager(userFileSystemPath).SetETagAsync(eTag);

                    if (mode == FileMode.CreateNew)
                    {
                        PlaceholderItem.GetItem(userFileSystemPath).SetOriginalPath(userFileSystemPath);
                    }
                }
                finally
                {
                    if (userFileSystemStream != null)
                    {
                        userFileSystemStream.Close();
                    }
                }

                PlaceholderItem.SetInSync(userFileSystemWinItem.SafeHandle, true);
            }
        }