欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ASP.NET Core 2.0集成Office Online Server(OWAS)实现办公文档的在线预览与编辑(支持word\excel\ppt\pdf等格式)

程序员文章站 2022-05-18 23:03:47
Office Online Server是微软开发的一套基于Office实现在线文档预览编辑的技术框架(支持当前主流的浏览器,且浏览器上无需安装任何插件,支持word、excel、ppt、pdf等文档格式),其客户端通过WebApi方式可集成到自已的应用中,支持Java、C#等语言。Office O... ......

office online server是微软开发的一套基于office实现在线文档预览编辑的技术框架(支持当前主流的浏览器,且浏览器上无需安装任何插件,支持word、excel、ppt、pdf等文档格式),其客户端通过webapi方式可集成到自已的应用中,支持java、c#等语言。office online server原名为:office web apps server(简称owas)。因为近期有asp.net core 2.0的项目中要实现在线文档预览与编辑,就想着将office online server集成到项目中来,通过网上查找,发现大部分的客户端的实现都是基于asp.net的,而我在实现到asp.net core 2.0的过程中也遇到了不少的问题,所以就有了今天这篇文章。

 

安装office online server

微软的东西在安装上都是很简单的,下载安装包一路”下一步“就可完成。也可参考如下说明来进行安装:

完成安装后会在服务器上的iis上自动创建两个网站,分别为:http80、http809。其中http80站绑定80、443端口,http809站绑定809、810端口。

 

业务关系

1、office online server服务端(wopi server),安装在服务器上用于受理来自客户端的预览、编辑请求等。服务端很吃内存的,单机一定不能低于8g内存。

2、office online server客户端(wopi client),这里因为集成在了自已的项目中,所以office online server客户端也就是自已的项目中的子系统。

用户通过项目中的业务系统请求客户端并发起对某一文档的预览或编辑请求,客户端接受请求后再通过调用服务端的webapi完成一系列约定通讯后,服务端在线输出文档并完成预览与编辑功能。

 

实现原理

可通过如下图(图片来自互联网)能清晰的看出浏览器、office online server服务端、office online server客户端之间的交互顺序与关系。在这过程中,office online server客户端需自行生成token及身份验证,这也是为保障office online server客户端的安全手段。

 

实现代码

客户端编写拦截器,拦截器中主要接受来自服务端的请求,并根据服务端的请求类型做出相应动作,请求类型包含如下几种:checkfileinfo、getfile、lock、getlock、refreshlock、unlock、unlockandrelock、putfile、putrelativefile、renamefile、deletefile、putuserinfo等。具体代码如下:

  1 using microsoft.aspnetcore.http;
  2 using newtonsoft.json;
  3 using system;
  4 using system.collections.generic;
  5 using system.io;
  6 using system.linq;
  7 using system.text;
  8 using system.threading;
  9 using system.threading.tasks;
 10 using system.web;
 11 //编写一个处理wopi请求的客户端拦截器
 12 namespace lezhima.wopi.base
 13 {
 14     public class contentprovider  
 15     {
 16         //声明请求代理
 17         private readonly requestdelegate _nextdelegate;
 18 
 19 
 20         public contentprovider(requestdelegate nextdelegate)
 21         {
 22             _nextdelegate = nextdelegate;
 23         }
 24 
 25 
 26         //拉截并接受所有请求
 27         public async task invoke(httpcontext context)
 28         {
 29 		//判断是否为来自wopi服务端的请求
 30             if (context.request.path.tostring().tolower().indexof("files") >= 0)
 31             {
 32                 wopirequest requestdata = parserequest(context.request);
 33 
 34                 switch (requestdata.type)
 35                 {
 36 			//获取文件信息
 37                     case requesttype.checkfileinfo:
 38                         await handlecheckfileinforequest(context, requestdata);
 39                         break;
 40 
 41                     //尝试解锁并重新锁定
 42                     case requesttype.unlockandrelock:
 43                         handleunlockandrelockrequest(context, requestdata);
 44                         break;
 45 
 46                     //获取文件
 47                     case requesttype.getfile:
 48                         await handlegetfilerequest(context, requestdata);
 49                         break;
 50 
 51                     //写入文件
 52                     case requesttype.putfile:
 53                         await handleputfilerequest(context, requestdata);
 54                         break;
 55 
 56                     default:
 57                         returnservererror(context.response);
 58                         break;
 59                 }
 60             }
 61             else
 62             {
 63                 await _nextdelegate.invoke(context);
 64             }
 65         }
 66 
 67 
 68 
 69 
 70         /// <summary>
 71         /// 接受并处理获取文件信息的请求
 72         /// </summary>
 73         /// <remarks>
 74         /// </remarks>
 75         private async task handlecheckfileinforequest(httpcontext context, wopirequest requestdata)
 76         {
 77 		//判断是否有合法token    
 78             if (!validateaccess(requestdata, writeaccessrequired: false))
 79             {
 80                 returninvalidtoken(context.response);
 81                 return;
 82             }
 83             //获取文件           
 84             ifilestorage storage = filestoragefactory.createfilestorage();
 85             datetime? lastmodifiedtime = datetime.now;
 86             try
 87             {
 88                 checkfileinforesponse responsedata = new checkfileinforesponse()
 89                 {
 90 			//获取文件名称
 91                     basefilename = path.getfilename(requestdata.id),
 92                     size = convert.toint32(size),
 93                     version = convert.todatetime((datetime)lastmodifiedtime).tofiletimeutc().tostring(),
 94                     supportslocks = true,
 95                     supportsupdate = true,
 96                     usercannotwriterelative = true,
 97 
 98                     readonly = false,
 99                     usercanwrite = true
100                 };
101 
102                 var jsonstring = jsonconvert.serializeobject(responsedata);
103 
104                 returnsuccess(context.response);
105 
106                 await context.response.writeasync(jsonstring);
107 
108             }
109             catch (unauthorizedaccessexception ex)
110             {
111                 returnfileunknown(context.response);
112             }
113         }
114 
115         /// <summary>
116         /// 接受并处理获取文件的请求
117         /// </summary>
118         /// <remarks>
119         /// </remarks>
120         private async task handlegetfilerequest(httpcontext context, wopirequest requestdata)
121         {
122      	//判断是否有合法token    
123             if (!validateaccess(requestdata, writeaccessrequired: false))
124             {
125                 returninvalidtoken(context.response);
126                 return;
127             }
128 
129 
130             //获取文件             
131             var stream = await storage.getfile(requestdata.fileid);
132 
133             if (null == stream)
134             {
135                 returnfileunknown(context.response);
136                 return;
137             }
138 
139             try
140             {
141                 int i = 0;
142                 list<byte> bytes = new list<byte>();
143                 do
144                 {
145                     byte[] buffer = new byte[1024];
146                     i = stream.read(buffer, 0, 1024);
147                     if (i > 0)
148                     {
149                         byte[] data = new byte[i];
150                         array.copy(buffer, data, i);
151                         bytes.addrange(data);
152                     }
153                 }
154                 while (i > 0);
155 
156 
157                 returnsuccess(context.response);
158 		    await context.response.body.writeasync(bytes, bytes.count);
159 
160             }
161             catch (unauthorizedaccessexception)
162             {
163                 returnfileunknown(context.response);
164             }
165             catch (filenotfoundexception ex)
166             {
167                 returnfileunknown(context.response);
168             }
169 
170         }
171 
172         /// <summary>
173         /// 接受并处理写入文件的请求
174         /// </summary>
175         /// <remarks>
176         /// </remarks>
177         private async task handleputfilerequest(httpcontext context, wopirequest requestdata)
178         {
179 		//判断是否有合法token    
180             if (!validateaccess(requestdata, writeaccessrequired: true))
181             {
182                 returninvalidtoken(context.response);
183                 return;
184             }
185 
186             try
187             {
188                 //写入文件			
189                 int result = await storage.uploadfile(requestdata.fileid, context.request.body);
190                 if (result != 0)
191                 {
192                     returnservererror(context.response);
193                     return;
194                 }
195 
196                 returnsuccess(context.response);
197             }
198             catch (unauthorizedaccessexception)
199             {
200                 returnfileunknown(context.response);
201             }
202             catch (ioexception ex)
203             {
204                 returnservererror(context.response);
205             }
206         }
207 
208 
209 
210         private static void returnservererror(httpresponse response)
211         {
212             returnstatus(response, 500, "server error");
213         }
214 
215     }
216 }

 

拦截器有了后,再到startup.cs文件中注入即可,具体代码如下:

  1         public void configure(iapplicationbuilder app, ihostingenvironment env)
  2         {
  3             if (env.isdevelopment())
  4             {
  5                 app.usedeveloperexceptionpage();
  6                 app.usebrowserlink();
  7             }
  8             else
  9             {
 10                 app.useexceptionhandler("/home/error");
 11             }
 12 
 13             app.usestaticfiles();
 14             app.useauthentication();
 15 
 16 	        //注入中间件拦截器,这是将咱们写的那个wopi客户端拦截器注入进来
 17             app.usemiddleware<contentprovider>();
 18 
 19             app.usemvc(routes =>
 20             {
 21                 routes.maproute(
 22                     name: "default",
 23                     template: "{controller=home}/{action=index}/{name?}");
 24             });
 25         }

 

至止,整个基于office online server技术框架在asp.net core上的文档预览/编辑功能就完成了。够简单的吧!!

 

总结

1、office online server服务端建议在服务器上独立部署,不要与其它业务系统混合部署。因为这货实在是太能吃内存了,其内部用了webcached缓存机制是导致内存增高的一个因素。

2、office online server很多资料上要求要用ad域,但我实际在集成客户端时没有涉及到这块,也就是说服务端是开放的,但客户端是通过自行颁发的token与验证来保障安全的。

3、利用编写中间件拦截器,并在startup.cs文件中注入中间件的方式来截获来自wopi服务端的所有请求,并对不同的请求类型做出相应的处理。