使用 OneDrive API 同步文件的正确方法 [英] Correct way to use OneDrive API to sync files

查看:20
本文介绍了使用 OneDrive API 同步文件的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我找不到任何描述使用 OneDrive 在 C# 中跨设备存储和保持应用文件同步的正确方法的文档

I can't find any documentation that outlines the correct way to use OneDrive to store and keep app files syncrhonised across devices in C#

我已阅读 OneDrive Dev Center 上的文档,但我不明白http 代码.(仅限自学 C#).

I have read the documentation at OneDrive Dev Center but I don't understand the http code. (self taught C# only).

我有点理解我使用 delta 方法从 OneDrive 获取更改的文件,然后在本地保存,但我不知道确切的方法,所以通过使用 GetAsync<> 方法.与 API 中可能处理得更好的实现相比,我当前的实现(以下供参考)在我看来相当笨拙.

I kind of understand that I use the delta method to get changed files from OneDrive, to then save locally, but I can't figure out exactly how, so have gotten around it by checking local vs OneDrive manually using the GetAsync<> methods. My current implementation (below for reference) seems to me to be rather clunky compared to what is probably handled better in the API.

另外,好像没有反向'delta'功能?也就是说,我在本地将文件写入应用程序,然后告诉 OneDrive 同步更改.那是因为我需要使用 PutAsync<> 方法实际上传它吗?(目前我在做什么)

In addition, it doesn't appear that there is a reverse 'delta' function? That is, where I write a file to the app locally, then tell OneDrive to sync the change. Is that because I need to actually upload it using the PutAsync<> method? (Currently what I am doing)

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;
    }

推荐答案

同步需要几个不同的步骤,其中一些步骤 OneDrive API 可以帮助您,另外一些您必须自己完成.

Synchronization requires a few different steps, some of which the OneDrive API will help you with, some of which you'll have to do yourself.

变更检测
第一阶段显然是检测是否有任何变化.OneDrive API 提供了两种机制来检测服务中的变化:

Change Detection
The first stage is obviously to detect whether anything has changed. The OneDrive API provides two mechanism to detect changes in the service:

  1. 可以使用带有 If-None-Match 的标准请求检测单个文件的更改:

  1. Changes for individual files can be detected using a standard request with an If-None-Match:

await this.userDrive.Drive.Special.AppRoot.ItemWithPath(remotePath).Content.Request(new Option[] { new HeaderOption("If-None-Match", "etag") }).GetAsync();

如果该文件根本不存在,您将返回404 Not Found.否则,如果文件未更改,您将返回 304 Not Modified.
否则,您将获得文件的当前状态.

If the file doesn't yet exist at all you'll get back a 404 Not Found. Else if the file has not changed you'll get back a 304 Not Modified.
Else you'll get the current state of the file.

可以使用 delta API 检测层次结构的变化:

Changes for a hierarchy can be detected using the delta API:

await this.userDrive.Drive.Special.AppRoot.Delta(previousDeltaToken).Request().GetAsync();

这将返回自上次调用 delta 以来更改的所有项目的当前状态.如果这是第一次调用,previousDeltaToken 将为空,API 将返回 AppRoot 中所有项目的当前状态.对于响应中的每个文件,您需要再次往返服务以获取内容.

This will return the current state for all items that changed since the last invocation of delta. If this is the first invocation, previousDeltaToken will be null and the API will return the current state for ALL items within the AppRoot. For each file in the response you'll need to make another round-trip to the service to get the content.

在本地,您需要枚举所有感兴趣的文件并比较时间戳以确定是否发生了某些变化.

On the local side you'll need to enumerate all of the files of interest and compare the timestamps to determine if something has changed.

显然,前面的步骤需要了解上次看到"状态,因此您的应用程序需要以某种形式的数据库/数据结构跟踪这一点.我建议跟踪以下内容:

Obviously the previous steps require knowledge of the "last seen" state, and so your application will need to keep track of this in some form of database/data structure. I'd suggest tracking the following:

+------------------+---------------------------------------------------------------------------+
|     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                                           |
+------------------+---------------------------------------------------------------------------+

此外,如果使用 delta 方法,您需要存储 delta 响应中的 token 值.这是独立于项目的,因此需要存储在某个全局字段中.

Additionally, if using the delta approach you'll need to store the token value from the delta response. This is item independent, so would need to be stored in some global field.

冲突解决
如果在双方都检测到更改,您的应用程序将需要通过冲突解决过程.不了解正在同步的文件的应用程序需要提示用户手动解决冲突,或者执行诸如 fork 文件之类的操作,以便现在有两个副本.但是,处理自定义文件格式的应用程序应该有足够的知识来有效地合并文件,而无需任何形式的用户交互.这显然完全取决于正在同步的文件.

Conflict Resolution
If changes were detected on both sides your app will need to go through a conflict resolution process. An app that lacks an understanding of the files being synced would need to either prompt the user for manual conflict resolution, or do something like fork the file so there are now two copies. However, apps that are dealing with custom file formats should have enough knowledge to effectively merge the files without any form of user interaction. What that entails is obviously completely dependent on the file being synced.

应用更改
最后一步是将合并的状态推送到需要的地方(例如,如果更改是本地的,则推送远程,如果更改是远程的,则推送本地,否则,如果更改在两个地方都推送).确保此步骤以这样一种方式发生,以避免替换在更改检测"步骤发生后写入的内容,这一点很重要.在本地,您可能会通过在此过程中锁定文件来完成此操作,但是您不能对远程文件执行此操作.相反,您需要使用 etag 值来确保服务仅在状态仍为您所期望的情况下才接受请求:

Apply Changes
The final step is to push the merged state to wherever is required (e.g. if the change was local then push remote, if the change was remote then push local, otherwise if the change was in both places push both places). It's important to make sure this step occurs in such a way as to avoid replacing content that was written after the "Change Detection" step has taken place. Locally you'd probably accomplish this by locking the file during the process, however you cannot do that with the remote file. Instead you'll want to use the etag value to make sure the service only accepts the request if the state is still what you expect:

await this.userDrive.Drive.Special.AppRoot.ItemWithPath(remotePath).Content.Request(new Option[] { new HeaderOption("If-Match", "etag") }).PutAsync(newContentStream);

这篇关于使用 OneDrive API 同步文件的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆