解决Onlyoffice文件版本发生变化的提示问题 和 使用动态key后无法多人协同编辑示问题
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; }