# 金山中台预览增加SDK以实现打印旋转等功能

# 改造背景

目前金山文档中台在线预览是请求金山文档中台预览接口返回预览地址,直接在页面上进行显示,此方案无法初始化金山文档中台的SDK,使用金山的SDK进行功能扩展。 比如追加实现打印、旋转、禁止pdf图片下载等功能。

本文档提供适配方法:实现初始化金山中台SDK,以方便做功能扩展。

# 适用版本

适用于V9.0及更老版本

V9.0SP1版本已经内置SDK

# 改造方案

预览文件的时候根据金山文档中台预览接口获取到预览地址, 通过WebOfficeSDK.config 方法在指定的挂载节点下创建一个文档应用,url参数为预览接口返回的地址,其他参数可以根据需求自定义,此时就可以调用文档应用对象的打印、旋转等方法。

# 适配版本

本次适配的中台版本为v6、v7,金山v5版本不适用。

# 可参考代码

Ctp-Studio 客开复用插件:金山中台在线预览支持打印旋转下载

1727060742992.png

# 涉及的工程

apps-api、apps-common、apps-office-plugins-front、apps-office-plugins、ctp-common、ctp-core

# 支持功能

1、公文、协同所有类型的正文在线预览支持打印、下载; 2、正文打印支持节点权限控制; 3、word、excel、ppt、pdf、ofd格式的附件线预览支持打印、下载; 4、pdf格式文件在线预览禁用图片下载; 5、pdf、ofd正文、附件支持左旋转、右旋转。

# 改动点

# apps-api

修改java类com.seeyon.apps.officePlugins.util.OfficeFileUtil.java

在pdfExts数组变量中加入“ofd”,原因是金山文档中台ofd格式文件预览调用的是pdf的接口,如下所示:

1726984637851.png

# apps-common

新增open-jssdk-v0.0.11.umd.js文件

此文件为金山文档中台官方提供的在线预览的sdk文件

# apps-office-plugins-front

修改BuildConfig.js文件

将金山在线预览需要编译的文件加入到entrys变量中,如下图所示:

1726984888309.png

新增webOfficePreview.html文件

webOfficePreview.html文件为挂载在线预览的页面,用于将在线预览的地址挂载到此页面元素中,页面渲染的时候需要加载open-jssdk-v0.0.11.umd.js和webOfficePreview.js文件,代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="Cache-control" content="no-cache">
    <meta http-equiv="Cache" content="no-cache">
    <title>在线预览</title>
    <script src="./../../js/polyfill.min.js?V=STATIC_SUFFIX"></script>
    <script src="./../../js/V3X.js?V=STATIC_SUFFIX"></script>

    <script type="text/javascript">
        var v3x = new V3X();
    </script>
    <style type="text/css">
        body,
        html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: rgb(243, 243, 243);
        }

        .plugin-container {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
        }

        .container {
            width: 100%;
            height: 100%;
        }

        .content {
            position: absolute;
            width: 410px;
            height: 220px;
            top: calc(50% - 110px);
            left: calc(50% - 205px);
        }

        .item {
            text-align: center;
        }

        .item img {
            width: 360px;
            height: auto;
            margin: 0 auto;
        }

        .tips {
            font-size: 14px;
            color: #999999;
            font-family: Arial, "Ping Fang SC", "Microsoft YaHei", Helvetica, sans-serif, "SimSun"
        }

        .edit {
            font-size: 14px;
            color: #FFFFFF;
            background: #5087E5;
            border: none;
            border-radius: 5px;
            width: 120px;
            height: 35px;
            margin-top: 20px;
            outline: none;
        }

        .edit:hover {
            cursor: pointer;
            transform: scale(1.1);
        }

        .edit:active {
            border: none;
        }
        #officeToolbar{
            border-bottom:1px solid #e2e6ed;
        }
        .icons{
            vertical-align: middle;
            display: inline-block;
            background-repeat: no-repeat;
            background-size: 100% 100%;
            flex-shrink: 0;
            width:16px;
            height:16px;
        }
        .icons-rotate-left{
            background-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjk5MzM0MDYwNzQ5IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjIyNDY4IiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPjxwYXRoIGQ9Ik05MjQuOCAzMzcuNmMtMjIuNi01My40LTU0LjktMTAxLjMtOTYtMTQyLjRzLTg5LTczLjQtMTQyLjQtOTZDNjMxLjEgNzUuOSA1NzIuNSA2NCA1MTIgNjRTMzkyLjkgNzUuOSAzMzcuNiA5OS4yYy01My40IDIyLjYtMTAxLjMgNTQuOS0xNDIuNCA5Ni0yMi40IDIyLjQtNDIuMiA0Ni44LTU5LjIgNzMuMVYyMjhjMC0xOS44LTE2LjItMzYtMzYtMzZzLTM2IDE2LjItMzYgMzZ2Mjg4YzAgMTkuOCAxNi4yIDM2IDM2IDM2czM2LTE2LjIgMzYtMzZ2LTUwLjJjNC4yLTM0LjggMTMuMi02OC43IDI3LTEwMS4yIDE5LjEtNDUuMSA0Ni40LTg1LjYgODEuMi0xMjAuNEMyNzkgMjA5LjQgMzE5LjUgMTgyIDM2NC42IDE2M2M0Ni43LTE5LjcgOTYuMy0yOS44IDE0Ny40LTI5LjggNTEuMiAwIDEwMC44IDEwIDE0Ny40IDI5LjggNDUuMSAxOS4xIDg1LjYgNDYuNCAxMjAuNCA4MS4yQzgxNC42IDI3OSA4NDIgMzE5LjUgODYxIDM2NC42YzE5LjcgNDYuNyAyOS44IDk2LjMgMjkuOCAxNDcuNCAwIDUxLjItMTAgMTAwLjgtMjkuOCAxNDcuNC0xOS4xIDQ1LjEtNDYuNCA4NS42LTgxLjIgMTIwLjRDNzQ1IDgxNC42IDcwNC41IDg0MiA2NTkuNCA4NjFjLTQ2LjcgMTkuNy05Ni4zIDI5LjgtMTQ3LjQgMjkuOC02NC42IDAtMTI4LjQtMTYuNS0xODQuNC00Ny44LTU0LjQtMzAuNC0xMDAuOS03NC4xLTEzNC42LTEyNi42LTEwLjMtMTYuMS0zMS43LTIwLjgtNDcuOC0xMC40LTE2LjEgMTAuMy0yMC44IDMxLjctMTAuNCA0Ny44IDM5LjggNjIgOTQuOCAxMTMuNyAxNTkuMSAxNDkuNiA2Ni4yIDM3IDE0MS43IDU2LjYgMjE4LjEgNTYuNiA2MC41IDAgMTE5LjEtMTEuOSAxNzQuNC0zNS4yIDUzLjQtMjIuNiAxMDEuMy01NC45IDE0Mi40LTk2IDQxLjEtNDEuMSA3My40LTg5IDk2LTE0Mi40Qzk0OC4xIDYzMS4xIDk2MCA1NzIuNSA5NjAgNTEycy0xMS45LTExOS4xLTM1LjItMTc0LjR6IiBmaWxsPSIjMzMzMzMzIiBwLWlkPSIyMjQ2OSI+PC9wYXRoPjxwYXRoIGQ9Ik0yNzUuNCA1NzUuNWM5LjUtMi41IDE5LjEgMi45IDIyLjMgMTIuMiAzLjUgMTAuMiA5LjkgMTcuNyAxOS4xIDIyLjYgNy4xIDMuOSAxNS4xIDUuOCAyNCA1LjggMTYuNiAwIDMwLjgtNi45IDQyLjUtMjAuOCAxMS43LTEzLjggMjAtMzIuNyAyNC45LTc1LjEtNy43IDEyLjItMTcuMyAyMC44LTI4LjcgMjUuOC0xMS40IDUtMjMuNyA3LjQtMzYuOCA3LjQtMjYuNyAwLTQ3LjctOC4zLTYzLjMtMjQuOS0xNS41LTE2LjYtMjMuMy0zNy45LTIzLjMtNjQuMSAwLTI1LjEgNy43LTQ3LjEgMjMtNjYuMiAxNS4zLTE5IDM3LjktMjguNiA2Ny44LTI4LjYgNDAuMyAwIDY4LjEgMTguMSA4My40IDU0LjQgOC41IDE5LjkgMTIuNyA0NC45IDEyLjcgNzQuOSAwIDMzLjgtNS4xIDYzLjgtMTUuMyA4OS45LTE2LjkgNDMuNS00NS41IDY1LjItODUuOCA2NS4yLTI3IDAtNDcuNi03LjEtNjEuNi0yMS4yLTEwLTEwLjEtMTYuNC0yMi0xOS4zLTM1LjgtMi05LjYgNC0xOS4xIDEzLjUtMjEuNmwwLjkgMC4xeiBtMTAzLTc0LjRjOS40LTcuNSAxNC4xLTIwLjYgMTQuMS0zOS4zIDAtMTYuOC00LjItMjkuMy0xMi43LTM3LjVTMzYwLjYgNDEyIDM0Ny41IDQxMmMtMTQgMC0yNS4yIDQuNy0zMy40IDE0LjEtOC4yIDkuNC0xMi40IDIyLTEyLjQgMzcuNyAwIDE0LjkgMy42IDI2LjcgMTAuOSAzNS41IDcuMiA4LjggMTguOCAxMy4xIDM0LjYgMTMuMSAxMS40IDAgMjEuOC0zLjggMzEuMi0xMS4zek02NDYuNiA0MTQuNGMxMi40IDIyLjggMTguNSA1NCAxOC41IDkzLjcgMCAzNy42LTUuNiA2OC43LTE2LjggOTMuMy0xNi4yIDM1LjMtNDIuOCA1Mi45LTc5LjYgNTIuOS0zMy4yIDAtNTcuOS0xNC40LTc0LjItNDMuMy0xMy41LTI0LjEtMjAuMy01Ni40LTIwLjMtOTcgMC0zMS40IDQuMS01OC40IDEyLjItODAuOSAxNS4yLTQyIDQyLjctNjMgODIuNS02MyAzNS45IDAgNjEuOCAxNC44IDc3LjcgNDQuM3ogbS00MC4yIDE3My4zYzkuNC0xMy45IDE0LTM5LjkgMTQtNzggMC0yNy40LTMuNC01MC0xMC4xLTY3LjctNi44LTE3LjctMTkuOS0yNi42LTM5LjQtMjYuNi0xNy45IDAtMzEgOC40LTM5LjMgMjUuMi04LjMgMTYuOC0xMi40IDQxLjYtMTIuNCA3NC4zIDAgMjQuNiAyLjYgNDQuNCA3LjkgNTkuNCA4LjEgMjIuOCAyMiAzNC4zIDQxLjYgMzQuMyAxNS43IDAgMjguMy03IDM3LjctMjAuOXpNODAzLjMgMzg3LjJjMTEuMiAxMS4zIDE2LjggMjUgMTYuOCA0MS4yIDAgMTYuNy01LjggMzAuNy0xNy41IDQxLjhDNzkxIDQ4MS40IDc3Ny40IDQ4NyA3NjIgNDg3Yy0xNy4xIDAtMzEuMi01LjgtNDIuMS0xNy40LTEwLjktMTEuNi0xNi40LTI1LjEtMTYuNC00MC42IDAtMTYuNSA1LjgtMzAuNCAxNy4zLTQxLjcgMTEuNS0xMS4zIDI1LjMtMTcgNDEuMi0xNyAxNi4zIDAgMzAuMSA1LjcgNDEuMyAxNi45ek03MzkuNSA0NTFjNi4yIDYuMiAxMy43IDkuMyAyMi41IDkuMyA4LjQgMCAxNS44LTMuMSAyMi4xLTkuMyA2LjMtNi4yIDkuNC0xMy43IDkuNC0yMi42IDAtOC41LTMuMS0xNS45LTkuMy0yMi4xLTYuMi02LjItMTMuNi05LjMtMjIuMi05LjNzLTE2LjEgMy4xLTIyLjQgOS4zYy02LjMgNi4yLTkuNCAxMy43LTkuNCAyMi42LTAuMSA4LjQgMyAxNS44IDkuMyAyMi4xeiIgZmlsbD0iIzMzMzMzMyIgcC1pZD0iMjI0NzAiPjwvcGF0aD48L3N2Zz4=")
        }
        .icons-rotate-right{
            background-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjk5MzM0NjY5NzI0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQwODA5IiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPjxwYXRoIGQ9Ik0xNDY0LjMgMjc5LjciIGZpbGw9IiMzMzMzMzMiIHAtaWQ9IjQwODEwIj48L3BhdGg+PHBhdGggZD0iTTUxMiA5NjBjLTYwLjUgMC0xMTkuMS0xMS45LTE3NC40LTM1LjItNTMuNC0yMi42LTEwMS4zLTU0LjktMTQyLjQtOTZzLTczLjQtODktOTYtMTQyLjRDNzUuOSA2MzEuMSA2NCA1NzIuNSA2NCA1MTJzMTEuOS0xMTkuMSAzNS4yLTE3NC40YzIyLjYtNTMuNCA1NC45LTEwMS4zIDk2LTE0Mi40czg5LTczLjQgMTQyLjQtOTZDMzkyLjkgNzUuOSA0NTEuNSA2NCA1MTIgNjRzMTE5LjEgMTEuOSAxNzQuNCAzNS4yYzUzLjQgMjIuNiAxMDEuMyA1NC45IDE0Mi40IDk2czczLjQgODkgOTYgMTQyLjRDOTQ4LjEgMzkyLjkgOTYwIDQ1MS41IDk2MCA1MTJjMCAxOS4xLTE1LjUgMzQuNi0zNC42IDM0LjZzLTM0LjYtMTUuNS0zNC42LTM0LjZjMC01MS4yLTEwLTEwMC44LTI5LjgtMTQ3LjQtMTkuMS00NS4xLTQ2LjQtODUuNi04MS4yLTEyMC40Qzc0NSAyMDkuNCA3MDQuNSAxODIgNjU5LjQgMTYzYy00Ni43LTE5LjctOTYuMy0yOS44LTE0Ny40LTI5LjgtNTEuMiAwLTEwMC44IDEwLTE0Ny40IDI5LjgtNDUuMSAxOS4xLTg1LjYgNDYuNC0xMjAuNCA4MS4yUzE4MiAzMTkuNSAxNjMgMzY0LjZjLTE5LjcgNDYuNy0yOS44IDk2LjMtMjkuOCAxNDcuNCAwIDUxLjIgMTAgMTAwLjggMjkuOCAxNDcuNCAxOS4xIDQ1LjEgNDYuNCA4NS42IDgxLjIgMTIwLjRDMjc5IDgxNC42IDMxOS41IDg0MiAzNjQuNiA4NjFjNDYuNyAxOS43IDk2LjMgMjkuOCAxNDcuNCAyOS44IDY0LjYgMCAxMjguNC0xNi41IDE4NC40LTQ3LjggNTQuNC0zMC40IDEwMC45LTc0LjEgMTM0LjYtMTI2LjYgMTAuMy0xNi4xIDMxLjctMjAuOCA0Ny44LTEwLjQgMTYuMSAxMC4zIDIwLjggMzEuNyAxMC40IDQ3LjgtMzkuOCA2Mi05NC44IDExMy43LTE1OS4xIDE0OS42LTY2LjIgMzctMTQxLjcgNTYuNi0yMTguMSA1Ni42eiIgZmlsbD0iIzMzMzMzMyIgcC1pZD0iNDA4MTEiPjwvcGF0aD48cGF0aCBkPSJNOTI0IDU1MmMtMTkuOCAwLTM2LTE2LjItMzYtMzZWMjI4YzAtMTkuOCAxNi4yLTM2IDM2LTM2czM2IDE2LjIgMzYgMzZ2Mjg4YzAgMTkuOC0xNi4yIDM2LTM2IDM2ek0yNzUuNCA1NzUuNWM5LjUtMi41IDE5LjEgMi45IDIyLjMgMTIuMiAzLjUgMTAuMiA5LjkgMTcuNyAxOS4xIDIyLjYgNy4xIDMuOSAxNS4xIDUuOCAyNCA1LjggMTYuNiAwIDMwLjgtNi45IDQyLjUtMjAuOCAxMS43LTEzLjggMjAtMzIuNyAyNC45LTc1LjEtNy43IDEyLjItMTcuMyAyMC44LTI4LjcgMjUuOC0xMS40IDUtMjMuNyA3LjQtMzYuOCA3LjQtMjYuNyAwLTQ3LjctOC4zLTYzLjMtMjQuOS0xNS41LTE2LjYtMjMuMy0zNy45LTIzLjMtNjQuMSAwLTI1LjEgNy43LTQ3LjEgMjMtNjYuMiAxNS4zLTE5IDM3LjktMjguNiA2Ny44LTI4LjYgNDAuMyAwIDY4LjEgMTguMSA4My40IDU0LjQgOC41IDE5LjkgMTIuNyA0NC45IDEyLjcgNzQuOSAwIDMzLjgtNS4xIDYzLjgtMTUuMyA4OS45LTE2LjkgNDMuNS00NS41IDY1LjItODUuOCA2NS4yLTI3IDAtNDcuNi03LjEtNjEuNi0yMS4yLTEwLTEwLjEtMTYuNC0yMi0xOS4zLTM1LjgtMi05LjYgNC0xOS4xIDEzLjUtMjEuNmwwLjkgMC4xeiBtMTAzLTc0LjRjOS40LTcuNSAxNC4xLTIwLjYgMTQuMS0zOS4zIDAtMTYuOC00LjItMjkuMy0xMi43LTM3LjVTMzYwLjYgNDEyIDM0Ny41IDQxMmMtMTQgMC0yNS4yIDQuNy0zMy40IDE0LjEtOC4yIDkuNC0xMi40IDIyLTEyLjQgMzcuNyAwIDE0LjkgMy42IDI2LjcgMTAuOSAzNS41IDcuMiA4LjggMTguOCAxMy4xIDM0LjYgMTMuMSAxMS40IDAgMjEuOC0zLjggMzEuMi0xMS4zek02NDYuNiA0MTQuNGMxMi40IDIyLjggMTguNSA1NCAxOC41IDkzLjcgMCAzNy42LTUuNiA2OC43LTE2LjggOTMuMy0xNi4yIDM1LjMtNDIuOCA1Mi45LTc5LjYgNTIuOS0zMy4yIDAtNTcuOS0xNC40LTc0LjItNDMuMy0xMy41LTI0LjEtMjAuMy01Ni40LTIwLjMtOTcgMC0zMS40IDQuMS01OC40IDEyLjItODAuOSAxNS4yLTQyIDQyLjctNjMgODIuNS02MyAzNS45IDAgNjEuOCAxNC44IDc3LjcgNDQuM3ogbS00MC4yIDE3My4zYzkuNC0xMy45IDE0LTM5LjkgMTQtNzggMC0yNy40LTMuNC01MC0xMC4xLTY3LjctNi44LTE3LjctMTkuOS0yNi42LTM5LjQtMjYuNi0xNy45IDAtMzEgOC40LTM5LjMgMjUuMi04LjMgMTYuOC0xMi40IDQxLjYtMTIuNCA3NC4zIDAgMjQuNiAyLjYgNDQuNCA3LjkgNTkuNCA4LjEgMjIuOCAyMiAzNC4zIDQxLjYgMzQuMyAxNS43IDAgMjguMy03IDM3LjctMjAuOXpNODAzLjMgMzg3LjJjMTEuMiAxMS4zIDE2LjggMjUgMTYuOCA0MS4yIDAgMTYuNy01LjggMzAuNy0xNy41IDQxLjhDNzkxIDQ4MS40IDc3Ny40IDQ4NyA3NjIgNDg3Yy0xNy4xIDAtMzEuMi01LjgtNDIuMS0xNy40LTEwLjktMTEuNi0xNi40LTI1LjEtMTYuNC00MC42IDAtMTYuNSA1LjgtMzAuNCAxNy4zLTQxLjcgMTEuNS0xMS4zIDI1LjMtMTcgNDEuMi0xNyAxNi4zIDAgMzAuMSA1LjcgNDEuMyAxNi45ek03MzkuNSA0NTFjNi4yIDYuMiAxMy43IDkuMyAyMi41IDkuMyA4LjQgMCAxNS44LTMuMSAyMi4xLTkuMyA2LjMtNi4yIDkuNC0xMy43IDkuNC0yMi42IDAtOC41LTMuMS0xNS45LTkuMy0yMi4xLTYuMi02LjItMTMuNi05LjMtMjIuMi05LjNzLTE2LjEgMy4xLTIyLjQgOS4zYy02LjMgNi4yLTkuNCAxMy43LTkuNCAyMi42LTAuMSA4LjQgMyAxNS44IDkuMyAyMi4xeiIgZmlsbD0iIzMzMzMzMyIgcC1pZD0iNDA4MTIiPjwvcGF0aD48L3N2Zz4=");
        }
        .icons-print{
            background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjM0Q0NzU3IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0xMiA0aC0xVjJINXYySDRWMmExIDEgMCAwMTEtMWg2YTEgMSAwIDAxMSAxdjJ6bTAgNXY0YTEgMSAwIDAxLTEgMUg1YTEgMSAwIDAxLTEtMVY5aDF2NGg2VjloMXoiLz48cGF0aCBkPSJNMTIgMTJ2LTFoMlY1SDJ2NmgydjFIMmExIDEgMCAwMS0xLTFWNWExIDEgMCAwMTEtMWgxMmExIDEgMCAwMTEgMXY2YTEgMSAwIDAxLTEgMWgtMnoiLz48cGF0aCBkPSJNMyA4aDEwdjFIM3ptOC0yaDJ2MWgtMnoiLz48L2c+PC9zdmc+");
        }
        .icons-download{
            background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAA9dJREFUeF7tm01oE1EQx/+zKRQv6kkvCoJ6UC9exEOT+FFMInhqm6SFHgQVL0JFDyIIVr2ICJ68iIIgYj7EL5Rm14prNn4i9CCIiOBB8CAo+HEwxezIBltjPva9TZt0Y94e8+bNe/PbmXnDyyyhyx/qcvuhACgPkCAQig33EzjBjO0A1khMabfIF4B0ZpgFI3XBy+LCEAhGEuNEdNyL0gWVJRhWLh2V3YMrgNDO+HrY2lMAi2UV+kGOmfbLeoI7gGjyHICDfjDK6x6mtZ4lzyeufhPNcwUQjianGNg4o8TS08KQES3YqvGaUNXsDdZE9rVoPZEHcKWCTgLARNsKuZSpAAgIKA9wAxSKJlUIqBxQQUAlwQ46BtUpoI5BVQe0txAKxoa3wra3iiqvinHniK1Xh9T7vfxbwciMN9JfXQq3PQc4AIj5oQcAnkSZ+YQCoDzA/yHQspsjZn7k6xDwFNAtEF7wJNgCmzyp7HoAoR2JUWh0ZYZa249BT6+rRcLByPApIj7mqO9KAI7h4Wgyw0C87QDKlWCTj8zdnazq/v6R5dM9tsFEYzJ65+1KrNlKUPZNyQIoe0EkEbY1TetaAA4E54V0NQBZj/kvQ0DWeEdu3gB4WbRZ2XqJVsbN3dbrLAC1/1Sblp7e1izQzvMABaCmV0F5gAoBlQNUElSngDoG50BAFUJu8BaiQeJPa96QpadPVu+tTs9i3TogHEsO2ECgkEtnRc7hKw/4Y3wGwAZiHM0b6dOVBsgACEaSu4hwC0CACQkRBN8AqDR+1miiQ1Yu5fQqlh8RgL5IfIcG7TYIi/5ejrpD8A2ARi25DD5Q0DPnRQC2RIdDNvgOgKX/uD3xdSuXSTj3pPXCwTcAGhhY3jMR7c3nUpcaecCWWHKTzXwXoGVVRt4KFNfFTXP8V6Nc4CsArhAYozZ4bVXjtgnSxsClewCtqDLybqC4KG6al3923H1Aw3BgPlEDAFgJYPW/RnIuUOS4aWZ/dNQpIMj49WxxeoHXVw1M9pbsocnJ7FeR8b6/EGniWwWz+Gt66MWDm59ljPc9ALecUGsgF0rcO/jEuPJJ1ngZAM8AbJ5RuFCNkiJPIMazEtPg4/upj16MFwIIR5NnGTjsVakH+bdMtF/mZrdxYsRLDTSQN1IfPKw7K+qHT2a+E2F3Ppe+ITKgDoQpYnswb2Tfi+Y2VQd4i8FmtzA7b5+lpy+KtMxCYLwKkD1g6tl3ojlN1wEzE/tiI0GN7T0AnH+AV81lQfe5fMTSM2dE+h0IPQikTOPaG5GsaNy33wCJNj5f4wrAfJHsVD1d7wG/AbuI0V8xA0d0AAAAAElFTkSuQmCC")
        }
        .btn{
            line-height: 40px;
            margin-left: 3px;
            margin-right: 3px;
            padding:2px 5px 2px 5px;
            cursor:pointer;
        }
        .btn-text{
            font-size:12px;
        }
        .btn:hover{
            background:#c0c6cf;
        }
    </style>
</head>

</head>

<body>
<div style="text-align: center;height:40px;display: none" id="officeToolbar">
        <span onclick="topPrint()" id="print" class="btn">
            <i class="icons icons-print"></i>
            <span class="btn-text">打印</span>
        </span>
    <span id="left-rotate" onclick="topRotate(-90)" class="btn">
            <i class="icons icons-rotate-left"></i>
            <span class="btn-text">左旋转</span>
        </span>
    <span id="right-rotate" onclick="topRotate(90)" class="btn">
            <i class="icons icons-rotate-right"></i>
            <span class="btn-text">右旋转</span>
        </span>
    <span onclick="topDownload()" class="btn">
            <i class="icons icons-download"></i>
            <span class="btn-text">下载</span>
        </span>
</div>
<div id="pluginContainer" class="plugin-container">
    <div class="container" style="height: 100% ;width: 100%;">
        <div class="content" id="wpsPdfContent" style="display: none;">

        </div>

    </div>
</div>
<script type="text/javascript" src="./../../js/jquery-debug.js?V=STATIC_SUFFIX"></script>
<script type="text/javascript" src="./../js/open-jssdk-v0.0.11.umd.js?V=STATIC_SUFFIX"></script>
<script type="text/javascript" src="./../js/webOffice/webOfficePreview.js?V=STATIC_SUFFIX"></script>
</body>

</html>

新增webOfficePreview.js文件

webOfficePreivew.js文件为在线预览的核心文件,用于获取金山文档中台预览地址、挂载预览地址、打印、旋转等

var webOfficeObj={};
var fileId;
var fileName;
var fileType;
var ctxPath="";
var viewUrl="";
var userAgentForm="pc";
window.onload = function () {
    var queryParams = getQueryParams();
    var officeToolbar=$(document.getElementById("officeToolbar"));
    var pluginContainer=$(document.getElementById("pluginContainer"));
    fileId=queryParams.fileId;
    fileName=queryParams.fileName;
    viewUrl=queryParams.viewUrl;
    ctxPath=queryParams.ctxPath;
    userAgentForm=queryParams.userAgentForm;
    if(fileName.lastIndexOf(".")>0){
        fileName=fileName.substring(0,fileName.lastIndexOf("."))
    }
    fileType=queryParams.fileSuffix;

    officeToolbar.show();
    if(userAgentForm=="pc") {
        if (top.officecanPrint == "false" || top.isFromTraceFlag) {
            $(document.getElementById("print")).hide();
        }
        if (fileId != top.fileId && fileId != top.pdfFileId && fileId != top.ofdFileId) {
            $(document.getElementById("print")).show();
        }
    }else{
        $(document.getElementById("print")).hide();
    }
    $(pluginContainer).height($(pluginContainer).height()-51);
    if(typeof(fileType)!="undefined"&&(fileType!="pdf"&&fileType!="ofd")){
        $(document.getElementById("right-rotate")).hide();
        $(document.getElementById("left-rotate")).hide();
    }
    loadFile()
}
/**
 * 获取token信息
 * @returns
 */
function getToken() {
    var webRoot=window.location.origin + ctxPath;
    let xhrFun = getAjaxFunc();
    return new Promise(function(resolve,reject){
        xhrFun({
            url: webRoot + '/webOfficeTolen.do?tko=WebOffice&m=k',
            type: 'GET',
            success: function(msg, status, xhr) {
                const token = xhr.getResponseHeader('tolen');
                const maxRetryTimes = 1;
                let hasRetryTimes = 0;
                const rollTime = 60 * 1000;

                const dealFail = () => {
                    // 允许网络不稳定,重试
                    if(hasRetryTimes >= maxRetryTimes){
                        // TODO  提示
                        const res = confirm("正文保存数据心跳失败,请保存数据到本地,重新打开页面!");
                        if(res){
                            return false;
                        }
                    }
                    hasRetryTimes++;
                    console.debug("delay RetryTimes[" + hasRetryTimes + "]");
                    return true;
                }

                const doDelay = () => {
                    xhrFun({
                        url:webRoot + '/webOfficeTolen.do?tko=WebOffice&m=u&tolen=' + token,
                        headers: {
                            'tolen':token
                        },
                        success: function(msg,status,xhr) {
                            //val
                            var val = xhr.getResponseHeader('val');
                            if(val == "1"){
                                console.debug("delay ok!");
                                // reset
                                hasRetryTimes = 0;
                                setTimeout(doDelay, rollTime);
                            }else{
                                if(dealFail()){
                                    setTimeout(doDelay, rollTime);
                                }
                            }
                        },
                        error:function(XMLHttpRequest, textStatus, errorThrown){
                            if(dealFail()){
                                setTimeout(doDelay, rollTime);
                            }
                        }
                    })
                }
                setTimeout(doDelay, rollTime);
                resolve(token);
            },
            error: function() {
                resolve(false);
            }
        })
    })
}
// 获取ajax
function getAjaxFunc() {
    return $.ajax ? $.ajax : window.top.jQuery.ajax;
}

function loadFile(){
    return new Promise(async (reslve, reject) => {
        const tolen = await getToken();
        const url = viewUrl + "&v=" + new Date().getTime()
        const refreshToken = () => {
            // 获取 token 函数
            return Promise.resolve({
                token: tolen, // 必需:你需要设置的 toekn
                timeout: 10 * 60 * 1000, //  必需:token 超时时间,以 10 分钟示例
            });
        };
        // 清空节点
        let dom = document.querySelector("#wpsPdfContent");
        dom.innerHTML = "";
        dom.setAttribute("class", "");
        dom.setAttribute("style", "width: 100%; height: 100%");
        webOfficeObj = OpenSDK.config({
            url: url,
            refreshToken,
            mount: dom,
            pdfOptions:{
                isInSafeMode:false,
            }
        });
        // 	文档打开事件
        webOfficeObj.on('fileOpen', (data) => {
            console.log('文件打开:', data);
            reslve(data);
        });
        if(tolen) {
            webOfficeObj.setToken({token: tolen,timeout: 10 * 60 * 1000})
        }
        //如果执行wps.ready()后不再往下执行,极大可能是因为weboffice在线编辑文档不可见
        console.log("init:wps.ready() 开始 ....");
        await webOfficeObj.ready();
        let officeIframe=document.querySelector("#office-iframe");
        officeIframe.setAttribute("style", "width: 100%; height: 100%");
        console.log("init:初始 wps.ready() 已执行");

    })
}
var currentAngle=0;
window.topPrint = async function topPrint(){
    webOfficeObj.executeCommandBar('TabPrintPreview');
}
window.topRotate=function topRotate(angle){
    currentAngle=currentAngle+angle;
    if(currentAngle>=360){
        currentAngle=currentAngle-360;
    }else if(currentAngle<0){
        currentAngle=360+angle;
    }
    webOfficeObj.Application.ActivePDF.RotatePage(currentAngle);
}
window.topDownload=function topDownload(){
    if(webOfficeObj){
        let xhrFun = getAjaxFunc();
        var webRoot = window.location.origin + ctxPath
        xhrFun({
            url: webRoot + "/rest/webOffice/getSecuritySeed?recordId=" + fileId + "&originalFileId=", //获取加密种子地址
            type: 'GET',
            success: function success(msg) {
                if (msg.code == 0) { //获取加密种子成功
                    const v = msg.data;
                    var realFilename = fileName;
                    realFilename += "."+fileType;
                    realFilename = realFilename.replace(/[\r\n]/g, "");
                    var downlaodUrl = webRoot + "/fileDownload.do?method=download&fileId=" + fileId + "&v=" + v + "&filename=" + encodeURIat(realFilename);
                    var a = document.createElement("a");
                    a.href = downlaodUrl;
                    a.download = realFilename;
                    a.target = "downloadFileFrame";
                    $("body") ? $("body").append(a) : document.body.append(a); // 修复firefox中无法触发click
                    a.click();
                    $(a).remove();
                } else {//文件不能下载的时候的操作
                }
            },
            error: function (err) {
                Alert(err);
            }
        });

    }
}

# apps-office-plugins

1、com.seeyon.apps.officePlugins.enums.WebOfficeEnums.java

在TypeEnum枚举中增加OnlinePreviewForV6枚举,如图所示:

1699869597771.png

2、com.seeyon.apps.officePlugins.helper.WebOfficeURLHelper.java

1)、增加setHeadersForWps4方法

public HttpHeaders setHeadersForWps4(URI url,HttpHeaders headers){
        String path=url.getPath()+"?"+url.getQuery();
        if (path.startsWith("/open")) {
            path = path.replace("/open", "");
        }
        String sha256body="";
        //日期格式化
        DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = dateFormat.format(new Date());
        String signature = null;
        try {
            signature = HMacUtil.HMACSHA256("WPS-4GET" +
                    path + "application/json" + date + sha256body, SystemGlobalConstant.WEBOFFICE_APPKEY);
        } catch (Exception e) {
            e.printStackTrace();
        }
        headers.add("Wps-Docs-Date",date);
        headers.add("Wps-Docs-Authorization", String.format("WPS-4 %s:%s", SystemGlobalConstant.WEBOFFICE_APPID, signature));
        return headers;
    }

2)、修改mapToWebOfficeUrlForPri方法

	/**
	 * weboffice为内网,map参数转为weboffice需要的url参数(代签名)
	 *
	 * @param params   需要组装的第3方参数
	 * @param typeEnum Url类型
	 * @return
	 * @throws BusinessException
	 */
	public String mapToWebOfficeUrlForPri(Map<String, Object> params, WebOfficeEnums.UrlTypeEnum typeEnum) throws BusinessException {
		if(MapUtils.isEmpty(params) && WebOfficeEnums.UrlTypeEnum.OnlineEdit.getKey().equals(typeEnum.getKey())){
			return null;
		}
		Boolean isPreview=false;
		//判断是否为预览
		if(params!=null&&params.get("viewMode")!=null&&params.get("viewMode").equals("view")) {
			isPreview=true;
		}
		String token = createAppToken(typeEnum);

		if(WebOfficeEnums.UrlTypeEnum.ContentFormat.getKey().equals(typeEnum.getKey())){
			return token;
		}
		String callBackUrl = SystemProperties.getInstance().getProperty("officeTrans.wps.callBackUrl");
		Boolean isV6=callBackUrl.contains("/webOffice/v6");
		UriComponentsBuilder builder;
		if(isV6&&isPreview) {
			builder = UriComponentsBuilder
					.fromHttpUrl(this.webOfficeUrl + WebOfficeEnums.UrlTypeEnum.OnlinePreviewForV6.getUrl().replace("{file_id}", MapUtils.getString(params, "fileId")));
		} else {
			// 设置token,生成weboffice认证后的url
			builder = UriComponentsBuilder
					.fromHttpUrl(this.webOfficeUrl + typeEnum.getUrl());
			builder.queryParam("app_token", token);
			builder.queryParam("file_id", MapUtils.getString(params, "fileId"));
		}
        String fileTypeCode= OfficeFileUtil.getFileTypeCode(MapUtils.getString(params, "fileType"));
        if(fileTypeCode!=null){
			// 在线编辑,需要增加type参数
			builder.queryParam("type", OfficeFileUtil.getFileTypeCode(MapUtils.getString(params, "fileType")));
		}
		params.forEach((k,v)->{
			if(v!=null){
				builder.queryParam(PARAM_PREFIX + k, MapUtils.getString(params, k, ""));
			}
		});
		URI urlResult = builder.build().toUri();
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity;
        if(isV6&&isPreview) {
            headers=setHeadersForWps4(urlResult,headers);
            requestEntity= new HttpEntity<>(null, headers);
            GetOnlinePreviewResponse previewResponse = restTemplate.exchange(urlResult, HttpMethod.GET, requestEntity, GetOnlinePreviewResponse.class).getBody();
            if(previewResponse.getCode().equals("200")){
                if(previewResponse.getData().containsKey("link")) {
                    return previewResponse.getData().get("link").toString().replace("wpsCachePreview","wpsPreview=1000000&simple");
                }
            }
        }
		requestEntity = new HttpEntity<>(null, headers);
		long t1 = System.currentTimeMillis();
		GetOnlineEditUrlResponse res;
		try {
			res = restTemplate.exchange(urlResult, HttpMethod.GET, requestEntity, GetOnlineEditUrlResponse.class).getBody();
		} catch (HttpClientErrorException e) {
			LOGGER.error("获取weboffice生成的URL出错:url="+urlResult.toString()+";"+e.getResponseBodyAsString(), e);
			throw new BusinessException(e.getResponseBodyAsString(),e);
		}
		LOGGER.info(MessageFormat.format("请求中台-2-:{0},耗时{1}ms", this.webOfficeUrl+ typeEnum.getUrl(),System.currentTimeMillis()-t1));
		return res.getUrl();
	}

3、com.seeyon.apps.wpsassist.manager.WpsAssistOfficeApiImpl.java

添加如下代码:

1699870006150.png

4、新增java类com.seeyon.apps.weboffice.util.HMacUtil.java

package com.seeyon.apps.weboffice.util;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @author ranjf
 * @description
 * @date 2023/9/26 15:50
 */
public class HMacUtil {
    public static String HMACSHA256(String data, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"),
                "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1,
                    3));
        }
        return sb.toString();
    }
    /**
     * 利用java原生的摘要实现SHA256加密
     * @param str 加密后的报文
     * @return
     */
    public static String getSHA256StrJava(byte[] str){
        MessageDigest messageDigest;
        String encodeStr = "";
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(str);
            encodeStr = byte2Hex(messageDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return encodeStr;
    }
    /**
     * 将byte转为16进制
     * @param bytes
     * @return
     */
    private static String byte2Hex(byte[] bytes){
        StringBuffer stringBuffer = new StringBuffer();
        String temp = null;
        for (int i=0;i<bytes.length;i++){
            temp = Integer.toHexString(bytes[i] & 0xFF);
            if (temp.length()==1){
                //1得到一位的进行补0操作
                stringBuffer.append("0");
            }
            stringBuffer.append(temp);
        }
        return stringBuffer.toString();
    }
}

5、新增Java类:com.seeyon.apps.officePlugins.vo.middleground.GetOnlinePreviewResponse.java

package com.seeyon.apps.officePlugins.vo.middleground;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.Map;

/**
 * @author ranjf
 * @description
 * @date 2023/9/27 15:56
 */
@JsonInclude(JsonInclude.Include.NON_NULL)
public class GetOnlinePreviewResponse {
    @JsonProperty("data")
    private Map<String,Object> data;
    @JsonProperty("code")
    private String code;
    @JsonProperty("msg")
    private String msg;

    public Map<String, Object> getData() {
        return data;
    }

    public void setData(Map<String, Object> data) {
        this.data = data;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

6、修改java类:com.seeyon.apps.weboffice.manager.WpsWebOfficePreviewAdapter.java

1)、修改filter方法

修改前:

1726994563054.png

修改后:

1726994430976.png

1726994523436.png

最终代码如下:

	public boolean filter(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// url中 包含文件名和其父级路径,如:-23634523412345123/-23634523412345123.html;-23634523412345123/file001.jpg
		String viewURL;
		String webOfficePreviewUrl= SystemEnvironment.getContextPath() +"/common/office/html/webOfficePreview.html";
		try {
			// 检查浏览器对于webOffice的兼容性
			checkBrowserCompatible(request);
			Object[] o = extractFileId(request.getRequestURI());
			String suffix = request.getParameter("fileSuffix");
			if(StringUtils.isNoneEmpty(suffix)){
				suffix=suffix.replace(".","");
			}
			long fileId = (Long) o[0];
            String fileName = request.getParameter("fileName");
            if(Strings.isBlank(fileName)){
                fileName=request.getParameter("dcs_titleName");
            }
            if(suffix==null){
                if(fileName!=null&&fileName.lastIndexOf(".")>-1){
                    suffix=fileName.substring(fileName.lastIndexOf(".")+1);
                }
            }
			viewURL = buildWpsPreviewUrl(request, String.valueOf(fileId),suffix);
			viewURL += "&_w_third_watermark=" + URLEncoder.encode(StringUtils.trimToEmpty(getWatermarkText(request)), "utf-8").replaceAll("\\+","%20");

			if (StringUtils.isNoneEmpty(fileName)){
				viewURL += "&_w_third_filename=" + URLEncoder.encode(fileName,"UTF-8");
			}

			if (StringUtils.isNoneEmpty(suffix)){
				viewURL += "&_w_third_suffix=" +suffix;
			}
			String fileCreateDate = request.getParameter("fileCreateDate");
			if (StringUtils.isNoneEmpty(fileCreateDate)){
				viewURL += "&_w_third_fileCreateDate=" + fileCreateDate;
			}

			//如果文件类型为空,并且不在支持的类型范围 走老的预览逻辑
			if(suffix!=null&& OfficeFileUtil.getFileTypeCode(suffix)!=null) {
				String userAgentForm=AppContext.getCurrentUser().getUserAgentFrom();
				String ctxPath=SystemEnvironment.getContextPath();
				webOfficePreviewUrl += "?ctxPath="+ctxPath+"&userAgentForm="+userAgentForm+"&fileId="+fileId+"&fileSuffix="+suffix+"&fileName="+URLEncoder.encode(fileName,StandardCharsets.UTF_8.toString())+"&viewUrl=" + URLEncoder.encode(viewURL, StandardCharsets.UTF_8.toString());
			}else {
				webOfficePreviewUrl=viewURL;
			}
			
		}catch (BrowserCompatibleException exception){
			LOG.error("BrowserCompatibleException",exception);
			return writeErrorMessageToBrower(response,9);
		}catch (Exception exception){
			LOG.error("",exception);
			return writeErrorMessageToBrower(response,8);
		}
		redirect(response,webOfficePreviewUrl);
		return false;
	}

2)、修改buildWpsPreviewUrl方法,添加如红框中的代码

1726986428774.png

7、修改Java类com.seeyon.apps.wpsassist.manager.WpsAssistOfficeApiImpl.java

SUPPORT_SUFFIX集合中添加MIMETypeSuffix.ofdMIMEType.getSuffix()

1726986282824.png

8、修改java类:com.seeyon.ctp.rest.resources.WebOfficeV6CallbackResource.java

1)、添加静态常量:REP_TOKEN

private static final String REP_TOKEN = "x-wps-weboffice-token";

2)、修改getFileInfo方法,修改点如图:

修改前:

1726986160121.png

修改后:

1726986051639.png

1726986088668.png

# ctp-common

1、com.seeyon.ctp.common.content.mainbody.handler.impl.AbstractOfficeMainbodyHandler.java

修改getContentHtml()方法,如下图:

1726982936830.png

2、com.seeyon.ctp.common.content.mainbody.handler.impl.OFDMainbodyHandler.java

修改getTransView()方法,如下图:

1726983230570.png

3、com.seeyon.ctp.common.taglibs.support.EditorSupport.java

修改showOfficeHtmlContent()方法,如下图所示:

1726983479379.png

**4、com.seeyon.ctp.common.taglibs.support.ShowContentSuppert.java **

修改showOfficeHtmlContent()方法,如下图所示:

1726983785631.png

com.seeyon.ctp.common.web.filter.WebOfficeAuthenticator.java

修改authenticate()方法,获取token的时候先从header获取,如果没有就从url中获取,获取token的代码改为如下所示:

String tolen = Strings.isBlank(request.getHeader(REP_TOKEN)) ? (Strings.isBlank(request.getParameter("_w_tolen"))?request.getParameter("_w_third_tolen"):request.getParameter("_w_tolen")) : request.getHeader(REP_TOKEN);

# ctp-core

修改java类:com.seeyon.ctp.common.office.trans.util.OfficeTransHelper.java

修改buildCacheUrl方法,修改点如图所示:

1726984351508.png

创建人:ranjunfeng
修改人:het、ranjf