https://msdn.microsoft.com/en-us/sync/bb887623
Introduction to Microsoft Sync Framework File Synchronization Provider
IntroductionMicrosoft Sync Framework is a comprehensive综合 synchronization同步 platform that enables collaboration合作 and offline scenarios方案;情节;剧本 for applications, services, and devices. Developers can build synchronization ecosystems生态系统 that integrate使…成整体 any application and any type of data, using any protocol over any network. As part of the new framework, we're also working on simplifying the development of end-to-end synchronization solutions by providing domain-specific components for common scenarios such as synchronizing relational databases, file systems, devices, etc. Towards this goal, we have developed a reusable provider for synchronizing the contents of file system directories on PCs and removable media such as USB thumb drives. In this article, I'll cover the details of this reusable file synchronization provider, along with enabled scenarios and sample code for getting started. |
The file synchronization provider is designed to be a generic, reusable component that can be used for synchronizing files and folders between any NTFS or FAT formatted file volumes卷.
It uses Sync Framework's powerful metadata元数据 model to enable peer-to-peer synchronization of file data with support for arbitrary任意的 topologies拓扑结构 (client/server, full-mesh全网状, P2P) including support for removable media such as flash drives, USB thumb drives, etc.
It enables 3rd parties to build file synchronization and roaming漫游 scenarios场景 into their end-to-end synchronization solutions without having to worry about directly interacting交互 with the file system.
Key features of the File system provider include:
Security note:安全提示
The file synchronization provider does not synchronization security information, such as the Discretionary Access Control List (DACL自主访问控制列表).
It is up to the application or user to correctly secure保护 the destination folders to help prevent unauthorized access.
Also, files in an encrypted folder are decrypted before they are sent and will not be encrypted in the destination folder.
Be aware that this means that even if the source folder is encrypted, the files will still not be encrypted when they are sent.
To help prevent unauthorized access or tampering篡改, the communication channel between the provider and the folder must be trusted.
One of the goals of the file synchronization provider design is to enable a broad set of scenarios一套广泛的场景 for synchronizing files and folders.
Since the file synchronization provider is available for use and redistribution再分配 through the Sync Framework,
ISVs【Independent Software Vendors独立软件开发商】 are now able to
build file synchronization functionality into their existing applications
or create new applications to enable interesting file and folder synchronization scenarios.
Some of the possible end-user scenarios enabled by the Sync Framework and this component include:
To put some of the following material材料 in context, let's start with a short code snippet片段 for how the file synchronization provider can be invoked by an application.
The following is part of a full code sample provided later in this article.
The code below is written in C# and uses the .NET wrapper classes for the file synchronization provider but the capabilities and patterns for C++ code to invoke the native COM component are very similar.
public static void SyncFileSystemReplicasOneWay(string sourceReplicaRootPath, string destinationReplicaRootPath,FileSyncScopeFilter filter, FileSyncOptions options) { FileSyncProvider sourceProvider = null; FileSyncProvider destinationProvider = null; try { sourceProvider = new FileSyncProvider( sourceReplicaRootPath, filter, options); destinationProvider = new FileSyncProvider( destinationReplicaRootPath, filter, options); destinationProvider.AppliedChange += new EventHandler<AppliedChangeEventArgs>(OnAppliedChange); destinationProvider.SkippedChange += new EventHandler<SkippedChangeEventArgs>(OnSkippedChange); SyncOrchestrator agent = new SyncOrchestrator(); agent.LocalProvider = sourceProvider; agent.RemoteProvider = destinationProvider; agent.Direction = SyncDirectionOrder.Upload; // Synchronize source to destination Console.WriteLine("Synchronizing changes to replica: " + destinationProvider.RootDirectoryPath); agent.Synchronize(); } finally { // Release resources if (sourceProvider != null) sourceProvider.Dispose(); if (destinationProvider != null) destinationProvider.Dispose(); } }
The file synchronization provider synchronizes incremental changes between two file system locations (also defined as a replica by Sync Framework).
In order to do this, at the start of every synchronization session, it needs to evaluate评估 incremental changes on each replica since the last synchronization.
This process is called change detection.
The provider stores a small amount of information (called metadata) that describes where and when the item was changed, giving a snapshot快照 of every file and folder in the replica复制品.
Changes are detected by comparing the current file metadata with the version last saved in the snapshot.
For files, the comparison is done on the file size, file times, file attributes, file name (case-sensitive), and optionally a hash of the file contents.
For folders, the comparison is done on folder attributes and folder name (case-sensitive).
A heuristic探索的 algorithm is used to determine file renames.
If a file was only renamed or moved, just that operation will be performed on the other replica, avoiding a full stream transfer, which can be expensive.
For folders, a rename or move is currently processed as a delete and create on other replicas.
Files inside the renamed folder however will be processed as renames and no unnecessary stream transfers will be done.
By default, the file synchronization provider detects changes at the start of every synchronization session.
This behavior can be modified by the application by using initialization flags and the explicit DetectChanges method on the provider.
Given the above method of detecting changes (to support FAT file systems), if there are a large number of files in the replica,
change detection may be an expensive operation and should be done only as often as required by the user experience of the application.
For interactive交互式的 applications, the file synchronization provider supports extensive广泛的;大量的;广阔的 progress reporting during the synchronization operation.
This enables applications to present a progress UI to the user and display the current state of the synchronization operation.
Progress information is reported to the application via events in managed code or callback methods in unmanaged code.
An application can skip a given change by handling the ApplyingChange event in managed code or the OnApplyingChange callback method in unmanaged code.
The file synchronization provider supports Preview mode synchronization which enables applications to inspect检查 and display what changes would happen during synchronization.
In preview mode, no changes are committed to the file system or the metadata, however most of the progress reporting notifications are still sent out for changes
that would be applied if synchronization were run without preview.
This can be used by the application to present verification UI to the user with all changes that will be made if the synchronization is executed.
Note that the file synchronization provider does not provide an upfront estimate前期预估 of the total number of files to be synchronized before the synchronization operation starts
because this can be expensive to figure out depending on the size of the file system replica and all applications may not want to pay the price.
Applications can, however, collect the same statistics by doing a two-pass approach: first run a Preview mode synchronization session between the two replicas to collect statistics about the total number of changes, including the number of creates, deletes, updates, etc., and then run the real synchronization session with the proper progress bar display.
An application using the file synchronization provider can configure the provider to filter out certain files or folders from the synchronization operation.
For example, the user may want to synchronize everything but media files from a given folder.
The file synchronization provider can be initialized with a scope filter that determines the set of files to synchronize.
Synchronization applications can choose to filter files based on file names, sub-directories, or file attributes.
Filter Type | Default | Example |
Filename-based exclusion | No files excluded | Do NOT synchronize files that match *.mov, *.wmv, or foo.txt |
Filename-based inclusion | All files included | ONLY synchronize files that match *.doc, *.txt, or special.jpg |
Subdirectory exclude | No sub-folders excluded | Do NOT synchronize the list of subdirectories that match "c:\users\videos". |
File attribute-based exclusion | Nothing excluded based on attributes | Do NOT synchronize system or hidden files |
Note that only files/folders that pass ALL the scope filters are included in the synchronization.
Also, applications should ensure that they use the same scope filter for all replicas in the synchronization community for a consistent user experience and to ensure convergence集合 of synchronized data between all replicas.
As mentioned above in the Progress Reporting section, applications can ask the provider to skip certain files by handling the ApplyingChange event in managed code or the OnApplyingChange callback method in unmanaged code.
Lastly, certain system files used by Windows Explorer are always excluded from the synchronization, such as desktop.ini and thumbs.db (they do need to be marked with both the SYSTEM and HIDDEN attributes for this to happen).
There are times when users will modify the same file or folder independently on different file system replicas. The next time these replicas are synchronized with each other, the file synchronization provider attempts to reconcile these conflicting changes in the best possible way. In general, the file synchronization provider conflict resolution follows certain key principles which are determined by desired user experience (these are also principles followed by and enabled by the underlying Sync Framework):
The file synchronization provider only supports a fixed policy for automatic resolution of conflicts.
The following is the general policy used by the file synchronization provider when automatically reconciling conflicting changes:
As can be seen there's a lot of intricate logic that needs to be implemented to handle these situations correctly, especially when synchronizing across more than two replicas with an arbitrary synchronization topology. The good news is that the file synchronization provider handles most of this without the application writer having to worry about it.
The file synchronization provider is designed to gracefully优雅 handle single file errors while synchronizing a set of files.
A host of potentially transient短暂的;路过的 errors can happen when trying to read or write files
(locked files, file changed after change detection due to concurrent file activity, access denied, not enough space on disk for a given file, etc.).
The file synchronization provider handles these by skipping these files during the synchronization operation such that the rest of the data can be synchronized successfully.
For every file change that is not applied successfully to the destination replica, an application callback method is invoked with the file details and error information.
In some cases, the synchronizing application may wish to handle these programmatically and try to re-synchronize again after fixing up the problem.
In other cases it can be presented to the user at the end of the synchronization session to ensure that the user is aware of files that didn't synchronize successfully.
It is also possible that the entire synchronization operation may fail sometimes due to a transient error,
in which case the proper error code will be returned from the synchronization session and the application can react accordingly.
For example, concurrent synchronization operations on the same replica are not currently supported,
so trying this will result in a "replica in use" error code that can be handled by retrying the synchronization after some time.
The file synchronization provider is designed to correctly handle local concurrent operations by other applications on files that may be part of an ongoing synchronization operation.
If a local concurrent change has happened on a file after the last change detection pass on the replica, either on the source or the destination,
to prevent loss of the concurrent change, any changes to that file will not be synchronized until the next synchronization session
(or the next change detection pass, if the application is using explicit change detection).
Also, when synchronizing changes to a file's contents, the new file stream will be applied atomically to the destination location to avoid the situation where the user ends up with a partially correct copy of the file.
public class FileSyncProviderSample { public static void Main(string[] args) { if (args.Length < 2 || string.IsNullOrEmpty(args[0]) || string.IsNullOrEmpty(args[1]) || !Directory.Exists(args[0]) || !Directory.Exists(args[1])) { Console.WriteLine( "Usage: FileSyncSample [valid directory path 1] [valid directory path 2]"); return; } string replica1RootPath = args[0]; string replica2RootPath = args[1]; try { // Set options for the synchronization operation FileSyncOptions options = FileSyncOptions.ExplicitDetectChanges | FileSyncOptions.RecycleDeletedFiles | FileSyncOptions.RecyclePreviousFileOnUpdates | FileSyncOptions.RecycleConflictLoserFiles; FileSyncScopeFilter filter = new FileSyncScopeFilter(); filter.FileNameExcludes.Add("*.lnk"); // Exclude all *.lnk files // Explicitly detect changes on both replicas upfront, to avoid two change // detection passes for the two-way synchronization DetectChangesOnFileSystemReplica( replica1RootPath, filter, options); DetectChangesOnFileSystemReplica( replica2RootPath, filter, options); // Synchronization in both directions SyncFileSystemReplicasOneWay(replica1RootPath, replica2RootPath, null, options); SyncFileSystemReplicasOneWay(replica2RootPath, replica1RootPath, null, options); } catch (Exception e) { Console.WriteLine("\nException from File Synchronization Provider:\n" + e.ToString()); } } public static void DetectChangesOnFileSystemReplica( string replicaRootPath, FileSyncScopeFilter filter, FileSyncOptions options) { FileSyncProvider provider = null; try { provider = new FileSyncProvider(replicaRootPath, replicaRootPath, filter, options); provider.DetectChanges(); } finally { // Release resources if (provider != null) provider.Dispose(); } } public static void SyncFileSystemReplicasOneWay( string sourceReplicaRootPath, string destinationReplicaRootPath, FileSyncScopeFilter filter, FileSyncOptions options) { FileSyncProvider sourceProvider = null; FileSyncProvider destinationProvider = null; try { sourceProvider = new FileSyncProvider( sourceReplicaRootPath, filter, options); destinationProvider = new FileSyncProvider( destinationReplicaRootPath, filter, options); destinationProvider.AppliedChange += new EventHandler<AppliedChangeEventArgs>(OnAppliedChange); destinationProvider.SkippedChange += new EventHandler<SkippedChangeEventArgs>(OnSkippedChange); SyncOrchestrator agent = new SyncOrchestrator(); agent.LocalProvider = sourceProvider; agent.RemoteProvider = destinationProvider; agent.Direction = SyncDirection.Upload; // Sync source to destination Console.WriteLine("Synchronizing changes to replica: " + destinationProvider.RootDirectoryPath); agent.Synchronize(); } finally { // Release resources if (sourceProvider != null) sourceProvider.Dispose(); if (destinationProvider != null) destinationProvider.Dispose(); } } public static void OnAppliedChange(object sender, AppliedChangeEventArgs args) { switch (args.ChangeType) { case ChangeType.Create: Console.WriteLine("-- Applied CREATE for file " + args.NewFilePath); break; case ChangeType.Delete: Console.WriteLine("-- Applied DELETE for file " + args.OldFilePath); break; case ChangeType.Overwrite: Console.WriteLine("-- Applied OVERWRITE for file " + args.OldFilePath); break; case ChangeType.Rename: Console.WriteLine("-- Applied RENAME for file " + args.OldFilePath + " as " + args.NewFilePath); break; } } public static void OnSkippedChange(object sender, SkippedChangeEventArgs args) { Console.WriteLine("-- Skipped applying " + args.ChangeType.ToString().ToUpper() + " for " + (!string.IsNullOrEmpty(args.CurrentFilePath) ? args.CurrentFilePath : args.NewFilePath) + " due to error"); if (args.Exception != null) Console.WriteLine(" [" + args.Exception.Message + "]"); } }
In this article we have seen how the file synchronization provider can be used to synchronize the contents of file system directories on PCs and removable media such as USB thumb drives, and discussed the details of this synchronization provider.
Microsoft Sync Framework includes all of the things required to integrate applications into an offline or collaboration based network by using the pre-created providers or writing new custom providers.
Providers enable any data source to participate in data synchronization regardless of network or device type.
For more information or to get a copy of Microsoft Sync Framework, please visit http://msdn.microsoft.com/sync.