首页 文章

正确使用OneDrive API同步文件的方法

提问于
浏览
1

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

我已经阅读了OneDrive Dev Center的文档,但我不理解http代码 . (仅限自学C#) .

我有点明白,我使用delta方法从OneDrive获取更改的文件,然后在本地保存,但我无法确切知道如何,所以通过使用 GetAsync<> 方法手动检查本地vs OneDrive来解决它 . 我认为当前的实现(下面仅供参考)与API中可能处理得更好相比,相当笨拙 .

另外,它没有'delta'功能?也就是说,我在本地向应用程序写入文件,然后告诉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;
    }

1 回答

  • 3

    同步需要几个不同的步骤,OneDrive API中的一些将帮助您,其中一些您将不得不自己做 .

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

    • 可以使用带有 If-None-Match 的标准请求检测单个文件的更改:
    await this.userDrive.Drive.Special.AppRoot.ItemWithPath(remotePath).Content.Request(new Option[] { new HeaderOption("If-None-Match", "etag") }).GetAsync();
    

    如果该文件没有't yet exist at all you'将返回 404 Not Found . 否则,如果文件没有改变,你将得到一个 304 Not Modified .
    否则,您将获得该文件的当前状态 .

    • 可以使用 delta API检测层次结构的更改:
    await this.userDrive.Drive.Special.AppRoot.Delta(previousDeltaToken).Request().GetAsync();
    

    这将返回自上次调用 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                                           |
    +------------------+---------------------------------------------------------------------------+
    

    此外,如果使用 delta 方法,则需要存储 delta 响应中的 token 值 . 这与项目无关,因此需要存储在某个全局字段中 .

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

    Apply Changes
    最后一步是将合并状态推送到需要的任何地方(例如,如果更改是本地的,则推送远程,如果更改是远程的,则推送本地,否则如果更改是在两个位置推送两个位置) . 它可能通过在此过程中锁定文件来实现此目的,但是您无法使用远程文件执行此操作 . 相反,如果状态仍然是您所期望的,您将需要使用etag值来确保服务仅接受请求:

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

相关问题