WPS本地镜像化在线文档操作以及样例

一个客户项目有引进在线文档操作需求,让我这边做一个demo调研下,给我的对接文档里有相关方法的说明,照着对接即可。但在真正对接过程中还是踩过不少坑,这儿对之前的对接工作做个记录。
按照习惯先来一个效果:
WPS本地镜像化在线文档操作以及样例_第1张图片

WPS本地镜像化在线文档操作以及样例_第2张图片

WPS本地镜像化在线文档操作以及样例_第3张图片

 Demo下载链接:https://download.csdn.net/download/qxyywy/88117444

接入指引


1. 申请授权证书并进行授权。
2. 登录系统后台页面
3. 创建、获取应用信息 access_key (简称ak)、 secret_key (简称 sk )。开发者在接口调用时,使用ak 、 sk 生成WPS-4签名进行鉴权。
4. 在线编辑、在线预览、格式转换接入按照对应开放能力文档接入。在线编辑、在线预览对接过程中需要设置回调地址。其中,在线编辑可通过配置开启历史版本功能。
5. 使用过程中需通过证书查询接口关注授权证书状态,若证书即将过期或者不可用,需进行更新证书操作。
6. 在线编辑或在线预览服务端对接完毕,对接方可使用JSSDK,调用API实现相关需求。

WPS-4签名算法

在对接时,耗费了一定时间在WPS-4签名处,对接文档中有WPS-4的说明和样例,自己在对接转换成NetCore的时候踩了一些坑,签名算法中最主要是:Wps-Docs-Authorization的计算方法
签名格式:WPS-4 {accessKey}:{signature}  注意WPS-4后面有空格。
signature:hmac-sha256(secret_key, Ver + HttpMethod + URI + Content-Type + WpsDocs-Date + sha256(HttpBody))
signature的样例如下:WPS-4POST/callback/path/demoapplication/jsonWed, 20 Apr 2022 01:33:07GMTfc005f51a6e75586d2d5d078b657dxxxdf9c1dfa6a7c0c0ba38c715daeb6ede9

这是文档中对签名算法的解释,对照着格式完成相关算法,具体算法如下:
signature的组装:

        /// 
        /// 获取签名
        /// 
        /// 请求方法,如:GET,POST
        /// 请求url,带querystring
        /// 请求body
        /// 日期
        /// 默认:application/json
        /// 应用SK
        /// 
        public static string WPS4Signature(string secretKey,string method,string uri, byte[] body=null,DateTime? date=null,string contentType= "application/json")
        {
            //获取uri路径
            string path = uri;
            //日期格式化
            if (date == null)
                date = DateTime.Now;
            string dateStr = String.Format("{0:r}", date);
            //open不参与签名,做替换处理
            if (path.StartsWith("/open"))
            {
                path = path.Replace("/open", "");
            }

            string sha256body;
            //body为空则为空,否则返回sha256(body)
            if (body != null && body.Length > 0)
            {
                sha256body = Sha256(body);
            }
            else
            {
                sha256body = "";
            }
            String signature = null;
            signature = HmacSHA256Encrypt($"WPS-4{method.ToUpper()}{path}{contentType}{dateStr}{sha256body}", secretKey);

            return signature;
        }

HmacSHA256加密算法:

        /// 
        /// HmacSHA256加密
        /// 
        /// 
        /// 
        /// 
        public static string HmacSHA256Encrypt(string secret, string signKey)
        {
            string signRet = string.Empty;
            using (HMACSHA256 mac = new HMACSHA256(Encoding.UTF8.GetBytes(signKey)))
            {
                byte[] hash = mac.ComputeHash(Encoding.UTF8.GetBytes(secret));
                //signRet = Convert.ToBase64String(hash);
                signRet = ToHexStrFromByte(hash); 
            }
            return signRet;
        }

Sha256转换:

        /// 
        /// Sha256转换
        /// 
        /// The input.
        /// A hash.
        public static string Sha256(this byte[] input)
        {
            if (input == null)
            {
                return null;
            }
            using (var sha = SHA256.Create())
            {
                var hash = sha.ComputeHash(input);
                return ToHexStrFromByte(hash);
            }
        }

字节数组转16进制字符串:

        /// 
        /// 字节数组转16进制字符串:空格分隔
        /// 
        /// 
        /// 
        public static string ToHexStrFromByte(this byte[] byteDatas)
        {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < byteDatas.Length; i++)
            {
                builder.Append(string.Format("{0:X2}", byteDatas[i]));
            }
            return builder.ToString().Trim().ToLower();
        }

获取在线预览链接

        /// 
        /// 获取在线预览链接
        /// 
        /// 
        /// 
        [Route("api/wps/previewgenarate")]
        [HttpPost]
        public Task GenarateWPSPreviewUrl(GenarateRequest request)
        {
            return Task.Run(() =>
            {
                string wpsHost = "http://10.4.**.**";
                string uri = $"/api/preview/v1/files/{defaultFileId}/link?type=w&preview_mode=high_definition";
                string fullUrl = $"{wpsHost}/open{uri}";
                Dictionary headers = new Dictionary();
                DateTime now = DateTime.Now;
                headers.Add("Content-Type", "application/json");
                headers.Add("Wps-Docs-Date", String.Format("{0:r}", now));
                var signature = WPSLocalSIgnatureHelper.WPS4Signature("SKrpaxjdwoetijjv", "get", uri, null, now);
                string docsAuthorization = WPSLocalSIgnatureHelper.WPS4SignAuthorization("UOMYPEVAHWQLTKJF", signature);
                headers.Add("Wps-Docs-Authorization", docsAuthorization);
                HttpHelper httpHelper = new HttpHelper();
                var resultTemp = httpHelper.Get(fullUrl, headers);
                var result = JsonConvert.DeserializeObject>(resultTemp);
                string url = "";
                if (result != null && result.data != null)
                {
                    url = result.data.link;
                }
                return new GenarateResult { Url = url };
            });
        }

这儿比较坑的地方来了,方法参数都组装好了,通过HttpWebRequest后端发起请求获取WPS中台返回的在线预览地址时,始终提示401报错获取不到数据。迫不得已自己通过APIPost组装了相关请求和参数居然又能获取到相关数据。对照了下请求头,又差异后调整了程序的请求头,保证和apiPost里的完全一致,依然返回401,通过查阅相关资料找到一个处理办法,在发起的请求后手动捕获WebException,然后在WebException里解析数据流获取信息。
HttpWebRequest Get方式访问
 

        /// 
        ///  Get方式访问
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public string Get(string url, string encode, string referer, Dictionary headers=null)
        {
            int num = _tryTimes;
            HttpWebRequest request = null;
            HttpWebResponse response = null;
            StreamReader reader = null;
            while (num-- >= 0)
            {
                try
                {
                    DelaySomeTime();
                    ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult);//验证服务器证书回调自动验证
                    request = (HttpWebRequest)WebRequest.Create(url);
                    request.Headers.Add("accept", "*/*");
                    request.Headers.Add("accept-encoding", "gzip, deflate, br");
                    request.Headers.Add("accept-language", "zh-CN");
                    request.Headers.Add("connection", "keep-alive");
                    
                    if (headers != null) 
                    {
                        foreach (var item in headers)
                        {
                            request.Headers.Add(item.Key, item.Value);
                        }
                    }
                    //request.UserAgent = reqUserAgent;
                    request.CookieContainer = _cookie;
                    request.Referer = referer;
                    request.Method = "GET";
                    request.Timeout = _timeOut;
                    if (_proxy != null && _proxy.Credentials != null)
                    {
                        request.UseDefaultCredentials = true;
                    }
                    request.Proxy = _proxy;
                    response = (HttpWebResponse)request.GetResponse();
                    reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(encode));
                    return reader.ReadToEnd();
                }
                catch (WebException ex)
                {
                    response = (HttpWebResponse)ex.Response; //解析401等错误返回的有效信息
                    var resultTemp = "";
                    Stream stream = response.GetResponseStream();
                    using (StreamReader readers = new StreamReader(stream, Encoding.UTF8))
                    {
                        resultTemp = readers.ReadToEnd();
                    }
                    return resultTemp;
                }
                catch (Exception ex)
                {
                    _logger.Error(url + "\r\n" + ex.ToString());
                    continue;
                }
                finally
                {
                    if (request != null)
                    {
                        request.Abort();
                    }
                    if (response != null)
                    {
                        response.Close();
                    }
                    if (reader != null)
                    {
                        reader.Close();
                    }
                }
            }
            return string.Empty;
        }

DemoHtml


到上面时后端的处理就基本完成,前端这边因为时临时demo就用了一个html来处理,真实项目中需要用VUE等处理也都类似。
以下为 html的代码:



    
        
        
        
        
        WPS Web Office(iframe)接入指南
        
        
        
        
        
    
    
		
课题名称:
课题申报单位:
课题负责人:
课题成员:
姓名所属部门年龄
姓名所属部门年龄
表格替换在第6页
图片上传:
图片替换在第7页


个人总结:需要在线编辑等相关操作,其实也可以用免费的组件组合,比如可以用onlyoffice+Aspose.Words去操作,仅个人见解。

你可能感兴趣的:(NETCore,WPS中台,wps,WPS中台,HmacSHA256算法,Sha256转换,401错误)