解决Onlyoffice文件版本发生变化的提示问题 和 使用动态key后无法多人协同编辑示问题

发布时间 2023-06-12 11:21:32作者: baivfhpwxf

1.原因分析

  Onlyoffice的保存文件的机制是靠回调接口保存文件的。文件一但保存成功。再次以上一次的key和最新的文档url地址为参数打开编辑器时,编辑器服务根据key去找redis中去找缓存并进行验证判断文件的版本是否发生了变化,如果一样就从缓存中读取文档数据。否则提示文件版本发生变化的提示。

2.解决方案

  知道了Onlyoffice编辑器的验证机制后就好解决问题了。

  我们可以创建一张表用于管理文件的版本号。版本号的创建规则自己定。

  思路:

    编辑器每次回调,我们都在 文件的版本 表中判断一下文件是否存在,不存在创建一条记录,存在的话版本号加1.

    再增加一个获取编辑器KEY的接口,接口逻辑:在表中找是否有对应的文档数据,如果有就在原来的版本号上加1.如果没有以1开始。

    这样的话,又可以解决文件版本问题的提示又可以解决应该动态KEY后文档无法协同的问题。

3.代码

  OnlyOfficeFile 表名,表结构如下:

 

 这里要说明一下我这里的key的组成:   type_id_VersionNo 例 FlowTask_00018de2895742fb8edba512db6f619c_1

回调接口代码:

/// <summary>
    /// 回调接口 CallbackApiHandler 的摘要说明
    /// </summary>
    public class CallbackApiHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            //context.Response.AddHeader("Access-Control-Allow-Origin", "*");
            //context.Response.AddHeader("Access-Control-Allow-Headers", " x-www-form-urlencoded, Content-Type,x-requested-with");
            //context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
            string errorMsg = string.Empty;
            string body = string.Empty;
            var fileDataJson = string.Empty;
            bool isSucceed = false;
            string search1 = string.Empty;
            string search2 = string.Empty;
            string status = string.Empty;
            var ip = CommonHelpLD.ServiceIp;
            var model = new OnlyOfficeFileModel();
            try
            {
                body = HttpContextHelper.GetRequestBody(context);
                var result1 = new LawcaseTaskService().AddLog(string.Empty, string.Empty, "CallbackApi", body, string.Empty, string.Empty, "回调接口1", ip);
                //LogHelper.WriteLog("回调接口1 开始 CallbackApiHandler 是否成功:" + isSucceed + " 接口信息:" + errorMsg + ",收到的请求参数" + body + ",fileDataJson:" + fileDataJson);
                if (!string.IsNullOrEmpty(body))
                {
                    var endIndex = body.IndexOf("fileDataJson");//, fileDataJson:
                    if (endIndex > 0)
                    {
                        body = body.Substring(0, endIndex - 15);
                        body = body.Trim().TrimEnd(',');
                        var result2 = new LawcaseTaskService().AddLog(string.Empty, string.Empty, "CallbackApi", body, string.Empty, string.Empty, "回调接口2", ip);
                        //LogHelper.WriteLog("回调接口2 开始 CallbackApiHandler 是否成功:" + isSucceed + " 接口信息:" + errorMsg + ",收到的请求参数" + body + ",fileDataJson:" + fileDataJson);
                    }
                    var fileData = new JavaScriptSerializer().Deserialize<CallbackApiReqModel>(body);
                    //var fileData = JsonConvert.DeserializeObject<CallbackApiReqModel>(body);
                    errorMsg = "try 0";

                    if (fileData != null && (fileData.status == 2 || fileData.status == 6))
                    {
                        status = fileData.status.ToString();
                        try
                        {
                            errorMsg += "try 1";
                            //fileData.Id = fileData.key.Contains("_") ? fileData.key.Substring(0, fileData.key.IndexOf("_")) : fileData.key;
                            if (fileData.key.Contains("_"))
                            {
                                var keys = fileData.key.Split('_');
                                if (keys.Length >= 3)
                                {
                                    fileData.Id = string.Format("{0}_{1}", keys[0], keys[1]);
                                }
                                else
                                {
                                    fileData.Id = keys[1];//FlowTask_8160b1f7efa64cdba7d4bd37e843d638
                                }
                            }
                            else
                            {
                                fileData.Id = fileData.key;
                            }
                            errorMsg += "try 2," + fileData.key;
                            var fileModel = FileHelper.GetFileObjModel(fileData.key);//(fileData.Id);
                            var sFilePath = FileHelper.GetFilePath(fileModel.FileType);
                            string sFileName = string.Format("{0}.docx", fileModel.Key);
                            var PATH_FOR_SAVE = sFilePath + "/" + sFileName; //文件的绝对路径
                            //if (File.Exists(PATH_FOR_SAVE))
                            //{
                            //    File.Delete(PATH_FOR_SAVE);
                            //    errorMsg += "try 1.1";
                            //}
                            search1 = fileModel.Key;
                            search2 = fileData.key;
                            model.FileKey = fileData.key;
                            model.Type = fileModel.FileType;
                            model.Name = sFileName;
                            model.DisplayName = sFileName;
                            model.TaskId = search1;
                            model.CreatedBy = "CallbackApi";
                            model.VersionNo = 1;
                            var model_o = OnlyOfficeFileService.GetModelByTaskId(model.TaskId, model.Type);
                            if (model_o != null)
                            {
                                model.VersionNo = model_o.VersionNo + 1;
                            }
                            var result3 = new LawcaseTaskService().AddLog(search1, search2, "CallbackApi", body, PATH_FOR_SAVE, "status:" + status, "回调接口3", ip);
                            var req = WebRequest.Create(fileData.url);
                            errorMsg += "try 2.1,PATH_FOR_SAVE:" + PATH_FOR_SAVE;

                            using (var stream = req.GetResponse().GetResponseStream())
                            {
                                errorMsg += "try 3";
                                using (var fs = File.Open(PATH_FOR_SAVE, FileMode.Create))
                                {
                                    var buffer = new byte[4096];
                                    int readed;
                                    errorMsg += "try 4";
                                    while ((readed = stream.Read(buffer, 0, 4096)) != 0)
                                    {
                                        fs.Write(buffer, 0, readed);
                                        //errorMsg = "try 5";
                                    }
                                }
                            }
                            isSucceed = true;
                            model.FilePath = PATH_FOR_SAVE.Replace(AppDomain.CurrentDomain.BaseDirectory, string.Empty);
                            errorMsg += " try 6";
                            try
                            {
                                if (fileModel.FileType.Equals("FlowTask"))
                                {
                                    var result = new LawcaseTaskService().UpdateTaskStatus(fileModel.Key, 1);
                                    errorMsg += " try 7 更新任务 修改文书任务为进行中...1,更新结果:" + result.Item1 + ",消息:" + result.Item2;
                                }
                            }
                            catch (Exception ex)
                            {
                                errorMsg += " try 7 更新任务 修改文书任务为进行中...1 异常:" + ex.Message;
                            }
                            errorMsg += " try 8";
                        }
                        catch (Exception ex)
                        {
                            isSucceed = false;
                            errorMsg += "异常2:" + ex.Message;
                        }
                    }
                    else
                    {
                        errorMsg += "try 66";
                        isSucceed = true;
                    }
                    fileDataJson = JsonConvert.SerializeObject(fileData);
                }
                else
                {
                    isSucceed = true;
                }
            }
            catch (Exception ex)
            {
                isSucceed = false;
                errorMsg += "异常1:" + ex.Message;
            }
            var rspJosn = "{\"error\":0}";//返回保存成功
            if (isSucceed)
            {
                OnlyOfficeFileService.SaveModel(model);
            }
            else
            {
                rspJosn = "{\"error\":1}";//返回保存失败
            }
            //LogHelper.WriteLog("回调接口4 结束 CallbackApiHandler 是否成功:" + isSucceed + " 接口信息:" + errorMsg + ",收到的请求参数" + body + ",fileDataJson:" + fileDataJson);
            var result4 = new LawcaseTaskService().AddLog(search1, search2, "CallbackApi", body, rspJosn, "status:" + status, "回调接口4," + errorMsg, ip);
            context.Response.Write(rspJosn);
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
/// <summary>
    /// 文件版本控制
    /// </summary>
    public class OnlyOfficeFileService
    {
        public static bool SaveModel(OnlyOfficeFileModel model)
        {
            var isSucceed = false;
            if (model.VersionNo > 0)
            {
                if (model.VersionNo == 1)
                {
                    isSucceed = AddModel(model);
                }
                else
                {
                    isSucceed = UpdateVersionNo(model);
                }
            }
            return isSucceed;
        }

        public static bool AddModel(OnlyOfficeFileModel model)
        {
            var isOk = false;
            try
            {
                var sql = string.Format(@"INSERT INTO [dbo].[OnlyOfficeFile]
                ([Name], [DisplayName], [TaskId], [FileKey], [Type], [VersionNo], [FilePath], [CreatedBy], [CreatedOn])
                VALUES
                ('{0}','{1}','{2}','{3}', '{4}',{5},'{6}','{7}','{8}')",
                    model.Name, model.DisplayName, model.TaskId, model.FileKey, model.Type, model.VersionNo, 
                    model.FilePath, model.CreatedBy, DateTime.Now.ToString());

                isOk = SqlDapperHelper.ExecuteSqlInt(DBTypeEnum.LDDB, sql, null) > 0;
            }
            catch (Exception ex)
            {

            }
            return isOk;
        }

        public static bool UpdateVersionNo(OnlyOfficeFileModel model)
        {
            var isOk = false;
            try
            {
                model.UpdatedBy = model.CreatedBy;
                var sql = @"UPDATE [OnlyOfficeFile] SET [VersionNo]=[VersionNo]+1,UpdatedBy=@UpdatedBy,UpdatedOn=@UpdatedOn 
                            WHERE TaskId=@taskId AND Type=@type";
                isOk = SqlDapperHelper.ExecuteSqlInt(DBTypeEnum.LDDB, sql, new { @taskId = model.TaskId, @type = model.Type, @UpdatedBy = model.UpdatedBy, @UpdatedOn = DateTime.Now }) > 0;
            }
            catch (Exception)
            {

                throw;
            }
            return isOk;
        }

        public static OnlyOfficeFileModel GetModelByTaskId(string taskId, string type)
        {
            var sql = @"SELECT * FROM OnlyOfficeFile WITH(NOLOCK) WHERE TaskId=@taskId AND Type=@type";// ORDER BY VersionNo DESC
            return SqlDapperHelper.ExecuteReaderReturnT<OnlyOfficeFileModel>(DBTypeEnum.LDDB, sql, new { @taskId = taskId, @type = type });
        }
    }

前端获取KEY的接口

/// <summary>
        /// 根据ID和类型获取文件key
        /// </summary>
        /// <param name="reqModel"></param>
        /// <returns></returns>
        [HttpPost]
        [NoLDAPPLoginFilter]
        public AjaxResult GetFileKey(OnlyOfficeFileSearchModel reqModel)
        {
            var rspMode = new AjaxResult();
            try
            {
                var key = string.Format("{0}_{1}", reqModel.Type, reqModel.TaskId);

                var model = _serviceOnlyOfficeFile.GetModelByWhere("TaskId=@TaskId AND Type=@Type", new { @TaskId = reqModel.TaskId, @Type = reqModel.Type });
                if (model != null)
                {
                    key = key + "_" + (model.VersionNo + 1);
                }
                else
                {
                    key = key + "_1";
                }
                rspMode.Success(key);
            }
            catch (Exception ex)
            {
                rspMode.Warning();
            }
            return rspMode;
        }