我找不到任何文档来概述使用OneDrive存储和保持应用程序文件在C#中跨设备同步的正确方法的任何文档
我已经在OneDrive开发中心阅读了文档,但是我不理解http代码。(仅限自学C#)。
我有点理解,我使用delta方法从OneDrive中获取更改的文件,然后将其保存到本地,但是我不知道确切的方式,因此通过使用GetAsync<>方法手动检查本地与OneDrive来解决它。与可能在API中处理得更好的东西相比,我目前的实现(下面作为参考)似乎比较笨拙。
此外,似乎没有反向“增量”功能吗?也就是说,我在本地将文件写入应用程序,然后告诉OneDrive同步更改。那是因为我需要使用该PutAsync<>方法实际上传它吗?(目前我在做什么)
public async Task<T> ReadFromXML<T>(string gamename, string filename)
{
string filepath = _appFolder + @"\" + gamename + @"\" + filename + ".xml";
T objectFromXML = default(T);
var srializer = new XmlSerializer(typeof(T));
Item oneDItem = null;
int casenum = 0;
//_userDrive is the IOneDriveClient
if (_userDrive != null && _userDrive.IsAuthenticated)
{
try
{
oneDItem = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Request().GetAsync();
if (oneDItem != null) casenum += 1;
}
catch (OneDriveException)
{ }
}
StorageFile localfile = null;
try
{
localfile = await ApplicationData.Current.LocalFolder.GetFileAsync(filepath);
if (localfile != null) casenum += 2;
}
catch (FileNotFoundException)
{ }
switch (casenum)
{
case 0:
//neither exist. Throws exception to tbe caught by the calling method, which should then instantiate a new object of type <T>
throw new FileNotFoundException();
case 1:
//OneDrive only - should copy the stream to a new local file then return the object
StorageFile writefile = await ApplicationData.Current.LocalFolder.CreateFileAsync(filepath, CreationCollisionOption.ReplaceExisting);
using (var newlocalstream = await writefile.OpenStreamForWriteAsync())
{
using (var oneDStream = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Content.Request().GetAsync())
{
oneDStream.CopyTo(newlocalstream);
}
}
using (var newreadstream = await writefile.OpenStreamForReadAsync())
{ objectFromXML = (T)srializer.Deserialize(newreadstream); }
break;
case 2:
//Local only - returns the object
using (var existinglocalstream = await localfile.OpenStreamForReadAsync())
{ objectFromXML = (T)srializer.Deserialize(existinglocalstream); }
break;
case 3:
//Both - compares last modified. If OneDrive, replaces local data then returns the object
var localinfo = await localfile.GetBasicPropertiesAsync();
var localtime = localinfo.DateModified;
var oneDtime = (DateTimeOffset)oneDItem.FileSystemInfo.LastModifiedDateTime;
switch (oneDtime > localtime)
{
case true:
using (var newlocalstream = await localfile.OpenStreamForWriteAsync())
{
using (var oneDStream = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Content.Request().GetAsync())
{ oneDStream.CopyTo(newlocalstream); }
}
using (var newreadstream = await localfile.OpenStreamForReadAsync())
{ objectFromXML = (T)srializer.Deserialize(newreadstream); }
break;
case false:
using (var existinglocalstream = await localfile.OpenStreamForReadAsync())
{ objectFromXML = (T)srializer.Deserialize(existinglocalstream); }
break;
}
break;
}
return objectFromXML;
}
Run Code Online (Sandbox Code Playgroud)
同步需要几个不同的步骤,其中一些可以通过OneDrive API来帮助您,有些则需要您自己完成。
更改检测
第一步显然是检测是否有任何更改。OneDrive API提供了两种机制来检测服务中的更改:
可以使用带有以下内容的标准请求来检测单个文件的更改If-None-Match:
await this.userDrive.Drive.Special.AppRoot.ItemWithPath(remotePath).Content.Request(new Option[] { new HeaderOption("If-None-Match", "etag") }).GetAsync();
Run Code Online (Sandbox Code Playgroud)
如果该文件根本不存在,则会返回一个404 Not Found。否则,如果文件未更改,则会返回一个304 Not Modified。
否则,您将获得文件的当前状态。
可以使用deltaAPI 检测层次结构的更改:
await this.userDrive.Drive.Special.AppRoot.Delta(previousDeltaToken).Request().GetAsync();
Run Code Online (Sandbox Code Playgroud)
这将返回自上次调用以来已更改的所有项目的当前状态delta。如果这是第一次调用,previousDeltaToken则将为null,并且API将返回中所有项目的当前状态AppRoot。对于响应中的每个文件,您都需要再次往返于服务以获取内容。
在本地,您需要枚举所有感兴趣的文件,并比较时间戳以确定是否已更改。
显然,前面的步骤需要了解“最后看到”状态,因此您的应用程序将需要以某种形式的数据库/数据结构来跟踪此状态。我建议跟踪以下内容:
+------------------+---------------------------------------------------------------------------+
| Property | Why? |
+------------------+---------------------------------------------------------------------------+
| Local Path | You'll need this so that you can map a local file to its service identity |
| Remote Path | You'll need this if you plan to address the remote file by path |
| Remote Id | You'll need this if you plan to address the remote file by unique id |
| Hash | The hash representing the current state of the file |
| Local Timestamp | Needed to detect local changes |
| Remote Timestamp | Needed for conflict resolution |
| Remote ETag | Needed to detect remote changes |
+------------------+---------------------------------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)
此外,如果使用此delta方法,则需要存储响应中的token值delta。这是与项目无关的,因此需要存储在某些全局字段中。
冲突解决
如果双方都检测到更改,则您的应用将需要经历冲突解决过程。如果一个应用程序不了解要同步的文件,则可能需要提示用户进行手动冲突解决,或者执行类似分叉文件的操作,因此现在有两个副本。但是,处理自定义文件格式的应用程序应具有足够的知识,可以有效地合并文件,而无需任何形式的用户交互。显然,这完全取决于要同步的文件。
应用更改
最后一步是将合并状态推到所需的任何位置(例如,如果更改是本地的,则推到远程,如果更改是远程的,则推到本地,否则,如果更改在两个地方都推到两个位置)。确保此步骤的发生方式很重要,以避免替换发生“更改检测”步骤之后写入的内容。在本地,您可能会在此过程中通过锁定文件来完成此操作,但是您不能使用远程文件来做到这一点。相反,您将希望使用etag值来确保服务仅在状态仍为您期望的状态时才接受请求:
await this.userDrive.Drive.Special.AppRoot.ItemWithPath(remotePath).Content.Request(new Option[] { new HeaderOption("If-Match", "etag") }).PutAsync(newContentStream);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3463 次 |
| 最近记录: |