Bootstrap

为你开发的应用赋能文档协作功能

为你开发的应用赋能文档协作功能

协同办公

学习如何集成OnlyOffice办公套件,为你的用户开启其文档协作功能,研究已有应用案例的代码示例。

在我们之前的文章里面,我们讲述了在开发web应用中,如何通过OnlyOffice来启用文档查看和编辑的功能,而这篇文档,我们将讲述如何引入多人共同实时协同编辑文档的能力,协同编辑是如何工作的,并且从已有的应用中展示示例代码。

集成基础概念

OnlyOffice是一套开源Office解决方案套件,它提供的API允许你在开发自己的网络应用时,使用OnlyOffice来实现同步和分享的功能,例如文档管理软件DMS、内容管理软件CMS以及任何其他网络应用平台,使其可以管理编辑文档、表格、幻灯片等办公文件。

基本集成功能:

  • 在系统前端打开各种类型Office文件,使用OnlyOffice的Java Script初始化编辑器;
  • 对文件的保存是在前端客户端中发送请求,到后端服务器执行保存的操作。

这就意味着,你可以打开存贮在你的DMS空间上的办公文件,使用OnlyOffice Docs软件编辑它们,直接保存回到DMS存储空间中去,我们为此写过一些文档,讲解了其中的技术原理,这些文档在这里

因此这里假定你已经了解了基本的文档协同编辑集成功能的概念,那么这里你就可以继续推进一步,深入了解同时编辑、存储文档版本功能的底层实现技术。

创建文件

OnlyOffice Doc软件的功能,首先是能打开存储在DMS存储空间里面的办公文档,然后进行编辑,并且能够多人同时编辑同一份文档。当然,你也可以在你的应用里面直接新建一个文档,办公文档一般包括主流的docx文档、xlsx表格以及pptx幻灯片三种,新建一个文档存在DMS中提供多人实时协同编辑。

这个功能并不是直接连接到编辑器中的,通过集成代码,我们添加现成已有的空白文档模板,并且支持多语言文字的版本。

当一个使用者新建一个文档,这个模板被拷贝到一个特定的文件夹,之后该文件即可被编辑器打开。

我们看一个例子,Alfresco 欧福瑞科公司产品是如何集成OnlyOffice的。

下面是如何集成按钮的代码

<alfresco-config>
    <config evaluator="string-compare" condition="DocumentLibrary">
        <create-content>
            <content id="document-onlyoffice-create-docx" type="javascript" label="actions.document.onlyoffice-create-docx">
                <param name="function">onOnlyofficeCreateDocx</param>
            </content>
        </create-content>
    </config>
</alfresco-config>

然后定义当点击按钮后操作的代码:

(function () {
    // function registration
    YAHOO.Bubbling.fire("registerAction",
    {
        actionName: "onOnlyofficeCreateDocx",
        fn: function (obj) {
            // opening the generated file
            window.open("onlyoffice-edit?nodeRef=" + obj.nodeRef + "&new=application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        }
    });
})();

开启协同编辑

同时编辑同一个文档、表格或幻灯片是OnlyOffice最重要的功能特征,多个不同使用者通过同时打开同一个文档辨识符钥匙(document.key)来实现的同时编辑同一个文档的功能。

这把钥匙是编辑器打开文档的唯一辨识符,每次保存修改写入存储空间的时候就会更新辨识符,因而,不能使用系统数据中真实文件的id来辨识确认文件,使用文件路径也不是好的技术选择,(例如在NextCloud里面,不同使用者可能把一个文件用不同的名字互相分享)。

操作系统会为文档计算哈希/hash值,这个数值是唯一的并且会随着文件的修改而实时更新变化,这非常适合当作document.key的使用。

下面就是在Confluence应用中集成该功能的代码

public static String getKeyOfFile(Long attachmentId) {
    String hashCode = AttachmentUtil.getHashCode(attachmentId); // built-in function for getting hash
    return hashCode;
}

如果你计划开启“强制自动保存”功能的话,并且开启同时编辑功能就是一个技术亮点。

开启强制保存功能的本意是每次点击保存按钮或者键盘快捷键ctrl+s,编辑器立刻保存当前的文档版本,然而,在同时编辑模式下,你不能使用当前版本的hash值去连接同时编辑,需要额外的另存一份文档辨识符钥匙document.key,从协同编辑的场景session的伊始就创建出来,生命周期直到一个协同编辑场景最后一个编辑者退出为止,甚至直到强制保存之后。

一个协同编辑场景Session是指,一个文档被第一个人用编辑器打开开始,到最后一个协同编辑者在编辑器中关闭这个文档为止。

下面就是在NextCloud里面,如何实现该功能的代码

public function getKey($file) {
    $fileId = $file->getId();
    // searching for the key
    $key = KeyManager::get($fileId);

    // if the key is empty, the file is not being edited
    if (empty($key) ) {
        // generate a unique key
        $key = $instanceId . "_" . $this->GUID();
        // save the key to the database for the next co-editing session
        KeyManager::set($fileId, $key);
    }
    return $key;
}

文档分享的权限管理

通常,系统允许使用者打开文档进行编辑或者仅仅是阅览,OnlyOffice Docs提供这两种典型的功能模式,但是其API还提供更高级的权限管理功能,例如,可以限制下载权限打印权限、以及禁止拷贝内容到内存

也可以把文档分享为只提供阅读功能、分享为提供审阅批注功能、或者其它模式,例如下面的OwnCloud集成的代码

// configuring editors initialization
$params = [
    "document" => [
        "fileType" => $ext,
        "key" => $key,
        "permissions" => [],
        "title" => $fileName,
        "url" => $fileUrl,
    ],
    "documentType" => $format["type"]
];
// file permissions for the current user
$storageShare = $file->getStorage()->getShare();
// file permissions attributes for the current user
$attributes = $storageShare->getAttributes();
// permission to download file
$params["document"]["permissions"]["download"] = $attributes->getAttribute("permissions", "download"); === true;
// permission to review file
$params["document"]["permissions"]["review"] = $attributes->getAttribute($this->appName, "review") === true;
// permission to fill forms
$params["document"]["permissions"]["fillForms"] = $attributes->getAttribute($this->appName, "fillForms") === true;
// permission to comment
$params["document"]["permissions"]["comment"] = $attributes->getAttribute($this->appName, "comment") === true;

关于这些文档分享权限管理的更多信息,可以参考这里

在实例间分享、协作文件

如果你的应用部署在多台终端机器上并且它们以某种逻辑互联,例如,在大学校园网络里面,部署有多个服务器实例,文件共享完全可以从一个实例发布服务,服务于另一个实例的一个终端使用者。

例如,NextCloud和OwnCloud使用了这个技术,使得文件可以在多个服务器实例中共享,这样的功能我们命名为联邦式的共享Federated Share。

为了实现联邦共享Federated Share,我们允许把多个实例连接到一台文档服务器Document Server,实现共同编辑,当文档服务器打开一个文档,它总是会要检查一下,该文档是否存在于另一个服务器实例instance,并且获取其文档辨识符钥匙document.key

下面就是在OwnCloud集成中的该功能代码:

// сhecking if file is from another instance
if ($file->getStorage()->instanceOfStorage(SharingExternalStorage::class)) {
    // making a request to get the key to the instance where file is stored
    $response = \OC::$server->getHTTPClientService()->newClient()->post($file->getStorage()->getRemote() . "/ocs/v2.php/apps/" . $this->appName . "/api/v1/key?format=json", [
        "body" => [
            "shareToken" => $file->getStorage()->getToken(), // file sharing token
            "path" => $file->getInternalPath() // path to file
        ]
    ]);
    // processing request response
    $body = \json_decode($response->getBody(), true);
    // extracting the key
    $key = $body["ocs"]["data"]["key"];
    // using the key 
    if (!empty($key)) {
        return $key;
    }
}
// getting key for current instance
$key = KeyManager::get($fileId);

安全性保障

在网络互联协同编辑办公领域里面,安全性总是首要考虑的因素。只有文档服务器Document Server才应该能够执行真实的文档读取、编辑和保存的操作,并且还需要确保使用者不能获取超过自身许可范围的数据。

为了保障安全,终端编辑器、内部服务器以及存储空间之间的命令交换、数据发送等,必须被经过基于密码的加密签名保护。在终端编辑器里密码key存在配置文件中,在存储服务空间一侧,它一般是系统设置中的一条项目。

签名使用Json网络令牌,确认权限来执行特定的操作来处理数据,包括所有的请求文件打开

下面就是Plone如何实现的安全性保障的代码。

# editors initialization configuration
config = {
    'documentType': fileUtils.getFileType(filename),
    'document': {
        'fileType': filetype,
        'key': url,
        'title': filename,
        'url': url
    },
    'editorConfig': {
        'callbackUrl': callbackUrl
    }
}
# adding token for configuration
config['token'] = jwt.encode(config, jwtSecret, algorithm="HS256").decode("utf-8")

版本历史

如果你的DMS数据存储管理服务支持文档版本管理的功能,那么就可以查阅文档修改历史甚至可以恢复到任意指定的版本。

对于那些允许记录存储额外信息的服务系统,可以添加打开历史功能,高亮显示变化部分,这是因为OnlyOffice分别存储了文档本身和修改历史,所以,当使用者打开一篇文档的版本历史时,修改历史也被发送到编辑器中

例如,在OwnCloud集成中的代码如下

var docEditor = new DocsAPI.DocEditor("iframeEditor", { // editor intialiazation
    ....
    events: { // subscribing to events
        ....
        "onRequestHistory": function (version) { // processing the event of opening history
            $.get(historyUrl, // requesting history from server
                function onSuccess(response) {
                    data = {
                        history: response, // forming data to open history
                    };
                    docEditor.refreshHistory(data); // passing history to the editor
                })
            }
    };
})

总结

这些就是最重要的但或许并不是所有最重要的集成代码实现,借此你就可以为你的用户而开发提供更好的文档协同编辑体验了,如果你还需要更深入学习这些技术点的深层原理,欢迎联系我们。
OnlyOffice使用效果截图
这是在测试计算机上打开两个不同的浏览器,用两个不同的使用者帐号登录同一个服务器,同时编辑同一个文档的画面截图,右下部分黑色是因为截图时候计算机的左侧扩展显示屏竖直方向放置,所以windows系统全屏截图,主桌面和扩展桌面一横一竖物理拼接,就会有右下部分纯黑色块。

附:本文所列举的这些代码属于哪些公司产品的简单列表

上文中提到DMS、CMS,文档管理软件、内容管理软件,并不用给出解释,而是通过上文中的功能描述和代码示例,就说明了DMS、CMS的功能了,并且,此文所列出来的代码都是来源明确的已经实用的代码,也就是下表中各家不同公司推出的DMS、CMS软件,这些软件在后台底层,使用了OnlyOffice的文档协同办公的功能,也就是说,你使用OnlyOffice,也可以自己开发类似列表里面这些应用系统的可能性,当然还依赖于你的开发能力。

;