介绍
前几篇中介绍了如何搭建预览服务器,在服务搭建起来之后,就应该考虑如何将预览服务与业务对接起来。Office Online Server本身支持WOPI协议,可以很方便的将业务附件与预览服务对接起来。WOPI全称Web Application Open Platform Interface Protocol,基于该协议扩展自己的代码可以实现对文件的鉴权以及下载。主要需要包括两个接口(最好基于restful来定义接口,更清晰):
- 提供一个/files/{fileId}的接口,该接口用于鉴权:也就是通过fileId,判断当前用户是否具备对该附件的访问权限,如果没权限,那么返回HTTP CODE=401;如果有权限,则根据fileId取出文件信息返回,需要包括BaseFileName、Size、Version以及OwnerId;
- 提供下载附件的接口,该接口的url必需是上一步中定义的url+”/content”,按restful接口的定义是某个文件的内容。该接口一样会将之前的一些参数信息带回来,该步骤也鉴权。如果没问题,那么预览服务就通过该接口下载附件,进而转换。接下来会重点进行分析。
详细过程分析
详细的过程,各个类型的附件预览大同小异,下面会以word为例进行分析。
- 用户请求Office Online Server的url:http://IP:port/wv/wordviewerframe.aspx?WOPISrc=http%3a%2f%2f192.168.2.102%3a8080%2fwopiserver%2ffiles%2f2%3faccess_token%3dOfficeOnlineServer,其中ip,port为预览服务器对应的地址,接下来的/wv/wordviwerframe.aspx是word预览的地址,该地址可以从http://IP:port/hosting/discovery中得出,接下来就比较重要了,请求要带一个参数WOPISrc,代表要访问的附件需要采用WOPI协议来访问下载(该协议的地址是需要urlencode过的),WOPISrc的地址也是对应上面介绍的自己搭建服务的地址/files/{fileId};
- Office Onlne Server收到请求后,根据WOPISrc参数进行urldecode,解码之后访问该url进行权限校验,判断是否有权限,如果权限没问题,则可以取到文件的基本信息;
- 上一个步骤如果权限没问题,则此时Office Online Server会向/files/{fileId}/content发起文件下载的请求,下载完成之后Office Online Server将会对该文件进行转换,转换完毕之后返回给最开始的调用端进行渲染显示了。
WOPISrc中自己自定义参数,该参数urldecode之后,会带到/files/{fileId}的请求中。
代码分析
核心代码如下:
/**
* 通过文件唯一标识以及token获取文件的详细信息
*
* @param fileId 文件的唯一标识,比如可以是MD5或者数据库的id。此处fileId为对应的文件名
* @param access_token 访问的token
* @return
*/
@ResponseBody
@RequestMapping(value = "/files/{fileId}", method = RequestMethod.GET)
public Map<String, String> getFileInfo(@PathVariable("fileId") String fileId,
@RequestParam("access_token") String token, HttpServletResponse response) {
boolean isTokenValid = TokenUtil.verifyToken(token);
if (!isTokenValid) {// 按照wopi协议,返回HTTP CODE=401
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return null;
}
Map<String, String> fileInfoMap = FileUtils.getFileInfo(FILE_Map.get(fileId));
if (fileInfoMap == null) {// 根据fileId找不到对应的文件,按照wopi协议返回HTTP CODE=404
response.setStatus(HttpStatus.NOT_FOUND.value());
return null;
}
return fileInfoMap;
}
/**
* 下载附件内容,与上面的方法在url上的区别为增加了/contents,意为下载该附件下的内容
*
* @param fileId 文件的唯一标识,比如可以是MD5或者数据库的id。此处fileId为对应的文件名
* @param response
* @throws Exception
*/
@RequestMapping(value = "/files/{fileId}/contents")
public void getFileContent(@PathVariable("fileId") String fileId,
@RequestParam("access_token") String access_token, HttpServletResponse response)
throws Exception {
System.out.println("download file...");
File file = FileUtils.getFile(FILE_Map.get(fileId));
if (file == null) {// 根据fileId找不到对应的文件,按照wopi协议返回HTTP CODE=404
response.setStatus(HttpStatus.NOT_FOUND.value());
} else {
response.addHeader("Content-Disposition",
"attachment;filename=" + new String(file.getName().getBytes("utf-8"), "ISO-8859-1"));
response.addHeader("Content-Length", String.valueOf(file.length()));
OutputStream out = null;
try (InputStream is = new FileInputStream(file)) {
byte[] buffer = new byte[is.available()];
is.read(buffer);
out = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
out.write(buffer);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
out.flush();
out.close();
}
}
}
}