From acffcbfe98e65b6b975c10e7679cee874fd7a954 Mon Sep 17 00:00:00 2001 From: yl-yue Date: Thu, 21 Jul 2022 03:19:46 +0000 Subject: [PATCH] =?UTF-8?q?!51=20=E5=AE=9E=E7=8E=B0=E9=A2=84=E8=A7=88?= =?UTF-8?q?=E5=8A=A0=E5=AF=86=E7=9A=84=EF=BC=88=E5=8F=97=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E4=BF=9D=E6=8A=A4=EF=BC=89office=E6=96=87=E4=BB=B6=20*=201.=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8DgetCorsFile=E6=8E=A5=E5=8F=A3=E9=AB=98?= =?UTF-8?q?=E5=8D=B1=E5=AE=89=E5=85=A8=E6=BC=8F=E6=B4=9E=20*=201.=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AF=86=E7=A0=81=E9=94=99=E8=AF=AF=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=EF=BC=88=E2=80=9C=E5=AF=86=E7=A0=81=E9=94=99=E8=AF=AF?= =?UTF-8?q?=EF=BC=8C=E8=AF=B7=E9=87=8D=E6=96=B0=E8=BE=93=E5=85=A5=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E3=80=82=E2=80=9D=EF=BC=89=20*=201.=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8DPPT=E9=87=8D=E5=A4=8D=E9=A2=84=E8=A7=88bug=EF=BC=8C?= =?UTF-8?q?=E6=AD=A4bug=E5=AF=BC=E8=87=B4ppt=E6=AF=8F=E6=AC=A1=E9=A2=84?= =?UTF-8?q?=E8=A7=88=E4=BC=9A=E6=89=A7=E8=A1=8C=E4=B8=A4=E6=AC=A1=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2=EF=BC=88=E8=AF=B7=E6=B1=82=E4=B8=A4=E6=AC=A1onlinePre?= =?UTF-8?q?view=E6=8E=A5=E5=8F=A3=EF=BC=89=EF=BC=8C=E5=9C=A8=E5=A4=A7?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=B0=A4=E5=85=B6=E8=80=97=E6=97=B6=EF=BC=88?= =?UTF-8?q?=E5=8F=8C=E5=80=8D=E6=97=B6=E2=80=A6=20*=201.=20=E3=80=90?= =?UTF-8?q?=E5=8A=A0=E5=AF=86office=E9=A2=84=E8=A7=88=E3=80=91=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=8F=97=E5=AF=86=E7=A0=81=E4=BF=9D=E6=8A=A4=E7=9A=84?= =?UTF-8?q?office=E6=96=87=E4=BB=B6=E6=A3=80=E6=9F=A5=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E6=8F=90=E5=8D=87=E6=97=A7=E6=96=87=E4=BB=B6=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E7=9A=84=E5=85=BC=E5=AE=B9=E6=80=A7=20*=201.=20?= =?UTF-8?q?=E3=80=90=E5=8A=A0=E5=AF=86office=E9=A2=84=E8=A7=88=E3=80=91?= =?UTF-8?q?=E4=BC=98=E5=8C=96office=E6=96=87=E4=BB=B6=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E5=8F=97=E5=AF=86=E7=A0=81=E4=BF=9D=E6=8A=A4=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E9=81=BF=E5=85=8D=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E6=80=A7=E8=AF=AF=E5=88=A4=20*=201.=20=E3=80=90=E5=8A=A0?= =?UTF-8?q?=E5=AF=86office=E9=A2=84=E8=A7=88=E3=80=91=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=87=8D=E6=96=B0=E8=BE=93=E5=85=A5=E5=AF=86=E7=A0=81=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E3=80=82=20*=201.=20=E3=80=90=E5=8A=A0=E5=AF=86office?= =?UTF-8?q?=E9=A2=84=E8=A7=88=E3=80=91=E4=BC=98=E5=8C=96=E5=BD=93=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E8=BE=93=E5=85=A5=E9=94=99=E8=AF=AF=E5=90=8E=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E6=98=AF=E6=8A=9B=E5=87=BA=E5=BC=82=E5=B8=B8=EF=BC=8C?= =?UTF-8?q?=E8=80=8C=E6=98=AF=E6=8F=90=E7=A4=BA=E7=94=A8=E6=88=B7=E9=87=8D?= =?UTF-8?q?=E6=96=B0=E8=BE=93=E5=85=A5=20*=201.=20=E4=BC=98=E5=8C=96prompt?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E6=A1=86=E7=9A=84=E8=BE=93=E5=85=A5=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E6=8F=90=E7=A4=BA=E6=A0=B7=E5=BC=8F=20*=201.=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=9F=BA=E4=BA=8EuserToken=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E5=8A=A0=E5=AF=86=E6=96=87=E4=BB=B6=EF=BC=8C=E6=B2=A1=E6=9C=89?= =?UTF-8?q?userToken=E7=9A=84=E5=8A=A0=E5=AF=86=E6=96=87=E4=BB=B6=E4=B8=8D?= =?UTF-8?q?=E7=BC=93=E5=AD=98=20*=201.=20=E4=BC=98=E5=8C=96docker=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E6=96=B9=E6=A1=88=EF=BC=8C=E4=BD=BF=E7=94=A8=E5=88=86?= =?UTF-8?q?=E5=B1=82=E6=9E=84=E5=BB=BA=E6=96=B9=E5=BC=8F=EF=BC=8C=E9=87=87?= =?UTF-8?q?=E7=94=A8=E5=B1=82=E7=BA=A7=E7=BC=93=E5=AD=98=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E6=85=A2=E5=8F=91=E5=B8=83=E6=85=A2=E7=AD=89?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E3=80=82=E4=BB=8E=E5=8E=9F=E6=9C=AC5?= =?UTF-8?q?=E5=88=86=E9=92=9F=E5=B7=A6=E5=8F=B3=E7=BC=A9=E7=9F=AD=E8=87=B3?= =?UTF-8?q?=E5=87=A0=E7=A7=92=20*=201.=20=E5=8A=A0=E5=AF=86=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=9A=82=E6=97=B6=E4=B8=8D=E7=BC=93=E5=AD=98=EF=BC=88?= =?UTF-8?q?=E5=90=8E=E7=BB=AD=E5=9F=BA=E4=BA=8E=E7=94=A8=E6=88=B7token?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=EF=BC=8C=E5=9F=BA=E4=BA=8E=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=BC=93=E5=AD=98=EF=BC=89=20*=201.=20=E4=BC=98=E5=8C=96office?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=8B=E8=BD=BD=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E8=B7=B3=E8=BF=87=E9=87=8D=E5=A4=8D=E4=B8=8B=E8=BD=BD=EF=BC=88?= =?UTF-8?q?=E5=A4=A7=E9=87=8F=E8=8A=82=E7=BA=A6=E5=B8=A6=E5=AE=BD=E4=B8=8E?= =?UTF-8?q?=E7=A3=81=E7=9B=98=E7=A9=BA=E9=97=B4=EF=BC=89=E3=80=82=20*=201.?= =?UTF-8?q?=20=E4=BF=AE=E5=A4=8D=E9=A2=84=E8=A7=88=E4=B8=8D=E5=90=8C?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E7=9A=84=E5=8A=A0=E5=AF=86office=E6=96=87?= =?UTF-8?q?=E4=BB=B6bug=20*=20=E5=AE=9E=E7=8E=B0=E9=A2=84=E8=A7=88?= =?UTF-8?q?=E5=8A=A0=E5=AF=86=E7=9A=84=EF=BC=88=E5=8F=97=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E4=BF=9D=E6=8A=A4=EF=BC=89office=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 39 +- docker/kkfileview-jdk/Dockerfile | 38 ++ docker/kkfileview-jdk/docker build.txt | 2 + .../kkfileview-jdk/fonts}/.gitkeep | 0 office-plugin/pom.xml | 17 +- .../jodconverter/AbstractConversionTask.java | 22 +- .../jodconverter/OfficeDocumentConverter.java | 24 +- .../jodconverter/model/FileProperties.java | 34 ++ server/pom.xml | 115 +++--- .../src/main/config/freemarker_implicit.ftl | 2 + .../cn/keking/config/ConfigConstants.java | 2 +- .../java/cn/keking/model/FileAttribute.java | 28 ++ .../cn/keking/service/FileHandlerService.java | 11 + .../cn/keking/service/OfficeToPdfService.java | 16 +- .../service/impl/OfficeFilePreviewImpl.java | 93 ++++- .../java/cn/keking/utils/DownloadUtils.java | 7 + .../java/cn/keking/utils/OfficeUtils.java | 62 +++ .../controller/OnlinePreviewController.java | 11 +- .../main/resources/static/js/bootbox.min.js | 6 + server/src/main/resources/static/pptx/ppt.js | 362 +++++++++--------- .../src/main/resources/web/commonHeader.ftl | 51 ++- .../main/resources/web/fileNotSupported.ftl | 38 +- server/src/main/resources/web/html.ftl | 22 +- server/src/main/resources/web/index.ftl | 5 +- server/src/main/resources/web/pdf.ftl | 3 +- server/src/main/resources/web/ppt.ftl | 228 +++++------ 26 files changed, 746 insertions(+), 492 deletions(-) create mode 100644 docker/kkfileview-jdk/Dockerfile create mode 100644 docker/kkfileview-jdk/docker build.txt rename {fonts => docker/kkfileview-jdk/fonts}/.gitkeep (100%) create mode 100644 office-plugin/src/main/java/org/artofsolving/jodconverter/model/FileProperties.java create mode 100644 server/src/main/java/cn/keking/utils/OfficeUtils.java create mode 100644 server/src/main/resources/static/js/bootbox.min.js diff --git a/Dockerfile b/Dockerfile index bcdd44ba..4fc88f03 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,42 +1,5 @@ -FROM ubuntu:20.04 +FROM keking/kkfileview-jdk:4.1.1 MAINTAINER chenjh "842761733@qq.com" ADD server/target/kkFileView-*.tar.gz /opt/ -COPY fonts/* /usr/share/fonts/chinese/ -RUN echo "deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse" > /etc/apt/sources.list &&\ - apt-get clean && apt-get update &&\ - apt-get install -y locales && apt-get install -y language-pack-zh-hans &&\ - localedef -i zh_CN -c -f UTF-8 -A /usr/share/locale/locale.alias zh_CN.UTF-8 && locale-gen zh_CN.UTF-8 &&\ - export DEBIAN_FRONTEND=noninteractive &&\ - apt-get install -y tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\ - apt-get install -y libxrender1 && apt-get install -y libxt6 && apt-get install -y libxext-dev && apt-get install -y libfreetype6-dev &&\ - apt-get install -y wget && apt-get install -y ttf-mscorefonts-installer && apt-get install -y fontconfig &&\ - apt-get install ttf-wqy-microhei &&\ - apt-get install ttf-wqy-zenhei &&\ - apt-get install xfonts-wqy &&\ - cd /tmp &&\ - wget https://kkfileview.keking.cn/server-jre-8u251-linux-x64.tar.gz &&\ - tar -zxf /tmp/server-jre-8u251-linux-x64.tar.gz && mv /tmp/jdk1.8.0_251 /usr/local/ &&\ - -# 安装 OpenOffice -# wget https://kkfileview.keking.cn/Apache_OpenOffice_4.1.6_Linux_x86-64_install-deb_zh-CN.tar.gz -cO openoffice_deb.tar.gz &&\ -# tar -zxf /tmp/openoffice_deb.tar.gz && cd /tmp/zh-CN/DEBS &&\ -# dpkg -i *.deb && dpkg -i desktop-integration/openoffice4.1-debian-menus_4.1.6-9790_all.deb &&\ - -# 安装 libreoffice - apt-get install -y libxinerama1 libcairo2 libcups2 libx11-xcb1 &&\ - wget https://kkfileview.keking.cn/LibreOffice_7.1.4_Linux_x86-64_deb.tar.gz -cO libreoffice_deb.tar.gz &&\ - tar -zxf /tmp/libreoffice_deb.tar.gz && cd /tmp/LibreOffice_7.1.4.2_Linux_x86-64_deb/DEBS &&\ - dpkg -i *.deb &&\ - - rm -rf /tmp/* && rm -rf /var/lib/apt/lists/* &&\ - cd /usr/share/fonts/chinese &&\ - mkfontscale &&\ - mkfontdir &&\ - fc-cache -fv -ENV JAVA_HOME /usr/local/jdk1.8.0_251 -ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar -ENV PATH $PATH:$JAVA_HOME/bin -ENV LANG zh_CN.UTF-8 -ENV LC_ALL zh_CN.UTF-8 ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-4.1.0-SNAPSHOT/bin ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dspring.config.location=/opt/kkFileView-4.1.0-SNAPSHOT/config/application.properties","-jar","/opt/kkFileView-4.1.0-SNAPSHOT/bin/kkFileView-4.1.0-SNAPSHOT.jar"] \ No newline at end of file diff --git a/docker/kkfileview-jdk/Dockerfile b/docker/kkfileview-jdk/Dockerfile new file mode 100644 index 00000000..05ea6b57 --- /dev/null +++ b/docker/kkfileview-jdk/Dockerfile @@ -0,0 +1,38 @@ +FROM ubuntu:20.04 +MAINTAINER chenjh "842761733@qq.com" +# 内置一些常用的中文字体,避免普遍性乱码 +COPY fonts/* /usr/share/fonts/chinese/ +RUN echo "deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse" > /etc/apt/sources.list &&\ + apt-get clean && apt-get update &&\ + apt-get install -y locales && apt-get install -y language-pack-zh-hans &&\ + localedef -i zh_CN -c -f UTF-8 -A /usr/share/locale/locale.alias zh_CN.UTF-8 && locale-gen zh_CN.UTF-8 &&\ + export DEBIAN_FRONTEND=noninteractive &&\ + apt-get install -y tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\ + apt-get install -y libxrender1 && apt-get install -y libxt6 && apt-get install -y libxext-dev && apt-get install -y libfreetype6-dev &&\ + apt-get install -y wget && apt-get install -y ttf-mscorefonts-installer && apt-get install -y fontconfig &&\ + apt-get install ttf-wqy-microhei &&\ + apt-get install ttf-wqy-zenhei &&\ + apt-get install xfonts-wqy &&\ + cd /tmp &&\ + wget https://kkfileview.keking.cn/server-jre-8u251-linux-x64.tar.gz &&\ + tar -zxf /tmp/server-jre-8u251-linux-x64.tar.gz && mv /tmp/jdk1.8.0_251 /usr/local/ &&\ + +# 安装 libreoffice + apt-get install -y libxinerama1 libcairo2 libcups2 libx11-xcb1 &&\ + wget https://kkfileview.keking.cn/LibreOffice_7.1.4_Linux_x86-64_deb.tar.gz -cO libreoffice_deb.tar.gz &&\ + tar -zxf /tmp/libreoffice_deb.tar.gz && cd /tmp/LibreOffice_7.1.4.2_Linux_x86-64_deb/DEBS &&\ + dpkg -i *.deb &&\ + +# 清理临时文件 + rm -rf /tmp/* && rm -rf /var/lib/apt/lists/* &&\ + cd /usr/share/fonts/chinese &&\ + mkfontscale &&\ + mkfontdir &&\ + fc-cache -fv + +ENV JAVA_HOME /usr/local/jdk1.8.0_251 +ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar +ENV PATH $PATH:$JAVA_HOME/bin +ENV LANG zh_CN.UTF-8 +ENV LC_ALL zh_CN.UTF-8 +ENTRYPOINT ["java","-version"] \ No newline at end of file diff --git a/docker/kkfileview-jdk/docker build.txt b/docker/kkfileview-jdk/docker build.txt new file mode 100644 index 00000000..2c865dc4 --- /dev/null +++ b/docker/kkfileview-jdk/docker build.txt @@ -0,0 +1,2 @@ +# 执行如下命令构建基础镜像,加快kkfileview docker镜像构建与发布 +docker build --tag keking/kkfileview-jdk:4.1.1 . \ No newline at end of file diff --git a/fonts/.gitkeep b/docker/kkfileview-jdk/fonts/.gitkeep similarity index 100% rename from fonts/.gitkeep rename to docker/kkfileview-jdk/fonts/.gitkeep diff --git a/office-plugin/pom.xml b/office-plugin/pom.xml index 91a9e43d..a8db4b03 100644 --- a/office-plugin/pom.xml +++ b/office-plugin/pom.xml @@ -3,7 +3,6 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 - kkFileView-parent cn.keking @@ -27,19 +26,9 @@ 2.7 - org.openoffice - juh - 3.2.1 - - - org.openoffice - ridl - 3.2.1 - - - org.openoffice - unoil - 3.2.1 + org.libreoffice + libreoffice + 7.1.4 diff --git a/office-plugin/src/main/java/org/artofsolving/jodconverter/AbstractConversionTask.java b/office-plugin/src/main/java/org/artofsolving/jodconverter/AbstractConversionTask.java index f7242612..21040cf9 100644 --- a/office-plugin/src/main/java/org/artofsolving/jodconverter/AbstractConversionTask.java +++ b/office-plugin/src/main/java/org/artofsolving/jodconverter/AbstractConversionTask.java @@ -12,18 +12,6 @@ // package org.artofsolving.jodconverter; -import static org.artofsolving.jodconverter.office.OfficeUtils.SERVICE_DESKTOP; -import static org.artofsolving.jodconverter.office.OfficeUtils.cast; -import static org.artofsolving.jodconverter.office.OfficeUtils.toUnoProperties; -import static org.artofsolving.jodconverter.office.OfficeUtils.toUrl; - -import java.io.File; -import java.util.Map; - -import org.artofsolving.jodconverter.office.OfficeContext; -import org.artofsolving.jodconverter.office.OfficeException; -import org.artofsolving.jodconverter.office.OfficeTask; - import com.sun.star.frame.XComponentLoader; import com.sun.star.frame.XStorable; import com.sun.star.io.IOException; @@ -32,6 +20,14 @@ import com.sun.star.lang.XComponent; import com.sun.star.task.ErrorCodeIOException; import com.sun.star.util.CloseVetoException; import com.sun.star.util.XCloseable; +import org.artofsolving.jodconverter.office.OfficeContext; +import org.artofsolving.jodconverter.office.OfficeException; +import org.artofsolving.jodconverter.office.OfficeTask; + +import java.io.File; +import java.util.Map; + +import static org.artofsolving.jodconverter.office.OfficeUtils.*; public abstract class AbstractConversionTask implements OfficeTask { @@ -47,6 +43,7 @@ public abstract class AbstractConversionTask implements OfficeTask { protected abstract Map getStoreProperties(File outputFile, XComponent document); + @Override public void execute(OfficeContext context) throws OfficeException { XComponent document = null; try { @@ -79,6 +76,7 @@ public abstract class AbstractConversionTask implements OfficeTask { } XComponentLoader loader = cast(XComponentLoader.class, context.getService(SERVICE_DESKTOP)); Map loadProperties = getLoadProperties(inputFile); + XComponent document = null; try { document = loader.loadComponentFromURL(toUrl(inputFile), "_blank", 0, toUnoProperties(loadProperties)); diff --git a/office-plugin/src/main/java/org/artofsolving/jodconverter/OfficeDocumentConverter.java b/office-plugin/src/main/java/org/artofsolving/jodconverter/OfficeDocumentConverter.java index dff7ff6f..b1cb702e 100644 --- a/office-plugin/src/main/java/org/artofsolving/jodconverter/OfficeDocumentConverter.java +++ b/office-plugin/src/main/java/org/artofsolving/jodconverter/OfficeDocumentConverter.java @@ -12,18 +12,18 @@ // package org.artofsolving.jodconverter; -import java.io.File; -import java.util.HashMap; -import java.util.Map; - +import com.sun.star.document.UpdateDocMode; import org.apache.commons.io.FilenameUtils; import org.artofsolving.jodconverter.document.DefaultDocumentFormatRegistry; import org.artofsolving.jodconverter.document.DocumentFormat; import org.artofsolving.jodconverter.document.DocumentFormatRegistry; +import org.artofsolving.jodconverter.model.FileProperties; import org.artofsolving.jodconverter.office.OfficeException; import org.artofsolving.jodconverter.office.OfficeManager; -import com.sun.star.document.UpdateDocMode; +import java.io.File; +import java.util.HashMap; +import java.util.Map; public class OfficeDocumentConverter { @@ -60,14 +60,22 @@ public class OfficeDocumentConverter { public void convert(File inputFile, File outputFile) throws OfficeException { String outputExtension = FilenameUtils.getExtension(outputFile.getName()); DocumentFormat outputFormat = formatRegistry.getFormatByExtension(outputExtension); - convert(inputFile, outputFile, outputFormat); + convert(inputFile, outputFile, outputFormat, null); } - public void convert(File inputFile, File outputFile, DocumentFormat outputFormat) throws OfficeException { + public void convert(File inputFile, File outputFile, FileProperties fileProperties) throws OfficeException { + String outputExtension = FilenameUtils.getExtension(outputFile.getName()); + DocumentFormat outputFormat = formatRegistry.getFormatByExtension(outputExtension); + convert(inputFile, outputFile, outputFormat, fileProperties); + } + + public void convert(File inputFile, File outputFile, DocumentFormat outputFormat, FileProperties fileProperties) throws OfficeException { String inputExtension = FilenameUtils.getExtension(inputFile.getName()); DocumentFormat inputFormat = formatRegistry.getFormatByExtension(inputExtension); + Map properties = fileProperties.toMap(); + properties.putAll(defaultLoadProperties); StandardConversionTask conversionTask = new StandardConversionTask(inputFile, outputFile, outputFormat); - conversionTask.setDefaultLoadProperties(defaultLoadProperties); + conversionTask.setDefaultLoadProperties(properties); conversionTask.setInputFormat(inputFormat); officeManager.execute(conversionTask); } diff --git a/office-plugin/src/main/java/org/artofsolving/jodconverter/model/FileProperties.java b/office-plugin/src/main/java/org/artofsolving/jodconverter/model/FileProperties.java new file mode 100644 index 00000000..7722dc0b --- /dev/null +++ b/office-plugin/src/main/java/org/artofsolving/jodconverter/model/FileProperties.java @@ -0,0 +1,34 @@ +package org.artofsolving.jodconverter.model; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by kl on 2018/1/17. + * Content : + */ +public class FileProperties { + + private String filePassword; + + public FileProperties() { + } + + public Map toMap() { + Map map = new HashMap(); + if (filePassword != null) { + map.put("Password", filePassword); + } + + return map; + } + + public String getFilePassword() { + return filePassword; + } + + public void setFilePassword(String filePassword) { + this.filePassword = filePassword; + } + +} diff --git a/server/pom.xml b/server/pom.xml index c5b123aa..12661b3e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -25,20 +25,12 @@ - org.springframework.boot - spring-boot-starter-freemarker - - - - net.sf.sevenzipjbinding - sevenzipjbinding - 16.02-2.01 - - - net.sf.sevenzipjbinding - sevenzipjbinding-all-platforms - 16.02-2.01 + cn.keking + office-plugin + ${project.version} + + org.springframework.boot spring-boot-starter-web @@ -53,28 +45,13 @@ org.springframework.boot spring-boot-starter-jetty - org.springframework.boot - spring-boot-starter-test - test - - - cn.keking - office-plugin - ${project.version} - - - org.apache.commons - commons-lang3 - 3.7 - - - - org.redisson - redisson - 3.2.0 + spring-boot-starter-freemarker + + + org.apache.poi poi @@ -83,7 +60,12 @@ org.apache.poi poi-scratchpad - 3.12 + 3.17 + + + org.apache.poi + poi-ooxml + 3.17 fr.opensagres.xdocreport @@ -106,6 +88,31 @@ fr.opensagres.xdocreport.document 1.0.5 + + + + + net.sf.sevenzipjbinding + sevenzipjbinding + 16.02-2.01 + + + net.sf.sevenzipjbinding + sevenzipjbinding-all-platforms + 16.02-2.01 + + + + org.apache.commons + commons-lang3 + 3.7 + + + org.redisson + redisson + 3.2.0 + + org.apache.commons @@ -134,23 +141,12 @@ antlr 2.7.7 - - commons-httpclient - commons-httpclient - 3.1 - test - - - commons-logging - commons-logging - - - commons-cli commons-cli 1.2 + commons-net @@ -210,7 +206,6 @@ javacv 1.5.2 - org.bytedeco javacpp @@ -224,43 +219,36 @@ 4.1.2-1.5.2 linux-x86_64 - org.bytedeco opencv 4.1.2-1.5.2 windows-x86_64 - org.bytedeco openblas 0.3.6-1.5.1 linux-x86_64 - org.bytedeco openblas 0.3.6-1.5.1 windows-x86_64 - org.bytedeco ffmpeg 4.2.1-1.5.2 linux-x86_64 - org.bytedeco ffmpeg 4.2.1-1.5.2 windows-x86_64 - - com.lowagie itext @@ -274,7 +262,6 @@ system ${basedir}/lib/jai_core-1.1.3.jar - javax.media jai_codec @@ -283,6 +270,25 @@ ${basedir}/lib/jai_codec-1.1.3.jar + + + org.springframework.boot + spring-boot-starter-test + test + + + commons-httpclient + commons-httpclient + 3.1 + test + + + commons-logging + commons-logging + + + + @@ -336,5 +342,4 @@ - diff --git a/server/src/main/config/freemarker_implicit.ftl b/server/src/main/config/freemarker_implicit.ftl index bc6a685c..0481d776 100644 --- a/server/src/main/config/freemarker_implicit.ftl +++ b/server/src/main/config/freemarker_implicit.ftl @@ -5,6 +5,8 @@ [#-- @ftlvariable name="file" type="cn.keking.model.FileAttribute" --] [#-- @ftlvariable name="fileName" type="java.lang.String" --] [#-- @ftlvariable name="fileTree" type="java.lang.String" --] +[#-- @ftlvariable name="needFilePassword" type="java.lang.Boolean" --] +[#-- @ftlvariable name="filePasswordError" type="java.lang.Boolean" --] [#-- @ftlvariable name="baseUrl" type="java.lang.String" --] [#-- @ftlvariable name="imgUrls" type="String" --] [#-- @ftlvariable name="textData" type="java.lang.String" --] diff --git a/server/src/main/java/cn/keking/config/ConfigConstants.java b/server/src/main/java/cn/keking/config/ConfigConstants.java index 6e23377c..beea9426 100644 --- a/server/src/main/java/cn/keking/config/ConfigConstants.java +++ b/server/src/main/java/cn/keking/config/ConfigConstants.java @@ -6,8 +6,8 @@ import org.springframework.stereotype.Component; import java.io.File; import java.util.Arrays; -import java.util.concurrent.CopyOnWriteArraySet; import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; /** * @author: chenjh diff --git a/server/src/main/java/cn/keking/model/FileAttribute.java b/server/src/main/java/cn/keking/model/FileAttribute.java index 6ba903b7..028bb602 100644 --- a/server/src/main/java/cn/keking/model/FileAttribute.java +++ b/server/src/main/java/cn/keking/model/FileAttribute.java @@ -1,6 +1,7 @@ package cn.keking.model; import cn.keking.config.ConfigConstants; +import org.artofsolving.jodconverter.model.FileProperties; /** * Created by kl on 2018/1/17. @@ -13,6 +14,8 @@ public class FileAttribute { private String name; private String url; private String fileKey; + private String filePassword; + private String userToken; private String officePreviewType = ConfigConstants.getOfficePreviewType(); private String tifPreviewType; private Boolean skipDownLoad = false; @@ -35,6 +38,12 @@ public class FileAttribute { this.officePreviewType = officePreviewType; } + public FileProperties toFileProperties() { + FileProperties fileProperties = new FileProperties(); + fileProperties.setFilePassword(filePassword); + return fileProperties; + } + public String getFileKey() { return fileKey; } @@ -43,6 +52,22 @@ public class FileAttribute { this.fileKey = fileKey; } + public String getFilePassword() { + return filePassword; + } + + public void setFilePassword(String filePassword) { + this.filePassword = filePassword; + } + + public String getUserToken() { + return userToken; + } + + public void setUserToken(String userToken) { + this.userToken = userToken; + } + public String getOfficePreviewType() { return officePreviewType; } @@ -82,6 +107,7 @@ public class FileAttribute { public void setUrl(String url) { this.url = url; } + public Boolean getSkipDownLoad() { return skipDownLoad; } @@ -89,6 +115,7 @@ public class FileAttribute { public void setSkipDownLoad(Boolean skipDownLoad) { this.skipDownLoad = skipDownLoad; } + public String getTifPreviewType() { return tifPreviewType; } @@ -96,4 +123,5 @@ public class FileAttribute { public void setTifPreviewType(String previewType) { this.tifPreviewType = previewType; } + } diff --git a/server/src/main/java/cn/keking/service/FileHandlerService.java b/server/src/main/java/cn/keking/service/FileHandlerService.java index 0a7a6c0a..272a7a3c 100644 --- a/server/src/main/java/cn/keking/service/FileHandlerService.java +++ b/server/src/main/java/cn/keking/service/FileHandlerService.java @@ -292,7 +292,18 @@ public class FileHandlerService { if (StringUtils.hasText(tifPreviewType)) { attribute.setTifPreviewType(tifPreviewType); } + + String filePassword = req.getParameter("filePassword"); + if (StringUtils.hasText(filePassword)) { + attribute.setFilePassword(filePassword); + } + + String userToken = req.getParameter("userToken"); + if (StringUtils.hasText(userToken)) { + attribute.setUserToken(userToken); + } } + return attribute; } diff --git a/server/src/main/java/cn/keking/service/OfficeToPdfService.java b/server/src/main/java/cn/keking/service/OfficeToPdfService.java index 98cfaf4e..37d63ac1 100644 --- a/server/src/main/java/cn/keking/service/OfficeToPdfService.java +++ b/server/src/main/java/cn/keking/service/OfficeToPdfService.java @@ -1,5 +1,6 @@ package cn.keking.service; +import cn.keking.model.FileAttribute; import org.artofsolving.jodconverter.OfficeDocumentConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,22 +21,23 @@ public class OfficeToPdfService { this.officePluginManager = officePluginManager; } - public void openOfficeToPDF(String inputFilePath, String outputFilePath) { - office2pdf(inputFilePath, outputFilePath); + public void openOfficeToPDF(String inputFilePath, String outputFilePath, FileAttribute fileAttribute) { + office2pdf(inputFilePath, outputFilePath, fileAttribute); } - public static void converterFile(File inputFile, String outputFilePath_end, OfficeDocumentConverter converter) { + public static void converterFile(File inputFile, String outputFilePath_end, OfficeDocumentConverter converter, FileAttribute fileAttribute) { File outputFile = new File(outputFilePath_end); // 假如目标路径不存在,则新建该路径 if (!outputFile.getParentFile().exists() && !outputFile.getParentFile().mkdirs()) { logger.error("创建目录【{}】失败,请检查目录权限!",outputFilePath_end); } - converter.convert(inputFile, outputFile); + + converter.convert(inputFile, outputFile, fileAttribute.toFileProperties()); } - public void office2pdf(String inputFilePath, String outputFilePath) { + public void office2pdf(String inputFilePath, String outputFilePath, FileAttribute fileAttribute) { OfficeDocumentConverter converter = officePluginManager.getDocumentConverter(); if (null != inputFilePath) { File inputFile = new File(inputFilePath); @@ -45,12 +47,12 @@ public class OfficeToPdfService { String outputFilePath_end = getOutputFilePath(inputFilePath); if (inputFile.exists()) { // 找不到源文件, 则返回 - converterFile(inputFile, outputFilePath_end,converter); + converterFile(inputFile, outputFilePath_end, converter, fileAttribute); } } else { if (inputFile.exists()) { // 找不到源文件, 则返回 - converterFile(inputFile, outputFilePath, converter); + converterFile(inputFile, outputFilePath, converter, fileAttribute); } } } diff --git a/server/src/main/java/cn/keking/service/impl/OfficeFilePreviewImpl.java b/server/src/main/java/cn/keking/service/impl/OfficeFilePreviewImpl.java index 1853a322..b7b1cfb3 100644 --- a/server/src/main/java/cn/keking/service/impl/OfficeFilePreviewImpl.java +++ b/server/src/main/java/cn/keking/service/impl/OfficeFilePreviewImpl.java @@ -3,11 +3,13 @@ package cn.keking.service.impl; import cn.keking.config.ConfigConstants; import cn.keking.model.FileAttribute; import cn.keking.model.ReturnResponse; -import cn.keking.service.FilePreview; -import cn.keking.utils.DownloadUtils; import cn.keking.service.FileHandlerService; +import cn.keking.service.FilePreview; import cn.keking.service.OfficeToPdfService; +import cn.keking.utils.DownloadUtils; +import cn.keking.utils.OfficeUtils; import cn.keking.web.filter.BaseUrlFilter; +import org.artofsolving.jodconverter.office.OfficeException; import org.springframework.stereotype.Service; import org.springframework.ui.Model; import org.springframework.util.StringUtils; @@ -42,33 +44,83 @@ public class OfficeFilePreviewImpl implements FilePreview { String baseUrl = BaseUrlFilter.getBaseUrl(); String suffix = fileAttribute.getSuffix(); String fileName = fileAttribute.getName(); + String filePassword = fileAttribute.getFilePassword(); + String userToken = fileAttribute.getUserToken(); boolean isHtml = suffix.equalsIgnoreCase("xls") || suffix.equalsIgnoreCase("xlsx"); String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + (isHtml ? "html" : "pdf"); - String outFilePath = FILE_DIR + pdfName; - // 判断之前是否已转换过,如果转换过,直接返回,否则执行转换 - if (!fileHandlerService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) { - String filePath; - ReturnResponse response = DownloadUtils.downLoad(fileAttribute, null); - if (response.isFailure()) { - return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg()); + String cacheFileName = userToken == null ? pdfName : userToken + "_" + pdfName; + String outFilePath = FILE_DIR + cacheFileName; + + // 下载远程文件到本地,如果文件在本地已存在不会重复下载 + ReturnResponse response = DownloadUtils.downLoad(fileAttribute, fileName); + if (response.isFailure()) { + return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg()); + } + String filePath = response.getContent(); + + /* + * 1. 缓存判断-如果文件已经进行转换过,就直接返回,否则执行转换 + * 2. 缓存判断-加密文件基于userToken进行缓存,如果没有就不缓存 + */ + boolean isCached = false; + boolean isUseCached = false; + boolean isPwdProtectedOffice = false; + if (ConfigConstants.isCacheEnabled()) { + // 全局开启缓存 + isUseCached = true; + if (fileHandlerService.listConvertedFiles().containsKey(cacheFileName)) { + // 存在缓存 + isCached = true; } - filePath = response.getContent(); - if (StringUtils.hasText(outFilePath)) { - officeToPdfService.openOfficeToPDF(filePath, outFilePath); - if (isHtml) { - // 对转换后的文件进行操作(改变编码方式) - fileHandlerService.doActionConvertedFile(outFilePath); + if (OfficeUtils.isPwdProtected(filePath)) { + isPwdProtectedOffice = true; + if (!StringUtils.hasLength(userToken)) { + // 不缓存没有userToken的加密文件 + isUseCached = false; } - if (ConfigConstants.isCacheEnabled()) { - // 加入缓存 - fileHandlerService.addConvertedFile(pdfName, fileHandlerService.getRelativePath(outFilePath)); + } + } else { + isPwdProtectedOffice = OfficeUtils.isPwdProtected(filePath); + } + + if (isCached == false) { + // 没有缓存执行转换逻辑 + if (isPwdProtectedOffice && !StringUtils.hasLength(filePassword)) { + // 加密文件需要密码 + model.addAttribute("needFilePassword", true); + return EXEL_FILE_PREVIEW_PAGE; + } else { + if (StringUtils.hasText(outFilePath)) { + try { + officeToPdfService.openOfficeToPDF(filePath, outFilePath, fileAttribute); + } catch (OfficeException e) { + if (isPwdProtectedOffice && OfficeUtils.isCompatible(filePath, filePassword) == false) { + // 加密文件密码错误,提示重新输入 + model.addAttribute("needFilePassword", true); + model.addAttribute("filePasswordError", true); + return EXEL_FILE_PREVIEW_PAGE; + } + + return otherFilePreview.notSupportedFile(model, fileAttribute, "抱歉,该文件版本不兼容,文件版本错误。"); + } + + if (isHtml) { + // 对转换后的文件进行操作(改变编码方式) + fileHandlerService.doActionConvertedFile(outFilePath); + } + if (isUseCached) { + // 加入缓存 + fileHandlerService.addConvertedFile(cacheFileName, fileHandlerService.getRelativePath(outFilePath)); + } } } } + if (!isHtml && baseUrl != null && (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType))) { - return getPreviewType(model, fileAttribute, officePreviewType, baseUrl, pdfName, outFilePath, fileHandlerService, OFFICE_PREVIEW_TYPE_IMAGE, otherFilePreview); + return getPreviewType(model, fileAttribute, officePreviewType, baseUrl, cacheFileName, outFilePath, fileHandlerService, OFFICE_PREVIEW_TYPE_IMAGE, otherFilePreview); } - model.addAttribute("pdfUrl", pdfName); + + model.addAttribute("pdfUrl", cacheFileName); return isHtml ? EXEL_FILE_PREVIEW_PAGE : PDF_FILE_PREVIEW_PAGE; } @@ -88,4 +140,5 @@ public class OfficeFilePreviewImpl implements FilePreview { return PICTURE_FILE_PREVIEW_PAGE; } } + } diff --git a/server/src/main/java/cn/keking/utils/DownloadUtils.java b/server/src/main/java/cn/keking/utils/DownloadUtils.java index 5966eaa7..d1d1a3c6 100644 --- a/server/src/main/java/cn/keking/utils/DownloadUtils.java +++ b/server/src/main/java/cn/keking/utils/DownloadUtils.java @@ -87,6 +87,13 @@ public class DownloadUtils { if (!dirFile.exists() && !dirFile.mkdirs()) { logger.error("创建目录【{}】失败,可能是权限不够,请检查", fileDir); } + + // 文件已在本地存在,跳过文件下载 + File realFile = new File(realPath); + if (realFile.exists()) { + fileAttribute.setSkipDownLoad(true); + } + return realPath; } diff --git a/server/src/main/java/cn/keking/utils/OfficeUtils.java b/server/src/main/java/cn/keking/utils/OfficeUtils.java new file mode 100644 index 00000000..8e9aa3d8 --- /dev/null +++ b/server/src/main/java/cn/keking/utils/OfficeUtils.java @@ -0,0 +1,62 @@ +package cn.keking.utils; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.poi.EncryptedDocumentException; +import org.apache.poi.extractor.ExtractorFactory; +import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; +import org.springframework.lang.Nullable; + +import java.io.FileInputStream; + +/** + * Office工具类 + * + * @author ylyue + * @since 2022/7/5 + */ +public class OfficeUtils { + + /** + * 判断office(word,excel,ppt)文件是否受密码保护 + * + * @param path office文件路径 + * @return 是否受密码保护 + */ + public static boolean isPwdProtected(String path) { + try { + ExtractorFactory.createExtractor(new FileInputStream(path)); + } catch (EncryptedDocumentException e) { + return true; + } catch (Exception e) { + Throwable[] throwables = ExceptionUtils.getThrowables(e); + for (Throwable throwable : throwables) { + if (throwable instanceof EncryptedDocumentException) { + return true; + } + } + } + + return false; + } + + /** + * 判断office文件是否可打开(兼容) + * + * @param path office文件路径 + * @param password 文件密码 + * @return 是否可打开(兼容) + */ + public static synchronized boolean isCompatible(String path, @Nullable String password) { + try { + Biff8EncryptionKey.setCurrentUserPassword(password); + ExtractorFactory.createExtractor(new FileInputStream(path)); + } catch (Exception e) { + return false; + } finally { + Biff8EncryptionKey.setCurrentUserPassword(null); + } + + return true; + } + +} diff --git a/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java b/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java index b6e88360..3b919a97 100644 --- a/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java +++ b/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java @@ -19,6 +19,7 @@ import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.util.HtmlUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -73,10 +74,13 @@ public class OnlinePreviewController { String fileUrls; try { fileUrls = new String(Base64.decodeBase64(urls)); + // 防止XSS攻击 + fileUrls = HtmlUtils.htmlEscape(fileUrls); } catch (Exception ex) { String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "urls"); return otherFilePreview.notSupportedFile(model, errorMsg); } + logger.info("预览文件url:{},urls:{}", fileUrls, urls); // 抽取文件并返回文件列表 String[] images = fileUrls.split("\\|"); @@ -102,6 +106,11 @@ public class OnlinePreviewController { */ @RequestMapping(value = "/getCorsFile", method = RequestMethod.GET) public void getCorsFile(String urlPath, HttpServletResponse response) { + if (urlPath == null || urlPath.toLowerCase().startsWith("file:") || urlPath.toLowerCase().startsWith("file%3") || !urlPath.toLowerCase().startsWith("http")) { + logger.info("读取跨域文件异常,可能存在非法访问,urlPath:{}", urlPath); + return; + } + logger.info("下载跨域pdf文件url:{}", urlPath); try { URL url = WebUtils.normalizedURL(urlPath); @@ -125,6 +134,4 @@ public class OnlinePreviewController { return "success"; } - - } diff --git a/server/src/main/resources/static/js/bootbox.min.js b/server/src/main/resources/static/js/bootbox.min.js new file mode 100644 index 00000000..d140a4da --- /dev/null +++ b/server/src/main/resources/static/js/bootbox.min.js @@ -0,0 +1,6 @@ +/** + * bootbox.js 5.3.2 + * + * http://bootboxjs.com/license.txt + */ +!function(e,t){'use strict';'function'==typeof define&&define.amd?define(['jquery'],t):'object'==typeof exports?module.exports=t(require('jquery')):e.bootbox=t(e.jQuery)}(this,function t(p,u){'use strict';var r,n,i,l;Object.keys||(Object.keys=(r=Object.prototype.hasOwnProperty,n=!{toString:null}.propertyIsEnumerable('toString'),l=(i=['toString','toLocaleString','valueOf','hasOwnProperty','isPrototypeOf','propertyIsEnumerable','constructor']).length,function(e){if('function'!=typeof e&&('object'!=typeof e||null===e))throw new TypeError('Object.keys called on non-object');var t,o,a=[];for(t in e)r.call(e,t)&&a.push(t);if(n)for(o=0;o
",header:"
",footer:'',closeButton:'',form:'
',button:'',option:'',promptMessage:'
',inputs:{text:'',textarea:'',email:'',select:'',checkbox:'
',radio:'
',date:'',time:'',number:'',password:'',range:''}},m={locale:'en',backdrop:'static',animate:!0,className:null,closeButton:!0,show:!0,container:'body',value:'',inputType:'text',swapButtonOrder:!1,centerVertical:!1,multiple:!1,scrollable:!1};function c(e,t,o){return p.extend(!0,{},e,function(e,t){var o=e.length,a={};if(o<1||2').attr('label',t.group)),o=i[t.group]);var a=p(f.option);a.attr('value',t.value).text(t.text),o.append(a)}),v(i,function(e,t){n.append(t)}),n.val(r.value);break;case'checkbox':var l=p.isArray(r.value)?r.value:[r.value];if(!(a=r.inputOptions||[]).length)throw new Error('prompt with "inputType" set to "checkbox" requires at least one option');n=p('
'),v(a,function(e,o){if(o.value===u||o.text===u)throw new Error('each option needs a "value" property and a "text" property');var a=p(f.inputs[r.inputType]);a.find('input').attr('value',o.value),a.find('label').append('\n'+o.text),v(l,function(e,t){t===o.value&&a.find('input').prop('checked',!0)}),n.append(a)});break;case'radio':if(r.value!==u&&p.isArray(r.value))throw new Error('prompt with "inputType" set to "radio" requires a single, non-array value for "value"');if(!(a=r.inputOptions||[]).length)throw new Error('prompt with "inputType" set to "radio" requires at least one option');n=p('
');var s=!0;v(a,function(e,t){if(t.value===u||t.text===u)throw new Error('each option needs a "value" property and a "text" property');var o=p(f.inputs[r.inputType]);o.find('input').attr('value',t.value),o.find('label').append('\n'+t.text),r.value!==u&&t.value===r.value&&(o.find('input').prop('checked',!0),s=!1),n.append(o)}),s&&n.find('input[type="radio"]').first().prop('checked',!0)}if(e.append(n),e.on('submit',function(e){e.preventDefault(),e.stopPropagation(),t.find('.bootbox-accept').trigger('click')}),''!==p.trim(r.message)){var c=p(f.promptMessage).html(r.message);e.prepend(c),r.message=e}else r.message=e;return(t=d.dialog(r)).off('shown.bs.modal'),t.on('shown.bs.modal',function(){n.focus()}),!0===o&&t.modal('show'),t},d.addLocale('en',{OK:'OK',CANCEL:'Cancel',CONFIRM:'OK'}),d}); \ No newline at end of file diff --git a/server/src/main/resources/static/pptx/ppt.js b/server/src/main/resources/static/pptx/ppt.js index c83f1e57..5ef1206c 100644 --- a/server/src/main/resources/static/pptx/ppt.js +++ b/server/src/main/resources/static/pptx/ppt.js @@ -2,7 +2,6 @@ * Copyright 2013 I Doc View * @author Godwin */ - var ratio = 0.75; var pages; var slideUrls = new Array(); @@ -11,247 +10,248 @@ var curSlide = 1; var totalSize = 1; // PPT当前获取到的总页数 var slideCount = 1; // PPT文件总页数 var size = (!!$.url().param('size') ? $.url().param('size') : 0); -$(document).ready(function() { - - // async method: - $.get('onlinePreview?' , params, function(data, status) { - var data = JSON.parse(data); - var code = data.code; - if (1 == code) { - uuid = data.uuid; - pages = data.data; - totalSize = pages.length; - slideCount = data.totalSize; - - // title - $('.container-fluid:first .btn:first').after('' + data.name + ''); - document.title = data.name; - - // set ratio - ratio = pages[0].ratio; - - // reset all content - resetContent(); - afterLoad(); - } else { - $('.container-fluid .row-fluid').html('
' + data.desc + '
'); - } +$(document).ready(function () { + var data = resultData + var code = data.code; + if (1 == code) { + uuid = data.uuid; + pages = data.data; + totalSize = pages.length; + slideCount = data.totalSize; - clearProgress(); - }); + // title + $('.container-fluid:first .btn:first').after('' + data.name + ''); + document.title = data.name; - // 是否显示全屏按钮 - $('.fullscreen-link').toggle(screenfull.enabled); - // 全屏事件 - $('.fullscreen-link').click(function(){ - if (screenfull.enabled) { - screenfull.toggle($('.slide-img-container')[0]); - } - }); - $(document).bind("fullscreenchange", function() { - if (screenfull.isFullscreen) { - $('.slide-img-container').css('background-color', 'black'); - $('.slide-img-container').contextMenu(true); - } else { - $('.slide-img-container').css('background-color', ''); - $('.slide-img-container').contextMenu(false); - } - }); - - $('.select-page-selector').change(function() { - var selectNum = $(".select-page-selector option:selected").text(); - gotoSlide(selectNum); - }); - $('.slide-img-container .ppt-turn-left-mask').click(function () { - preSlide(); - }); - $('.slide-img-container .ppt-turn-right-mask').click(function () { - nextSlide(); - }); + // set ratio + ratio = pages[0].ratio; - // Right click (NOT supported in SOUGOU browser) - /* - $.contextMenu({ + // reset all content + resetContent(); + + afterLoad(); + } else { + $('.container-fluid .row-fluid').html('
' + data.desc + '
'); + } + clearProgress(); + + // 是否显示全屏按钮 + $('.fullscreen-link').toggle(screenfull.enabled); + // 全屏事件 + $('.fullscreen-link').click(function () { + if (screenfull.enabled) { + screenfull.toggle($('.slide-img-container')[0]); + } + }); + $(document).bind("fullscreenchange", function () { + if (screenfull.isFullscreen) { + $('.slide-img-container').css('background-color', 'black'); + $('.slide-img-container').contextMenu(true); + } else { + $('.slide-img-container').css('background-color', ''); + $('.slide-img-container').contextMenu(false); + } + }); + + $('.select-page-selector').change(function () { + var selectNum = $(".select-page-selector option:selected").text(); + gotoSlide(selectNum); + }); + $('.slide-img-container .ppt-turn-left-mask').click(function () { + preSlide(); + }); + $('.slide-img-container .ppt-turn-right-mask').click(function () { + nextSlide(); + }); + + // Right click (NOT supported in SOUGOU browser) + /* + $.contextMenu({ selector: '.slide-img-container', items: { - "next": { + "next": { name: "下一张", callback: function(key, options) { - nextSlide(); + nextSlide(); } }, "previous": { name: "上一张", callback: function(key, options) { - preSlide(); + preSlide(); } }, "sep1": "---------", "exit": { name: "结束放映", callback: function(key, options) { - $('.slide-img-container').fullScreen(false); + $('.slide-img-container').fullScreen(false); } }, } }); */ - $('.slide-img-container').contextMenu(false); - - // Swipe method is NOT supported in IE6, so it should be the last one. - try { - $('.slide-img-container').swipeleft(function() { nextSlide(); }); - $('.slide-img-container').swiperight(function() { preSlide(); }); - } catch (err) { + $('.slide-img-container').contextMenu(false); - } + // Swipe method is NOT supported in IE6, so it should be the last one. + try { + $('.slide-img-container').swipeleft(function () { + nextSlide(); + }); + $('.slide-img-container').swiperight(function () { + preSlide(); + }); + } catch (err) { + + } }); var remainContentInterval; -function checkRemainContent () { - clearInterval(remainContentInterval); - if (slideCount == totalSize) { - return; - } + +function checkRemainContent() { + clearInterval(remainContentInterval); + if (slideCount == totalSize) { + return; + } } function resetContent() { - remainContentInterval = setInterval(checkRemainContent, 8000); + remainContentInterval = setInterval(checkRemainContent, 8000); - // clear all content - $('.row-fluid .span2').empty(); - $('.select-page-selector').empty(); - $('.select-page-selector-sync').empty(); - $('.slide-img-container img').remove(); + // clear all content + $('.row-fluid .span2').empty(); + $('.select-page-selector').empty(); + $('.select-page-selector-sync').empty(); + $('.slide-img-container img').remove(); - // 限制预览页数开始 - var viewCheck = authMap.view; - if (!!viewCheck && (viewCheck > 1) && (pages.length > viewCheck)) { - $('.navbar').after('
试读结束,支付后阅读全文!
'); - totalSize = viewCheck; - clearInterval(remainContentInterval); - } - // 限制预览页数结束 + // 限制预览页数开始 + var viewCheck = authMap.view; + if (!!viewCheck && (viewCheck > 1) && (pages.length > viewCheck)) { + $('.navbar').after('
试读结束,支付后阅读全文!
'); + totalSize = viewCheck; + clearInterval(remainContentInterval); + } + // 限制预览页数结束 - // pages - for (i = 0; i < totalSize; i++) { - var page = pages[i]; - slideUrls[i] = page.url; - slideThumbUrls[i] = page.thumbUrl; - $('.row-fluid .span2').append('
' + (i + 1) + '/' + slideCount + '
'); - $('.select-page-selector').append(''); - $('.select-page-selector-sync').append(''); - } + // pages + for (i = 0; i < totalSize; i++) { + var page = pages[i]; + slideUrls[i] = page.url; + slideThumbUrls[i] = page.thumbUrl; + $('.row-fluid .span2').append('
' + (i + 1) + '/' + slideCount + '
'); + $('.select-page-selector').append(''); + $('.select-page-selector-sync').append(''); + } - // 未转换完成提示信息 - if (totalSize < slideCount) { - $('.row-fluid .span2').prepend('
转换中(' + Math.floor((totalSize / slideCount) * 100) + '%),请稍候……
'); - } + // 未转换完成提示信息 + if (totalSize < slideCount) { + $('.row-fluid .span2').prepend('
转换中(' + Math.floor((totalSize / slideCount) * 100) + '%),请稍候……
'); + } - $('.slide-img-container').append(''); - var thumbnailWidth = $('.thumbnail:first').width(); - var thumbnailHeight = thumbnailWidth * ratio; - $('.thumbnail').height(thumbnailHeight); - $('.thumbnail>img').width(thumbnailWidth).height(thumbnailHeight); + $('.slide-img-container').append(''); + var thumbnailWidth = $('.thumbnail:first').width(); + var thumbnailHeight = thumbnailWidth * ratio; + $('.thumbnail').height(thumbnailHeight); + $('.thumbnail>img').width(thumbnailWidth).height(thumbnailHeight); - var slideImgContainerWidth = $('.slide-img-container:first').width(); - var slideImgContainerHeight = slideImgContainerWidth * ratio; - $('.slide-img-container').height(slideImgContainerHeight); + var slideImgContainerWidth = $('.slide-img-container:first').width(); + var slideImgContainerHeight = slideImgContainerWidth * ratio; + $('.slide-img-container').height(slideImgContainerHeight); - resetImgSize(); + resetImgSize(); - var percent = Math.ceil((curSlide / slideUrls.length) * 100); - $('.thumbnail[page="' + curSlide + '"]').addClass('ppt-thumb-border'); + var percent = Math.ceil((curSlide / slideUrls.length) * 100); + $('.thumbnail[page="' + curSlide + '"]').addClass('ppt-thumb-border'); - // $('.thumbnail[page="' + curSlide + '"]').animate({scrollTop:($(window).height()/2)}, 'slow'); + // $('.thumbnail[page="' + curSlide + '"]').animate({scrollTop:($(window).height()/2)}, 'slow'); - $('.select-page-selector').val(curSlide); - $('.bottom-paging-progress .bar').width('' + percent + '%'); + $('.select-page-selector').val(curSlide); + $('.bottom-paging-progress .bar').width('' + percent + '%'); - $('.thumbnail').click(function () { - var page_num = $(this).attr('page'); - gotoSlide(page_num); - }); + $('.thumbnail').click(function () { + var page_num = $(this).attr('page'); + gotoSlide(page_num); + }); } -$(window).resize(function() { - resetImgSize(); +$(window).resize(function () { + resetImgSize(); }); function resetImgSize() { - var leftW = $('.row-fluid .span2').width() + 40; - var windowW = $(window).width(); - if (windowW < 768) { - leftW = -40; - $('.hidden-phone').css('display', 'none'); - $('.span9').removeClass('offset2'); - } else { - $('.hidden-phone').css('display', 'block'); - $('.span9').addClass('offset2'); - } - var ww = $(window).width() - 120 - leftW; - var wh = $(window).height() - 90; - if (screenfull.isFullscreen) { - ww = ww + 90 + leftW; - wh = wh + 80; - } - if (wh / ww < ratio) { - $('.slide-img-container').height(wh); - $('.slide-img-container').width(wh / ratio); - } else { - $('.slide-img-container').width(ww); - $('.slide-img-container').height(ww * ratio); - } + var leftW = $('.row-fluid .span2').width() + 40; + var windowW = $(window).width(); + if (windowW < 768) { + leftW = -40; + $('.hidden-phone').css('display', 'none'); + $('.span9').removeClass('offset2'); + } else { + $('.hidden-phone').css('display', 'block'); + $('.span9').addClass('offset2'); + } + var ww = $(window).width() - 120 - leftW; + var wh = $(window).height() - 90; + if (screenfull.isFullscreen) { + ww = ww + 90 + leftW; + wh = wh + 80; + } + if (wh / ww < ratio) { + $('.slide-img-container').height(wh); + $('.slide-img-container').width(wh / ratio); + } else { + $('.slide-img-container').width(ww); + $('.slide-img-container').height(ww * ratio); + } } -$(document).keydown(function(event){ - if (event.keyCode == 37 || event.keyCode == 38 || event.keyCode == 33) { // 37 left, 38 up, 33 pageUp - preSlide(); - } else if (event.keyCode == 39 || event.keyCode == 40 || event.keyCode == 32 || event.keyCode == 34){ // 39 right, 40 down, 32 space, 34 pageDown - nextSlide(); - } else if (event.keyCode == 13) { - screenfull.toggle($('.slide-img-container')[0]); - } +$(document).keydown(function (event) { + if (event.keyCode == 37 || event.keyCode == 38 || event.keyCode == 33) { // 37 left, 38 up, 33 pageUp + preSlide(); + } else if (event.keyCode == 39 || event.keyCode == 40 || event.keyCode == 32 || event.keyCode == 34) { // 39 right, 40 down, 32 space, 34 pageDown + nextSlide(); + } else if (event.keyCode == 13) { + screenfull.toggle($('.slide-img-container')[0]); + } }); function getCurSlide() { - return curSlide; + return curSlide; } function preSlide() { - var preSlide = eval(Number(getCurSlide()) - 1); - gotoSlide(preSlide); + var preSlide = eval(Number(getCurSlide()) - 1); + gotoSlide(preSlide); } function nextSlide() { - var nextSlide = eval(Number(getCurSlide()) + 1); - gotoSlide(nextSlide); + var nextSlide = eval(Number(getCurSlide()) + 1); + gotoSlide(nextSlide); } function gotoSlide(slide) { - var slideSum = slideUrls.length; - if (slide <= 0) { - slide = 1; - } else if (slideSum < slide) { - slide = slideSum; - } - curSlide = slide; - /* - $(".slide-img-container img").fadeOut(function() { - $(this).attr("src", slideUrls[slide - 1]).fadeIn(); - }); - */ - $(".slide-img-container img").attr("src", slideUrls[slide - 1]); - var percent = Math.ceil((curSlide / slideUrls.length) * 100); - $('.thumbnail').removeClass('ppt-thumb-border'); - $('.thumbnail[page="' + slide + '"]').addClass('ppt-thumb-border'); - var thumbTop = slide * ($('.thumbnail[page="' + 1 + '"]').height() + 10 + $('.thumb-page-number-container').height()) - ($(document).height() / 2); - $('.span2 ').animate({scrollTop:(thumbTop)}, 'slow'); - $('.select-page-selector').val(slide); - $('.select-page-selector-sync').val(slide); - $('.bottom-paging-progress .bar').width('' + percent + '%'); + var slideSum = slideUrls.length; + if (slide <= 0) { + slide = 1; + } else if (slideSum < slide) { + slide = slideSum; + } + curSlide = slide; + /* + $(".slide-img-container img").fadeOut(function() { + $(this).attr("src", slideUrls[slide - 1]).fadeIn(); + }); + */ + $(".slide-img-container img").attr("src", slideUrls[slide - 1]); + var percent = Math.ceil((curSlide / slideUrls.length) * 100); + $('.thumbnail').removeClass('ppt-thumb-border'); + $('.thumbnail[page="' + slide + '"]').addClass('ppt-thumb-border'); + var thumbTop = slide * ($('.thumbnail[page="' + 1 + '"]').height() + 10 + $('.thumb-page-number-container').height()) - ($(document).height() / 2); + $('.span2 ').animate({scrollTop: (thumbTop)}, 'slow'); + $('.select-page-selector').val(slide); + $('.select-page-selector-sync').val(slide); + $('.bottom-paging-progress .bar').width('' + percent + '%'); -} \ No newline at end of file +} diff --git a/server/src/main/resources/web/commonHeader.ftl b/server/src/main/resources/web/commonHeader.ftl index 7969ad50..6441afc3 100644 --- a/server/src/main/resources/web/commonHeader.ftl +++ b/server/src/main/resources/web/commonHeader.ftl @@ -1,9 +1,9 @@ - - +<#setting classic_compatible=true> + @@ -33,7 +33,52 @@ } } + // 中文环境 + var locale_zh_CN = { + OK: '确定', + CONFIRM: '确认', + CANCEL: '取消' + }; + bootbox.addLocale('locale_zh_CN', locale_zh_CN); + + /** + * 需要文件密码 + */ + function needFilePassword() { + if ('${needFilePassword}' == 'true') { + let promptTitle = "你正在预览加密文件,请输入文件密码。"; + if ('${filePasswordError}' == 'true') { + promptTitle = "密码错误,请重新输入密码。"; + } + + bootbox.prompt({ + title: promptTitle, + inputType: 'password', + centerVertical: true, + locale: 'locale_zh_CN', + callback: function (filePassword) { + if (filePassword != null) { + const locationHref = window.location.href; + const isInclude = locationHref.includes("filePassword="); + let redirectUrl = null; + if (isInclude) { + const url = new URL(locationHref); + url.searchParams.set("filePassword", filePassword); + redirectUrl = url.href; + } else { + redirectUrl = locationHref + '&filePassword=' + filePassword; + } + + window.location.replace(redirectUrl); + } else { + location.reload(); + } + } + }); + } + } + - diff --git a/server/src/main/resources/web/fileNotSupported.ftl b/server/src/main/resources/web/fileNotSupported.ftl index 69699143..6bce7939 100644 --- a/server/src/main/resources/web/fileNotSupported.ftl +++ b/server/src/main/resources/web/fileNotSupported.ftl @@ -1,42 +1,46 @@ - - + +
- + - 该文件类型(${fileType})系统暂时不支持在线预览,说明: + 该(${fileType})文件,系统暂不支持在线预览,具体原因如下:

${msg}

- 有任何疑问,请加 官方QQ群:613025121 咨询
+ - \ No newline at end of file diff --git a/server/src/main/resources/web/html.ftl b/server/src/main/resources/web/html.ftl index 7b233182..4ee3e627 100644 --- a/server/src/main/resources/web/html.ftl +++ b/server/src/main/resources/web/html.ftl @@ -2,25 +2,31 @@ - + + 文件预览 <#include "*/commonHeader.ftl"> - + + + + - \ No newline at end of file + diff --git a/server/src/main/resources/web/index.ftl b/server/src/main/resources/web/index.ftl index 19688d40..91dcfb98 100644 --- a/server/src/main/resources/web/index.ftl +++ b/server/src/main/resources/web/index.ftl @@ -262,13 +262,14 @@ }, { field: 'action', title: '操作' - },] + }] }).on('pre-body.bs.table', function (e, data) { // 每个data添加一列用来操作 $(data).each(function (index, item) { item.action = "预览" + "删除"; }); + return data; }).on('post-body.bs.table', function (e, data) { return data; @@ -281,7 +282,6 @@ urlField.val(b64Encoded); }); - function showLoadingDiv() { var height = window.document.documentElement.clientHeight - 1; $(".loading_container").css("height", height).show(); @@ -307,6 +307,7 @@ dataType: "json" /*设置返回值类型为文本*/ }); }); + var gitalk = new Gitalk({ clientID: '525d7f16e17aab08cef5', clientSecret: 'd1154e3aee5c8f1cbdc918b5c97a4f4157e0bfd9', diff --git a/server/src/main/resources/web/pdf.ftl b/server/src/main/resources/web/pdf.ftl index 54485336..7827fcb6 100644 --- a/server/src/main/resources/web/pdf.ftl +++ b/server/src/main/resources/web/pdf.ftl @@ -1,5 +1,4 @@ - @@ -7,6 +6,7 @@ PDF预览 <#include "*/commonHeader.ftl"> + <#if pdfUrl?contains("http://") || pdfUrl?contains("https://")> <#assign finalUrl="${pdfUrl}"> @@ -20,6 +20,7 @@ onclick="goForImage()"/> + - + + - - - +
加载中...
-
-