(四)文件预览之预览服务实现


介绍

前几篇中介绍了如何搭建预览服务器,在服务搭建起来之后,就应该考虑如何将预览服务与业务对接起来。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();
        }
        }
    }
}

文章作者: zzq0324
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 zzq0324 !
  目录