云服务开发文档在线编辑系统

从工作实习的时候我就参与了一个项目叫做“云文档管理系统”,说白了就是文件的上传、下载、预览、分享、回收站等等一些操作。上传下载以及分享都很Easy,复杂的就在文档预览上,图片、视频、音频都有现成的插件可以使用,Office文档的在线预览相对来说还是比较复杂的,当时也是看好多把Office文档转换成html进行预览的,也有转换成Pdf预览的,即使都实现预览效果又怎样。客户提出一个需求叫做“文档版本修改历史留存、可增加批注”,当时这个需求简直让人头大,我不知道如何下手。我记得我当时的主管对这个需求也是很无助啊,过了几天他就告诉我他找到一个插件,不过只能在IE浏览器上使用Active X控件才能实现,而且调试起来超级麻烦。我记得当时这个功能还是废弃了。

时隔五年,我偶然间发现了一个文档在线预览的服务,大家可以参考我的另一篇博客《如何实现文档在线预览》,这里我就不再过多赘述了。即使到目前我也只是把文档在线预览功能找到了解决方案,可是文档在线编辑一直是我的一个心结。2021年开年到现在,每天工作都很繁忙,午休的时间累积在一起我写了一个基于云服务的文档在线编辑系统(基础功能基本已经实现),如果有需要的小伙伴可以参照我下面介绍的步骤来体验一下:

  • 开通开发者权限

我们进入云服务官网,申请加入开发者,跟着导航一步一步走就OK了,等待审核通过,你会得到appId和appKey,这俩参数在调用接口时候会用到。记一次基于云服务开发文档在线编辑系统的开发记录,支持版本记录、可增加批注。

  • 验签方法封装

验签方法,就是对你调用接口的参数进行签名,被调用方拿到你的参数要进行校验,校验通过才算是有效调用。

/// <summary>

/// 生成验签数据 sign

/// </summary>

public class Signclient

{

public static string generateSign(string secret, Dictionary<string, string[]> paramMap)

{

string fullParamStr = uniqSortParams(paramMap);

return HmacSHA256(fullParamStr, secret);

}

public static string uniqSortParams(Dictionary<string, string[]> paramMap)

{

paramMap.Remove("sign");

paramMap = paramMap.OrderBy(o => o.Key).ToDictionary(o => o.Key.ToString(), p => p.Value);

StringBuilder strB = new StringBuilder();

foreach (KeyValuePair<string, string[]> kvp in paramMap)

{

string key = kvp.Key;

string[] value = kvp.Value;

if (value.Length > 0)

{

Array.Sort(value);

foreach (string temp in value)

{

strB.Append(key).Append("=").Append(temp);

}

}

else

{

strB.Append(key).Append("=");

}

}

return strB.ToString();

}

public static string HmacSHA256(string data, string key)

{

string signRet = string.Empty;

using (HMACSHA256 mac = new HMACSHA256(Encoding.UTF8.GetBytes(key)))

{

byte[] hash = mac.ComputeHash(Encoding.UTF8.GetBytes(data));

signRet = ToHexString(hash); ;

}

return signRet;

}

public static string ToHexString(byte[] bytes)

{

string hexString = string.Empty;

if (bytes != null)

{

StringBuilder strB = new StringBuilder();

foreach (byte b in bytes)

{

strB.AppendFormat("{0:X2}", b);

}

hexString = strB.ToString();

}

return hexString;

}

}

记住,这个sign很重要,因为我刚开始把appKey当做sign传入参数进行调用,总是报错,后来才知道是我签名传了个寂寞。

记一次基于云服务开发文档在线编辑系统的开发记录,支持版本记录、可增加批注。

  • 接口调用

准备工作已经准备完毕了,下面就要开始接口调用了,API提供了新建文档、本地文档上传、文件删除、文件版本删除等等,我这里不一一调用了,只做了几个我项目中用到的来罗列一下,我界面做的比较丑,凑合看。。

 

本地文档上传,文档上传成功之后返回的结果如下,包含第一个文件版本Id和文件的Id,这样我的文档就上传到云服务了,我们拿着文件版本ID,就可以进行在线编辑了。

记一次基于云服务开发文档在线编辑系统的开发记录,支持版本记录、可增加批注。

我们拿过来刚才的文件版本ID,进行在线编辑功能测试,我这里直接做了个跳转,跳转多的页面就是在线编辑页面,如果你在Postman调用的话,会得到一大串HTML代码,就算你粘贴过来,也是缺少css和js的,因为打开的方式就不对。我们来看一下效果:

记一次基于云服务开发文档在线编辑系统的开发记录,支持版本记录、可增加批注。

在线编辑效果:整体效果非常好,而且可以进行批注。

记一次基于云服务开发文档在线编辑系统的开发记录,支持版本记录、可增加批注。

  • 回调函数

在线编辑是可以了,但是还没完。因为你用之前的版本ID再次打开会发现,什么也没更改,这是为什么呢?因为我们修改的内容已经作为新版本进行保存了,因为我是在本机进行测试,没有发布到服务器,所以我也不知道保存后的文档版本ID是多少,我根据文件版本名字发现了规律,那就是从0开始依次累加,那我直接在文件ID后加下划线 _1进行测试,果然打开了我上次修改并保存的那个文档。于是我又进入API文档发现,这个在线编辑是实时本地保存的,一旦你离开在线编辑,它就会回调给你的接口,这里我们先配置一下接口:

记一次基于云服务开发文档在线编辑系统的开发记录,支持版本记录、可增加批注。

这里乍一看是个外网地址,其实是我映射内网的地址,我搭建了内网映射服务,这样我就可以在外网调试的时候,映射到我本机电脑进行调试了,于是我编辑完文档,并返回,这是回调地址就起了作用了,值得注意的是,路由地址要按照接口给出的3rd/edit/callBack进行配置,否则你的接口接收不到任何东西。

记一次基于云服务开发文档在线编辑系统的开发记录,支持版本记录、可增加批注。

好了,我们接收到了云服务给我们回调的数据,这样我们就可以根据这些数据进行数据操作了。

能力有限,只会C#这一编程语言,仅供参考。

namespace WebApplication.Controllers

{

/// <summary>

/// 基于WebUploader插件的图片上传实例

/// </summary>

public class UploadController : Controller

{

public static readonly string appId = "yozojqut3Leq7916";

public static readonly string appKey = "5f83670ada246fc8e0d1********";

#region 文件上传

/// <summary>

/// 文件上传

/// </summary>

/// <returns></returns>

public ActionResult FileUpload()

{

return View();

}

/// <summary>

/// 上传文件方法

/// </summary>

/// <param name="form"></param>

/// <param name="file"></param>

/// <returns></returns>

[HttpPost]

public ActionResult UploadFile(FormCollection form, HttpPostedFileBase file)

{

Dictionary<string, string[]> dic = new Dictionary<string, string[]>();

dic.Add("appId", new string[] { appId });

string sign = Signclient.generateSign(appKey, dic);

try

{

if (Request.Files.Count == 0)

{

throw new Exception("请选择上传文件!");

}

using (HttpClient client = new HttpClient())

{

var postContent = new MultipartFormDataContent();

HttpContent fileStreamContent = new StreamContent(file.InputStream);

postContent.Add(fileStreamContent, "file", file.FileName);

var requestUri = "http://dmc.yozocloud.cn/api/file/upload?appId=" + appId + "&sign=" + sign + "";

var response = client.PostAsync(requestUri, postContent).Result;

Task<string> t = response.Content.ReadAsStringAsync();

return Json(new

{

Status = response.StatusCode.GetHashCode(),

Message = response.StatusCode.GetHashCode() == 200 ? "上传文件成功" : "上传文件失败",

Data = t.Result

});

}

}

catch (Exception ex)

{

//扔出异常

throw;

}

}

#endregion

#region 文件删除

/// <summary>

/// 删除文件

/// </summary>

/// <returns></returns>

public ActionResult DelFile()

{

return View();

}

/// <summary>

/// 删除文件版本

/// </summary>

/// <returns></returns>

public ActionResult DelFileVersion()

{

return View();

}

[HttpGet]

public ActionResult FileDelete(string fileId)

{

Dictionary<string, string[]> dic = new Dictionary<string, string[]>();

dic.Add("fileId", new string[] { fileId });

dic.Add("appId", new string[] { appId });

string sign = Signclient.generateSign(appKey, dic);

using (HttpClient client = new HttpClient())

{

var requestUri = "http://dmc.yozocloud.cn/api/file/delete/file?fileId=" + fileId + "&appId=" + appId + "&sign=" + sign + "";

var response = client.GetAsync(requestUri).Result;

Task<string> t = response.Content.ReadAsStringAsync();

return Json(new

{

Status = response.StatusCode.GetHashCode(),

Message = response.StatusCode.GetHashCode() == 200 ? "请求成功" : "请求失败",

Data = t.Result

},JsonRequestBehavior.AllowGet);

}

}

[HttpGet]

public ActionResult FileVersionDelete(string fileVersionId)

{

Dictionary<string, string[]> dic = new Dictionary<string, string[]>();

dic.Add("fileVersionId", new string[] { fileVersionId });

dic.Add("appId", new string[] { appId });

string sign = Signclient.generateSign(appKey, dic);

using (HttpClient client = new HttpClient())

{

var requestUri = "http://dmc.yozocloud.cn/api/file/delete/version?fileVersionId=" + fileVersionId + "&appId=" + appId + "&sign=" + sign + "";

var response = client.GetAsync(requestUri).Result;

Task<string> t = response.Content.ReadAsStringAsync();

return Json(new

{

Status = response.StatusCode.GetHashCode(),

Message = response.StatusCode.GetHashCode() == 200 ? "请求成功" : "请求失败",

Data = t.Result

}, JsonRequestBehavior.AllowGet);

}

}

#endregion

#region 新建文档

/// <summary>

/// 文档类型,文件名

/// </summary>

/// <param name="templateType"></param>

/// <param name="fileName"></param>

/// <returns></returns>

public ActionResult NewDoc(string templateType, string fileName)

{

Dictionary<string, string[]> dic = new Dictionary<string, string[]>();

dic.Add("templateType", new string[] { templateType });

dic.Add("fileName", new string[] { fileName });

dic.Add("appId", new string[] { appId });

string sign = Signclient.generateSign(appKey, dic);

using (HttpClient client = new HttpClient())

{

var requestUri = "http://dmc.yozocloud.cn/api/file/template?templateType=" + templateType + "&fileName=" + fileName + "&appId=" + appId + "&sign=" + sign + "";

var response = client.GetAsync(requestUri).Result;

Task<string> t = response.Content.ReadAsStringAsync();

return Json(new

{

Status = response.StatusCode.GetHashCode(),

Message = response.StatusCode.GetHashCode() == 200 ? "删除文件版本成功" : "删除文件版本失败",

Data = t.Result

});

}

}

#endregion

/// <summary>

/// 在线编辑

/// </summary>

/// <returns></returns>

public ActionResult FileEdit()

{

return View();

}

[HttpGet]

public ActionResult GetFileEdit(string fileversionId)

{

Dictionary<string, string[]> dic = new Dictionary<string, string[]>();

dic.Add("fileVersionId", new string[] { fileversionId });

dic.Add("appId", new string[] { appId });

string sign = Signclient.generateSign(appKey, dic);

string ret = "http://eic.yozocloud.cn/api/edit/file?fileVersionId=" + fileversionId + "&appId=" + appId + "&sign=" + sign + "";

return Redirect(ret);

}

[HttpPost]

[Route("3rd/edit/callBack")]

public ActionResult EditCallBack(string oldFileId, string newFileId, string message, int errorCode)

{

//文件ID

//575716913322135553

//文件版本 依次累加 0 1 2 3 4

//575716913322135553_0 、 7

return Json(new

{

oldFileId = oldFileId,

newFileId = newFileId,

message = message,

errorCode = errorCode

});

}

}

}

有兴趣的同志可以一起交流。

Github已开源此Demo

以上是 云服务开发文档在线编辑系统 的全部内容, 来源链接: utcz.com/a/119856.html

回到顶部