!51 实现预览加密的(受密码保护)office文件

* 1. 修复getCorsFile接口高危安全漏洞
* 1. 优化密码错误提示(“密码错误,请重新输入密码。”)
* 1. 修复PPT重复预览bug,此bug导致ppt每次预览会执行两次转换(请求两次onlinePreview接口),在大文件尤其耗时(双倍时…
* 1. 【加密office预览】优化受密码保护的office文件检查逻辑,提升旧文件格式的兼容性
* 1. 【加密office预览】优化office文件是否受密码保护判断逻辑,避免兼容性误判
* 1. 【加密office预览】优化重新输入密码提示。
* 1. 【加密office预览】优化当密码输入错误后,不是抛出异常,而是提示用户重新输入
* 1. 优化prompt提示框的输入密码提示样式
* 1. 实现基于userToken缓存加密文件,没有userToken的加密文件不缓存
* 1. 优化docker构建方案,使用分层构建方式,采用层级缓存解决构建慢发布慢等问题。从原本5分钟左右缩短至几秒
* 1. 加密文件暂时不缓存(后续基于用户token实现,基于用户缓存)
* 1. 优化office文件下载逻辑,跳过重复下载(大量节约带宽与磁盘空间)。
* 1. 修复预览不同类型的加密office文件bug
* 实现预览加密的(受密码保护)office文件
This commit is contained in:
yl-yue 2022-07-21 03:19:46 +00:00 committed by 陈精华
parent 04703aa03c
commit acffcbfe98
26 changed files with 746 additions and 492 deletions

View File

@ -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"]

View File

@ -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"]

View File

@ -0,0 +1,2 @@
# 执行如下命令构建基础镜像加快kkfileview docker镜像构建与发布
docker build --tag keking/kkfileview-jdk:4.1.1 .

View File

@ -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">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>kkFileView-parent</artifactId>
<groupId>cn.keking</groupId>
@ -27,19 +26,9 @@
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.openoffice</groupId>
<artifactId>juh</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.openoffice</groupId>
<artifactId>ridl</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.openoffice</groupId>
<artifactId>unoil</artifactId>
<version>3.2.1</version>
<groupId>org.libreoffice</groupId>
<artifactId>libreoffice</artifactId>
<version>7.1.4</version>
</dependency>
<dependency>
<!-- for the command line tool -->

View File

@ -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<String,?> 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<String,?> loadProperties = getLoadProperties(inputFile);
XComponent document = null;
try {
document = loader.loadComponentFromURL(toUrl(inputFile), "_blank", 0, toUnoProperties(loadProperties));

View File

@ -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<String, Object> 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);
}

View File

@ -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<String, Object> toMap() {
Map<String, Object> 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;
}
}

View File

@ -25,20 +25,12 @@
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- rar5 的支持 和其他众多压缩支持 可参考 package net.sf.sevenzipjbinding.ArchiveFormat; -->
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding</artifactId>
<version>16.02-2.01</version>
</dependency>
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding-all-platforms</artifactId>
<version>16.02-2.01</version>
<groupId>cn.keking</groupId>
<artifactId>office-plugin</artifactId>
<version>${project.version}</version>
</dependency>
<!-- web start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
@ -53,28 +45,13 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.keking</groupId>
<artifactId>office-plugin</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<!-- REDISSON -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.2.0</version>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- web end -->
<!-- poi start -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
@ -83,7 +60,12 @@
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>3.12</version>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
@ -106,6 +88,31 @@
<artifactId>fr.opensagres.xdocreport.document</artifactId>
<version>1.0.5</version>
</dependency>
<!-- poi start -->
<!-- rar5 的支持 和其他众多压缩支持 可参考 package net.sf.sevenzipjbinding.ArchiveFormat; -->
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding</artifactId>
<version>16.02-2.01</version>
</dependency>
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding-all-platforms</artifactId>
<version>16.02-2.01</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.2.0</version>
</dependency>
<!-- 解压(apache) -->
<dependency>
<groupId>org.apache.commons</groupId>
@ -134,23 +141,12 @@
<artifactId>antlr</artifactId>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.2</version>
</dependency>
<!-- FTP -->
<dependency>
<groupId>commons-net</groupId>
@ -210,7 +206,6 @@
<artifactId>javacv</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
@ -224,43 +219,36 @@
<version>4.1.2-1.5.2</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.1.2-1.5.2</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.6-1.5.1</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.6-1.5.1</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>4.2.1-1.5.2</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>4.2.1-1.5.2</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
@ -274,7 +262,6 @@
<scope>system</scope>
<systemPath>${basedir}/lib/jai_core-1.1.3.jar</systemPath>
</dependency>
<dependency>
<groupId>javax.media</groupId>
<artifactId>jai_codec</artifactId>
@ -283,6 +270,25 @@
<systemPath>${basedir}/lib/jai_codec-1.1.3.jar</systemPath>
</dependency>
<!-- test dependency - start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- test dependency - end -->
</dependencies>
<build>
@ -336,5 +342,4 @@
</plugin>
</plugins>
</build>
</project>

View File

@ -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" --]

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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<String> 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<String> 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;
}
}
}

View File

@ -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;
}

View File

@ -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 {
/**
* 判断officeword,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;
}
}

View File

@ -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";
}
}

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,6 @@
* Copyright 2013 I Doc View
* @author Godwin <I Doc View>
*/
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('<a class="brand lnk-file-title" style="text-decoration: none;" href="' + contextPath + '/doc/download/' + uuid + (!!queryStr ? '?' + queryStr : '') + '" title="' + data.name + '">' + data.name + '</a>');
document.title = data.name;
// set ratio
ratio = pages[0].ratio;
// reset all content
resetContent();
afterLoad();
} else {
$('.container-fluid .row-fluid').html('<section><div class="alert alert-error">' + data.desc + '</div></section>');
}
$(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('<a class="brand lnk-file-title" style="text-decoration: none;">' + data.name + '</a>');
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('<section><div class="alert alert-error">' + data.desc + '</div></section>');
}
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('<div class="alert alert-info" style="text-align: center; color: red;">试读结束,支付后阅读全文!</div>');
totalSize = viewCheck;
clearInterval(remainContentInterval);
}
// 限制预览页数结束
// 限制预览页数开始
var viewCheck = authMap.view;
if (!!viewCheck && (viewCheck > 1) && (pages.length > viewCheck)) {
$('.navbar').after('<div class="alert alert-info" style="text-align: center; color: red;">试读结束,支付后阅读全文!</div>');
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('<div class="thumbnail" page="' + (i + 1) + '"><img src="' + page.thumbUrl + '"></div><div class="thumb-page-number-container">' + (i + 1) + '/' + slideCount + '</div>');
$('.select-page-selector').append('<option>' + (i + 1) + '</option>');
$('.select-page-selector-sync').append('<option>' + (i + 1) + '</option>');
}
// pages
for (i = 0; i < totalSize; i++) {
var page = pages[i];
slideUrls[i] = page.url;
slideThumbUrls[i] = page.thumbUrl;
$('.row-fluid .span2').append('<div class="thumbnail" page="' + (i + 1) + '"><img src="' + page.thumbUrl + '"></div><div class="thumb-page-number-container">' + (i + 1) + '/' + slideCount + '</div>');
$('.select-page-selector').append('<option>' + (i + 1) + '</option>');
$('.select-page-selector-sync').append('<option>' + (i + 1) + '</option>');
}
// 未转换完成提示信息
if (totalSize < slideCount) {
$('.row-fluid .span2').prepend('<div style="color: red;">转换中(' + Math.floor((totalSize / slideCount) * 100) + '%)请稍候</div>');
}
// 未转换完成提示信息
if (totalSize < slideCount) {
$('.row-fluid .span2').prepend('<div style="color: red;">转换中(' + Math.floor((totalSize / slideCount) * 100) + '%)请稍候</div>');
}
$('.slide-img-container').append('<img src="' + slideUrls[curSlide - 1] + '" class="img-polaroid" style="height: 100%;">');
var thumbnailWidth = $('.thumbnail:first').width();
var thumbnailHeight = thumbnailWidth * ratio;
$('.thumbnail').height(thumbnailHeight);
$('.thumbnail>img').width(thumbnailWidth).height(thumbnailHeight);
$('.slide-img-container').append('<img src="' + slideUrls[curSlide - 1] + '" class="img-polaroid" style="height: 100%;">');
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 + '%');
}
}

View File

@ -1,9 +1,9 @@
<#setting classic_compatible=true>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"/>
<script src="js/jquery-3.0.0.min.js" type="text/javascript"></script>
<script src="js/jquery.form.min.js" type="text/javascript"></script>
<script src="bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<script src="js/bootbox.min.js" type="text/javascript"></script>
<script src="js/watermark.js" type="text/javascript"></script>
<script src="js/base64.min.js" type="text/javascript"></script>
@ -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();
}
}
});
}
}
</script>
<style>
* {
margin: 0;
@ -44,6 +89,4 @@
height: 100%;
width: 100%;
}
</style>

View File

@ -1,42 +1,46 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta charset="utf-8"/>
<style type="text/css">
body{
body {
margin: 0 auto;
width:900px;
width: 900px;
background-color: #CCB;
}
.container{
.container {
width: 700px;
height: 700px;
margin: 0 auto;
}
img{
width:auto;
height:auto;
max-width:100%;
max-height:100%;
img {
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
padding-bottom: 36px;
}
span{
span {
display: block;
font-size:20px;
color:blue;
font-size: 20px;
color: blue;
}
</style>
</head>
<body>
<div class="container">
<img src="images/sorry.jpg" />
<img src="images/sorry.jpg"/>
<span>
文件类型(${fileType})系统暂时不支持在线预览<b>说明</b>
(${fileType})文件系统暂不支持在线预览具体原因如下
<p style="color: red;">${msg}</p>
有任何疑问请加&nbsp;<a href="https://jq.qq.com/?_wv=1027&k=5c0UAtu">官方QQ群613025121</a>&nbsp;咨询
</span>
</div>
<script>
console.log(`有任何疑问请加<a href="https://jq.qq.com/?_wv=1027&k=5c0UAtu">官方QQ群613025121</a> 咨询`);
</script>
</body>
</html>

View File

@ -2,25 +2,31 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0">
<title>文件预览</title>
<#include "*/commonHeader.ftl">
</head>
<body>
<iframe src="${pdfUrl}" width="100%" frameborder="0"></iframe>
<iframe src="${pdfUrl}" width="100%" frameborder="0"></iframe>
</body>
<script type="text/javascript">
document.getElementsByTagName('iframe')[0].height = document.documentElement.clientHeight-10;
needFilePassword();
</script>
<script type="text/javascript">
document.getElementsByTagName('iframe')[0].height = document.documentElement.clientHeight - 10;
/**
* 页面变化调整高度
*/
window.onresize = function(){
window.onresize = function () {
var fm = document.getElementsByTagName("iframe")[0];
fm.height = window.document.documentElement.clientHeight-10;
fm.height = window.document.documentElement.clientHeight - 10;
}
/*初始化水印*/
window.onload = function() {
initWaterMark();
window.onload = function () {
initWaterMark();
}
</script>
</html>
</html>

View File

@ -262,13 +262,14 @@
}, {
field: 'action',
title: '操作'
},]
}]
}).on('pre-body.bs.table', function (e, data) {
// 每个data添加一列用来操作
$(data).each(function (index, item) {
item.action = "<a class='btn btn-default' target='_blank' href='${baseUrl}onlinePreview?url=" + encodeURIComponent(Base64.encode('${baseUrl}' + item.fileName)) + "'>预览</a>" +
"<a class='btn btn-default' href='javascript:void(0);' onclick='deleteFile(\"" + item.fileName + "\")'>删除</a>";
});
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',

View File

@ -1,5 +1,4 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
@ -7,6 +6,7 @@
<title>PDF预览</title>
<#include "*/commonHeader.ftl">
</head>
<body>
<#if pdfUrl?contains("http://") || pdfUrl?contains("https://")>
<#assign finalUrl="${pdfUrl}">
@ -20,6 +20,7 @@
onclick="goForImage()"/>
</#if>
</body>
<script type="text/javascript">
var url = '${finalUrl}';
var baseUrl = '${baseUrl}'.endsWith('/') ? '${baseUrl}' : '${baseUrl}' + '/';

View File

@ -1,149 +1,136 @@
<#if RequestParameters['name']??>
{
"code": 1,
"name": "PPT预览",
"totalSize": ${imgurls?size},
"curPage": 1,
"totalPage": 1,
"pageSize": 10,
"titles": null,
"data": [
<#assign index = 0>
<#list imgurls as img>
<#if index != 0>,</#if>{
"uuid": null,
"title": null,
"content": null,
"text": null,
"url": "${img}",
"destFile": null,
"viewCount": 0,
"downloadCount": 0,
"ctime": null,
"thumbUrl": "${img}",
"largeUrl": null,
"ratio": 0.5625,
"note": null
}<#assign index = index + 1>
</#list>],
"desc": "Success"
}
<#else>
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link href="pptx/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="pptx/idocv/idocv_common.min.css" rel="stylesheet">
<link href="pptx/jquery.contextMenu.css" rel="stylesheet">
<!-- BOOTSTRAP STYLE start -->
<!-- Le styles -->
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link href="pptx/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="pptx/idocv/idocv_common.min.css" rel="stylesheet">
<link href="pptx/jquery.contextMenu.css" rel="stylesheet">
<!-- BOOTSTRAP STYLE end -->
<#-- 手机端预览兼容 -->
<script type="text/javascript">
var windowWidth = document.documentElement.clientWidth;
var searchStr = window.location.search.substr(1);
if ((windowWidth < 768 || (/micromessenger/.test(navigator.userAgent.toLowerCase()))) && (!searchStr || searchStr.indexOf('type=') < 0)) {
var redirectUrl = window.location.pathname + '?type=mobile' + (!!searchStr ? ('&' + searchStr) : '');
window.location.replace(redirectUrl);
}
var windowWidth = document.documentElement.clientWidth;
var searchStr = window.location.search.substr(1);
if ((windowWidth < 768 || (/micromessenger/.test(navigator.userAgent.toLowerCase()))) && (!searchStr || searchStr.indexOf('type=') < 0)) {
var redirectUrl = window.location.pathname + '?type=mobile' + (!!searchStr ? ('&' + searchStr) : '');
window.location.replace(redirectUrl);
}
</script>
<style type="text/css">
.thumbnail{
/*
max-width: 200px;
*/
cursor: pointer;
}
.thumbnail {
/*
max-width: 200px;
*/
cursor: pointer;
}
</style>
<!--[if lt IE 9]>
<script src="/static/bootstrap/js/html5shiv.js"></script>
<script src="/static/bootstrap/js/html5shiv.js"></script>
<![endif]-->
</head>
</head>
<body onload="resetImgSize();" class="ppt-body">
<body onload="resetImgSize();" class="ppt-body">
<div class="loading-mask" style="display: block;">
<div class="loading-zone">
<div class="text"><img src="pptx/img/loader_indicator_lite.gif">加载中...</div>
</div>
</div>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<!-- FILE NAME HERE -->
<!-- SIGN UP & SIGN IN -->
<div class="nav-collapse collapse">
<p class="navbar-text pull-right">
<a href="#" title="全屏" class="fullscreen-link"><i class="icon-fullscreen icon-white"></i></a>
</p>
</div><!--/.nav-collapse -->
</div>
</div>
</div>
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<!-- FILE NAME HERE -->
<!-- SIGN UP & SIGN IN -->
<div class="container-fluid" style="max-height: 100%;">
<div class="row-fluid">
<div class="span2 hidden-phone" style="position: fixed; top: 60px; left: 20px; bottom: 20px; padding-right: 10px; border-right: 3px solid #c8c8c8; max-height: 100%; overflow: auto; text-align: center;">
<!--Sidebar content-->
<!--
<div class="thumbnail">
<img src="">
</div>
1/20<br />
-->
<div class="nav-collapse collapse">
<p class="navbar-text pull-right">
<a href="#" title="全屏" class="fullscreen-link"><i class="icon-fullscreen icon-white"></i></a>
</p>
</div><!--/.nav-collapse -->
</div>
</div>
</div>
<div class="container-fluid" style="max-height: 100%;">
<div class="row-fluid">
<div class="span2 hidden-phone"
style="position: fixed; top: 60px; left: 20px; bottom: 20px; padding-right: 10px; border-right: 3px solid #c8c8c8; max-height: 100%; overflow: auto; text-align: center;">
<!--Sidebar content-->
<!--
<div class="thumbnail">
<img src="">
</div>
1/20<br />
-->
</div>
<div class="span9 offset2">
<div class="slide-img-container">
<div class="ppt-turn-left-mask"></div>
<div class="ppt-turn-right-mask"></div>
<!--
<img src="" class="img-polaroid" style="max-height: 100%;">
-->
</div>
<!-- ONLY AVAILABLE ON MOBILE -->
<div class="span12 visible-phone text-center" style="position: fixed; bottom: 10px; left: 0px; z-index: 1000;">
<select class="select-page-selector span1" style="width: 80px; margin-top: 10px;">
<!-- PAGE NUMBERS HERE -->
</select>
</div>
<div class="slide-img-container">
<div class="ppt-turn-left-mask"></div>
<div class="ppt-turn-right-mask"></div>
<!--
<img src="" class="img-polaroid" style="max-height: 100%;">
-->
</div>
<!-- ONLY AVAILABLE ON MOBILE -->
<div class="span12 visible-phone text-center"
style="position: fixed; bottom: 10px; left: 0px; z-index: 1000;">
<select class="select-page-selector span1" style="width: 80px; margin-top: 10px;">
<!-- PAGE NUMBERS HERE -->
</select>
</div>
</div>
</div>
</div>
<div class="progress progress-striped active bottom-paging-progress">
<div class="bar" style="width: 0%;"></div>
</div>
<!-- JavaSript
================================================== -->
</div>
<div class="progress progress-striped active bottom-paging-progress">
<div class="bar" style="width: 0%;"></div>
</div>
<!-- JavaSript ================================================== -->
<script src="pptx/jquery-3.5.1.min.js"></script>
<script src="pptx/jquery.contextMenu.js?v=11.2.5_20210128"></script>
<script src="pptx/idocv/idocv_common.min.js"></script>
<script>
var resultData = {
"code": 1,
"name": "PPT预览",
"totalSize": ${imgurls?size},
"curPage": 1,
"totalPage": 1,
"pageSize": 10,
"titles": null,
"data": [
<#assign index = 0>
<#list imgurls as img>
<#if index != 0>, </#if>{
"uuid": null,
"title": null,
"content": null,
"text": null,
"url": "${img}",
"destFile": null,
"viewCount": 0,
"downloadCount": 0,
"ctime": null,
"thumbUrl": "${img}",
"largeUrl": null,
"ratio": 0.5625,
"note": null
}<#assign index = index + 1>
</#list>],
"desc": "Success"
}
var contextPath = '';
var version = '12';
// var urlObj = $.url($.url().attr('source').replace(contextPath, ''));
@ -166,10 +153,7 @@
}
</script>
<!-- 客户自定义JS -->
<script src="pptx/jquery.mobile-events.min.js"></script>
<script src="pptx/ppt.js"></script>
<script src="pptx/jquery.mobile-events.min.js"></script>
<script src="pptx/ppt.js"></script>
</body>
</html>
</body>
</html>
</#if>