回调处理程序

文档编辑服务JavaScript APIcallbackUrl 通知 文档存储服务 有关文档编辑的状态。 文档编辑服务 使用POST 请求,请求信息在正文中。

参数及其说明:

参数 描述 类型 出现
定义当用户对文档执行操作时接收到的对象。type 字段值可以具有以下值:
  • 0 - 用户断开与文档共同编辑的连接,
  • 1 - 新用户连接到文档共同编辑,
  • 2 - 用户单击 强制保存按钮
userid 字段值是用户标识符。
object数组 可选的
定义具有文档更改历史的对象数组。仅当 status 值等于 23 时,对象才存在。 必须作为对象的属性 changes 以参数形式发送给 refreshHistory 方法。 自 4.2 版起已删除,请改用 history object数组 可选的
使用文档编辑数据定义文件的链接,用于跟踪和显示文档更改历史记录。 仅当 status 等于 23 时,链接才存在。必须保存文件,并且必须使用 setHistoryData 方法将其地址作为 changesUrl 参数发送,以显示与特定文档版本对应的更改。 string 可选的
定义从 url 参数指定的链接下载文档的扩展名。 文件类型默认为 OOXML,但如果启用了 assemblyFormatAsOrigin 服务器设置,则文件将以原始格式保存。 string 可选的
定义执行 强制保存 请求时的启动器类型。 可以有以下值:
  • 0 - 为 命令服务执行强制保存请求,
  • 1 - 每次保存完成时都会执行强制保存请求(例如单击 保存 按钮),这仅在 forcesave 选项设置为 true时可用,
  • 2 - 强制保存请求由计时器按服务器配置中的设置执行,
  • 3 - 每次提交表单时都会执行强制保存请求(例如 提交表单 按钮被点击 )。
该类型仅在 status 值等于 67 时出现。
integer 可选的
用提交的表单数据,定义 JSON 文件的 URL。 此处描述了包含表单数据的数组结构。 该文件包含以下参数:
  • key - 表单键。 如果当前表单是单选按钮,则该字段包含表单组键,
    type: string,
    example: "Text1";
  • tag - 表单标签,
    type: string,
    example: "";
  • value - 当前表单值,
    type: string,
    example: "inner text";
  • type - 表单类型 (text, checkBox, picture, comboBox, dropDownList, dateTime, radio),
    type: string,
    example: "text".
status 值等于 6 并且 forcesavetype 值等于 3 时,该对象才存在。
对象 可选的
定义有文档更改历史的对象。 仅当 status 值等于 23 时,对象才存在。它包含对象 changesserverVersion,它们必须作为对象的属性 changesserverVersion 以参数形式发送给 refreshHistory 方法。 对象 可选的
定义编辑的文档标识符。 string 必需的
定义文档的状态。 可以有以下值:
  • 1 - 正在编辑文档,
  • 2 - 文档已准备好保存,
  • 3 - 发生文档保存错误,
  • 4 - 文档已关闭,没有任何更改,
  • 6 - 正在编辑文档,但保存了当前文档状态,
  • 7 - 强制保存文档时发生错误。
integer 必需的
定义已编辑的要由文档存储服务保存的文档的链接。 仅当 status 值等于 2, 3, 67 时,链接才存在。 string 可选的
定义发送到forcesaveinfo 命令的命令服务的自定义信息(如果它在请求中存在)。 string 可选的
定义打开文档进行编辑的用户的标识符列表;当文档被更改时,用户将返回最后编辑文档的用户的标识符(对于 status 2status 6 的应答)。 string数组 可选的

从 7.0 版开始, callbackUrl 来自同一用户的最后一个标签。在 7.0 版之前, callbackUrl 来自第一个用户标签。

状态 描述

每次用户连接或断开文档共同编辑时都会收到它。他们的 callbackUrl 被使用。

请注意,当用户在遭遇Internet 故障后返回到没有更改的文档时也可以收到 status 1。这种情况可以描述如下:

  • 当用户打开文档时,会发送 status 1
  • 如果 Internet 连接丢失并且用户没有对文档进行任何更改,则会发送 status 4。 屏幕上显示错误并在查看器中打开文档。
  • 在 100 秒内,互联网连接恢复,用户重新连接到文档并再次发送 status 1
  • 现在用户可以继续编辑文档。将收到 status 24,具体取决于用户是否对文档进行了任何更改。

它在编辑文档关闭后 10 秒 收到,该用户的标识符是最后一个将更改发送到文档编辑服务的用户。 对文件进行最后更改的用户的 callbackUrl 被使用。

它是在最后一个用户关闭所编辑的没有更改的文档情况下收到的。他们的 callbackUrl 被使用。

在执行强制保存请求时接收到。

callbackUrl 依赖于 forcesavetype 参数:

  • 如果 forcesavetype 参数设置为 1,则使用点击 保存 按钮的用户的 callbackUrl
  • 如果 forcesavetype 参数设置为 02,则使用对文件进行最后更改的用户的 callbackUrl
  • 如果 forcesavetype 参数设置为3,则使用点击 提交 按钮的用户的 callbackUrl

从版本 5.5 到版本 6.1,始终使用对文件进行最后更改的用户的 callbackUrl

{
    "actions": [{"type": 1, "userid": "78e1e841"}],
    "key": "Khirz6zTPdfd7",
    "status": 1,
    "users": ["6d5a81d0", "78e1e841"]
}
{
    "actions": [{"type": 0, "userid": "78e1e841"}],
    "changesurl": "https://documentserver/url-to-changes.zip",
    "history": {
        "changes": changes,
        "serverVersion": serverVersion
    },
    "filetype": "docx",
    "key": "Khirz6zTPdfd7",
    "status": 2,
    "url": "https://documentserver/url-to-edited-document.docx",
    "users": ["6d5a81d0"]
}
{
    "key": "Khirz6zTPdfd7",
    "status": 4
}
{
    "changesurl": "https://documentserver/url-to-changes.zip",
    "forcesavetype": 0,
    "history": {
        "changes": changes,
        "serverVersion": serverVersion
    },
    "filetype": "docx",
    "key": "Khirz6zTPdfd7",
    "status": 6,
    "url": "https://documentserver/url-to-edited-document.docx",
    "users": ["6d5a81d0"],
    "userdata": "sample userdata"
}

文档存储服务 必须返回以下响应,否则 文档编辑器 将显示错误消息:

{
    "error": 0
}

文档管理器文档存储服务 要么包含在社区版服务器中,要么必须由在自己的服务器上使用 ONLYOFFICE 文档服务器的软件集成商实施。

public class WebEditor : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        string body;
        using (var reader = new StreamReader(context.Request.InputStream))
            body = reader.ReadToEnd();

        var fileData = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(body);
        if ((int) fileData["status"] == 2)
        {
            var req = WebRequest.Create((string) fileData["url"]);

            using (var stream = req.GetResponse().GetResponseStream())
            using (var fs = File.Open(PATH_FOR_SAVE, FileMode.Create))
            {
                var buffer = new byte[4096];
                int readed;
                while ((readed = stream.Read(buffer, 0, 4096)) != 0)
                    fs.Write(buffer, 0, readed);
            }
        }
        context.Response.Write("{\"error\":0}");
    }
}
PATH_FOR_SAVE 是保存文件的计算机文件夹的绝对路径,包括文件名。
public class IndexServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter writer = response.getWriter();

        Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A");
        String body = scanner.hasNext() ? scanner.next() : "";

        JSONObject jsonObj = (JSONObject) new JSONParser().parse(body);

        if((long) jsonObj.get("status") == 2)
        {
            String downloadUri = (String) jsonObj.get("url");

            URL url = new URL(downloadUri);
            java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
            InputStream stream = connection.getInputStream();

            File savedFile = new File(pathForSave);
            try (FileOutputStream out = new FileOutputStream(savedFile)) {
                int read;
                final byte[] bytes = new byte[1024];
                while ((read = stream.read(bytes)) != -1) {
                    out.write(bytes, 0, read);
                }

                out.flush();
            }

            connection.disconnect();
        }
        writer.write("{\"error\":0}");
    }
}
pathForSave 是保存文件的计算机文件夹的绝对路径,包括文件名。
var fs = require("fs");
var syncRequest = require("sync-request");

app.post("/track", function (req, res) {

    var updateFile = function (response, body, path) {
        if (body.status == 2)
        {
            var file = syncRequest("GET", body.url);
            fs.writeFileSync(path, file.getBody());
        }

        response.write("{\"error\":0}");
        response.end();
    }

    var readbody = function (request, response, path) {
        var content = "";
        request.on("data", function (data) {
            content += data;
        });
        request.on("end", function () {
            var body = JSON.parse(content);
            updateFile(response, body, path);
        });
    }

    if (req.body.hasOwnProperty("status")) {
        updateFile(res, req.body, pathForSave);
    } else {
        readbody(req, res, pathForSave)
    }
});
pathForSave 是保存文件的计算机文件夹的绝对路径,包括文件名。
<?php

if (($body_stream = file_get_contents("php://input"))===FALSE){
    echo "Bad Request";
}

$data = json_decode($body_stream, TRUE);

if ($data["status"] == 2){
    $downloadUri = $data["url"];
        
    if (($new_data = file_get_contents($downloadUri))===FALSE){
        echo "Bad Response";
    } else {
        file_put_contents($path_for_save, $new_data, LOCK_EX);
    }
}
echo "{\"error\":0}";

?>
$path_for_save 是保存文件的计算机文件夹的绝对路径,包括文件名。
class ApplicationController< ActionController::Base
    def index
        body = request.body.read

        file_data = JSON.parse(body)
        status = file_data["status"].to_i

        if status == 2
            download_uri = file_data["url"]
            uri = URI.parse(download_uri)
            http = Net::HTTP.new(uri.host, uri.port)

            if download_uri.start_with?("https")
                http.use_ssl = true
                http.verify_mode = OpenSSL::SSL::VERIFY_NONE
            end

            req = Net::HTTP::Get.new(uri.request_uri)
            res = http.request(req)
            data = res.body

            File.open(path_for_save, "wb") do |file|
                file.write(data)
            end
        end
        render :text =>"{\"error\":0}"
    end
end
path_for_save 是保存文件的计算机文件夹的绝对路径,包括文件名。