Compare commits

..

1 Commits

3687 changed files with 1322 additions and 88450 deletions

2
.gitignore vendored
View File

@@ -17,6 +17,7 @@ target/
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
@@ -28,3 +29,4 @@ nbdist/
server/src/main/cache/
server/src/main/file/
server/src/main/log

View File

@@ -28,5 +28,5 @@ 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-3.5.1/bin
ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider","-Dspring.config.location=/opt/kkFileView-3.5.1/config/application.properties","-jar","/opt/kkFileView-3.5.1/bin/kkFileView-3.5.1.jar"]
ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-2.2.1/bin
ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider","-Dspring.config.location=/opt/kkFileView-2.2.1/config/application.properties","-jar","/opt/kkFileView-2.2.1/bin/kkFileView-2.2.1.jar"]

View File

@@ -2,14 +2,14 @@
此项目为文件文档在线预览项目解决方案对标业内付费产品有[永中office](http://dcs.yozosoft.com/)】【[office365](http://www.officeweb365.com/)】【[idocv](https://www.idocv.com/)】等在取得公司高层同意后以Apache协议开源出来反哺社区在此特别感谢@唐老大的支持以及@端木详笑的贡献。该项目使用流行的spring boot搭建易上手和部署基本支持主流办公文档的在线预览如doc,docx,Excel,pdf,txt,zip,rar,图片等等
### 项目特性
1. 支持 office, pdf, cad 等办公文档
1. 支持 txt, xml(渲染), md(渲染), java, php, py, js, css 等所有纯文本
1. 支持 zip, rar, jar, tar, gzip 等压缩包
1. 支持 jpg, jpeg, png, gif, tif, tiff 等图片预览翻转缩放镜像
1. 使用 spring-boot 开发预览服务搭建部署非常简便
1. rest 接口提供服务语言平台特性(java,php,python,go,php....)都支持应用接入简单方便
1. 支持officepdf, cad等办公文档
1. 支持txt,java,php,py,md,js,css等所有纯文本
1. 支持zip,rar,jar,tar,gzip等压缩包
1. 支持jpgjpegpnggif等图片预览翻转缩放镜像
1. 使用spring boot开发预览服务搭建部署非常简便
1. rest接口提供服务跨平台特性(java,php,python,go,php....)都支持应用接入简单方便
1. 抽象预览服务接口方便二次开发非常方便添加其他类型文件预览支持
1. 最最重要 Apache 协议开源代码 pull 下来想干嘛就干嘛
1. 最最重要Apache协议开源代码pull下来想干嘛就干嘛
### 官网及文档
@@ -26,7 +26,7 @@
1. English documenthttps://gitee.com/kekingcn/file-online-preview/blob/master/README.en.md
### 联系我们加入组织
> 我们会用心回答解决大家在项目使用中的问题也请大家在提问前至少 Google baidu 珍爱生命远离无效的交流沟通
> 我们会用心回答解决大家在项目使用中的问题也请大家在提问前至少Google或baidu珍爱生命远离无效的交流沟通
![](./doc/KK开源技术交流2群群聊二维码.png)
@@ -99,60 +99,16 @@ pdf预览模式预览效果如下
- jodconverter
> 依赖外部环境
- redis (可选默认不用)
- OpenOffice 或者 LibreOffice( Windows 下已内置Linux 脚本启动模式会自动安装Mac OS 下需要手动安装)
- OpenOffice或者LibreOffice(Windows下已内置Linux会自动安装Mac OS下需要手动安装)
1. 第一步pull 项目 https://github.com/kekingcn/file-online-preview.git
1. 第一步pull项目https://github.com/kekingcn/file-online-preview.git
3. 第二步运行 ServerMain main 方法服务启动后访问 http://localhost:8012/
3. 第二步运行FilePreviewApplication的main方法服务启动后访问http://localhost:8012/
会看到如下界面代表服务启动成功
![输入图片说明](https://gitee.com/uploads/images/2017/1213/100221_ea15202e_492218.png "屏幕截图.png")
### 历史更新记录
> 2021年1月28日
2020农历年最后一个版本发布主要包含了部分 UI 改进和解决了 QQ 群友 Issue 里反馈的 Bug 修复最最重要的是发个新版过个好年
1. 引入galimatias,解决不规范文件名导致文件下载异常
2. 更新index接入演示界面UI风格
3. 更新markdown文件预览UI风格
4. 更新XML文件预览UI风格调整类文本预览架构更方便扩展
5. 更新simTxT文件预览UI风格
6. 调整多图连续预览上下翻图的UI
7. 采用apache-common-io包简化所有的文件下载io操作
8. XML文件预览支持切换纯文本模式
9. 增强url base64解码失败时的提示信息
10. 修复导包错误以及图片预览 bug
11. 修复发行包运行时找不到日志目录的问题
12. 修复压缩包内多图连续预览的bug
13. 修复大小写文件类型后缀没通用匹配的问题
14. 指定Base64转码采用Apache Commons-code中的实现修复base64部分jdk版本下出现的异常
15. 修复类文本类型HTML文件预览的bug
16. 修复dwg文件预览时无法在jpg和pdf两种类型之间切换
17. escaping of dangerous characters to prevent reflected xss
18. 修复重复编码导致文档转图片预览失败的问题&编码规范
> 2020年12月27日
2020年年终大版本更新架构全面设计代码全面重构代码质量全面提升二次开发更便捷欢迎拉源码品鉴提issuepr共同建设
1. 架构模块调整,大量的代码重构代码质量提升N个等级欢迎品鉴
2. 增强XML文件预览效果新增XML文档数结构预览
3. 新增markdown文件预览支持预览支持md渲染和源文本切换支持
4. 切换底层web server为jetty解决这个issuehttps://github.com/kekingcn/kkFileView/issues/168
5. 引入cpdetector解决文件编码识别问题
6. url采用base64+urlencode双编码彻底解决各种奇葩文件名预览问题
7. 新增配置项office.preview.switch.disabled控制offic文件预览切换开关
8. 优化文本类型文件预览逻辑采用Base64传输内容避免预览时再次请求文件内容
9. office预览图片模式禁用图片放大效果达到图片和pdf预览效果一致的体验
10. 直接代码静态设置pdfbox兼容低版本jdk在IDEA中运行也不会有警告提示
11. 移除guavahutool等非必须的工具包减少代码体积
12. Office组件加载异步化提速应用启动速度最快到5秒内
13. 合理设置预览消费队列的线程数
14. 修复压缩包里文件再次预览失败的bug
15. 修复图片预览的bug
> 2020年05月20日
1. 新增支持全局水印并支持通过参数动态改变水印内容
2. 新增支持CAD文件预览
@@ -207,13 +163,5 @@ pdf预览模式预览效果如下
1. 引入pdf.js预览doc等文件支持doc标题生成pdf预览菜单支持手机端预览
### 使用登记
如果这个项目解决了你的实际问题可在 https://gitee.com/kekingcn/file-online-preview/issues/IGSBV
如果这个项目解决了你的实际问题可在https://gitee.com/kekingcn/file-online-preview/issues/IGSBV
登记下如果节省了你的三方预览服务费用也愿意支持下的话可点击下方捐助请作者喝杯咖啡也是非常感谢
### Stars 趋势图
#### Gitee
[![Stargazers over time](https://whnb.wang/img/kekingcn/file-online-preview)](https://whnb.wang/kekingcn/file-online-preview?e=86400)
#### GitHub
[![Stargazers over time](https://starchart.cc/kekingcn/kkFileView.svg)](https://starchart.cc/kekingcn/kkFileView)

View File

@@ -3,15 +3,13 @@
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>filepreview</artifactId>
<groupId>cn.keking</groupId>
<version>3.5.1</version>
</parent>
<groupId>cn.keking</groupId>
<artifactId>office-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<!-- required for org.hyperic:sigar -->

View File

@@ -81,7 +81,7 @@ public class OfficeUtils {
}
if (PlatformUtils.isWindows()) {
// %ProgramFiles(x86)% on 64-bit machines; %ProgramFiles% on 32-bit ones
String officePluginPath = ConfigUtils.getOfficePluginPath();
String homePath = ConfigUtils.getHomePath();
String programFiles = System.getenv("ProgramFiles(x86)");
if (programFiles == null) {
programFiles = System.getenv("ProgramFiles");
@@ -89,7 +89,7 @@ public class OfficeUtils {
return findOfficeHome(
programFiles + File.separator + "OpenOffice 4",
programFiles + File.separator + "LibreOffice 4",
officePluginPath + File.separator + "windows-office"
homePath + File.separator + "office"
);
} else if (PlatformUtils.isMac()) {
return findOfficeHome(

View File

@@ -9,7 +9,6 @@ import java.io.File;
public class ConfigUtils {
private static final String MAIN_DIRECTORY_NAME = "server";
private static final String OFFICE_PLUGIN_NAME = "office-plugin";
public static String getHomePath() {
String userDir = System.getenv("KKFILEVIEW_BIN_FOLDER");
@@ -29,23 +28,6 @@ public class ConfigUtils {
return userDir;
}
public static String getOfficePluginPath() {
String userDir = System.getenv("KKFILEVIEW_BIN_FOLDER");
if (userDir == null) {
userDir = System.getProperty("user.dir");
}
if (userDir.endsWith("bin")) {
userDir = userDir.substring(0, userDir.length() - 4);
} else {
String separator = File.separator;
if (!userDir.contains(OFFICE_PLUGIN_NAME)) {
userDir = userDir + separator + OFFICE_PLUGIN_NAME;
}
}
return userDir;
}
public static String getCustomizedConfigPath() {
String homePath = getHomePath();
String separator = java.io.File.separator;

View File

@@ -1,5 +0,0 @@
======================================================================
OpenOffice 4.1.6 ReadMe
======================================================================
Windows 内置集成的的 OpenOffice

68
pom.xml
View File

@@ -2,60 +2,36 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<groupId>cn.keking</groupId>
<artifactId>filepreview</artifactId>
<version>3.5.1</version>
<properties>
<java.version>1.8</java.version>
<spring.boot.version>2.4.2</spring.boot.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<version>2.2.1</version>
<modules>
<module>office-plugin</module>
<module>server</module>
</modules>
<packaging>pom</packaging>
<profiles>
<profile>
<id>dev</id>
<properties>
<build.exclude.resource></build.exclude.resource>
</properties>
<activation>
<!--默认激活开发环境-->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>release</id>
<properties>
<build.exclude.resource>*.properties</build.exclude.resource>
</properties>
</profile>
</profiles>
<name>file-online-preview</name>
<description>专注文件在线预览服务</description>
<url>https://github.com/kekingcn/kkFileView</url>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
</license>
</licenses>
<scm>
<url>https://github.com/kekingcn/kkFileView</url>
<connection>scm:git:git://github.com/kekingcn/kkFileView.git</connection>
<developerConnection>scm:git:ssh://git@github.com/kekingcn/kkFileView.git</developerConnection>
</scm>
<developers>
<developer>
<id>klboke</id>
<name>kl</name>
<email>g632104866@gmail.com</email>
<url>http://www.kailing.pub</url>
</developer>
<developer>
<id>gitchenjh</id>
<name>chenjinghua</name>
<email>842761733@qq.com</email>
</developer>
</developers>
<issueManagement>
<system>github</system>
<url>https://github.com/kekingcn/kkFileView/issues</url>
</issueManagement>
</project>

View File

@@ -2,27 +2,31 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>filepreview</artifactId>
<groupId>cn.keking</groupId>
<version>3.5.1</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>cn.keking</groupId>
<artifactId>kkFileView</artifactId>
<version>2.2.1</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<repositories>
<repository>
<!-- required for org.hyperic:sigar -->
<id>jboss-public-repository-group</id>
<url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -51,7 +55,13 @@
<dependency>
<groupId>cn.keking</groupId>
<artifactId>office-plugin</artifactId>
<version>${project.version}</version>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<artifactId>commons-io</artifactId>
<groupId>commons-io</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
@@ -67,7 +77,7 @@
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
<version>3.12</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
@@ -95,6 +105,11 @@
<artifactId>fr.opensagres.xdocreport.document</artifactId>
<version>1.0.5</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<!-- 解压(apache) -->
<dependency>
<groupId>org.apache.commons</groupId>
@@ -149,7 +164,7 @@
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.17</version>
<version>1.4.15</version>
</dependency>
<dependency>
<groupId>com.googlecode.concurrentlinkedhashmap</groupId>
@@ -186,72 +201,7 @@
<scope>system</scope>
<systemPath>${basedir}/lib/cpdetector-1.04.jar</systemPath>
</dependency>
<!-- url 规范化 -->
<dependency>
<groupId>io.mola.galimatias</groupId>
<artifactId>galimatias</artifactId>
<version>0.2.1</version>
</dependency>
<!-- 以下是bytedeco 基于opencv ffmpeg封装的javacv用于视频处理 -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.5.2</version>
</dependency>
<!-- 此版本中主要兼容linux和windows系统如需兼容其他系统平台请引入对应依赖即可 -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<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>
</dependencies>
<build>
<resources>
<resource>
@@ -263,6 +213,9 @@
</resource>
<resource>
<directory>src/main/config</directory>
<excludes>
<exclude>${build.exclude.resource}</exclude>
</excludes>
<filtering>true</filtering>
</resource>
</resources>
@@ -270,25 +223,16 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/main/assembly/dist-win32.xml</descriptor>
<descriptor>src/main/assembly/dist-linux.xml</descriptor>
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>

View File

@@ -35,10 +35,10 @@
<outputDirectory>${file.separator}log</outputDirectory>
</fileSet>
<fileSet>
<directory>../office-plugin/windows-office</directory>
<outputDirectory>${file.separator}windows-office</outputDirectory>
<directory>src/main/office</directory>
<outputDirectory>${file.separator}office</outputDirectory>
</fileSet>
<fileSet>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>${file.separator}bin</outputDirectory>
<includes>

View File

@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
<id>make-assembly</id>
<formats>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>src/main/bin</directory>
<outputDirectory>${file.separator}bin</outputDirectory>
<includes>
<include>*.sh</include>
</includes>
<fileMode>755</fileMode>
<lineEnding>unix</lineEnding>
</fileSet>
<fileSet>
<directory>src/main/config</directory>
<outputDirectory>${file.separator}config</outputDirectory>
<lineEnding>unix</lineEnding>
</fileSet>
<fileSet>
<directory>src/main/log</directory>
<outputDirectory>${file.separator}log</outputDirectory>
<lineEnding>unix</lineEnding>
</fileSet>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>${file.separator}bin</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
</fileSets>
</assembly>

View File

@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
<id>make-assembly</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>src/main/bin</directory>
<outputDirectory>${file.separator}bin</outputDirectory>
<includes>
<include>*.bat</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/config</directory>
<outputDirectory>${file.separator}config</outputDirectory>
</fileSet>
<fileSet>
<directory>src/main/log</directory>
<outputDirectory>${file.separator}log</outputDirectory>
</fileSet>
<fileSet>
<directory>../office-plugin/windows-office</directory>
<outputDirectory>${file.separator}windows-office</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>${file.separator}bin</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
</fileSets>
</assembly>

View File

@@ -6,4 +6,4 @@ echo Starting kkFileView...
echo Please check log file in ../log/kkFileView.log for more information
echo You can get help in our official homesite: https://kkFileView.keking.cn
echo If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers
java -Dspring.config.location=..\config\application.properties -jar kkFileView-3.5.1.jar -> ..\log\kkFileView.log
java -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=..\config\application.properties -jar kkFileView-2.2.1.jar -> ..\log\kkFileView.log

View File

@@ -29,4 +29,4 @@ echo "Starting kkFileView..."
echo "Please execute ./showlog.sh to check log for more information"
echo "You can get help in our official homesite: https://kkFileView.keking.cn"
echo "If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers"
nohup java -Dfile.encoding=UTF-8 -Dspring.config.location=../config/application.properties -jar kkFileView-3.5.1.jar > ../log/kkFileView.log 2>&1 &
nohup java -Dfile.encoding=UTF-8 -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=../config/application.properties -jar kkFileView-2.2.1.jar > ../log/kkFileView.log 2>&1 &

View File

@@ -1,10 +1,7 @@
#######################################不可动态配置需要重启生效#######################################
server.port = ${KK_SERVER_PORT:8012}
server.servlet.context-path= ${KK_CONTEXT_PATH:/}
server.servlet.encoding.charset = utf-8
#文件上传限制
spring.servlet.multipart.max-file-size=500MB
spring.servlet.multipart.max-request-size=500MB
server.context-path = ${KK_CONTEXT_PATH:/}
spring.http.encoding.charset = utf8
## Freemarker 配置
spring.freemarker.template-loader-path = classpath:/web/
spring.freemarker.cache = false
@@ -16,11 +13,10 @@ spring.freemarker.expose-session-attributes = true
spring.freemarker.request-context-attribute = request
spring.freemarker.suffix = .ftl
# office-plugin
## office转换服务的进程数默认开启两个进程
office.plugin.server.ports = 2001,2002
## office 转换服务 task 超时时间默认五分钟
office.plugin.task.timeout = 5m
server.tomcat.uri-encoding = UTF-8
#文件上传限制
spring.http.multipart.max-request-size=500MB
spring.http.multipart.max-file-size=500MB
#文件资源路径默认为打包根路径下的file目录下
#file.dir = D:\\kkFileview\\
@@ -33,7 +29,7 @@ office.home = ${KK_OFFICE_HOME:default}
cache.type = ${KK_CACHE_TYPE:jdk}
#redis连接只有当cache.type = redis时才有用
spring.redisson.address = ${KK_SPRING_REDISSON_ADDRESS:127.0.0.1:6379}
spring.redisson.password = ${KK_SPRING_REDISSON_PASSWORD:}
spring.redisson.password = ${KK_SPRING_REDISSON_PASSWORD:123456}
#缓存是否自动清理 true 为开启注释掉或其他值都为关闭
cache.clean.enabled = ${KK_CACHE_CLEAN_ENABLED:true}
#缓存自动清理时间cache.clean.enabled = true时才有用cron表达式基于Quartz cron
@@ -55,11 +51,6 @@ cache.enabled = ${KK_CACHE_ENABLED:true}
simText = ${KK_SIMTEXT:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd}
#多媒体类型默认如下可自定义添加
media = ${KK_MEDIA:mp3,wav,mp4,flv}
#是否开启多媒体类型转视频格式转换,目前可转换视频格式有avi,mov,wmv,3gp,rm
#请谨慎开启此功能建议异步调用添加到处理队列并且增加任务队列处理线程防止视频转换占用完线程资源转换比较耗费时间,并且控制了只能串行处理转换任务
media.convert.disable = ${KK_MEDIA_CONVERT_DISABLE:false}
#支持转换的视频类型
convertMedias = ${KK_CONVERTMEDIAS:avi,mov,wmv,mkv,3gp,rm}
#office类型文档(word ppt)样式默认为图片(image)可配置为pdf预览时也有按钮切换
office.preview.type = ${KK_OFFICE_PREVIEW_TYPE:image}
#是否关闭office预览切换开关默认为false可配置为true关闭

View File

@@ -1,10 +1,5 @@
[#ftl]
[#-- @implicitly included --]
[#-- @ftlvariable name="initializeMemorySize" type="java.lang.Number" --]
[#-- @ftlvariable name="currentUrl" type="java.lang.String" --]
[#-- @ftlvariable name="file" type="cn.keking.model.FileAttribute" --]
[#-- @ftlvariable name="fileName" type="java.lang.String" --]
[#-- @ftlvariable name="fileTree" type="java.lang.String" --]
[#-- @ftlvariable name="baseUrl" type="java.lang.String" --]
[#-- @ftlvariable name="imgUrls" type="String" --]
[#-- @ftlvariable name="textData" type="java.lang.String" --]

View File

@@ -1,33 +1,22 @@
package cn.keking;
import cn.keking.config.AppBanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.util.StopWatch;
@SpringBootApplication
@EnableScheduling
@ComponentScan(value = "cn.keking.*")
public class ServerMain {
private static final Logger logger = LoggerFactory.getLogger(ServerMain.class);
public static void main(String[] args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = new SpringApplicationBuilder(ServerMain.class)
.logStartupInfo(false)
.banner(new AppBanner())
.run(args);
stopWatch.stop();
Integer port = context.getBean(ServerProperties.class).getPort();
logger.info("kkFileView 服务启动完成,耗时:{}s演示页请访问: http://127.0.0.1:{} ", stopWatch.getTotalTimeSeconds(), port);
}
public static void main(String[] args) {
ServerMain.staticInitSystemProperty();
SpringApplication.run(ServerMain.class, args);
}
private static void staticInitSystemProperty(){
//pdfbox兼容低版本jdk
System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider");
}
}

View File

@@ -1,28 +0,0 @@
package cn.keking.config;
import org.springframework.boot.Banner;
import org.springframework.core.env.Environment;
import java.io.PrintStream;
/**
* @author kl (http://kailing.pub)
* @since 2021/2/8
*/
public class AppBanner implements Banner {
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
out.println(
" _ _ ______ _ _ __ __ _ \n" +
" | | | | | ____| (_) | | \\ \\ / / (_) \n" +
" | | __ | | __ | |__ _ | | ___ \\ \\ / / _ ___ __ __\n" +
" | |/ / | |/ / | __| | | | | / _ \\ \\ \\/ / | | / _ \\ \\ \\ /\\ / /\n" +
" | < | < | | | | | | | __/ \\ / | | | __/ \\ V V / \n" +
" |_|\\_\\ |_|\\_\\ |_| |_| |_| \\___| \\/ |_| \\___| \\_/\\_/ \n" +
" \n" +
" => Spring Boot :: (v2.4.2) QQ1 :: 613025121\n" +
" => kkFileView :: (v3.5) QQ2 :: 484680571\n" +
" => github :: https://github.com/kekingcn/kkFileView\n" +
" => gitee :: https://gitee.com/kekingcn/file-online-preview\n");
}
}

View File

@@ -16,25 +16,18 @@ import java.util.Set;
@Component
public class ConfigConstants {
static {
//pdfbox兼容低版本jdk
System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider");
}
private static Boolean cacheEnabled;
private static String[] simTexts = {};
private static String[] medias = {};
private static String[] convertMedias = {};
private static String mediaConvertDisable;
private static String officePreviewType;
private static String officePreviewSwitchDisabled;
private static String ftpUsername;
private static String ftpPassword;
private static String ftpControlEncoding;
private static String baseUrl;
private static String fileDir = ConfigUtils.getHomePath() + File.separator + "file" + File.separator;
private static CopyOnWriteArraySet<String> trustHostSet;
private static String pdfDownloadDisable;
private static Boolean CACHE_ENABLED;
private static String[] SIM_TEXT = {};
private static String[] MEDIA = {};
private static String OFFICE_PREVIEW_TYPE;
private static String OFFICE_PREVIEW_SWITCH_DISABLED;
private static String FTP_USERNAME;
private static String FTP_PASSWORD;
private static String FTP_CONTROL_ENCODING;
private static String BASE_URL;
private static String FILE_DIR = ConfigUtils.getHomePath() + File.separator + "file" + File.separator;
private static CopyOnWriteArraySet<String> TRUST_HOST_SET;
private static String PDF_DOWNLOAD_DISABLE;
public static final String DEFAULT_CACHE_ENABLED = "true";
public static final String DEFAULT_TXT_TYPE = "txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd";
@@ -49,9 +42,8 @@ public class ConfigConstants {
public static final String DEFAULT_TRUST_HOST = "default";
public static final String DEFAULT_PDF_DOWNLOAD_DISABLE = "true";
public static Boolean isCacheEnabled() {
return cacheEnabled;
return CACHE_ENABLED;
}
@Value("${cache.enabled:true}")
@@ -60,11 +52,11 @@ public class ConfigConstants {
}
public static void setCacheEnabledValueValue(Boolean cacheEnabled) {
ConfigConstants.cacheEnabled = cacheEnabled;
CACHE_ENABLED = cacheEnabled;
}
public static String[] getSimText() {
return simTexts;
return SIM_TEXT;
}
@Value("${simText:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd}")
@@ -74,11 +66,11 @@ public class ConfigConstants {
}
public static void setSimTextValue(String[] simText) {
ConfigConstants.simTexts = simText;
SIM_TEXT = simText;
}
public static String[] getMedia() {
return medias;
return MEDIA;
}
@Value("${media:mp3,wav,mp4,flv}")
@@ -88,38 +80,11 @@ public class ConfigConstants {
}
public static void setMediaValue(String[] Media) {
ConfigConstants.medias = Media;
}
public static String[] getConvertMedias() {
return convertMedias;
}
@Value("${convertMedias:avi,mov,wmv,mkv,3gp,rm}")
public void setConvertMedias(String convertMedia) {
String[] mediaArr = convertMedia.split(",");
setConvertMediaValue(mediaArr);
}
public static void setConvertMediaValue(String[] ConvertMedia) {
ConfigConstants.convertMedias = ConvertMedia;
}
public static String getMediaConvertDisable() {
return mediaConvertDisable;
}
@Value("${media.convert.disable:true}")
public void setMediaConvertDisable(String mediaConvertDisable) {
setMediaConvertDisableValue(mediaConvertDisable);
}
public static void setMediaConvertDisableValue(String mediaConvertDisable) {
ConfigConstants.mediaConvertDisable = mediaConvertDisable;
MEDIA = Media;
}
public static String getOfficePreviewType() {
return officePreviewType;
return OFFICE_PREVIEW_TYPE;
}
@Value("${office.preview.type:image}")
@@ -128,11 +93,11 @@ public class ConfigConstants {
}
public static void setOfficePreviewTypeValue(String officePreviewType) {
ConfigConstants.officePreviewType = officePreviewType;
OFFICE_PREVIEW_TYPE = officePreviewType;
}
public static String getFtpUsername() {
return ftpUsername;
return FTP_USERNAME;
}
@Value("${ftp.username:}")
@@ -141,11 +106,11 @@ public class ConfigConstants {
}
public static void setFtpUsernameValue(String ftpUsername) {
ConfigConstants.ftpUsername = ftpUsername;
FTP_USERNAME = ftpUsername;
}
public static String getFtpPassword() {
return ftpPassword;
return FTP_PASSWORD;
}
@Value("${ftp.password:}")
@@ -154,11 +119,11 @@ public class ConfigConstants {
}
public static void setFtpPasswordValue(String ftpPassword) {
ConfigConstants.ftpPassword = ftpPassword;
FTP_PASSWORD = ftpPassword;
}
public static String getFtpControlEncoding() {
return ftpControlEncoding;
return FTP_CONTROL_ENCODING;
}
@Value("${ftp.control.encoding:UTF-8}")
@@ -167,11 +132,11 @@ public class ConfigConstants {
}
public static void setFtpControlEncodingValue(String ftpControlEncoding) {
ConfigConstants.ftpControlEncoding = ftpControlEncoding;
FTP_CONTROL_ENCODING = ftpControlEncoding;
}
public static String getBaseUrl() {
return baseUrl;
return BASE_URL;
}
@Value("${base.url:default}")
@@ -180,11 +145,11 @@ public class ConfigConstants {
}
public static void setBaseUrlValue(String baseUrl) {
ConfigConstants.baseUrl = baseUrl;
BASE_URL = baseUrl;
}
public static String getFileDir() {
return fileDir;
return FILE_DIR;
}
@Value("${file.dir:default}")
@@ -197,7 +162,7 @@ public class ConfigConstants {
if (!fileDir.endsWith(File.separator)) {
fileDir = fileDir + File.separator;
}
ConfigConstants.fileDir = fileDir;
FILE_DIR = fileDir;
}
}
@@ -219,15 +184,15 @@ public class ConfigConstants {
}
public static Set<String> getTrustHostSet() {
return trustHostSet;
return TRUST_HOST_SET;
}
private static void setTrustHostSet(CopyOnWriteArraySet<String> trustHostSet) {
ConfigConstants.trustHostSet = trustHostSet;
ConfigConstants.TRUST_HOST_SET = trustHostSet;
}
public static String getPdfDownloadDisable() {
return pdfDownloadDisable;
return PDF_DOWNLOAD_DISABLE;
}
@@ -236,18 +201,17 @@ public class ConfigConstants {
setPdfDownloadDisableValue(pdfDownloadDisable);
}
public static void setPdfDownloadDisableValue(String pdfDownloadDisable) {
ConfigConstants.pdfDownloadDisable = pdfDownloadDisable;
PDF_DOWNLOAD_DISABLE = pdfDownloadDisable;
}
public static String getOfficePreviewSwitchDisabled() {
return officePreviewSwitchDisabled;
return OFFICE_PREVIEW_SWITCH_DISABLED;
}
@Value("${office.preview.switch.disabled:true}")
public void setOfficePreviewSwitchDisabled(String officePreviewSwitchDisabled) {
ConfigConstants.officePreviewSwitchDisabled = officePreviewSwitchDisabled;
OFFICE_PREVIEW_SWITCH_DISABLED = officePreviewSwitchDisabled;
}
public static void setOfficePreviewSwitchDisabledValue(String officePreviewSwitchDisabled) {
ConfigConstants.officePreviewSwitchDisabled = officePreviewSwitchDisabled;
OFFICE_PREVIEW_SWITCH_DISABLED = officePreviewSwitchDisabled;
}
}

View File

@@ -0,0 +1,19 @@
package cn.keking.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author chenjh
* @since 2020/5/18 13:41
*/
@Configuration
public class RFCConfig {
@Bean
public Boolean setRequestTargetAllow() {
// RFC 7230RFC 3986规范不允许url相关特殊字符手动指定Tomcat url允许特殊符号 如{}做入参其他符号按需添加。见tomcat的HttpParser源码。
System.setProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow", "|{}");
return true;
}
}

View File

@@ -1,7 +1,6 @@
package cn.keking.config;
import io.netty.channel.nio.NioEventLoopGroup;
import org.apache.commons.lang3.StringUtils;
import org.redisson.client.codec.Codec;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
@@ -64,7 +63,7 @@ public class RedissonConfig {
.setConnectTimeout(connectTimeout)
.setIdleConnectionTimeout(idleConnectionTimeout)
.setPingTimeout(pingTimeout)
.setPassword(StringUtils.trimToNull(password));
.setPassword(password);
Codec codec=(Codec) ClassUtils.forName(getCodec(), ClassUtils.getDefaultClassLoader()).newInstance();
config.setCodec(codec);
config.setThreads(thread);

View File

@@ -4,14 +4,14 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @author: chenjh
* @since: 2019/4/16 20:04
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
public class WebConfig extends WebMvcConfigurerAdapter {
private final static Logger LOGGER = LoggerFactory.getLogger(WebConfig.class);
/**
@@ -22,5 +22,6 @@ public class WebConfig implements WebMvcConfigurer {
String filePath = ConfigConstants.getFileDir();
LOGGER.info("Add resource locations: {}", filePath);
registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/","classpath:/resources/","classpath:/static/","classpath:/public/","file:" + filePath);
super.addResourceHandlers(registry);
}
}

View File

@@ -1,4 +1,4 @@
package cn.keking.service;
package cn.keking.extend;
import org.artofsolving.jodconverter.document.DocumentFamily;
import org.artofsolving.jodconverter.document.DocumentFormat;
@@ -13,9 +13,9 @@ import java.util.Map;
* @author yudian-it
* @date 2017/12/5
*/
public class OfficePluginExtendFormatRegistry extends SimpleDocumentFormatRegistry {
public class ControlDocumentFormatRegistry extends SimpleDocumentFormatRegistry {
public OfficePluginExtendFormatRegistry() {
public ControlDocumentFormatRegistry() {
DocumentFormat pdf = new DocumentFormat("Portable Document Format", "pdf", "application/pdf");
pdf.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "writer_pdf_Export"));
pdf.setStoreProperties(DocumentFamily.SPREADSHEET, Collections.singletonMap("FilterName", "calc_pdf_Export"));

View File

@@ -10,84 +10,48 @@ import java.util.Map;
* Content :文件类型文本office压缩包等等
*/
public enum FileType {
picture("pictureFilePreviewImpl"),
compress("compressFilePreviewImpl"),
office("officeFilePreviewImpl"),
simText("simTextFilePreviewImpl"),
pdf("pdfFilePreviewImpl"),
other("otherFilePreviewImpl"),
media("mediaFilePreviewImpl"),
markdown("markdownFilePreviewImpl"),
xml("xmlFilePreviewImpl"),
cad("cadFilePreviewImpl");
PICTURE("pictureFilePreviewImpl"),
COMPRESS("compressFilePreviewImpl"),
OFFICE("officeFilePreviewImpl"),
SIMTEXT("simTextFilePreviewImpl"),
PDF("pdfFilePreviewImpl"),
CODE("codeFilePreviewImpl"),
OTHER("otherFilePreviewImpl"),
MEDIA("mediaFilePreviewImpl"),
MARKDOWN("markdownFilePreviewImpl"),
XML("xmlFilePreviewImpl"),
FLV("flvFilePreviewImpl"),
CAD("cadFilePreviewImpl"),
TIFF("tiffFilePreviewImpl");
private static final String[] OFFICE_TYPES = {"docx", "wps", "doc", "xls", "xlsx", "ppt", "pptx"};
private static final String[] PICTURE_TYPES = {"jpg", "jpeg", "png", "gif", "bmp", "ico", "raw"};
private static final String[] OFFICE_TYPES = {"docx", "doc", "xls", "xlsx", "ppt", "pptx"};
private static final String[] PICTURE_TYPES = {"jpg", "jpeg", "png", "gif", "bmp", "ico", "RAW"};
private static final String[] ARCHIVE_TYPES = {"rar", "zip", "jar", "7-zip", "tar", "gzip", "7z"};
private static final String[] TIFF_TYPES = {"tif", "tiff"};
private static final String[] SSIM_TEXT_TYPES = ConfigConstants.getSimText();
private static final String[] CODES = {"java", "c", "php", "go", "python", "py", "js", "html", "ftl", "css", "lua", "sh", "rb", "yml", "json", "h", "cpp", "cs", "aspx", "jsp"};
private static final String[] SIMTEXT_TYPES = ConfigConstants.getSimText();
private static final String[] MEDIA_TYPES = ConfigConstants.getMedia();
public static final String[] MEDIA_TYPES_CONVERT = ConfigConstants.getConvertMedias();
private static final Map<String, FileType> FILE_TYPE_MAPPER = new HashMap<>();
static {
for (String office : OFFICE_TYPES) {
FILE_TYPE_MAPPER.put(office, FileType.OFFICE);
FILE_TYPE_MAPPER.put(office, FileType.office);
}
for (String picture : PICTURE_TYPES) {
FILE_TYPE_MAPPER.put(picture, FileType.PICTURE);
FILE_TYPE_MAPPER.put(picture, FileType.picture);
}
for (String archive : ARCHIVE_TYPES) {
FILE_TYPE_MAPPER.put(archive, FileType.COMPRESS);
FILE_TYPE_MAPPER.put(archive, FileType.compress);
}
for (String text : SSIM_TEXT_TYPES) {
FILE_TYPE_MAPPER.put(text, FileType.SIMTEXT);
for (String text : SIMTEXT_TYPES) {
FILE_TYPE_MAPPER.put(text, FileType.simText);
}
for (String media : MEDIA_TYPES) {
FILE_TYPE_MAPPER.put(media, FileType.MEDIA);
FILE_TYPE_MAPPER.put(media, FileType.media);
}
for (String media : MEDIA_TYPES_CONVERT) {
FILE_TYPE_MAPPER.put(media, FileType.MEDIA);
}
for (String tif : TIFF_TYPES) {
FILE_TYPE_MAPPER.put(tif, FileType.TIFF);
}
for (String code : CODES) {
FILE_TYPE_MAPPER.put(code, FileType.CODE);
}
FILE_TYPE_MAPPER.put("md", FileType.MARKDOWN);
FILE_TYPE_MAPPER.put("xml", FileType.XML);
FILE_TYPE_MAPPER.put("pdf", FileType.PDF);
FILE_TYPE_MAPPER.put("dwg", FileType.CAD);
FILE_TYPE_MAPPER.put("flv", FileType.FLV);
FILE_TYPE_MAPPER.put("md", FileType.markdown);
FILE_TYPE_MAPPER.put("xml", FileType.xml);
FILE_TYPE_MAPPER.put("pdf", FileType.pdf);
FILE_TYPE_MAPPER.put("dwg", FileType.cad);
}
private static FileType to(String fileType) {
return FILE_TYPE_MAPPER.getOrDefault(fileType, OTHER);
}
/**
* 查看文件类型(防止参数中存在.点号或者其他特殊字符,所以先抽取文件名,然后再获取文件类型)
*
* @param url url
* @return 文件类型
*/
public static FileType typeFromUrl(String url) {
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return typeFromFileName(fileName);
}
public static FileType typeFromFileName(String fileName) {
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
String lowerCaseFileType = fileType.toLowerCase();
return FileType.to(lowerCaseFileType);
public static FileType to(String fileType){
return FILE_TYPE_MAPPER.getOrDefault(fileType,other);
}
private final String instanceName;

View File

@@ -4,18 +4,11 @@ import java.io.Serializable;
/**
* 接口返回值结构
*
* @author yudian-it
* @date 2017/11/17
*/
public class ReturnResponse<T> implements Serializable {
public class ReturnResponse<T> implements Serializable{
private static final long serialVersionUID = 313975329998789878L;
public static final int SUCCESS_CODE = 0;
public static final int FAILURE_CODE = 1;
public static final String SUCCESS_MSG = "SUCCESS";
public static final String FAILURE_MSG = "FAILURE";
/**
* 返回状态
* 0. 成功
@@ -38,30 +31,6 @@ public class ReturnResponse<T> implements Serializable {
this.content = content;
}
public static ReturnResponse<Object> failure(String errMsg) {
return new ReturnResponse<>(FAILURE_CODE, errMsg, null);
}
public static ReturnResponse<Object> failure() {
return failure(FAILURE_MSG);
}
public static ReturnResponse<Object> success(){
return success(null);
}
public static ReturnResponse<Object> success(Object content) {
return new ReturnResponse<>(SUCCESS_CODE, SUCCESS_MSG, content);
}
public boolean isSuccess(){
return SUCCESS_CODE == code;
}
public boolean isFailure(){
return !isSuccess();
}
public int getCode() {
return code;
}

View File

@@ -2,7 +2,7 @@ package cn.keking.service;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileType;
import cn.keking.utils.KkFileUtils;
import cn.keking.utils.FileUtils;
import cn.keking.web.filter.BaseUrlFilter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -48,7 +48,7 @@ public class CompressFileReader {
String baseUrl = BaseUrlFilter.getBaseUrl();
String archiveFileName = fileHandlerService.getFileNameFromPath(filePath);
try {
ZipFile zipFile = new ZipFile(filePath, KkFileUtils.getFileEncode(filePath));
ZipFile zipFile = new ZipFile(filePath, FileUtils.getFileEncode(filePath));
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
// 排序
entries = sortZipEntries(entries);
@@ -67,8 +67,8 @@ public class CompressFileReader {
}
String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName);
parentName = (level - 1) + "_" + parentName;
FileType type = FileType.typeFromUrl(childName);
if (type.equals(FileType.PICTURE)) {//添加图片文件到图片列表
FileType type = fileHandlerService.typeFromUrl(childName);
if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl + childName);
}
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey);
@@ -120,8 +120,8 @@ public class CompressFileReader {
headersToBeExtracted.add(Collections.singletonMap(childName, header));
}
String parentName = getLast2FileName(fullName, "\\", archiveFileName);
FileType type = FileType.typeFromUrl(childName);
if (type.equals(FileType.PICTURE)) {//添加图片文件到图片列表
FileType type = fileHandlerService.typeFromUrl(childName);
if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl + childName);
}
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey);
@@ -163,8 +163,8 @@ public class CompressFileReader {
}
String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName);
parentName = (level - 1) + "_" + parentName;
FileType type = FileType.typeFromUrl(childName);
if (type.equals(FileType.PICTURE)) {//添加图片文件到图片列表
FileType type = fileHandlerService.typeFromUrl(childName);
if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl + childName);
}
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey);
@@ -382,7 +382,7 @@ public class CompressFileReader {
} catch (IOException e) {
e.printStackTrace();
}
KkFileUtils.deleteFileByPath(filePath);
FileUtils.deleteFileByPath(filePath);
}
private void extractZipFile(String childName, InputStream zipFile) {
@@ -439,7 +439,7 @@ public class CompressFileReader {
} catch (IOException e) {
e.printStackTrace();
}
KkFileUtils.deleteFileByPath(filePath);
FileUtils.deleteFileByPath(filePath);
}
}
@@ -468,7 +468,7 @@ public class CompressFileReader {
} catch (IOException e) {
e.printStackTrace();
}
KkFileUtils.deleteFileByPath(filePath);
FileUtils.deleteFileByPath(filePath);
}
private void extractRarFile(String childName, FileHeader header, Archive archive) {

View File

@@ -1,5 +1,7 @@
package cn.keking.config;
package cn.keking.service;
import cn.keking.config.ConfigConstants;
import cn.keking.config.WatermarkConfigConstants;
import org.artofsolving.jodconverter.office.OfficeUtils;
import org.artofsolving.jodconverter.util.ConfigUtils;
import org.slf4j.Logger;
@@ -11,7 +13,6 @@ import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
/**
* @auther: chenjh
@@ -57,7 +58,7 @@ public class ConfigRefreshComponent {
text = properties.getProperty("simText", ConfigConstants.DEFAULT_TXT_TYPE);
media = properties.getProperty("media", ConfigConstants.DEFAULT_MEDIA_TYPE);
officePreviewType = properties.getProperty("office.preview.type", ConfigConstants.DEFAULT_OFFICE_PREVIEW_TYPE);
officePreviewSwitchDisabled = properties.getProperty("office.preview.switch.disabled", ConfigConstants.DEFAULT_OFFICE_PREVIEW_SWITCH_DISABLED);
officePreviewSwitchDisabled = properties.getProperty("office.preview.switch.disabled", ConfigConstants.DEFAULT_OFFICE_PREVIEW_TYPE);
ftpUsername = properties.getProperty("ftp.username", ConfigConstants.DEFAULT_FTP_USERNAME);
ftpPassword = properties.getProperty("ftp.password", ConfigConstants.DEFAULT_FTP_PASSWORD);
ftpControlEncoding = properties.getProperty("ftp.control.encoding", ConfigConstants.DEFAULT_FTP_CONTROL_ENCODING);
@@ -80,7 +81,7 @@ public class ConfigRefreshComponent {
setWatermarkConfig(properties);
bufferedReader.close();
fileReader.close();
TimeUnit.SECONDS.sleep(1);
Thread.sleep(1000L);
}
} catch (IOException | InterruptedException e) {
LOGGER.error("读取配置文件异常", e);

View File

@@ -10,7 +10,6 @@ import org.springframework.ui.ExtendedModelMap;
import javax.annotation.PostConstruct;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* Created by kl on 2018/1/19.
@@ -62,7 +61,7 @@ public class FileConvertQueueTask {
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(url,null);
FileType fileType = fileAttribute.getType();
logger.info("正在处理预览转换任务url{},预览类型:{}", url, fileType);
if(fileType.equals(FileType.COMPRESS) || fileType.equals(FileType.OFFICE) || fileType.equals(FileType.CAD)) {
if(fileType.equals(FileType.compress) || fileType.equals(FileType.office) || fileType.equals(FileType.cad)) {
FilePreview filePreview = previewFactory.get(fileAttribute);
filePreview.filePreviewHandle(url, new ExtendedModelMap(), fileAttribute);
} else {
@@ -71,7 +70,7 @@ public class FileConvertQueueTask {
}
} catch (Exception e) {
try {
TimeUnit.SECONDS.sleep(10);
Thread.sleep(1000*10);
} catch (Exception ex){
ex.printStackTrace();
}

View File

@@ -4,28 +4,13 @@ import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.service.cache.CacheService;
import cn.keking.utils.KkFileUtils;
import cn.keking.utils.WebUtils;
import com.aspose.cad.Color;
import com.aspose.cad.fileformats.cad.CadDrawTypeMode;
import com.aspose.cad.imageoptions.CadRasterizationOptions;
import com.aspose.cad.imageoptions.PdfOptions;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.tools.imageio.ImageIOUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -36,15 +21,10 @@ import java.util.Map;
@Component
public class FileHandlerService {
private final Logger logger = LoggerFactory.getLogger(FileHandlerService.class);
private static final String DEFAULT_CONVERTER_CHARSET = System.getProperty("sun.jnu.encoding");
private final String fileDir = ConfigConstants.getFileDir();
private final CacheService cacheService;
@Value("${server.tomcat.uri-encoding:UTF-8}")
private String uriEncoding;
public FileHandlerService(CacheService cacheService) {
this.cacheService = cacheService;
}
@@ -71,6 +51,35 @@ public class FileHandlerService {
return cacheService.getPdfImageCache(key);
}
/**
* 查看文件类型(防止参数中存在.点号或者其他特殊字符,所以先抽取文件名,然后再获取文件类型)
*
* @param url url
* @return 文件类型
*/
public FileType typeFromUrl(String url) {
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return this.typeFromFileName(fileName);
}
private FileType typeFromFileName(String fileName) {
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
return FileType.to(fileType);
}
/**
* 从url中剥离出文件名
*
* @param url 格式如http://www.com.cn/20171113164107_月度绩效表模板(新).xls?UCloudPublicKey=ucloudtangshd@weifenf.com14355492830001993909323&Expires=&Signature=I D1NOFtAJSPT16E6imv6JWuq0k=
* @return 文件名
*/
public String getFileNameFromURL(String url) {
// 因为url的参数中可能会存在/的情况所以直接url.lastIndexOf("/")会有问题
// 所以先从处将url截断然后运用url.lastIndexOf("/")获取文件名
String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
return noQueryUrl.substring(noQueryUrl.lastIndexOf("/") + 1);
}
/**
* 从路径中获取文件负
@@ -165,88 +174,19 @@ public class FileHandlerService {
}
/**
* pdf文件转换成jpg图片集
* @param pdfFilePath pdf文件路径
* @param pdfName pdf文件名称
* @param baseUrl 基础访问地址
* @return 图片访问集合
* 获取文件后缀
*
* @param url url
* @return 文件后缀
*/
public List<String> pdf2jpg(String pdfFilePath, String pdfName, String baseUrl) {
List<String> imageUrls = new ArrayList<>();
Integer imageCount = this.getConvertedPdfImage(pdfFilePath);
String imageFileSuffix = ".jpg";
String pdfFolder = pdfName.substring(0, pdfName.length() - 4);
String urlPrefix;
try {
urlPrefix = baseUrl + URLEncoder.encode(pdfFolder, uriEncoding).replaceAll("\\+", "%20");
} catch (UnsupportedEncodingException e) {
logger.error("UnsupportedEncodingException", e);
urlPrefix = baseUrl + pdfFolder;
}
if (imageCount != null && imageCount > 0) {
for (int i = 0; i < imageCount; i++) {
imageUrls.add(urlPrefix + "/" + i + imageFileSuffix);
}
return imageUrls;
}
try {
File pdfFile = new File(pdfFilePath);
PDDocument doc = PDDocument.load(pdfFile);
int pageCount = doc.getNumberOfPages();
PDFRenderer pdfRenderer = new PDFRenderer(doc);
int index = pdfFilePath.lastIndexOf(".");
String folder = pdfFilePath.substring(0, index);
File path = new File(folder);
if (!path.exists() && !path.mkdirs()) {
logger.error("创建转换文件【{}】目录失败,请检查目录权限!", folder);
}
String imageFilePath;
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
imageFilePath = folder + File.separator + pageIndex + imageFileSuffix;
BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 105, ImageType.RGB);
ImageIOUtil.writeImage(image, imageFilePath, 105);
imageUrls.add(urlPrefix + "/" + pageIndex + imageFileSuffix);
}
doc.close();
this.addConvertedPdfImage(pdfFilePath, pageCount);
} catch (IOException e) {
logger.error("Convert pdf to jpg exception, pdfFilePath{}", pdfFilePath, e);
}
return imageUrls;
private String suffixFromUrl(String url) {
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return suffixFromFileName(fileName);
}
/**
* cad文件转pdf
* @param inputFilePath cad文件路径
* @param outputFilePath pdf输出文件路径
* @return 转换是否成功
*/
public boolean cadToPdf(String inputFilePath, String outputFilePath) {
com.aspose.cad.Image cadImage = com.aspose.cad.Image.load(inputFilePath);
CadRasterizationOptions cadRasterizationOptions = new CadRasterizationOptions();
cadRasterizationOptions.setLayouts(new String[]{"Model"});
cadRasterizationOptions.setNoScaling(true);
cadRasterizationOptions.setBackgroundColor(Color.getWhite());
cadRasterizationOptions.setPageWidth(cadImage.getWidth());
cadRasterizationOptions.setPageHeight(cadImage.getHeight());
cadRasterizationOptions.setPdfProductLocation("center");
cadRasterizationOptions.setAutomaticLayoutsScaling(true);
cadRasterizationOptions.setDrawType(CadDrawTypeMode.UseObjectColor);
PdfOptions pdfOptions = new PdfOptions();
pdfOptions.setVectorRasterizationOptions(cadRasterizationOptions);
File outputFile = new File(outputFilePath);
OutputStream stream;
try {
stream = new FileOutputStream(outputFile);
cadImage.save(stream, pdfOptions);
cadImage.close();
return true;
} catch (FileNotFoundException e) {
logger.error("PDFFileNotFoundExceptioninputFilePath{}", inputFilePath, e);
return false;
}
private String suffixFromFileName(String fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
/**
@@ -263,12 +203,12 @@ public class FileHandlerService {
String fullFileName = WebUtils.getUrlParameterReg(url, "fullfilename");
if (StringUtils.hasText(fullFileName)) {
fileName = fullFileName;
type = FileType.typeFromFileName(fullFileName);
suffix = KkFileUtils.suffixFromFileName(fullFileName);
type = this.typeFromFileName(fullFileName);
suffix = suffixFromFileName(fullFileName);
} else {
fileName = WebUtils.getFileNameFromURL(url);
type = FileType.typeFromUrl(url);
suffix = WebUtils.suffixFromUrl(url);
fileName = getFileNameFromURL(url);
type = typeFromUrl(url);
suffix = suffixFromUrl(url);
}
attribute.setType(type);
attribute.setName(fileName);
@@ -276,7 +216,7 @@ public class FileHandlerService {
attribute.setUrl(url);
if (req != null) {
String officePreviewType = req.getParameter("officePreviewType");
String fileKey = WebUtils.getUrlParameterReg(url,"fileKey");
String fileKey = req.getParameter("fileKey");
if (StringUtils.hasText(officePreviewType)) {
attribute.setOfficePreviewType(officePreviewType);
}
@@ -286,27 +226,4 @@ public class FileHandlerService {
}
return attribute;
}
/**
* @return 已转换过的视频文件集合(缓存)
*/
public Map<String, String> listConvertedMedias() {
return cacheService.getMediaConvertCache();
}
/**
* 添加转换后的视频文件缓存
* @param fileName
* @param value
*/
public void addConvertedMedias(String fileName, String value) {
cacheService.putMediaConvertCache(fileName, value);
}
/**
* @return 已转换视频文件缓存,根据文件名获取
*/
public String getConvertedMedias(String key) {
return cacheService.getMediaConvertCache(key);
}
}

View File

@@ -8,20 +8,5 @@ import org.springframework.ui.Model;
* Content :
*/
public interface FilePreview {
String FLV_FILE_PREVIEW_PAGE = "flv";
String PDF_FILE_PREVIEW_PAGE = "pdf";
String COMPRESS_FILE_PREVIEW_PAGE = "compress";
String MEDIA_FILE_PREVIEW_PAGE = "media";
String PICTURE_FILE_PREVIEW_PAGE = "picture";
String TIFF_FILE_PREVIEW_PAGE = "tiff";
String OFFICE_PICTURE_FILE_PREVIEW_PAGE = "officePicture";
String TXT_FILE_PREVIEW_PAGE = "txt";
String CODE_FILE_PREVIEW_PAGE = "code";
String EXEL_FILE_PREVIEW_PAGE = "html";
String XML_FILE_PREVIEW_PAGE = "xml";
String MARKDOWN_FILE_PREVIEW_PAGE = "markdown";
String NOT_SUPPORTED_FILE_PAGE = "fileNotSupported";
String filePreviewHandle(String url, Model model, FileAttribute fileAttribute);
}

View File

@@ -1,6 +1,7 @@
package cn.keking.service;
import com.sun.star.document.UpdateDocMode;
import cn.keking.extend.ControlDocumentFormatRegistry;
import org.apache.commons.lang3.StringUtils;
import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.artofsolving.jodconverter.office.DefaultOfficeManagerConfiguration;
@@ -8,8 +9,6 @@ import org.artofsolving.jodconverter.office.OfficeManager;
import org.artofsolving.jodconverter.office.OfficeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.convert.DurationStyle;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@@ -21,11 +20,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -43,12 +38,6 @@ public class OfficePluginManager {
private OfficeManager officeManager;
@Value("${office.plugin.server.ports:2001,2002}")
private String serverPorts;
@Value("${office.plugin.task.timeout:5m}")
private String timeOut;
@PostConstruct
public void initOfficeManager() {
new Thread(this::startOfficeManager).start();
@@ -69,14 +58,9 @@ public class OfficePluginManager {
try {
DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration();
configuration.setOfficeHome(officeHome);
String []portsString = serverPorts.split(",");
int[] ports = Arrays.stream(portsString).mapToInt(Integer::parseInt).toArray();
configuration.setPortNumbers(ports);
long timeout = DurationStyle.detectAndParse(timeOut).toMillis();
configuration.setPortNumber(8100);
// 设置任务执行超时为5分钟
configuration.setTaskExecutionTimeout(timeout);
configuration.setTaskExecutionTimeout(1000 * 60 * 5L);
// 设置任务队列超时为24小时
//configuration.setTaskQueueTimeout(1000 * 60 * 60 * 24L);
officeManager = configuration.buildOfficeManager();
@@ -88,7 +72,7 @@ public class OfficePluginManager {
}
public OfficeDocumentConverter getDocumentConverter() {
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager, new OfficePluginExtendFormatRegistry());
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager, new ControlDocumentFormatRegistry());
converter.setDefaultLoadProperties(getLoadProperties());
return converter;
}

View File

@@ -1,8 +1,6 @@
package cn.keking.service;
import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.File;
@@ -12,8 +10,6 @@ import java.io.File;
*/
@Component
public class OfficeToPdfService {
private final static Logger logger = LoggerFactory.getLogger(OfficeToPdfService.class);
private final OfficePluginManager officePluginManager;
public OfficeToPdfService(OfficePluginManager officePluginManager) {
@@ -25,11 +21,12 @@ public class OfficeToPdfService {
}
public static void converterFile(File inputFile, String outputFilePath_end, OfficeDocumentConverter converter) {
public static void converterFile(File inputFile, String outputFilePath_end,
OfficeDocumentConverter converter) {
File outputFile = new File(outputFilePath_end);
// 假如目标路径不存在,则新建该路径
if (!outputFile.getParentFile().exists() && !outputFile.getParentFile().mkdirs()) {
logger.error("创建目录【{}】失败,请检查目录权限!",outputFilePath_end);
if (!outputFile.getParentFile().exists()) {
outputFile.getParentFile().mkdirs();
}
converter.convert(inputFile, outputFile);
}

View File

@@ -12,18 +12,15 @@ public interface CacheService {
String FILE_PREVIEW_PDF_KEY = "converted-preview-pdf-file";
String FILE_PREVIEW_IMGS_KEY = "converted-preview-imgs-file";//压缩包内图片文件集合
String FILE_PREVIEW_PDF_IMGS_KEY = "converted-preview-pdfimgs-file";
String FILE_PREVIEW_MEDIA_CONVERT_KEY = "converted-preview-media-file";
String TASK_QUEUE_NAME = "convert-task";
Integer DEFAULT_PDF_CAPACITY = 500000;
Integer DEFAULT_IMG_CAPACITY = 500000;
Integer DEFAULT_PDFIMG_CAPACITY = 500000;
Integer DEFAULT_MEDIACONVERT_CAPACITY = 500000;
void initPDFCachePool(Integer capacity);
void initIMGCachePool(Integer capacity);
void initPdfImagesCachePool(Integer capacity);
void initMediaConvertCachePool(Integer capacity);
void putPDFCache(String key, String value);
void putImgCache(String key, List<String> value);
Map<String, String> getPDFCache();
@@ -32,9 +29,6 @@ public interface CacheService {
List<String> getImgCache(String key);
Integer getPdfImageCache(String key);
void putPdfImageCache(String pdfFilePath, int num);
Map<String, String> getMediaConvertCache();
void putMediaConvertCache(String key, String value);
String getMediaConvertCache(String key);
void cleanCache();
void addQueueTask(String url);
String takeQueueTask() throws InterruptedException;

View File

@@ -26,7 +26,6 @@ public class CacheServiceJDKImpl implements CacheService {
private Map<String, String> pdfCache;
private Map<String, List<String>> imgCache;
private Map<String, Integer> pdfImagesCache;
private Map<String, String> mediaConvertCache;
private static final int QUEUE_SIZE = 500000;
private final BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(QUEUE_SIZE);
@@ -35,7 +34,6 @@ public class CacheServiceJDKImpl implements CacheService {
initPDFCachePool(CacheService.DEFAULT_PDF_CAPACITY);
initIMGCachePool(CacheService.DEFAULT_IMG_CAPACITY);
initPdfImagesCachePool(CacheService.DEFAULT_PDFIMG_CAPACITY);
initMediaConvertCachePool(CacheService.DEFAULT_MEDIACONVERT_CAPACITY);
}
@Override
@@ -81,21 +79,6 @@ public class CacheServiceJDKImpl implements CacheService {
pdfImagesCache.put(pdfFilePath, num);
}
@Override
public Map<String, String> getMediaConvertCache() {
return mediaConvertCache;
}
@Override
public void putMediaConvertCache(String key, String value) {
mediaConvertCache.put(key, value);
}
@Override
public String getMediaConvertCache(String key) {
return mediaConvertCache.get(key);
}
@Override
public void cleanCache() {
initPDFCachePool(CacheService.DEFAULT_PDF_CAPACITY);
@@ -133,12 +116,4 @@ public class CacheServiceJDKImpl implements CacheService {
.maximumWeightedCapacity(capacity).weigher(Weighers.singleton())
.build();
}
@Override
public void initMediaConvertCachePool(Integer capacity) {
mediaConvertCache = new ConcurrentLinkedHashMap.Builder<String, String>()
.maximumWeightedCapacity(capacity).weigher(Weighers.singleton())
.build();
}
}

View File

@@ -28,14 +28,17 @@ public class CacheServiceRedisImpl implements CacheService {
}
@Override
public void initPDFCachePool(Integer capacity) { }
@Override
public void initIMGCachePool(Integer capacity) { }
@Override
public void initPdfImagesCachePool(Integer capacity) { }
public void initPDFCachePool(Integer capacity) {
}
@Override
public void initMediaConvertCachePool(Integer capacity) {
public void initIMGCachePool(Integer capacity) {
}
@Override
public void initPdfImagesCachePool(Integer capacity) {
}
@@ -85,23 +88,6 @@ public class CacheServiceRedisImpl implements CacheService {
convertedList.fastPut(pdfFilePath, num);
}
@Override
public Map<String, String> getMediaConvertCache() {
return redissonClient.getMapCache(FILE_PREVIEW_MEDIA_CONVERT_KEY);
}
@Override
public void putMediaConvertCache(String key, String value) {
RMapCache<String, String> convertedList = redissonClient.getMapCache(FILE_PREVIEW_MEDIA_CONVERT_KEY);
convertedList.fastPut(key, value);
}
@Override
public String getMediaConvertCache(String key) {
RMapCache<String, String> convertedList = redissonClient.getMapCache(FILE_PREVIEW_MEDIA_CONVERT_KEY);
return convertedList.get(key);
}
@Override
public void cleanCache() {
cleanPdfCache();

View File

@@ -31,8 +31,11 @@ public class CacheServiceRocksDBImpl implements CacheService {
}
private static final String DB_PATH = ConfigUtils.getHomePath() + File.separator + "cache";
private static final int QUEUE_SIZE = 500000;
private static final Logger LOGGER = LoggerFactory.getLogger(CacheServiceRocksDBImpl.class);
private final BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(QUEUE_SIZE);
private RocksDB db;
@@ -73,11 +76,6 @@ public class CacheServiceRocksDBImpl implements CacheService {
}
@Override
public void initMediaConvertCachePool(Integer capacity) {
}
@Override
public void putPDFCache(String key, String value) {
try {
@@ -176,40 +174,6 @@ public class CacheServiceRocksDBImpl implements CacheService {
}
}
@Override
public Map<String, String> getMediaConvertCache() {
Map<String, String> result = new HashMap<>();
try{
result = (Map<String, String>) toObject(db.get(FILE_PREVIEW_MEDIA_CONVERT_KEY.getBytes()));
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
return result;
}
@Override
public void putMediaConvertCache(String key, String value) {
try {
Map<String, String> mediaConvertCacheItem = getMediaConvertCache();
mediaConvertCacheItem.put(key, value);
db.put(FILE_PREVIEW_MEDIA_CONVERT_KEY.getBytes(), toByteArray(mediaConvertCacheItem));
} catch (RocksDBException | IOException e) {
LOGGER.error("Put into RocksDB Exception" + e);
}
}
@Override
public String getMediaConvertCache(String key) {
String result = "";
try{
Map<String, String> map = (Map<String, String>) toObject(db.get(FILE_PREVIEW_MEDIA_CONVERT_KEY.getBytes()));
result = map.get(key);
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
return result;
}
@Override
public void cleanCache() {
try {

View File

@@ -4,8 +4,10 @@ import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
import cn.keking.utils.CadUtils;
import cn.keking.utils.DownloadUtils;
import cn.keking.service.FileHandlerService;
import cn.keking.utils.PdfUtils;
import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
@@ -20,38 +22,47 @@ import static cn.keking.service.impl.OfficeFilePreviewImpl.getPreviewType;
@Service
public class CadFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final CadUtils cadUtils;
private final PdfUtils pdfUtils;
public CadFilePreviewImpl(FileHandlerService fileHandlerService, CadUtils cadUtils, PdfUtils pdfUtils) {
this.fileHandlerService = fileHandlerService;
this.cadUtils = cadUtils;
this.pdfUtils = pdfUtils;
}
private static final String OFFICE_PREVIEW_TYPE_IMAGE = "image";
private static final String OFFICE_PREVIEW_TYPE_ALL_IMAGES = "allImages";
private static final String FILE_DIR = ConfigConstants.getFileDir();
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
public CadFilePreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
// 预览Type参数传了就取参数的没传取系统默认
String officePreviewType = fileAttribute.getOfficePreviewType() == null ? ConfigConstants.getOfficePreviewType() : fileAttribute.getOfficePreviewType();
String officePreviewType = model.asMap().get("officePreviewType") == null ? ConfigConstants.getOfficePreviewType() : model.asMap().get("officePreviewType").toString();
String baseUrl = BaseUrlFilter.getBaseUrl();
String fileName = fileAttribute.getName();
String suffix=fileAttribute.getSuffix();
String fileName=fileAttribute.getName();
String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + "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());
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
}
filePath = response.getContent();
if (StringUtils.hasText(outFilePath)) {
boolean convertResult = fileHandlerService.cadToPdf(filePath, outFilePath);
boolean convertResult = cadUtils.cadToPdf(filePath, outFilePath);
if (!convertResult) {
return otherFilePreview.notSupportedFile(model, fileAttribute, "cad文件转换异常请联系管理员");
model.addAttribute("fileType", suffix);
model.addAttribute("msg", "cad文件转换异常请联系管理员");
return "fileNotSupported";
}
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
@@ -60,10 +71,10 @@ public class CadFilePreviewImpl implements FilePreview {
}
}
if (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, pdfName, outFilePath, pdfUtils, OFFICE_PREVIEW_TYPE_IMAGE);
}
model.addAttribute("pdfUrl", pdfName);
return PDF_FILE_PREVIEW_PAGE;
return "pdf";
}

View File

@@ -1,26 +0,0 @@
package cn.keking.service.impl;
import cn.keking.model.FileAttribute;
import cn.keking.service.FilePreview;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
/**
* @author kl (http://kailing.pub)
* @since 2021/2/18
*/
@Component
public class CodeFilePreviewImpl implements FilePreview {
private final SimTextFilePreviewImpl filePreviewHandle;
public CodeFilePreviewImpl(SimTextFilePreviewImpl filePreviewHandle) {
this.filePreviewHandle = filePreviewHandle;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
filePreviewHandle.filePreviewHandle(url, model, fileAttribute);
return CODE_FILE_PREVIEW_PAGE;
}
}

View File

@@ -20,12 +20,10 @@ public class CompressFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final CompressFileReader compressFileReader;
private final OtherFilePreviewImpl otherFilePreview;
public CompressFilePreviewImpl(FileHandlerService fileHandlerService, CompressFileReader compressFileReader, OtherFilePreviewImpl otherFilePreview) {
public CompressFilePreviewImpl(FileHandlerService fileHandlerService, CompressFileReader compressFileReader) {
this.fileHandlerService = fileHandlerService;
this.compressFileReader = compressFileReader;
this.otherFilePreview = otherFilePreview;
}
@Override
@@ -36,8 +34,10 @@ public class CompressFilePreviewImpl implements FilePreview {
// 判断文件名是否存在(redis缓存读取)
if (!StringUtils.hasText(fileHandlerService.getConvertedFile(fileName)) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
}
String filePath = response.getContent();
if ("zip".equalsIgnoreCase(suffix) || "jar".equalsIgnoreCase(suffix) || "gzip".equalsIgnoreCase(suffix)) {
@@ -55,9 +55,11 @@ public class CompressFilePreviewImpl implements FilePreview {
}
if (fileTree != null && !"null".equals(fileTree)) {
model.addAttribute("fileTree", fileTree);
return COMPRESS_FILE_PREVIEW_PAGE;
return "compress";
} else {
return otherFilePreview.notSupportedFile(model, fileAttribute, "压缩文件类型不受支持尝试在压缩的时候选择RAR4格式");
model.addAttribute("fileType", suffix);
model.addAttribute("msg", "压缩文件类型不受支持尝试在压缩的时候选择RAR4格式");
return "fileNotSupported";
}
}
}

View File

@@ -1,27 +0,0 @@
package cn.keking.service.impl;
import cn.keking.model.FileAttribute;
import cn.keking.service.FilePreview;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
/**
* @author : kl
* create : 2020-12-27 2:50 下午
* flv文件预览处理实现
**/
@Service
public class FlvFilePreviewImpl implements FilePreview {
private final MediaFilePreviewImpl mediaFilePreview;
public FlvFilePreviewImpl(MediaFilePreviewImpl mediaFilePreview) {
this.mediaFilePreview = mediaFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
mediaFilePreview.filePreviewHandle(url,model,fileAttribute);
return FLV_FILE_PREVIEW_PAGE;
}
}

View File

@@ -5,6 +5,8 @@ import cn.keking.service.FilePreview;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import static com.sun.glass.ui.Clipboard.TEXT_TYPE;
/**
* @author kl (http://kailing.pub)
* @since 2020/12/25
@@ -21,7 +23,7 @@ public class MarkdownFilePreviewImpl implements FilePreview {
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
return MARKDOWN_FILE_PREVIEW_PAGE;
model.addAttribute(TEXT_TYPE,"markdown");
return simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
}
}

View File

@@ -1,22 +1,13 @@
package cn.keking.service.impl;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils;
import cn.keking.service.FileHandlerService;
import cn.keking.web.filter.BaseUrlFilter;
import org.artofsolving.jodconverter.util.ConfigUtils;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import java.io.File;
/**
* @author : kl
* @authorboke : kailing.pub
@@ -27,13 +18,9 @@ import java.io.File;
public class MediaFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
private static Object LOCK=new Object();
public MediaFilePreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) {
public MediaFilePreviewImpl(FileHandlerService fileHandlerService) {
this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
}
@Override
@@ -41,126 +28,23 @@ public class MediaFilePreviewImpl implements FilePreview {
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileAttribute.getName());
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
if (0 != response.getCode()) {
model.addAttribute("fileType", fileAttribute.getSuffix());
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
} else {
url=BaseUrlFilter.getBaseUrl() + fileHandlerService.getRelativePath(response.getContent());
fileAttribute.setUrl(url);
model.addAttribute("mediaUrl", BaseUrlFilter.getBaseUrl() + fileHandlerService.getRelativePath(response.getContent()));
}
}
if(checkNeedConvert(fileAttribute.getSuffix())){
url=convertUrl(fileAttribute);
}else{
//正常media类型
String[] medias = ConfigConstants.getMedia();
for(String media:medias){
if(media.equals(fileAttribute.getSuffix())){
model.addAttribute("mediaUrl", url);
return MEDIA_FILE_PREVIEW_PAGE;
}
}
return otherFilePreview.notSupportedFile(model, fileAttribute, "暂不支持");
} else {
model.addAttribute("mediaUrl", url);
}
model.addAttribute("mediaUrl", url);
return MEDIA_FILE_PREVIEW_PAGE;
String suffix=fileAttribute.getSuffix();
if ("flv".equalsIgnoreCase(suffix)) {
return "flv";
}
return "media";
}
/**
* 检查视频文件处理逻辑
* 返回处理过后的url
* @return url
*/
private String convertUrl(FileAttribute fileAttribute) {
String url = fileAttribute.getUrl();
if(fileHandlerService.listConvertedMedias().containsKey(url)){
url= fileHandlerService.getConvertedMedias(url);
}else{
if(!fileHandlerService.listConvertedMedias().containsKey(url)){
synchronized(LOCK){
if(!fileHandlerService.listConvertedMedias().containsKey(url)){
String convertedUrl=convertToMp4(fileAttribute);
//加入缓存
fileHandlerService.addConvertedMedias(url,convertedUrl);
url=convertedUrl;
}
}
}
}
return url;
}
/**
* 检查视频文件转换是否已开启,以及当前文件是否需要转换
* @return
*/
private boolean checkNeedConvert(String suffix) {
//1.检查开关是否开启
if("false".equals(ConfigConstants.getMediaConvertDisable())){
return false;
}
//2.检查当前文件是否需要转换
String[] mediaTypesConvert = FileType.MEDIA_TYPES_CONVERT;
String type = suffix;
for(String temp : mediaTypesConvert){
if(type.equals(temp)){
return true;
}
}
return false;
}
/**
* 将浏览器不兼容视频格式转换成MP4
* @param fileAttribute
* @return
*/
private static String convertToMp4(FileAttribute fileAttribute) {
//说明:这里做临时处理,取上传文件的目录
String homePath = ConfigUtils.getHomePath();
String filePath = homePath+File.separator+"file"+File.separator+"demo"+File.separator+fileAttribute.getName();
String convertFileName=fileAttribute.getUrl().replace(fileAttribute.getSuffix(),"mp4");
File file=new File(filePath);
FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber(file);
String fileName = null;
Frame captured_frame = null;
FFmpegFrameRecorder recorder = null;
try {
fileName = file.getAbsolutePath().replace(fileAttribute.getSuffix(),"mp4");
File desFile=new File(fileName);
//判断一下防止穿透缓存
if(desFile.exists()){
return fileName;
}
frameGrabber.start();
recorder = new FFmpegFrameRecorder(fileName, frameGrabber.getImageWidth(), frameGrabber.getImageHeight(), frameGrabber.getAudioChannels());
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); //avcodec.AV_CODEC_ID_H264  //AV_CODEC_ID_MPEG4
recorder.setFormat("mp4");
recorder.setFrameRate(frameGrabber.getFrameRate());
//recorder.setSampleFormat(frameGrabber.getSampleFormat()); //
recorder.setSampleRate(frameGrabber.getSampleRate());
recorder.setAudioChannels(frameGrabber.getAudioChannels());
recorder.setFrameRate(frameGrabber.getFrameRate());
recorder.start();
while ((captured_frame = frameGrabber.grabFrame()) != null) {
try {
recorder.setTimestamp(frameGrabber.getTimestamp());
recorder.record(captured_frame);
} catch (Exception e) {
}
}
recorder.stop();
recorder.release();
frameGrabber.stop();
} catch (Exception e) {
e.printStackTrace();
}
//是否删除源文件
//file.delete();
return convertFileName;
}
}

View File

@@ -7,6 +7,7 @@ import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils;
import cn.keking.service.FileHandlerService;
import cn.keking.service.OfficeToPdfService;
import cn.keking.utils.PdfUtils;
import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
@@ -21,27 +22,27 @@ import java.util.List;
@Service
public class OfficeFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final PdfUtils pdfUtils;
private final OfficeToPdfService officeToPdfService;
public OfficeFilePreviewImpl(FileHandlerService fileHandlerService, PdfUtils pdfUtils, OfficeToPdfService officeToPdfService) {
this.fileHandlerService = fileHandlerService;
this.pdfUtils = pdfUtils;
this.officeToPdfService = officeToPdfService;
}
public static final String OFFICE_PREVIEW_TYPE_IMAGE = "image";
public static final String OFFICE_PREVIEW_TYPE_ALL_IMAGES = "allImages";
private static final String FILE_DIR = ConfigConstants.getFileDir();
private final FileHandlerService fileHandlerService;
private final OfficeToPdfService officeToPdfService;
private final OtherFilePreviewImpl otherFilePreview;
public OfficeFilePreviewImpl(FileHandlerService fileHandlerService, OfficeToPdfService officeToPdfService, OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService;
this.officeToPdfService = officeToPdfService;
this.otherFilePreview = otherFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
// 预览Type参数传了就取参数的没传取系统默认
String officePreviewType = fileAttribute.getOfficePreviewType();
String baseUrl = BaseUrlFilter.getBaseUrl();
String suffix = fileAttribute.getSuffix();
String fileName = fileAttribute.getName();
String suffix=fileAttribute.getSuffix();
String fileName=fileAttribute.getName();
boolean isHtml = suffix.equalsIgnoreCase("xls") || suffix.equalsIgnoreCase("xlsx");
String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + (isHtml ? "html" : "pdf");
String outFilePath = FILE_DIR + pdfName;
@@ -49,8 +50,10 @@ public class OfficeFilePreviewImpl implements FilePreview {
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());
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
}
filePath = response.getContent();
if (StringUtils.hasText(outFilePath)) {
@@ -66,23 +69,25 @@ public class OfficeFilePreviewImpl implements FilePreview {
}
}
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, pdfName, outFilePath, pdfUtils, OFFICE_PREVIEW_TYPE_IMAGE);
}
model.addAttribute("pdfUrl", pdfName);
return isHtml ? EXEL_FILE_PREVIEW_PAGE : PDF_FILE_PREVIEW_PAGE;
return isHtml ? "html" : "pdf";
}
static String getPreviewType(Model model, FileAttribute fileAttribute, String officePreviewType, String baseUrl, String pdfName, String outFilePath, FileHandlerService fileHandlerService, String officePreviewTypeImage, OtherFilePreviewImpl otherFilePreview) {
List<String> imageUrls = fileHandlerService.pdf2jpg(outFilePath, pdfName, baseUrl);
static String getPreviewType(Model model, FileAttribute fileAttribute, String officePreviewType, String baseUrl, String pdfName, String outFilePath, PdfUtils pdfUtils, String officePreviewTypeImage) {
List<String> imageUrls = pdfUtils.pdf2jpg(outFilePath, pdfName, baseUrl);
if (imageUrls == null || imageUrls.size() < 1) {
return otherFilePreview.notSupportedFile(model, fileAttribute, "office转图片异常请联系管理员");
model.addAttribute("msg", "office转图片异常请联系管理员");
model.addAttribute("fileType",fileAttribute.getSuffix());
return "fileNotSupported";
}
model.addAttribute("imgurls", imageUrls);
model.addAttribute("currentUrl", imageUrls.get(0));
if (officePreviewTypeImage.equals(officePreviewType)) {
return OFFICE_PICTURE_FILE_PREVIEW_PAGE;
return "officePicture";
} else {
return PICTURE_FILE_PREVIEW_PAGE;
return "picture";
}
}
}

View File

@@ -11,41 +11,10 @@ import org.springframework.ui.Model;
*/
@Service
public class OtherFilePreviewImpl implements FilePreview {
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
return this.notSupportedFile(model, fileAttribute, "系统还不支持该格式文件的在线预览");
model.addAttribute("fileType",fileAttribute.getSuffix());
model.addAttribute("msg", "系统还不支持该格式文件的在线预览");
return "fileNotSupported";
}
/**
* 通用的预览失败,导向到不支持的文件响应页面
*
* @return 页面
*/
public String notSupportedFile(Model model, FileAttribute fileAttribute, String errMsg) {
return this.notSupportedFile(model, fileAttribute.getSuffix(), errMsg);
}
/**
* 通用的预览失败,导向到不支持的文件响应页面
*
* @return 页面
*/
public String notSupportedFile(Model model, String errMsg) {
return this.notSupportedFile(model, "未知", errMsg);
}
/**
* 通用的预览失败,导向到不支持的文件响应页面
*
* @return 页面
*/
public String notSupportedFile(Model model, String fileType, String errMsg) {
model.addAttribute("fileType", fileType);
model.addAttribute("msg", errMsg);
return NOT_SUPPORTED_FILE_PAGE;
}
}

View File

@@ -6,6 +6,7 @@ import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils;
import cn.keking.service.FileHandlerService;
import cn.keking.utils.PdfUtils;
import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
@@ -20,17 +21,18 @@ import java.util.List;
public class PdfFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
private final PdfUtils pdfUtils;
private static final String FILE_DIR = ConfigConstants.getFileDir();
public PdfFilePreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) {
public PdfFilePreviewImpl(FileHandlerService fileHandlerService, PdfUtils pdfUtils) {
this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
this.pdfUtils = pdfUtils;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String fileName = fileAttribute.getName();
String suffix=fileAttribute.getSuffix();
String fileName=fileAttribute.getName();
String officePreviewType = fileAttribute.getOfficePreviewType();
String baseUrl = BaseUrlFilter.getBaseUrl();
String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + "pdf";
@@ -39,8 +41,10 @@ public class PdfFilePreviewImpl implements FilePreview {
//当文件不存在时,就去下载
if (!fileHandlerService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
}
outFilePath = response.getContent();
if (ConfigConstants.isCacheEnabled()) {
@@ -48,24 +52,28 @@ public class PdfFilePreviewImpl implements FilePreview {
fileHandlerService.addConvertedFile(pdfName, fileHandlerService.getRelativePath(outFilePath));
}
}
List<String> imageUrls = fileHandlerService.pdf2jpg(outFilePath, pdfName, baseUrl);
List<String> imageUrls = pdfUtils.pdf2jpg(outFilePath, pdfName, baseUrl);
if (imageUrls == null || imageUrls.size() < 1) {
return otherFilePreview.notSupportedFile(model, fileAttribute, "pdf转图片异常请联系管理员");
model.addAttribute("msg", "pdf转图片异常请联系管理员");
model.addAttribute("fileType",fileAttribute.getSuffix());
return "fileNotSupported";
}
model.addAttribute("imgurls", imageUrls);
model.addAttribute("currentUrl", imageUrls.get(0));
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType)) {
return OFFICE_PICTURE_FILE_PREVIEW_PAGE;
return "officePicture";
} else {
return PICTURE_FILE_PREVIEW_PAGE;
return "picture";
}
} else {
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
if (!fileHandlerService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, pdfName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
}
model.addAttribute("pdfUrl", fileHandlerService.getRelativePath(response.getContent()));
if (ConfigConstants.isCacheEnabled()) {
@@ -79,6 +87,6 @@ public class PdfFilePreviewImpl implements FilePreview {
model.addAttribute("pdfUrl", url);
}
}
return PDF_FILE_PREVIEW_PAGE;
return "pdf";
}
}

View File

@@ -19,11 +19,9 @@ import java.util.List;
public class PictureFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
public PictureFilePreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) {
public PictureFilePreviewImpl(FileHandlerService fileHandlerService) {
this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
}
@Override
@@ -38,19 +36,21 @@ public class PictureFilePreviewImpl implements FilePreview {
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
if (0 != response.getCode()) {
model.addAttribute("fileType", fileAttribute.getSuffix());
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
} else {
String file = fileHandlerService.getRelativePath(response.getContent());
imgUrls.clear();
imgUrls.add(file);
model.addAttribute("imgUrls", imgUrls);
model.addAttribute("imgurls", imgUrls);
model.addAttribute("currentUrl", file);
}
} else {
model.addAttribute("imgUrls", imgUrls);
model.addAttribute("imgurls", imgUrls);
model.addAttribute("currentUrl", url);
}
return PICTURE_FILE_PREVIEW_PAGE;
return "picture";
}
}

View File

@@ -4,12 +4,10 @@ import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils;
import cn.keking.utils.KkFileUtils;
import jodd.io.FileUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.web.util.HtmlUtils;
import org.springframework.util.Base64Utils;
import java.io.File;
import java.io.IOException;
@@ -22,29 +20,31 @@ import java.nio.charset.StandardCharsets;
@Service
public class SimTextFilePreviewImpl implements FilePreview {
private final OtherFilePreviewImpl otherFilePreview;
public SimTextFilePreviewImpl(OtherFilePreviewImpl otherFilePreview) {
this.otherFilePreview = otherFilePreview;
}
public static final String TEXT_TYPE = "textType";
public static final String DEFAULT_TEXT_TYPE = "simText";
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String fileName = fileAttribute.getName();
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
if (0 != response.getCode()) {
model.addAttribute("msg", response.getMsg());
model.addAttribute("fileType", fileAttribute.getSuffix());
return "fileNotSupported";
}
try {
File originFile = new File(response.getContent());
String charset = KkFileUtils.getFileEncode(originFile);
String fileData = FileUtil.readString(originFile, charset);
fileData = HtmlUtils.htmlEscape(fileData, StandardCharsets.UTF_8.name());
model.addAttribute("textData", Base64.encodeBase64String(fileData.getBytes()));
String xmlString = FileUtils.readFileToString(originFile, StandardCharsets.UTF_8);
model.addAttribute("textData", Base64Utils.encodeToString(xmlString.getBytes()));
} catch (IOException e) {
return otherFilePreview.notSupportedFile(model, fileAttribute, e.getLocalizedMessage());
model.addAttribute("msg", e.getLocalizedMessage());
model.addAttribute("fileType", fileAttribute.getSuffix());
return "fileNotSupported";
}
return TXT_FILE_PREVIEW_PAGE;
if (!model.containsAttribute(TEXT_TYPE)) {
model.addAttribute(TEXT_TYPE, DEFAULT_TEXT_TYPE);
}
return "txt";
}
}

View File

@@ -1,38 +0,0 @@
package cn.keking.service.impl;
import cn.keking.model.FileAttribute;
import cn.keking.service.FilePreview;
import cn.keking.utils.WebUtils;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
/**
* tiff 图片文件处理
* @author kl (http://kailing.pub)
* @since 2021/2/8
*/
@Service
public class TiffFilePreviewImpl implements FilePreview {
private final PictureFilePreviewImpl pictureFilePreview;
private static final String INITIALIZE_MEMORY_SIZE = "initializeMemorySize";
//默认初始化 50MB 内存
private static final long INITIALIZE_MEMORY_SIZE_VALUE_DEFAULT = 1024L * 1024 * 50;
public TiffFilePreviewImpl(PictureFilePreviewImpl pictureFilePreview) {
this.pictureFilePreview = pictureFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
pictureFilePreview.filePreviewHandle(url,model,fileAttribute);
String fileSize = WebUtils.getUrlParameterReg(url,INITIALIZE_MEMORY_SIZE);
if(StringUtils.hasText(fileSize)){
model.addAttribute(INITIALIZE_MEMORY_SIZE,fileSize);
}else {
model.addAttribute(INITIALIZE_MEMORY_SIZE,Long.toString(INITIALIZE_MEMORY_SIZE_VALUE_DEFAULT));
}
return TIFF_FILE_PREVIEW_PAGE;
}
}

View File

@@ -5,6 +5,8 @@ import cn.keking.service.FilePreview;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import static com.sun.glass.ui.Clipboard.TEXT_TYPE;
/**
* @author kl (http://kailing.pub)
* @since 2020/12/25
@@ -18,9 +20,10 @@ public class XmlFilePreviewImpl implements FilePreview {
this.simTextFilePreview = simTextFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
return XML_FILE_PREVIEW_PAGE;
model.addAttribute(TEXT_TYPE,"xml");
return simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
}
}

View File

@@ -0,0 +1,50 @@
package cn.keking.utils;
import com.aspose.cad.Color;
import com.aspose.cad.fileformats.cad.CadDrawTypeMode;
import com.aspose.cad.imageoptions.CadRasterizationOptions;
import com.aspose.cad.imageoptions.PdfOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
/**
* @author chenjhc
* @since 2019/11/21 14:34
*/
@Component
public class CadUtils {
private final Logger logger = LoggerFactory.getLogger(CadUtils.class);
public boolean cadToPdf(String inputFilePath, String outputFilePath) {
com.aspose.cad.Image cadImage = com.aspose.cad.Image.load(inputFilePath);
CadRasterizationOptions cadRasterizationOptions = new CadRasterizationOptions();
cadRasterizationOptions.setLayouts(new String[]{"Model"});
cadRasterizationOptions.setNoScaling(true);
cadRasterizationOptions.setBackgroundColor(Color.getWhite());
cadRasterizationOptions.setPageWidth(cadImage.getWidth());
cadRasterizationOptions.setPageHeight(cadImage.getHeight());
cadRasterizationOptions.setPdfProductLocation("center");
cadRasterizationOptions.setAutomaticLayoutsScaling(true);
cadRasterizationOptions.setDrawType(CadDrawTypeMode.UseObjectColor);
PdfOptions pdfOptions = new PdfOptions();
pdfOptions.setVectorRasterizationOptions(cadRasterizationOptions);
File outputFile = new File(outputFilePath);
OutputStream stream;
try {
stream = new FileOutputStream(outputFile);
cadImage.save(stream, pdfOptions);
cadImage.close();
return true;
} catch (FileNotFoundException e) {
logger.error("PDFFileNotFoundExceptioninputFilePath{}", inputFilePath, e);
return false;
}
}
}

View File

@@ -2,19 +2,16 @@ package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.model.ReturnResponse;
import io.mola.galimatias.GalimatiasParseException;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import static cn.keking.utils.KkFileUtils.isFtpUrl;
import static cn.keking.utils.KkFileUtils.isHttpUrl;
/**
* @author yudian-it
*/
@@ -33,26 +30,42 @@ public class DownloadUtils {
*/
public static ReturnResponse<String> downLoad(FileAttribute fileAttribute, String fileName) {
String urlStr = fileAttribute.getUrl();
String type = fileAttribute.getSuffix();
ReturnResponse<String> response = new ReturnResponse<>(0, "下载成功!!!", "");
String realPath = DownloadUtils.getRelFilePath(fileName, fileAttribute);
UUID uuid = UUID.randomUUID();
if (null == fileName) {
fileName = uuid + "." + type;
} else { // 文件后缀不一致时以type为准(针对simText【将类txt文件转为txt】)
fileName = fileName.replace(fileName.substring(fileName.lastIndexOf(".") + 1), type);
}
String realPath = fileDir + fileName;
File dirFile = new File(fileDir);
if (!dirFile.exists() && !dirFile.mkdirs()) {
logger.error("创建目录【{}】失败,可能是权限不够,请检查", fileDir);
}
try {
URL url = WebUtils.normalizedURL(urlStr);
if (isHttpUrl(url)) {
File realFile = new File(realPath);
FileUtils.copyURLToFile(url,realFile);
} else if (isFtpUrl(url)) {
URL url = new URL(urlStr);
if (url.getProtocol() != null && (url.getProtocol().toLowerCase().startsWith("file") || url.getProtocol().toLowerCase().startsWith("http"))) {
byte[] bytes = getBytesFromUrl(urlStr);
OutputStream os = new FileOutputStream(realPath);
saveBytesToOutStream(bytes, os);
} else if (url.getProtocol() != null && "ftp".equalsIgnoreCase(url.getProtocol())) {
String ftpUsername = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME);
String ftpPassword = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD);
String ftpControlEncoding = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING);
FtpUtils.download(fileAttribute.getUrl(), realPath, ftpUsername, ftpPassword, ftpControlEncoding);
} else {
response.setCode(1);
response.setContent(null);
response.setMsg("url不能识别url" + urlStr);
}
response.setContent(realPath);
response.setMsg(fileName);
if (FileType.simText.equals(fileAttribute.getType())) {
convertTextPlainFileCharsetToUtf8(realPath);
}
return response;
} catch (IOException | GalimatiasParseException e) {
} catch (IOException e) {
logger.error("文件下载失败url{}", urlStr, e);
response.setCode(1);
response.setContent(null);
@@ -65,27 +78,82 @@ public class DownloadUtils {
}
}
/**
* 获取真实文件绝对路径
*
* @param fileName 文件名
* @return 文件路径
*/
private static String getRelFilePath(String fileName, FileAttribute fileAttribute) {
String type = fileAttribute.getSuffix();
if (null == fileName) {
UUID uuid = UUID.randomUUID();
fileName = uuid + "." + type;
} else { // 文件后缀不一致时以type为准(针对simText【将类txt文件转为txt】)
fileName = fileName.replace(fileName.substring(fileName.lastIndexOf(".") + 1), type);
public static byte[] getBytesFromUrl(String urlStr) throws IOException {
InputStream is = getInputStreamFromUrl(urlStr);
if (is == null) {
// urlStr = URLUtil.normalize(urlStr, true, true);
is = getInputStreamFromUrl(urlStr);
if (is == null) {
logger.error("文件下载异常url{}", urlStr);
throw new IOException("文件下载异常url" + urlStr);
}
}
String realPath = fileDir + fileName;
File dirFile = new File(fileDir);
if (!dirFile.exists() && !dirFile.mkdirs()) {
logger.error("创建目录【{}】失败,可能是权限不够,请检查", fileDir);
}
return realPath;
return getBytesFromStream(is);
}
public static void saveBytesToOutStream(byte[] b, OutputStream os) throws IOException {
os.write(b);
os.close();
}
private static InputStream getInputStreamFromUrl(String urlStr) {
try {
URL url = new URL(urlStr);
URLConnection connection = url.openConnection();
if (connection instanceof HttpURLConnection) {
connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
}
return connection.getInputStream();
} catch (IOException e) {
logger.warn("连接url异常url{}", urlStr);
return null;
}
}
private static byte[] getBytesFromStream(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
byte[] b = baos.toByteArray();
is.close();
baos.close();
return b;
}
/**
* 转换文本文件编码为utf8
* 探测源文件编码,探测到编码切不为utf8则进行转码
*
* @param filePath 文件路径
*/
private static void convertTextPlainFileCharsetToUtf8(String filePath) throws IOException {
File sourceFile = new File(filePath);
if (sourceFile.exists() && sourceFile.isFile() && sourceFile.canRead()) {
String encoding = FileUtils.getFileEncode(filePath);
if (!FileUtils.DEFAULT_FILE_ENCODING.equals(encoding)) {
// 不为utf8,进行转码
File tmpUtf8File = new File(filePath + ".utf8");
Writer writer = new OutputStreamWriter(new FileOutputStream(tmpUtf8File), StandardCharsets.UTF_8);
Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile), encoding));
char[] buf = new char[1024];
int read;
while ((read = reader.read(buf)) > 0) {
writer.write(buf, 0, read);
}
reader.close();
writer.close();
// 删除源文件
if (!sourceFile.delete()) {
logger.error("源文件【{}】删除失败,请检查文件目录权限!", filePath);
}
// 重命名
if (tmpUtf8File.renameTo(sourceFile)) {
logger.error("临时文件【{}】重命名失败,请检查文件路径权限!", tmpUtf8File.getPath());
}
}
}
}
}

View File

@@ -6,39 +6,19 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Objects;
public class KkFileUtils {
public class FileUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(KkFileUtils.class);
private static final Logger LOGGER = LoggerFactory.getLogger(FileUtils.class);
public static final String DEFAULT_FILE_ENCODING = "UTF-8";
/**
* 判断url是否是http资源
*
* @param url url
* @return 是否http
*/
public static boolean isHttpUrl(URL url) {
return url.getProtocol().toLowerCase().startsWith("file") || url.getProtocol().toLowerCase().startsWith("http");
}
/**
* 判断url是否是ftp资源
*
* @param url url
* @return 是否ftp
*/
public static boolean isFtpUrl(URL url) {
return "ftp".equalsIgnoreCase(url.getProtocol());
}
/**
* 删除单个文件
*
* @param fileName 要删除的文件的文件名
* @param fileName
* 要删除的文件的文件名
* @return 单个文件删除成功返回true否则返回false
*/
public static boolean deleteFileByName(String fileName) {
@@ -59,26 +39,17 @@ public class KkFileUtils {
}
/**
* 检测文件编码格式
* 判断文件编码格式
*
* @param filePath 绝对路径
* @return 编码格式
*/
public static String getFileEncode(String filePath) {
return getFileEncode(new File(filePath));
}
/**
* 检测文件编码格式
*
* @param file 检测的文件
* @return 编码格式
*/
public static String getFileEncode(File file) {
File file = new File(filePath);
CharsetPrinter cp = new CharsetPrinter();
try {
String encoding = cp.guessEncoding(file);
LOGGER.info("检测到文件【{}】编码: {}", file.getAbsolutePath(), encoding);
LOGGER.info("检测到文件【{}】编码: {}", filePath, encoding);
return encoding;
} catch (IOException e) {
LOGGER.warn("文件编码获取失败采用默认的编码格式UTF-8", e);
@@ -86,17 +57,6 @@ public class KkFileUtils {
}
}
/**
* 通过文件名获取文件后缀
*
* @param fileName 文件名称
* @return 文件后缀
*/
public static String suffixFromFileName(String fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
}
/**
* 根据文件路径删除文件
*
@@ -112,7 +72,8 @@ public class KkFileUtils {
/**
* 删除目录及目录下的文件
*
* @param dir 要删除的目录的文件路径
* @param dir
* 要删除的目录的文件路径
* @return 目录删除成功返回true否则返回false
*/
public static boolean deleteDirectory(String dir) {
@@ -132,13 +93,13 @@ public class KkFileUtils {
for (int i = 0; i < Objects.requireNonNull(files).length; i++) {
// 删除子文件
if (files[i].isFile()) {
flag = KkFileUtils.deleteFileByName(files[i].getAbsolutePath());
flag = FileUtils.deleteFileByName(files[i].getAbsolutePath());
if (!flag) {
break;
}
} else if (files[i].isDirectory()) {
} else if (files[i].isDirectory()) {
// 删除子目录
flag = KkFileUtils.deleteDirectory(files[i].getAbsolutePath());
flag = FileUtils.deleteDirectory(files[i].getAbsolutePath());
if (!flag) {
break;
}

View File

@@ -0,0 +1,79 @@
package cn.keking.utils;
import cn.keking.service.FileHandlerService;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.tools.imageio.ImageIOUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
@Component
public class PdfUtils {
private final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
private final FileHandlerService fileHandlerService;
@Value("${server.tomcat.uri-encoding:UTF-8}")
private String uriEncoding;
public PdfUtils(FileHandlerService fileHandlerService) {
this.fileHandlerService = fileHandlerService;
}
public List<String> pdf2jpg(String pdfFilePath, String pdfName, String baseUrl) {
List<String> imageUrls = new ArrayList<>();
Integer imageCount = fileHandlerService.getConvertedPdfImage(pdfFilePath);
String imageFileSuffix = ".jpg";
String pdfFolder = pdfName.substring(0, pdfName.length() - 4);
String urlPrefix = null;
try {
urlPrefix = baseUrl + URLEncoder.encode(URLEncoder.encode(pdfFolder, uriEncoding).replaceAll("\\+", "%20"), uriEncoding);
} catch (UnsupportedEncodingException e) {
logger.error("UnsupportedEncodingException", e);
urlPrefix = baseUrl + pdfFolder;
}
if (imageCount != null && imageCount > 0) {
for (int i = 0; i < imageCount ; i++)
imageUrls.add(urlPrefix + "/" + i + imageFileSuffix);
return imageUrls;
}
try {
File pdfFile = new File(pdfFilePath);
PDDocument doc = PDDocument.load(pdfFile);
int pageCount = doc.getNumberOfPages();
PDFRenderer pdfRenderer = new PDFRenderer(doc);
int index = pdfFilePath.lastIndexOf(".");
String folder = pdfFilePath.substring(0, index);
File path = new File(folder);
if (!path.exists()) {
path.mkdirs();
}
String imageFilePath;
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
imageFilePath = folder + File.separator + pageIndex + imageFileSuffix;
BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 105, ImageType.RGB);
ImageIOUtil.writeImage(image, imageFilePath, 105);
imageUrls.add(urlPrefix + "/" + pageIndex + imageFileSuffix);
}
doc.close();
fileHandlerService.addConvertedPdfImage(pdfFilePath, pageCount);
} catch (IOException e) {
logger.error("Convert pdf to jpg exception, pdfFilePath{}", pdfFilePath, e);
}
return imageUrls;
}
}

View File

@@ -1,7 +1,7 @@
package cn.keking.config;
package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import cn.keking.service.cache.CacheService;
import cn.keking.utils.KkFileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
@@ -14,13 +14,13 @@ import org.springframework.stereotype.Component;
*/
@Component
@ConditionalOnExpression("'${cache.clean.enabled:false}'.equals('true')")
public class SchedulerCleanConfig {
public class ShedulerClean {
private final Logger logger = LoggerFactory.getLogger(SchedulerCleanConfig.class);
private final Logger logger = LoggerFactory.getLogger(ShedulerClean.class);
private final CacheService cacheService;
public SchedulerCleanConfig(CacheService cacheService) {
public ShedulerClean(CacheService cacheService) {
this.cacheService = cacheService;
}
@@ -31,7 +31,7 @@ public class SchedulerCleanConfig {
public void clean() {
logger.info("Cache clean start");
cacheService.cleanCache();
KkFileUtils.deleteDirectory(fileDir);
FileUtils.deleteDirectory(fileDir);
logger.info("Cache clean end");
}
}

View File

@@ -1,9 +1,5 @@
package cn.keking.utils;
import io.mola.galimatias.GalimatiasParseException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
@@ -12,16 +8,6 @@ import java.util.Map;
* create : 2020-12-27 1:30 上午
**/
public class WebUtils {
/**
* 获取标准的URL
* @param urlStr url
* @return 标准的URL
*/
public static URL normalizedURL(String urlStr) throws GalimatiasParseException, MalformedURLException {
return io.mola.galimatias.URL.parse(urlStr).toJavaURL();
}
/**
* 获取url中的参数
*
@@ -71,30 +57,4 @@ public class WebUtils {
}
return strAllParam;
}
/**
* 从url中剥离出文件名
*
* @param url 格式如http://www.com.cn/20171113164107_月度绩效表模板(新).xls?UCloudPublicKey=ucloudtangshd@weifenf.com14355492830001993909323&Expires=&Signature=I D1NOFtAJSPT16E6imv6JWuq0k=
* @return 文件名
*/
public static String getFileNameFromURL(String url) {
// 因为url的参数中可能会存在/的情况所以直接url.lastIndexOf("/")会有问题
// 所以先从处将url截断然后运用url.lastIndexOf("/")获取文件名
String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
return noQueryUrl.substring(noQueryUrl.lastIndexOf("/") + 1);
}
/**
* 从url中获取文件后缀
*
* @param url url
* @return 文件后缀
*/
public static String suffixFromUrl(String url) {
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return KkFileUtils.suffixFromFileName(fileName);
}
}

View File

@@ -15,9 +15,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import org.springframework.web.util.HtmlUtils;
/**
*
@@ -30,7 +28,9 @@ public class FileController {
private final Logger logger = LoggerFactory.getLogger(FileController.class);
private final String fileDir = ConfigConstants.getFileDir();
private final String demoDir = "demo";
private final String demoPath = demoDir + File.separator;
@RequestMapping(value = "fileUpload", method = RequestMethod.POST)
@@ -38,10 +38,6 @@ public class FileController {
// 获取文件名
String fileName = file.getOriginalFilename();
//判断是否为IE浏览器的文件名IE浏览器下文件名会带有盘符信息
// escaping dangerous characters to prevent XSS
fileName = HtmlUtils.htmlEscape(fileName, StandardCharsets.UTF_8.name());
// Check for Unix-style path
int unixSep = fileName.lastIndexOf('/');
// Check for Windows-style path
@@ -53,7 +49,7 @@ public class FileController {
}
// 判断是否存在同名文件
if (existsFile(fileName)) {
return new ObjectMapper().writeValueAsString(ReturnResponse.failure("存在同名文件,请先删除原有文件再次上传"));
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "存在同名文件,请先删除原有文件再次上传", null));
}
File outFile = new File(fileDir + demoPath);
if (!outFile.exists() && !outFile.mkdirs()) {
@@ -62,10 +58,10 @@ public class FileController {
logger.info("上传文件:{}", fileDir + demoPath + fileName);
try(InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream(fileDir + demoPath + fileName)) {
StreamUtils.copy(in, out);
return new ObjectMapper().writeValueAsString(ReturnResponse.success(null));
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(0, "SUCCESS", null));
} catch (IOException e) {
logger.error("文件上传失败", e);
return new ObjectMapper().writeValueAsString(ReturnResponse.failure());
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "FAILURE", null));
}
}
@@ -79,7 +75,7 @@ public class FileController {
if (file.exists() && !file.delete()) {
logger.error("删除文件【{}】失败,请检查目录权限!",file.getPath());
}
return new ObjectMapper().writeValueAsString(ReturnResponse.success());
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(0, "SUCCESS", null));
}
@RequestMapping(value = "listFiles", method = RequestMethod.GET)

View File

@@ -5,18 +5,14 @@ import cn.keking.service.FilePreview;
import cn.keking.service.FilePreviewFactory;
import cn.keking.service.cache.CacheService;
import cn.keking.service.impl.OtherFilePreviewImpl;
import cn.keking.utils.DownloadUtils;
import cn.keking.service.FileHandlerService;
import cn.keking.utils.WebUtils;
import fr.opensagres.xdocreport.core.io.IOUtils;
import io.mola.galimatias.GalimatiasParseException;
import jodd.io.NetUtil;
import org.apache.commons.codec.binary.Base64;
import com.thoughtworks.xstream.core.util.Base64JavaUtilCodec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@@ -24,97 +20,73 @@ import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.List;
import static cn.keking.service.FilePreview.PICTURE_FILE_PREVIEW_PAGE;
/**
* @author yudian-it
*/
@Controller
public class OnlinePreviewController {
public static final String BASE64_DECODE_ERROR_MSG = "Base64解码失败请检查你的 %s 是否采用 Base64 + urlEncode 双重编码了!";
private final Logger logger = LoggerFactory.getLogger(OnlinePreviewController.class);
private final FilePreviewFactory previewFactory;
private final CacheService cacheService;
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService, OtherFilePreviewImpl otherFilePreview) {
public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService) {
this.previewFactory = filePreviewFactory;
this.fileHandlerService = fileHandlerService;
this.cacheService = cacheService;
this.otherFilePreview = otherFilePreview;
}
@RequestMapping(value = "/onlinePreview")
public String onlinePreview(String url, Model model, HttpServletRequest req) {
String fileUrl;
try {
fileUrl = new String(Base64.decodeBase64(url), StandardCharsets.UTF_8);
} catch (Exception ex) {
String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "url");
return otherFilePreview.notSupportedFile(model, errorMsg);
}
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(fileUrl, req);
model.addAttribute("file", fileAttribute);
String fileUrl = new String(Base64Utils.decodeFromString(url));
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(fileUrl,req);
FilePreview filePreview = previewFactory.get(fileAttribute);
logger.info("预览文件url{}previewType{}", fileUrl, fileAttribute.getType());
return filePreview.filePreviewHandle(fileUrl, model, fileAttribute);
}
@RequestMapping(value = "/picturesPreview")
public String picturesPreview(String urls, Model model, HttpServletRequest req) throws UnsupportedEncodingException {
String fileUrls;
try {
fileUrls = new String(Base64.decodeBase64(urls));
} 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("\\|");
List<String> imgUrls = Arrays.asList(images);
model.addAttribute("imgUrls", imgUrls);
public String picturesPreview(Model model, HttpServletRequest req) throws UnsupportedEncodingException {
String urls = req.getParameter("urls");
String currentUrl = req.getParameter("currentUrl");
if (StringUtils.hasText(currentUrl)) {
String decodedCurrentUrl = new String(Base64.decodeBase64(currentUrl));
model.addAttribute("currentUrl", decodedCurrentUrl);
} else {
model.addAttribute("currentUrl", imgUrls.get(0));
}
return PICTURE_FILE_PREVIEW_PAGE;
logger.info("预览文件url{}urls{}", currentUrl, urls);
// 路径转码
String decodedUrl = URLDecoder.decode(urls, "utf-8");
String decodedCurrentUrl = URLDecoder.decode(currentUrl, "utf-8");
// 抽取文件并返回文件列表
String[] imgs = decodedUrl.split("\\|");
List<String> imgUrls = Arrays.asList(imgs);
model.addAttribute("imgUrls", imgUrls);
model.addAttribute("currentUrl",decodedCurrentUrl);
return "picture";
}
/**
* 根据url获取文件内容
* 当pdfjs读取存在跨域问题的文件时将通过此接口读取
*
* @param urlPath url
* @param urlPath url
* @param response response
*/
@RequestMapping(value = "/getCorsFile", method = RequestMethod.GET)
public void getCorsFile(String urlPath, HttpServletResponse response) {
logger.info("下载跨域pdf文件url{}", urlPath);
try {
URL url = WebUtils.normalizedURL(urlPath);
byte[] bytes = NetUtil.downloadBytes(url.toString());
IOUtils.write(bytes, response.getOutputStream());
} catch (IOException | GalimatiasParseException e) {
byte[] bytes = DownloadUtils.getBytesFromUrl(urlPath);
DownloadUtils.saveBytesToOutStream(bytes, response.getOutputStream());
} catch (IOException e) {
logger.error("下载跨域pdf文件异常url{}", urlPath, e);
}
}
/**
* 通过api接口入队
*
* @param url 请编码后在入队
*/
@RequestMapping("/addTask")

View File

@@ -30,6 +30,8 @@ public class FilterConfiguration {
Set<String> filterUri = new HashSet<>();
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
filterUri.add("/getCorsFile");
filterUri.add("/addTask");
TrustHostFilter filter = new TrustHostFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);

View File

@@ -3,7 +3,6 @@ package cn.keking.web.filter;
import cn.keking.config.ConfigConstants;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.Base64Utils;
import org.springframework.util.FileCopyUtils;
import javax.servlet.*;
@@ -35,9 +34,6 @@ public class TrustHostFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String url = getSourceUrl(request);
if(url != null){
url = new String(Base64Utils.decodeFromString(url), StandardCharsets.UTF_8);
}
String host = getHost(url);
if (host != null &&!ConfigConstants.getTrustHostSet().isEmpty() && !ConfigConstants.getTrustHostSet().contains(host)) {
String html = this.notTrustHost.replace("${current_host}", host);

View File

@@ -1,7 +0,0 @@
README.txt
log目录是用来存放kkFileView.log的预览服务的运行情况最终都会反映到这个日志文件里
可以提供给开发运维排查系统问题如果通过kkFileView.log还无法定位问题所在请你在寻求
kk官方支持时QQ群一 613025121QQ群二 484680571将此日志文件一并携带并按照这个
格式重命名日志文件 kkFileView-QQ昵称-时间日期.logkkFileView-kl博主-2020-12-27.log
所有收集的日志文件我们都会存档供所有的kk用户作为排查案例使用所以在你提供日志前
先自行处理日志文件里的业务敏感内容

Some files were not shown because too many files have changed in this diff Show More