├── HTML ├── .DS_Store ├── README.md ├── index.html └── jwps.js ├── Readme.md └── WPSApi ├── Controllers └── WPSController.cs ├── Core └── LogMiddlware.cs ├── Helper ├── TimestampHelper.cs └── WPSSignatureHelper.cs ├── Model ├── CreateWPSFileModel.cs ├── FileInfoResult.cs ├── GenarateModel.cs ├── GetFileByVersionModel.cs ├── GetHistoryModel.cs ├── GetUserInfoModel.cs ├── RenameFileModel.cs ├── RequestParam.cs ├── SaveFileResult.cs ├── UserModel.cs ├── WPSBaseModel.cs ├── WPSEnum.cs └── WPSNotifyRequest.cs ├── Program.cs ├── Properties ├── PublishProfiles │ ├── FolderProfile.pubxml │ └── FolderProfile.pubxml.user └── launchSettings.json ├── Startup.cs ├── WPSApi.csproj ├── WPSApi.sln ├── appsettings.Development.json ├── appsettings.json └── log4net.config /HTML/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guolaopi/WPSOnlineDemo/bb70910f900ebb811debfa82a10649fdcb0ee29c/HTML/.DS_Store -------------------------------------------------------------------------------- /HTML/README.md: -------------------------------------------------------------------------------- 1 | # WPS Web Office JS-SDK 说明文档 2 | 3 | ## 概述 4 | 5 | WPS Web Office JS-SDK 是[WPS开放平台](https://open.wps.cn/) 面向网页开发者提供的网页开发工具包。 6 | 7 | 通过使用 JS-SDK,网页开发者可以为 WPS Web Office 自定义菜单、分享等能力,同时可以直接使用高级 API 来操作文档,为用户提供更优质的网页体验。 8 | 9 | 此文档面向网页开发者介绍 WPS Web Office JS-SDK 如何使用及相关注意事项。 10 | 11 | ## JSSDK 使用 12 | 13 | ### 引入 JS 文件 14 | 15 | 在需要调用的页面引入如下 JS 文件(jwps-1.0.0.js) 16 | 17 | 备注:支持使用 AMD/CMD 标准模块加载方法加载 18 | 19 | ### 接入 WPS Web Office 20 | 21 | ```js 22 | WPS.config({ 23 | wpsUrl: 'https://wwo.wps.cn/office/w/66858743377433009?_w_appid=xxx&_w_id=xxx&_w_type=1&_w_userid=xxx&_w_timestamp=xxx&_w_permission=write&_w_tokentype=1&&_w_signature=xxx' // 如文字(Word)接入地址 24 | }) 25 | ``` 26 | JS-SDK 会自动创建 iframe(#wps-iframe),它默认会挂载到 body 下。 27 | 28 | ### 设置 token 29 | 30 | > url参数带上_w_tokentype=1(此参数同样需要签名) 31 | ```js 32 | wps = WPS.config({ 33 | wpsUrl: 'https://wwo.wps.cn/office/w/66858743377433009?_w_appid=xxx&_w_id=xxx&_w_type=1&_w_userid=xxx&_w_timestamp=xxx&_w_permission=write&_w_tokentype=1&_w_signature=xxx' 34 | }) 35 | 36 | // 获取到 token 后,设置 37 | wps.setToken({token: 'your-token'}) 38 | ``` 39 | 40 | ### 自定义 Web Office(iframe) 挂载点 41 | 42 | ```js 43 | WPS.config({ 44 | mount: document.querySelector('#container'), 45 | wpsUrl: 'https://wwo.wps.cn/office/w/66858743377433009?xxx' 46 | }) 47 | ``` 48 | 49 | ### 头部配置 50 | 51 | #### 分享按钮 52 | 53 | ```js 54 | WPS.config({ 55 | wpsUrl: 'https://wwo.wps.cn/office/w/66858743377433009?xxx', 56 | headers: { 57 | shareBtn: { 58 | tooltip: '分享', 59 | subscribe(wps) { 60 | console.log(wps) 61 | } 62 | } 63 | } 64 | }) 65 | ``` 66 | 67 | #### 左边其他按钮(配置自定义菜单) 68 | 69 | ```js 70 | WPS.config({ 71 | wpsUrl: 'https://wwo.wps.cn/office/w/66858743377433009?xxx', 72 | headers: { 73 | otherMenuBtn: { 74 | tooltip: '更多菜单', 75 | items: [ 76 | { 77 | type: 'custom', 78 | icon: 'http://ep.wps.cn/index/images/logo_white2.png', // 移动端显示 Icon 79 | text: '自定义菜单', 80 | subscribe(wps) { 81 | console.log(wps) 82 | } 83 | } 84 | ] 85 | } 86 | } 87 | }) 88 | ``` 89 | 90 | ### 高级 API 91 | 92 | #### 导出 PDF 93 | 94 | ```js 95 | // 文字 96 | wps.WpsApplication().ActiveDocument.ExportAsFixedFormatAsync() // 导出所有 97 | 98 | // 表格 99 | wps.EtApplication().ActiveWorkbook.ExportAsFixedFormatAsync() // 导出所有工作表 100 | wps.EtApplication().ActiveWorkbook.ActiveSheet.ExportAsFixedFormatAsync() // 导出当前工作表 101 | 102 | // 演示 103 | wps.WppApplication().ActivePresentation.ExportAsFixedFormatAsync() // 导出所有 104 | ``` 105 | 106 | 示例:自定义菜单中使用高级 API 107 | 108 | ```js 109 | WPS.config({ 110 | wpsUrl: 'https://wwo.wps.cn/office/w/66858743377433009?xxx', 111 | headers: { 112 | otherMenuBtn: { 113 | tooltip: '更多菜单', 114 | items: [ 115 | { 116 | type: 'custom', 117 | icon: 'http://ep.wps.cn/index/images/logo_white2.png',// 移动端显示的 Icon 118 | text: 'API 导出 PDF', 119 | async subscribe(wps) { 120 | let result = "" 121 | if (wps.WpsApplication) { // 文字 122 | result = await wps.WpsApplication().ActiveDocument.ExportAsFixedFormatAsync() 123 | console.table(result) 124 | } 125 | if (wps.EtApplication) { // 表格 126 | result = await wps.EtApplication().ActiveWorkbook.ExportAsFixedFormatAsync() 127 | console.table(result) 128 | result = await wps.EtApplication().ActiveWorkbook.ActiveSheet.ExportAsFixedFormatAsync() 129 | console.table(result) 130 | } 131 | if (wps.WppApplication) { // 演示 132 | result = await wps.WppApplication().ActivePresentation.ExportAsFixedFormatAsync() 133 | console.table(result) 134 | } 135 | } 136 | } 137 | ] 138 | } 139 | } 140 | }) 141 | ``` 142 | 143 | 示例:配置完直接测试高级 API 144 | ```js 145 | WPS.config({ 146 | wpsUrl: 'https://wwo.wps.cn/office/w/66858743377433009?xxx', 147 | }) 148 | 149 | await wps.ready() // 需要等待 web office 内核初始化完成 150 | 151 | let result = "" 152 | if (wps.WpsApplication) { // 文字 153 | result = await wps.WpsApplication().ActiveDocument.ExportAsFixedFormatAsync() 154 | console.table(result) 155 | } 156 | if (wps.EtApplication) { // 表格 157 | result = await wps.EtApplication().ActiveWorkbook.ExportAsFixedFormatAsync() 158 | console.table(result) 159 | result = await wps.EtApplication().ActiveWorkbook.ActiveSheet.ExportAsFixedFormatAsync() 160 | console.table(result) 161 | } 162 | if (wps.WppApplication) { // 演示 163 | result = await wps.WppApplication().ActivePresentation.ExportAsFixedFormatAsync() 164 | console.table(result) 165 | } 166 | ``` 167 | 168 | #### 保存版本 169 | 170 | ```js 171 | WPS.config({ 172 | wpsUrl: 'https://wwo.wps.cn/office/w/66858743377433009?xxx', 173 | }) 174 | 175 | await wps.ready() // 需要等待 web office 内核初始化完成 176 | 177 | await wps.save() // 保存版本 178 | ``` 179 | 180 | -------------------------------------------------------------------------------- /HTML/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | WPS Web Office(iframe)接入指南 12 | 32 | 33 | 34 | 35 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /HTML/jwps.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).WPS={})}(this,function(e){"use strict";var t,n,o=function(){return(o=Object.assign||function(e){for(var t,n=1,o=arguments.length;n=0&&e._handleList.splice(n,1),window.removeEventListener("message",t,!1)},e.empty=function(){for(;e._handleList.length;)window.removeEventListener("message",e._handleList.shift(),!1)},e.parse=function(e){return"object"==typeof e?e:JSON.parse(e)},e._handleList=[],e}();!function(e){e.unknown="unknown",e.spreadsheet="s",e.writer="w",e.presentation="p",e.pdf="f"}(t||(t={})),function(e){e.wps="w",e.et="s",e.presentation="p",e.pdf="f"}(n||(n={}));var i,a,p,s,c,u,d,f=(i=0,function(){return++i}),l=(a=null,function(e,t){if(!a){a=document.createElement("iframe");var n={id:"wps-iframe",src:e,scrolling:"no",frameborder:"0",allowfullscreen:"allowfullscreen",webkitallowfullscreen:"true",mozallowfullscreen:"true"};for(var o in n)a.setAttribute(o,n[o]);t?t.appendChild(a):document.body.appendChild(a),a.destroy=function(){a.parentNode.removeChild(a),a=null}}return a}),m=function(e){l().contentWindow.postMessage(JSON.stringify(e),"*")},v=function(e){return new Promise(function(t){var n=f();e.type=w();var o=function(e){var i=r.parse(e.data);"wps.api.reply"===i.eventName&&i.msgId===n&&(t(i.data),r.remove(o))};r.add(o),m({eventName:"wps.jssdk.api",data:e,msgId:n})})},y=function(e){var t=o({},e),n=t.headers,r=void 0===n?{}:n,i=t.subscriptions,a=void 0===i?{}:i,p=(t.wpsUrl,r.backBtn),s=void 0===p?{}:p,c=r.shareBtn,u=void 0===c?{}:c,d=r.otherMenuBtn,f=void 0===d?{}:d,l=function(e,t){e.subscribe&&"function"==typeof e.subscribe&&(e.callback=t,a[t]=e.subscribe,delete e.subscribe)};if(l(s,"wpsconfig_back_btn"),l(u,"wpsconfig_share_btn"),l(f,"wpsconfig_other_menu_btn"),f.items&&Array.isArray(f.items)){var m=[];f.items.forEach(function(e,t){switch(void 0===e&&(e={}),e.type){case"export_img":e.type=1,e.callback="export_img";break;case"export_pdf":e.type=1,e.callback="export_pdf";break;case"save_version":e.type=1,e.callback="save_version";break;case"about_wps":e.type=1,e.callback="about_wps";break;case"split_line":e.type=2;break;case"custom":e.type=3,l(e,"wpsconfig_other_menu_btn_"+t),m.push(e)}}),m.length&&(b||F)&&(f.items=m)}if("object"==typeof a.print){var v="wpsconfig_print";"function"==typeof a.print.subscribe&&(a[v]=a.print.subscribe,t.print={callback:v},void 0!==a.print.custom&&(t.print.custom=a.print.custom)),delete a.print}"function"==typeof a.exportPdf&&(a[v="wpsconfig_export_pdf"]=a.exportPdf,t.exportPdf={callback:v},delete a.exportPdf);return o({},t,{subscriptions:a})},w=(p="",function(e){if(void 0===e&&(e=""),!p&&e){var o=e.toLowerCase();-1!==o.indexOf("/office/s/")&&(p=t.spreadsheet),-1!==o.indexOf("/office/w/")&&(p=t.writer),-1!==o.indexOf("/office/p/")&&(p=t.presentation),-1!==o.indexOf("/office/f/")&&(p=t.pdf)}if(!p){var r=e.match(/[\?&]type=([a-z]+)/)||[];p=n[r[1]]||""}return p}),x=window.navigator.userAgent.toLowerCase(),b=/Android|webOS|iPhone|iPod|BlackBerry|iPad/i.test(x),F=function(){try{return-1!==window._parent.location.search.indexOf("from=wxminiprogram")}catch(e){return!1}}();!function(e){e[e.wdExportFormatPDF=17]="wdExportFormatPDF",e[e.wdExportFormatXPS=18]="wdExportFormatXPS"}(s||(s={})),function(e){e[e.wdExportAllDocument=0]="wdExportAllDocument",e[e.wdExportSelection=1]="wdExportSelection",e[e.wdExportCurrentPage=2]="wdExportCurrentPage",e[e.wdExportFromTo=3]="wdExportFromTo"}(c||(c={})),function(e){e[e.wdExportDocumentContent=0]="wdExportDocumentContent",e[e.wdExportDocumentWithMarkup=7]="wdExportDocumentWithMarkup"}(u||(u={})),function(e){e[e.title=1]="title",e[e.tag=2]="tag"}(d||(d={}));var A;!function(e){e[e.xlTypePDF=0]="xlTypePDF",e[e.xlTypeXPS=1]="xlTypeXPS"}(A||(A={}));var P,E,h;!function(e){e[e.ppFixedFormatTypePDF=2]="ppFixedFormatTypePDF",e[e.ppFixedFormatTypeXPS=1]="ppFixedFormatTypeXPS"}(P||(P={})),function(e){e[e.ppPrintAll=1]="ppPrintAll",e[e.ppPrintCurrent=3]="ppPrintCurrent"}(E||(E={})),function(e){e[e.msoFalse=0]="msoFalse",e[e.msoTrue=-1]="msoTrue"}(h||(h={}));var g,k,D=function(){return new Promise(function(e){return k=e})},_=function(e){return"wps.advanced.api.ready"===e||"web_loaded"===e},T=function(e,t){void 0===e&&(e={});r.add(function(n){var o=r.parse(n.data),i=o.eventName,a=void 0===i?"":i,p=o.data,s=void 0===p?null:p,c=o.url,u=void 0===c?null:c;-1===["wps.jssdk.api"].indexOf(a)&&("ready"===a&&(m({eventName:"setConfig",data:e}),g.tokenData&&m({eventName:"setToken",data:g.tokenData}),k(),g.iframeReady=!0),_(a)&&S(),"function"==typeof t[a]&&t[a](g,u||s))})},S=function(){var e=w(g.url);e===t.writer&&function(e){e.WpsApplication=function(){return{ActiveDocument:{ExportAsFixedFormatAsync:function(e){var t={api:"WpsApplication().ActiveDocument.ExportAsFixedFormatAsync",args:o({ExportFormat:s.wdExportFormatPDF,Range:c.wdExportAllDocument,From:1,To:1,Item:u.wdExportDocumentWithMarkup,IncludeDocProps:!0},"object"==typeof e?e:{})};return v(t)},ImportDataIntoFieldsAsync:function(e){var t={api:"WpsApplication().ActiveDocument.ImportDataIntoFieldsAsync",args:{Data:e.Data,Options:e.Options}};return v(t)}},Enum:o({},s,c,u)}}}(g),e===t.spreadsheet&&function(e){e.EtApplication=function(){return{ActiveWorkbook:{ExportAsFixedFormatAsync:function(e){var t={api:"EtApplication().ActiveWorkbook.ExportAsFixedFormatAsync",args:o({Type:A.xlTypePDF,IncludeDocProps:!0},"object"==typeof e?e:{})};return v(t)},ActiveSheet:{ExportAsFixedFormatAsync:function(e){var t={api:"EtApplication().ActiveWorkbook.ActiveSheet.ExportAsFixedFormatAsync",args:o({Type:A.xlTypePDF,IncludeDocProps:!0},"object"==typeof e?e:{})};return v(t)}}},Enum:o({},A)}}}(g),e===t.presentation&&function(e){e.WppApplication=function(){return{ActivePresentation:{ExportAsFixedFormatAsync:function(e){var t={api:"WppApplication().ActivePresentation.ExportAsFixedFormatAsync",args:o({FixedFormatType:P.ppFixedFormatTypePDF,RangeType:E.ppPrintAll,FrameSlides:h.msoTrue},"object"==typeof e?e:{})};return v(t)}},Enum:o({},P,E)}}}(g)};console.log("WPS WebOffice JS-SDK V1.0.9"),e.config=function(e){void 0===e&&(e={}),g&&g.destroy();try{var t,n=y(e),i=n.wpsUrl,a=n.subscriptions,p=void 0===a?{}:a,s=n.mount,c=l(i,void 0===s?null:s);return delete n.mount,delete n.wpsUrl,delete n.subscriptions,D(),g={url:i,version:"1.0.9",iframe:c,Enum:o({},h),iframeReady:!1,tokenData:null,setToken:function(e){g.tokenData=e,g.iframeReady&&m({eventName:"setToken",data:e})},ready:function(){return t||(t=new Promise(function(e){var t=function(n){var o=r.parse(n.data).eventName;_(o)&&(e(),r.remove(t))};r.add(t)}))},destroy:function(){c.destroy(),r.empty(),g=null},save:function(){return v({api:"save"})}},T(n,p),g.ready(),g}catch(e){console.error(e)}},Object.defineProperty(e,"__esModule",{value:!0})}); 2 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ## 这是个 WPS 在线编辑服务的.NET CORE 版的 demo 2 | 3 | ## 注意如果部署在 IIS 上,需要在 IIS-网站-模块中移除 WebDAVModule 来允许 put 请求 4 | 5 | ## WPS 开放平台文档 [链接](https://open.wps.cn/docs/wwo/access/api-list) 6 | 7 | 在 appsettings.json 中配置 WPSConfig 的 AppId 以及 AppSecret 即可使用 8 | 9 | 简单测试方式: 10 | 1. 配置好AppId以及AppSecret 11 | 2. 在 WPSApi/Controllers/WPSController 的 FileInfo 方法返回值中将“download_url”属性设为你要在线预览的文件的**外网下载地址** 12 | 3. 生成并部署WPSApi 13 | 4. 在 HTML/index.html中调用WPSApi的GenarateWPSUrl接口即可看到效果 -------------------------------------------------------------------------------- /WPSApi/Controllers/WPSController.cs: -------------------------------------------------------------------------------- 1 | using log4net; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using WPSApi.Helper; 9 | using WPSApi.Model; 10 | 11 | namespace WPSApi.Controllers 12 | { 13 | [ApiController] 14 | public class WPSController : ControllerBase 15 | { 16 | private ILog _logger = LogManager.GetLogger("wps", ""); 17 | 18 | /// 19 | /// 简单过滤一下HttpRequest参数 20 | /// 21 | /// 22 | /// 23 | private RequestParam FilterRequestForWPS(HttpRequest Request) 24 | { 25 | var result = new RequestParam(); 26 | result.FileId = Request.Headers["x-weboffice-file-id"].ToString(); 27 | var queryStr = Request.QueryString.ToString(); 28 | queryStr = queryStr.StartsWith("?") ? queryStr.Substring(1) : queryStr; 29 | if (string.IsNullOrEmpty(queryStr) || string.IsNullOrEmpty(result.FileId)) 30 | { 31 | return new RequestParam 32 | { 33 | code = 403, 34 | msg = "参数错误,无法打开文件", 35 | Status = false 36 | }; 37 | } 38 | 39 | // url参数序列化成Dictionary 40 | result.Params = queryStr.Split("&", StringSplitOptions.RemoveEmptyEntries).ToDictionary(p => p.Split("=")[0], p => p.Split("=")[1]); 41 | 42 | // 此处判断是否传递了自定义的 _w_userId 参数,如果不需要此参数的话可以注释该判断 43 | if (!result.Params.ContainsKey("_w_userId")) 44 | { 45 | return new RequestParam 46 | { 47 | code = 403, 48 | msg = "用户异常", 49 | Status = false 50 | }; 51 | } 52 | return result; 53 | } 54 | 55 | /// 56 | /// 生成iframe用的url(此方法非WPS官方的,主要是为了签名,也可以自己实现) 57 | /// 58 | /// 59 | /// 60 | [Route("api/wps/genarate")] 61 | [HttpPost] 62 | public Task GenarateWPSUrl(GenarateRequest request) 63 | { 64 | return Task.Run(() => 65 | { 66 | var url = WPSSignatureHelper.GenarateUrl(request.FileId, 67 | request.FileType, 68 | new Dictionary { 69 | { "_w_userId", request.UserId }, 70 | { "_w_fileName", request.FileName } 71 | }); 72 | // 上面的写法是在生成的url中带了两个自定义参数 _w_userId 和 _w_fileName,可以根据业务自己扩展,生成url是这样的: 73 | // https://wwo.wps.cn/office/w/123?_w_appid=123456&_w_fileName=x.docx&_w_userId=5024&_w_signature=xxxxx 74 | 75 | 76 | // 也可以不写自定义参数,这样生成的url会只有 _w_appId 和 _w_ signatrue,例如:https://wwo.wps.cn/office/w/123?_w_appid=123456&_w_signature=xxxxx 77 | //var url = WPSHelper.GenarateUrl(request.FileId,request.FileType); 78 | 79 | return new GenarateResult { Url = url }; 80 | }); 81 | } 82 | 83 | #region WPS官方要求实现的回调接口 84 | 85 | /// 86 | /// 获取文件元数据 87 | /// 88 | /// 89 | [Route("v1/3rd/file/info")] 90 | [HttpGet] 91 | public Task FileInfo() 92 | { 93 | return Task.Run(() => 94 | { 95 | // 简单的过滤下不合理的请求,可注释,以下接口基本上都有此过滤 96 | var request = FilterRequestForWPS(Request); 97 | if (!request.Status) 98 | { 99 | return new FileInfoResult { code = request.code, msg = request.msg }; 100 | } 101 | 102 | // 获取自定义参数 103 | var userId = request.Params["_w_userId"].ToString(); 104 | 105 | // 从数据库查询用户名、文件 等信息...... 106 | 107 | // 创建时间和修改时间默认全是现在,可更改,但是注意时间戳是11位的(秒) 108 | var now = TimestampHelper.GetCurrentTimestamp(); 109 | 110 | var result = new FileInfoResult 111 | { 112 | file = new Model.WPSFile 113 | { 114 | id = request.FileId, 115 | name = "文件名", 116 | version = 1, 117 | size = 1024, // WPS单位是B 118 | create_time = now, 119 | creator = "创建者用户名", 120 | modify_time = now, 121 | modifier = "修改者用户名", 122 | download_url = "文件下载链接", 123 | user_acl = new User_acl 124 | { 125 | history = 1, // 允许查看历史版本 126 | rename = 1, // 允许重命名 127 | copy = 1 // 允许复制 128 | }, 129 | watermark = new Watermark 130 | { 131 | type = 0, // 1为有水印 132 | value = "水印文字" 133 | } 134 | }, 135 | user = new UserForFile() 136 | { 137 | id = userId, 138 | name = "用户名", 139 | //permission = "read", 140 | permission = "write", // write为允许编辑,read为只能查看 141 | avatar_url = "用户头像url", 142 | } 143 | }; 144 | return result; 145 | }); 146 | } 147 | 148 | /// 149 | /// 获取用户信息 150 | /// 151 | /// 包含一个名为ids的字符串数组,里面是用户id 152 | /// 153 | [Route("v1/3rd/user/info")] 154 | [HttpPost] 155 | public Task GetUserInfo(GetUserInfoRequest body) 156 | { 157 | return Task.Run(() => 158 | { 159 | var request = FilterRequestForWPS(Request); 160 | if (!request.Status) 161 | { 162 | return new UserModel(); 163 | } 164 | 165 | var result = new UserModel 166 | { 167 | id = "用户ID", 168 | name = "用户名", 169 | avatar_url = "用户头像url" 170 | }; 171 | return result; 172 | }); 173 | } 174 | 175 | /// 176 | /// 通知此文件目前有哪些人正在协作 177 | /// 178 | /// 179 | /// 180 | [Route("v1/3rd/file/online")] 181 | [HttpPost] 182 | public Task Online(GetUserInfoRequest body) 183 | { 184 | return Task.Run(() => 185 | { 186 | var result = new WPSBaseModel 187 | { 188 | code = 200, 189 | msg = "success" 190 | }; 191 | return result; 192 | }); 193 | } 194 | 195 | /// 196 | /// 上传文件新版本(保存文件) 197 | /// 198 | /// 传来的文件流 199 | /// 200 | [Route("v1/3rd/file/save")] 201 | [HttpPost] 202 | public Task SaveFile(IFormFile file) 203 | { 204 | return Task.Run(async () => 205 | { 206 | try 207 | { 208 | var request = FilterRequestForWPS(Request); 209 | if (!request.Status) 210 | { 211 | return new SaveFileResult { code = request.code, msg = request.msg }; 212 | } 213 | 214 | using (var stream = System.IO.File.Create("保存的文件名")) 215 | { 216 | await file.CopyToAsync(stream); 217 | } 218 | 219 | var result = new SaveFileResult 220 | { 221 | file = new WPSFileModel 222 | { 223 | download_url = "新的文件下载链接", 224 | id = request.FileId, 225 | name = request.Params["_w_fileName"].ToString() 226 | } 227 | }; 228 | return result; 229 | } 230 | catch (Exception ex) 231 | { 232 | _logger.Error("save file failed: ", ex); 233 | return new SaveFileResult { code = 403, msg = "保存出现异常" }; 234 | } 235 | }); 236 | } 237 | 238 | /// 239 | /// 获取特定版本的文件信息 240 | /// 241 | /// 版本号 242 | /// 243 | [Route("/v1/3rd/file/version")] 244 | [HttpGet] 245 | public Task Version(int version) 246 | { 247 | return Task.Run(() => 248 | { 249 | var request = FilterRequestForWPS(Request); 250 | if (!request.Status) 251 | { 252 | return new GetFileByVersionResult { code = request.code, msg = request.msg }; 253 | } 254 | 255 | // 从数据库查询文件信息...... 256 | 257 | // 创建时间和修改时间默认全是现在 258 | var now = TimestampHelper.GetCurrentTimestamp(); 259 | var result = new GetFileByVersionResult 260 | { 261 | id = request.FileId, 262 | name = "文件名", 263 | version = 1, 264 | size = 1024, 265 | create_time = now, 266 | creator = "创建者用户名", 267 | modify_time = now, 268 | modifier = "修改者用户名", 269 | download_url = "文件下载url" 270 | }; 271 | return result; 272 | }); 273 | } 274 | 275 | /// 276 | /// 文件重命名 277 | /// 278 | /// 包含一个name的字符串属性,值为保存的新文件名 279 | /// 280 | [Route("v1/3rd/file/rename")] 281 | [HttpPut] 282 | public Task RenameFile(RenameFileRequest body) 283 | { 284 | return Task.Run(() => 285 | { 286 | var request = FilterRequestForWPS(Request); 287 | if (!request.Status) 288 | { 289 | return new WPSBaseModel { code = request.code, msg = request.msg }; 290 | } 291 | 292 | var result = new WPSBaseModel 293 | { 294 | code = 200, 295 | msg = "success" 296 | }; 297 | return result; 298 | }); 299 | } 300 | 301 | /// 302 | /// 获取所有历史版本文件信息 303 | /// 304 | /// 305 | /// 306 | [Route("v1/3rd/file/history")] 307 | [HttpPost] 308 | public Task GetHistory(GetHistoryRequest body) 309 | { 310 | return Task.Run(() => 311 | { 312 | var request = FilterRequestForWPS(Request); 313 | if (!request.Status) 314 | { 315 | return new GetHistoryResult { code = request.code, msg = request.msg }; 316 | } 317 | // 从数据库查询用户、文件信息等...... 318 | 319 | // 创建时间和修改时间默认全是现在 320 | var now = TimestampHelper.GetCurrentTimestamp(); 321 | 322 | // 不需要使用历史版本功能的此处也请返回,如果此接口不通时,文档加载会报错:“GetFileInfoFailed” 323 | var result = new GetHistoryResult 324 | { 325 | histories = new List 326 | { 327 | new HistroyModel 328 | { 329 | id=request.FileId, 330 | name="文件名", 331 | size=1024, // 单位B 332 | version=1, 333 | download_url="文件下载链接", 334 | create_time=now, 335 | modify_time=now, 336 | creator=new UserModel 337 | { 338 | id="创建者ID", 339 | name="创建者名", 340 | avatar_url = "创建者头像url" 341 | }, 342 | modifier=new UserModel 343 | { 344 | id="修改者ID", 345 | name="修改者名", 346 | avatar_url = "修改者头像url" 347 | } 348 | } 349 | } 350 | }; 351 | return result; 352 | }); 353 | } 354 | 355 | /// 356 | /// 新建文件 357 | /// 358 | /// 359 | [Route("v1/3rd/file/new")] 360 | [HttpPost] 361 | public Task NewFile(CreateWPSFileRequest request) 362 | { 363 | return Task.Run(async () => 364 | { 365 | try 366 | { 367 | var filterRequest = FilterRequestForWPS(Request); 368 | if (!filterRequest.Status) 369 | { 370 | return new CreateWPSFileResult { code = filterRequest.code, msg = filterRequest.msg }; 371 | } 372 | 373 | using (var stream = System.IO.File.Create("保存的文件名")) 374 | { 375 | await request.file.CopyToAsync(stream); 376 | } 377 | 378 | var result = new CreateWPSFileResult 379 | { 380 | redirect_url = "新的文件访问链接", 381 | user_id = "创建人id" 382 | }; 383 | return result; 384 | } 385 | catch (Exception ex) 386 | { 387 | _logger.Error("save file failed: ", ex); 388 | return new CreateWPSFileResult { code = 403, msg = "新建文件出现异常" }; 389 | } 390 | }); 391 | } 392 | 393 | /// 394 | /// 回调通知 395 | /// 396 | /// 397 | /// 398 | [Route("v1/3rd/onnotify")] 399 | [HttpPost] 400 | public Task WPSNotify(WPSNotifyRequest body) 401 | { 402 | return Task.Run(() => 403 | { 404 | var result = new WPSBaseModel 405 | { 406 | code = 200, 407 | msg = "success" 408 | }; 409 | return result; 410 | }); 411 | } 412 | 413 | #endregion 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /WPSApi/Core/LogMiddlware.cs: -------------------------------------------------------------------------------- 1 | using log4net; 2 | using Microsoft.AspNetCore.Http; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace WPSApi.Core 9 | { 10 | /// 11 | /// 日志中间件,记录访问信息 12 | /// 13 | public class LogMiddlware 14 | { 15 | private ILog _logger = LogManager.GetLogger("wps", ""); 16 | private RequestDelegate _next; 17 | public LogMiddlware(RequestDelegate next) 18 | { 19 | _next = next; 20 | } 21 | public async Task Invoke(HttpContext context) 22 | { 23 | _logger.Debug($"被访问,访问路径为:{(context.Request.Path.HasValue ? context.Request.Path.Value : "无")}"); 24 | await _next(context); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /WPSApi/Helper/TimestampHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WPSApi.Helper 4 | { 5 | /// 6 | /// 时间戳帮助类 7 | /// 8 | public class TimestampHelper 9 | { 10 | /// 11 | /// 日期转换为时间戳(时间戳单位秒) 12 | /// 13 | /// 14 | /// 15 | public static int GetCurrentTimestamp() 16 | { 17 | DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 18 | return (int)(DateTime.Now.AddHours(-8) - Jan1st1970).TotalSeconds; 19 | } 20 | 21 | /// 22 | /// 日期转换为时间戳(时间戳单位秒) 23 | /// 24 | /// 25 | /// 26 | public static int ConvertToTimeStamp(DateTime time) 27 | { 28 | DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 29 | return (int)(time.AddHours(-8) - Jan1st1970).TotalSeconds; 30 | } 31 | 32 | /// 33 | /// 时间戳转换为日期(时间戳单位秒) 34 | /// 35 | /// 36 | /// 37 | public static DateTime ConvertToDateTime(long timeStamp) 38 | { 39 | var start = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 40 | return start.AddSeconds(timeStamp).AddHours(8); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /WPSApi/Helper/WPSSignatureHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | using System.Web; 6 | using WPSApi.Model; 7 | 8 | namespace WPSApi.Helper 9 | { 10 | /// 11 | /// WPS生成签名的帮助类 12 | /// 13 | public class WPSSignatureHelper 14 | { 15 | // url示例: https://wwo.wps.cn/office/w/999888777?_w_appid=d2a400fa455e42208c74a3de41d3cb3b&_w_myName=self&_w_signature=G8lG%2Bf0A%2BSbqrVFoMpmuLVOE8tM%3D 16 | 17 | /// 18 | /// AppId 19 | /// 20 | private static string AppId = string.Empty; 21 | 22 | /// 23 | /// AppKey 24 | /// 25 | private static string AppSecretKey = string.Empty; 26 | 27 | /// 28 | /// 配置AppId和Appkey 29 | /// 30 | /// 31 | /// 32 | public static void Config(string appId, string appSecretKey) 33 | { 34 | AppId = appId; 35 | AppSecretKey = appSecretKey; 36 | } 37 | 38 | /// 39 | /// 对字符串进行HMACSH1加密 40 | /// 41 | /// 42 | /// 43 | public static string ToHMACSHA1(string encryptText) 44 | { 45 | HMACSHA1 hmacsha1 = new HMACSHA1(); 46 | hmacsha1.Key = System.Text.Encoding.Default.GetBytes(AppSecretKey); 47 | byte[] dataBuffer = System.Text.Encoding.Default.GetBytes(encryptText); 48 | byte[] hashBytes = hmacsha1.ComputeHash(dataBuffer); 49 | return Convert.ToBase64String(hashBytes); 50 | } 51 | 52 | /// 53 | /// 对参数签名 54 | /// 55 | /// 56 | /// 签名后的字符串 57 | public static string Signature(Dictionary paramDic) 58 | { 59 | var sortParam = paramDic.OrderBy(p => p.Key).ToDictionary(p => p.Key, p => p.Value); 60 | sortParam.Add("_w_secretkey", AppSecretKey); 61 | var paramStr = string.Join("", sortParam.Select(p => $"{p.Key}={p.Value}").ToArray()); 62 | var signature = ToHMACSHA1(paramStr); 63 | sortParam.Remove("_w_secretkey"); 64 | sortParam.Add("_w_signature", HttpUtility.UrlEncode(signature)); 65 | return string.Join("&", sortParam.Select(p => $"{p.Key}={p.Value}").ToArray()); 66 | } 67 | 68 | /// 69 | /// 生成签名后iframe用的url 70 | /// 71 | /// 72 | /// 73 | /// 74 | /// 75 | // 此处直接读取了配置的appid,所以生成url时不用传递appid 76 | public static string GenarateUrl(string fileId, FileType fileType, Dictionary paramDic = null) 77 | { 78 | if (string.IsNullOrEmpty(AppId) || string.IsNullOrEmpty(AppSecretKey)) 79 | { 80 | throw new ArgumentException("未配置 AppId 和 AppSecretKey,请在 Startup.cs 的 Startup() 构造函数中进行配置。"); 81 | } 82 | if (paramDic == null) 83 | { 84 | paramDic = new Dictionary(); 85 | } 86 | 87 | paramDic.Add("_w_appid", AppId); 88 | var paramStr = Signature(paramDic); 89 | return $"https://wwo.wps.cn/office/{fileType.ToString()}/{fileId}?{paramStr}"; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /WPSApi/Model/CreateWPSFileModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | 3 | namespace WPSApi.Model 4 | { 5 | /// 6 | /// 新建文件 入参 7 | /// 8 | public class CreateWPSFileRequest 9 | { 10 | /// 11 | /// 新建文件名 12 | /// 13 | public string name { get; set; } 14 | 15 | /// 16 | /// 新的文件 17 | /// 18 | public IFormFile file { get; set; } 19 | } 20 | 21 | /// 22 | /// 新建文件出参 23 | /// 24 | public class CreateWPSFileResult : WPSBaseModel 25 | { 26 | /// 27 | /// 一个可以访问到新创建文档的url 28 | /// 29 | // 官方给出的url样例是这样的:http://wwo.wps.cn/office/w/<:fileid>?_w_fname=example.doc&_w_userid=1000&_w_appid=xxx&_w_signature=xxx 需要签名 30 | public string redirect_url { get; set; } 31 | 32 | /// 33 | /// 创建人id 34 | /// 35 | public string user_id { get; set; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /WPSApi/Model/FileInfoResult.cs: -------------------------------------------------------------------------------- 1 | namespace WPSApi.Model 2 | { 3 | /// 4 | /// 获取文件元数据 出参 5 | /// 6 | public class FileInfoResult : WPSBaseModel 7 | { 8 | /// 9 | /// 文件 10 | /// 11 | public WPSFile file { get; set; } 12 | 13 | /// 14 | /// 用户信息 15 | /// 16 | public UserForFile user { get; set; } 17 | } 18 | 19 | /// 20 | /// 用户权限控制 21 | /// 22 | public class User_acl 23 | { 24 | /// 25 | /// 是否允许重命名,1:是 0:否 26 | /// 27 | public int rename { get; set; } 28 | 29 | /// 30 | /// 是否允许查看历史记录,1:是 0:否 31 | /// 32 | public int history { get; set; } 33 | 34 | /// 35 | /// 是否允许复制,1:是 0:否 36 | /// 37 | public int copy { get; set; } 38 | } 39 | 40 | /// 41 | /// 水印,只实现了简单的水印显示,如需配置水印的字体透明度等样式需要自己添加属性,参见:https://open.wps.cn/docs/wwo/access/api-list#des2 watermark部分 42 | /// 43 | public class Watermark 44 | { 45 | /// 46 | /// 是否有水印, 1:有 0:无 47 | /// 48 | public int type { get; set; } 49 | 50 | /// 51 | /// 水印字符串 52 | /// 53 | public string value { get; set; } 54 | } 55 | 56 | /// 57 | /// 文件model 58 | /// 59 | public class WPSFile 60 | { 61 | /// 62 | /// 文件id 63 | /// 64 | public string id { get; set; } 65 | 66 | /// 67 | /// 文件名 68 | /// 69 | public string name { get; set; } 70 | 71 | /// 72 | /// 版本号 73 | /// 74 | public int version { get; set; } 75 | 76 | /// 77 | /// 文件大小,单位B 78 | /// 79 | public int size { get; set; } 80 | 81 | /// 82 | /// 创建者用户名 83 | /// 84 | public string creator { get; set; } 85 | 86 | /// 87 | /// 创建时间的时间戳,单位秒 88 | /// 89 | public int create_time { get; set; } 90 | 91 | /// 92 | /// 修改者的用户名 93 | /// 94 | public string modifier { get; set; } 95 | 96 | /// 97 | /// 修改时间的时间戳,单位秒 98 | /// 99 | public int modify_time { get; set; } 100 | 101 | /// 102 | /// 文件下载url 103 | /// 104 | public string download_url { get; set; } 105 | 106 | /// 107 | /// 用户权限控制配置 108 | /// 109 | public User_acl user_acl { get; set; } 110 | 111 | /// 112 | /// 水印配置 113 | /// 114 | public Watermark watermark { get; set; } 115 | } 116 | 117 | /// 118 | /// 用户对于此文件的信息 119 | /// 120 | public class UserForFile 121 | { 122 | /// 123 | /// 用户id 124 | /// 125 | public string id { get; set; } 126 | 127 | /// 128 | /// 用户名 129 | /// 130 | public string name { get; set; } 131 | 132 | /// 133 | /// 用户对文件的权限,只能取 “read” 和 “write” 两个字符串,表示只读和可修改 134 | /// 135 | public string permission { get; set; } 136 | 137 | /// 138 | /// 用户头像url 139 | /// 140 | public string avatar_url { get; set; } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /WPSApi/Model/GenarateModel.cs: -------------------------------------------------------------------------------- 1 | namespace WPSApi.Model 2 | { 3 | /// 4 | /// 生成WPS的iframe url的入参,可以自己扩展参数,然后放到DIctionary中进行签名,参见写好的例子 5 | /// 6 | public class GenarateRequest 7 | { 8 | /// 9 | /// 用户id 10 | /// 11 | public string UserId { get; set; } 12 | 13 | /// 14 | /// 文件id 15 | /// 16 | public string FileId { get; set; } 17 | 18 | /// 19 | /// 文件名 20 | /// 21 | public string FileName { get; set; } 22 | 23 | /// 24 | /// 文件类型 25 | /// 26 | public FileType FileType { get; set; } 27 | 28 | /// 29 | /// 是否只读 30 | /// 31 | public string ReadOnly { get; set; } 32 | } 33 | 34 | /// 35 | /// 36 | /// 37 | public class GenarateResult 38 | { 39 | public string Url { get; set; } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /WPSApi/Model/GetFileByVersionModel.cs: -------------------------------------------------------------------------------- 1 | namespace WPSApi.Model 2 | { 3 | /// 4 | /// 获取特定版本的文件信息 出参 5 | /// 6 | public class GetFileByVersionResult : WPSBaseModel 7 | { 8 | /// 9 | /// 文件id 10 | /// 11 | public string id { get; set; } 12 | 13 | /// 14 | /// 文件名 15 | /// 16 | public string name { get; set; } 17 | 18 | /// 19 | /// 版本号 20 | /// 21 | public int version { get; set; } 22 | 23 | /// 24 | /// 文件大小,单位B 25 | /// 26 | public int size { get; set; } 27 | 28 | /// 29 | /// 创建者用户名 30 | /// 31 | public string creator { get; set; } 32 | 33 | /// 34 | /// 创建时间的时间戳(单位秒) 35 | /// 36 | public int create_time { get; set; } 37 | 38 | /// 39 | /// 修改者用户名 40 | /// 41 | public string modifier { get; set; } 42 | 43 | /// 44 | /// 修改时间的时间戳(单位秒) 45 | /// 46 | public int modify_time { get; set; } 47 | 48 | /// 49 | /// 下载链接 50 | /// 51 | public string download_url { get; set; } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /WPSApi/Model/GetHistoryModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace WPSApi.Model 4 | { 5 | /// 6 | /// 获取历史版本入参 7 | /// 8 | public class GetHistoryRequest 9 | { 10 | /// 11 | /// 文件id 12 | /// 13 | public string id { get; set; } 14 | 15 | /// 16 | /// 记录偏移量 17 | /// 18 | public int offset { get; set; } 19 | 20 | /// 21 | /// 记录总数 22 | /// 23 | public int count { get; set; } 24 | } 25 | 26 | /// 27 | /// 获取历史版本出参 28 | /// 29 | public class GetHistoryResult : WPSBaseModel 30 | { 31 | /// 32 | /// 历史记录列表 33 | /// 34 | public List histories { get; set; } 35 | } 36 | 37 | /// 38 | /// 文件历史记录model 39 | /// 40 | public class HistroyModel 41 | { 42 | /// 43 | /// 文件id 44 | /// 45 | public string id { get; set; } 46 | 47 | /// 48 | /// 文件名 49 | /// 50 | public string name { get; set; } 51 | 52 | /// 53 | /// 版本号 54 | /// 55 | public int version { get; set; } 56 | 57 | /// 58 | /// 文件大小,单位是B 59 | /// 60 | public int size { get; set; } 61 | 62 | /// 63 | /// 下载链接 64 | /// 65 | public string download_url { get; set; } 66 | 67 | /// 68 | /// 创建时间的时间戳(单位秒) 69 | /// 70 | public int create_time { get; set; } 71 | 72 | /// 73 | /// 修改时间的时间戳(单位秒) 74 | /// 75 | public int modify_time { get; set; } 76 | 77 | /// 78 | /// 创建人 79 | /// 80 | public UserModel creator { get; set; } 81 | 82 | /// 83 | /// 修改人 84 | /// 85 | public UserModel modifier { get; set; } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /WPSApi/Model/GetUserInfoModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace WPSApi.Model 4 | { 5 | /// 6 | /// 获取用户信息 入参model 7 | /// 通知此文件目前有那些人正在协作 入参model 8 | /// 9 | public class GetUserInfoRequest 10 | { 11 | /// 12 | /// 用户id数组 13 | /// 14 | public List ids { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /WPSApi/Model/RenameFileModel.cs: -------------------------------------------------------------------------------- 1 | namespace WPSApi.Model 2 | { 3 | /// 4 | /// 文件重命名 入参 5 | /// 6 | public class RenameFileRequest 7 | { 8 | /// 9 | /// 新的文件名 10 | /// 11 | public string name { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /WPSApi/Model/RequestParam.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace WPSApi.Model 4 | { 5 | /// 6 | /// 用来过滤下WPS回调的参数(包含url参数/headers) 7 | /// 继承了ErrorModel,如果请求参数不正常可直接返回error信息 8 | /// 9 | public class RequestParam : WPSBaseModel 10 | { 11 | /// 12 | /// 请求参数是否正常 13 | /// 14 | public bool Status { get; set; } = true; 15 | 16 | /// 17 | /// 文件ID 18 | /// 19 | public string FileId { get; set; } 20 | 21 | /// 22 | /// url参数 23 | /// 24 | public Dictionary Params { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /WPSApi/Model/SaveFileResult.cs: -------------------------------------------------------------------------------- 1 | namespace WPSApi.Model 2 | { 3 | /// 4 | /// 上传文件新版本 出参 5 | /// 6 | public class SaveFileResult : WPSBaseModel 7 | { 8 | /// 9 | /// 文件信息 10 | /// 11 | public WPSFileModel file { get; set; } 12 | } 13 | 14 | /// 15 | /// 文件model 16 | /// 17 | public class WPSFileModel 18 | { 19 | /// 20 | /// 文件id 21 | /// 22 | public string id { get; set; } 23 | 24 | /// 25 | /// 文件名 26 | /// 27 | public string name { get; set; } 28 | 29 | /// 30 | /// 下载链接 31 | /// 32 | public string download_url { get; set; } 33 | 34 | /// 35 | /// 版本号 36 | /// 37 | public int version { get; set; } 38 | 39 | /// 40 | /// 文件大小,单位B 41 | /// 42 | public int size { get; set; } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /WPSApi/Model/UserModel.cs: -------------------------------------------------------------------------------- 1 | namespace WPSApi.Model 2 | { 3 | /// 4 | /// 用户信息model 5 | /// 6 | public class UserModel 7 | { 8 | /// 9 | /// 用户id 10 | /// 11 | public string id { get; set; } 12 | 13 | /// 14 | /// 用户名 15 | /// 16 | public string name { get; set; } 17 | 18 | /// 19 | /// 用户头像地址 20 | /// 21 | public string avatar_url { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /WPSApi/Model/WPSBaseModel.cs: -------------------------------------------------------------------------------- 1 | namespace WPSApi.Model 2 | { 3 | /// 4 | /// 基础model,包含了code和msg,基本上wps回调接口的出参都继承了它,方便出错的时候直接返回错误信息 5 | /// 6 | public class WPSBaseModel 7 | { 8 | public int code { get; set; } = 200; 9 | public string msg { get; set; } = "success"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /WPSApi/Model/WPSEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace WPSApi.Model 7 | { 8 | /// 9 | /// 文件类型枚举 10 | /// 11 | public enum FileType 12 | { 13 | /// 14 | /// 表格文件 15 | /// 16 | s = 0, 17 | 18 | /// 19 | /// 文字文件 20 | /// 21 | w = 1, 22 | 23 | /// 24 | /// 演示文件 25 | /// 26 | p = 2, 27 | 28 | /// 29 | /// PDF文件 30 | /// 31 | f = 3 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /WPSApi/Model/WPSNotifyRequest.cs: -------------------------------------------------------------------------------- 1 | namespace WPSApi.Model 2 | { 3 | /// 4 | /// 回调通知 入参 5 | /// 6 | public class WPSNotifyRequest 7 | { 8 | /// 9 | /// 回调命令的参数 10 | /// 11 | public string cmd { get; set; } 12 | 13 | /// 14 | /// 回调命令的内容 由于官方给的示例中body内容不固定,所以此处使用了object,可参考文档根据自己需求进行修改 15 | /// 16 | public object body { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /WPSApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace WPSApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /WPSApi/Properties/PublishProfiles/FolderProfile.pubxml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | FileSystem 9 | FileSystem 10 | Release 11 | Any CPU 12 | 13 | True 14 | False 15 | netcoreapp2.1 16 | b98b5653-3584-405e-a67e-71bac1681546 17 | false 18 | bin\Release\netcoreapp2.1\publish\ 19 | True 20 | 21 | -------------------------------------------------------------------------------- /WPSApi/Properties/PublishProfiles/FolderProfile.pubxml.user: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | <_PublishTargetUrl>E:\项目\GWQ\WPSApi\bin\Release\netcoreapp2.1\publish\ 10 | 11 | -------------------------------------------------------------------------------- /WPSApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:59182", 8 | "sslPort": 0 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "v1/3rd/file/info", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "WPSApi": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "v1/3rd/file/info", 24 | "applicationUrl": "http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /WPSApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using log4net; 2 | using log4net.Config; 3 | using log4net.Repository; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using System.IO; 10 | using WPSApi.Core; 11 | using WPSApi.Helper; 12 | 13 | namespace WPSApi 14 | { 15 | public class Startup 16 | { 17 | public ILoggerRepository LoggerRepository; 18 | public Startup(IConfiguration configuration) 19 | { 20 | Configuration = configuration; 21 | // 配置日志 22 | LoggerRepository = LogManager.CreateRepository("wps"); 23 | XmlConfigurator.Configure(LoggerRepository, new FileInfo("log4net.config")); 24 | 25 | // 配置WPS的 AppId 和 AppSecretKey 26 | WPSSignatureHelper.Config(configuration["WPSConfig:AppId"], configuration["WPSConfig:AppSecretKey"]); 27 | } 28 | 29 | public IConfiguration Configuration { get; } 30 | 31 | // This method gets called by the runtime. Use this method to add services to the container. 32 | public void ConfigureServices(IServiceCollection services) 33 | { 34 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 35 | services.AddCors(p => 36 | { 37 | p.AddPolicy("wps", policy => 38 | { 39 | policy.AllowAnyOrigin() 40 | .AllowAnyMethod() 41 | .AllowAnyHeader() 42 | .AllowCredentials(); 43 | }); 44 | }); 45 | } 46 | 47 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 48 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 49 | { 50 | if (env.IsDevelopment()) 51 | { 52 | app.UseDeveloperExceptionPage(); 53 | } 54 | app.UseCors("wps"); 55 | app.UseMiddleware(); 56 | app.UseMvc(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /WPSApi/WPSApi.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /WPSApi/WPSApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29806.167 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WPSApi", "WPSApi.csproj", "{B98B5653-3584-405E-A67E-71BAC1681546}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {B98B5653-3584-405E-A67E-71BAC1681546}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {B98B5653-3584-405E-A67E-71BAC1681546}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {B98B5653-3584-405E-A67E-71BAC1681546}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {B98B5653-3584-405E-A67E-71BAC1681546}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {E7342BD3-ADF1-4139-B6E5-23870B63516B} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /WPSApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /WPSApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*", 8 | "WPSConfig": { 9 | "AppId": "123456", 10 | "AppSecretKey": "123456" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WPSApi/log4net.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | --------------------------------------------------------------------------------