Compare commits

..

28 Commits

Author SHA1 Message Date
chenkailing
27ae58a0f4 加回 apache-commons-io 包依赖,office组件中有依赖,后续再统一吧 2021-01-09 17:07:15 +08:00
chenkailing
4d83674806 移除 apache-commons-io 包依赖,采用jodd的io工具替代 2021-01-09 15:38:21 +08:00
kl
12c85c60c7 XML文件预览支持切换纯文本模式 2020-12-31 13:28:25 +08:00
kl
4747ee93b2 修复类文本类型HTML文件预览的bug 2020-12-29 18:37:50 +08:00
kl
5ec53c4b33 指定Base64转码采用Apache Commons-code中的实现,修复base64部分jdk版本下出现的异常 2020-12-29 18:32:24 +08:00
kl
ee7f7f50cc 修复大小写文件类型后缀没通用匹配的问题 2020-12-29 09:29:16 +08:00
chenkailing
92869e8d6c 文本文件编码默认UTF-8输出到页面 2020-12-28 23:56:38 +08:00
kl
c66dda239f 采用apache-common-io包简化所有的文件下载io操作 2020-12-28 18:31:12 +08:00
kl
e927760c40 调整多图连续预览上下翻图的UI 2020-12-28 16:45:11 +08:00
kl
8783f9059a 修复压缩包内多图连续预览的bug 2020-12-28 16:45:11 +08:00
kl
12e1239267 前端模板抽象出通用的header,将水印等初始化内容挪到通用header里 2020-12-28 16:45:11 +08:00
kl
7c963193ef 更新simTxT文件预览UI风格 2020-12-28 14:54:42 +08:00
kl
bce9a624d7 更新XML文件预览UI风格,调整类文本预览架构,更方便扩展 2020-12-28 14:54:42 +08:00
kl
dac3606b4e 更新markdown文件预览UI风格 2020-12-28 14:54:42 +08:00
kl
eb00385d76 更新index接入演示界面UI风格 2020-12-28 13:36:40 +08:00
kl
986f562266 引入galimatias,解决不规范文件名导致文件下载异常 2020-12-28 11:43:27 +08:00
kl
11d0441ed4 加回漏掉的pdfjs 2020-12-28 11:22:59 +08:00
kl
ef46e2c51e 增强url base64解码失败时的提示信息 2020-12-28 11:22:59 +08:00
kl
0a3c03f18b 修复发行包运行时找不到日志目录的问题 2020-12-28 11:22:59 +08:00
chenkailing
7a7e1a1855 修复导包错误以及图片预览bug 2020-12-27 19:18:52 +08:00
chenkailing
f530f441d5 更新版本到3.3.0 2020-12-27 16:54:30 +08:00
chenkailing
10160e8104 添加2020年最后的发版信息 2020-12-27 16:42:03 +08:00
chenkailing
602e80ee9e 独立flv文件预览实现 2020-12-27 15:17:35 +08:00
chenkailing
9c83860e1b 抽象通用的预览异常接口实现 2020-12-27 14:48:07 +08:00
chenkailing
1f1970232b 精简util模块,ReturenResponse重构 2020-12-27 14:07:46 +08:00
chenkailing
594bd895ec 修复压缩包里文件再次预览失败的bug 2020-12-27 12:38:05 +08:00
chenkailing
486c09b24a 文件url采用base64加encodeURI双重编码,彻底解决各种奇葩文件名导致的下载异常 2020-12-27 01:46:12 +08:00
chenkailing
aaf396fbc8 忽略log目录 2020-12-26 19:28:45 +08:00
62 changed files with 84381 additions and 2071 deletions

2
.gitignore vendored
View File

@@ -17,7 +17,6 @@ target/
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
@@ -29,4 +28,3 @@ 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-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"]
ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-3.3.0/bin
ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider","-Dspring.config.location=/opt/kkFileView-3.3.0/config/application.properties","-jar","/opt/kkFileView-3.3.0/bin/kkFileView-3.3.0.jar"]

View File

@@ -109,6 +109,26 @@ pdf预览模式预览效果如下
### 历史更新记录
> 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文件预览

View File

@@ -5,7 +5,7 @@
<groupId>cn.keking</groupId>
<artifactId>filepreview</artifactId>
<version>2.2.1</version>
<version>3.3.0</version>
<modules>
<module>office-plugin</module>
<module>server</module>

View File

@@ -12,7 +12,7 @@
<groupId>cn.keking</groupId>
<artifactId>kkFileView</artifactId>
<version>2.2.1</version>
<version>3.3.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -56,12 +56,6 @@
<groupId>cn.keking</groupId>
<artifactId>office-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<artifactId>commons-io</artifactId>
<groupId>commons-io</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
@@ -105,11 +99,6 @@
<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>
@@ -201,6 +190,12 @@
<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>
</dependencies>
<build>
<resources>

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 -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=..\config\application.properties -jar kkFileView-2.2.1.jar -> ..\log\kkFileView.log
java -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=..\config\application.properties -jar kkFileView-3.3.0.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 -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 &
nohup java -Dfile.encoding=UTF-8 -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=../config/application.properties -jar kkFileView-3.3.0.jar > ../log/kkFileView.log 2>&1 &

View File

@@ -1,5 +1,9 @@
[#ftl]
[#-- @implicitly included --]
[#-- @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" --]
[#-- @ftlvariable name="xmlContent" type="java.lang.String" --]

View File

@@ -1,7 +1,5 @@
package cn.keking.service;
package cn.keking.config;
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;
@@ -13,6 +11,7 @@ import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
/**
* @auther: chenjh
@@ -81,7 +80,7 @@ public class ConfigRefreshComponent {
setWatermarkConfig(properties);
bufferedReader.close();
fileReader.close();
Thread.sleep(1000L);
TimeUnit.SECONDS.sleep(1);
}
} catch (IOException | InterruptedException e) {
LOGGER.error("读取配置文件异常", e);

View File

@@ -1,7 +1,7 @@
package cn.keking.utils;
package cn.keking.config;
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 ShedulerClean {
public class SchedulerCleanConfig {
private final Logger logger = LoggerFactory.getLogger(ShedulerClean.class);
private final Logger logger = LoggerFactory.getLogger(SchedulerCleanConfig.class);
private final CacheService cacheService;
public ShedulerClean(CacheService cacheService) {
public SchedulerCleanConfig(CacheService cacheService) {
this.cacheService = cacheService;
}
@@ -31,7 +31,7 @@ public class ShedulerClean {
public void clean() {
logger.info("Cache clean start");
cacheService.cleanCache();
FileUtils.deleteDirectory(fileDir);
KkFileUtils.deleteDirectory(fileDir);
logger.info("Cache clean end");
}
}

View File

@@ -1,388 +0,0 @@
package cn.keking.hutool;
import java.awt.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* 十六进制简写为hex或下标16在数学中是一种逢16进1的进位制一般用数字0到9和字母A到F表示其中:A~F即10~15。<br>
* 例如十进制数57在二进制写作111001在16进制写作39。<br>
* 像java,c这样的语言为了区分十六进制和十进制数值,会在十六进制数的前面加上 0x,比如0x20是十进制的32,而不是十进制的20<br>
* <p>
* 参考https://my.oschina.net/xinxingegeya/blog/287476
*
* @author Looly
*/
public class HexUtil {
/**
* 用于建立十六进制字符的输出的小写字符数组
*/
private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* 用于建立十六进制字符的输出的大写字符数组
*/
private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
/**
* 判断给定字符串是否为16进制数<br>
* 如果是,需要使用对应数字类型对象的<code>decode</code>方法解码<br>
* 例如:{@code Integer.decode}方法解码int类型的16进制数字
*
* @param value 值
* @return 是否为16进制
*/
public static boolean isHexNumber(String value) {
final int index = (value.startsWith("-") ? 1 : 0);
if (value.startsWith("0x", index) || value.startsWith("0X", index) || value.startsWith("#", index)) {
try {
Long.decode(value);
} catch (NumberFormatException e) {
return false;
}
return true;
} else {
return false;
}
}
// ---------------------------------------------------------------------------------------------------- encode
/**
* 将字节数组转换为十六进制字符数组
*
* @param data byte[]
* @return 十六进制char[]
*/
public static char[] encodeHex(byte[] data) {
return encodeHex(data, true);
}
/**
* 将字节数组转换为十六进制字符数组
*
* @param str 字符串
* @param charset 编码
* @return 十六进制char[]
*/
public static char[] encodeHex(String str, Charset charset) {
return encodeHex(StrUtil.bytes(str, charset), true);
}
/**
* 将字节数组转换为十六进制字符数组
*
* @param data byte[]
* @param toLowerCase <code>true</code> 传换成小写格式 <code>false</code> 传换成大写格式
* @return 十六进制char[]
*/
public static char[] encodeHex(byte[] data, boolean toLowerCase) {
return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
/**
* 将字节数组转换为十六进制字符串
*
* @param data byte[]
* @return 十六进制String
*/
public static String encodeHexStr(byte[] data) {
return encodeHexStr(data, true);
}
/**
* 将字节数组转换为十六进制字符串,结果为小写
*
* @param data 被编码的字符串
* @param charset 编码
* @return 十六进制String
*/
public static String encodeHexStr(String data, Charset charset) {
return encodeHexStr(StrUtil.bytes(data, charset), true);
}
/**
* 将字节数组转换为十六进制字符串结果为小写默认编码是UTF-8
*
* @param data 被编码的字符串
* @return 十六进制String
*/
public static String encodeHexStr(String data) {
return encodeHexStr(data, StandardCharsets.UTF_8);
}
/**
* 将字节数组转换为十六进制字符串
*
* @param data byte[]
* @param toLowerCase <code>true</code> 传换成小写格式 <code>false</code> 传换成大写格式
* @return 十六进制String
*/
public static String encodeHexStr(byte[] data, boolean toLowerCase) {
return encodeHexStr(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
// ---------------------------------------------------------------------------------------------------- decode
/**
* 将十六进制字符数组转换为字符串默认编码UTF-8
*
* @param hexStr 十六进制String
* @return 字符串
*/
public static String decodeHexStr(String hexStr) {
return decodeHexStr(hexStr, StandardCharsets.UTF_8);
}
/**
* 将十六进制字符数组转换为字符串
*
* @param hexStr 十六进制String
* @param charset 编码
* @return 字符串
*/
public static String decodeHexStr(String hexStr, Charset charset) {
if (StrUtil.isEmpty(hexStr)) {
return hexStr;
}
return decodeHexStr(hexStr.toCharArray(), charset);
}
/**
* 将十六进制字符数组转换为字符串
*
* @param hexData 十六进制char[]
* @param charset 编码
* @return 字符串
*/
public static String decodeHexStr(char[] hexData, Charset charset) {
return StrUtil.str(decodeHex(hexData), charset);
}
/**
* 将十六进制字符数组转换为字节数组
*
* @param hexData 十六进制char[]
* @return byte[]
* @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常
*/
public static byte[] decodeHex(char[] hexData) {
int len = hexData.length;
if ((len & 0x01) != 0) {
throw new RuntimeException("Odd number of characters.");
}
byte[] out = new byte[len >> 1];
// two characters form the hex value.
for (int i = 0, j = 0; j < len; i++) {
int f = toDigit(hexData[j], j) << 4;
j++;
f = f | toDigit(hexData[j], j);
j++;
out[i] = (byte) (f & 0xFF);
}
return out;
}
/**
* 将十六进制字符串解码为byte[]
*
* @param hexStr 十六进制String
* @return byte[]
*/
public static byte[] decodeHex(String hexStr) {
if (StrUtil.isEmpty(hexStr)) {
return null;
}
return decodeHex(hexStr.toCharArray());
}
// ---------------------------------------------------------------------------------------- Color
/**
* 将{@link Color}编码为Hex形式
*
* @param color {@link Color}
* @return Hex字符串
* @since 3.0.8
*/
public static String encodeColor(Color color) {
return encodeColor(color, "#");
}
/**
* 将{@link Color}编码为Hex形式
*
* @param color {@link Color}
* @param prefix 前缀字符串,可以是#、0x等
* @return Hex字符串
* @since 3.0.8
*/
public static String encodeColor(Color color, String prefix) {
final StringBuilder builder = new StringBuilder(prefix);
String colorHex;
colorHex = Integer.toHexString(color.getRed());
if (1 == colorHex.length()) {
builder.append('0');
}
builder.append(colorHex);
colorHex = Integer.toHexString(color.getGreen());
if (1 == colorHex.length()) {
builder.append('0');
}
builder.append(colorHex);
colorHex = Integer.toHexString(color.getBlue());
if (1 == colorHex.length()) {
builder.append('0');
}
builder.append(colorHex);
return builder.toString();
}
/**
* 将Hex颜色值转为
*
* @param hexColor 16进制颜色值可以以#开头也可以用0x开头
* @return {@link Color}
* @since 3.0.8
*/
public static Color decodeColor(String hexColor) {
return Color.decode(hexColor);
}
/**
* 将指定int值转换为Unicode字符串形式常用于特殊字符例如汉字转Unicode形式<br>
* 转换的字符串如果u后不足4位则前面用0填充例如
*
* <pre>
* '我' =》\u4f60
* </pre>
*
* @param value int值也可以是char
* @return Unicode表现形式
*/
public static String toUnicodeHex(int value) {
final StringBuilder builder = new StringBuilder(6);
builder.append("\\u");
String hex = toHex(value);
int len = hex.length();
if (len < 4) {
builder.append("0000", 0, 4 - len);// 不足4位补0
}
builder.append(hex);
return builder.toString();
}
/**
* 将指定char值转换为Unicode字符串形式常用于特殊字符例如汉字转Unicode形式<br>
* 转换的字符串如果u后不足4位则前面用0填充例如
*
* <pre>
* '我' =》\u4f60
* </pre>
*
* @param ch char值
* @return Unicode表现形式
* @since 4.0.1
*/
public static String toUnicodeHex(char ch) {
return "\\u" +//
DIGITS_LOWER[(ch >> 12) & 15] +//
DIGITS_LOWER[(ch >> 8) & 15] +//
DIGITS_LOWER[(ch >> 4) & 15] +//
DIGITS_LOWER[(ch) & 15];
}
/**
* 转为16进制字符串
*
* @param value int值
* @return 16进制字符串
* @since 4.4.1
*/
public static String toHex(int value) {
return Integer.toHexString(value);
}
/**
* 转为16进制字符串
*
* @param value int值
* @return 16进制字符串
* @since 4.4.1
*/
public static String toHex(long value) {
return Long.toHexString(value);
}
/**
* 将byte值转为16进制并添加到{@link StringBuilder}中
*
* @param builder {@link StringBuilder}
* @param b byte
* @param toLowerCase 是否使用小写
* @since 4.4.1
*/
public static void appendHex(StringBuilder builder, byte b, boolean toLowerCase) {
final char[] toDigits = toLowerCase ? DIGITS_LOWER : DIGITS_UPPER;
int high = (b & 0xf0) >>> 4;//高位
int low = b & 0x0f;//低位
builder.append(toDigits[high]);
builder.append(toDigits[low]);
}
// ---------------------------------------------------------------------------------------- Private method start
/**
* 将字节数组转换为十六进制字符串
*
* @param data byte[]
* @param toDigits 用于控制输出的char[]
* @return 十六进制String
*/
private static String encodeHexStr(byte[] data, char[] toDigits) {
return new String(encodeHex(data, toDigits));
}
/**
* 将字节数组转换为十六进制字符数组
*
* @param data byte[]
* @param toDigits 用于控制输出的char[]
* @return 十六进制char[]
*/
private static char[] encodeHex(byte[] data, char[] toDigits) {
final int len = data.length;
final char[] out = new char[len << 1];//len*2
// two characters from the hex value.
for (int i = 0, j = 0; i < len; i++) {
out[j++] = toDigits[(0xF0 & data[i]) >>> 4];// 高位
out[j++] = toDigits[0x0F & data[i]];// 低位
}
return out;
}
/**
* 将十六进制字符转换成一个整数
*
* @param ch 十六进制char
* @param index 十六进制字符在字符数组中的位置
* @return 一个整数
* @throws RuntimeException 当ch不是一个合法的十六进制字符时抛出运行时异常
*/
private static int toDigit(char ch, int index) {
int digit = Character.digit(ch, 16);
if (digit == -1) {
throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index);
}
return digit;
}
// ---------------------------------------------------------------------------------------- Private method end
}

View File

@@ -1,283 +0,0 @@
package cn.keking.hutool;
import java.nio.charset.Charset;
/**
* 字符串工具类
*
* @author xiaoleilu
*
*/
public class StrUtil {
public static final String EMPTY = "";
/**
* 是否空白符<br>
* 空白符包括空格、制表符、全角空格和不间断空格<br>
*
* @see Character#isWhitespace(int)
* @see Character#isSpaceChar(int)
* @param c 字符
* @return 是否空白符
* @since 4.0.10
*/
public static boolean isBlankChar(int c) {
return Character.isWhitespace(c) || Character.isSpaceChar(c) || c == '\ufeff' || c == '\u202a';
}
/**
* 是否空白符<br>
* 空白符包括空格、制表符、全角空格和不间断空格<br>
*
* @param c 字符
* @return 是否空白符
* @see Character#isWhitespace(int)
* @see Character#isSpaceChar(int)
* @since 4.0.10
*/
public static boolean isBlankChar(char c) {
return isBlankChar((int) c);
}
/**
* 字符串是否为空白 空白的定义如下: <br>
* 1、为null <br>
* 2、为不可见字符如空格<br>
* 3、""<br>
*
* @param str 被检测的字符串
* @return 是否为空
*/
public static boolean isBlank(CharSequence str) {
int length;
if ((str == null) || ((length = str.length()) == 0)) {
return true;
}
for (int i = 0; i < length; i++) {
// 只要有一个非空字符即为非空字符串
if (false == isBlankChar(str.charAt(i))) {
return false;
}
}
return true;
}
/**
* 字符串是否为空,空的定义如下:<br>
* 1、为null <br>
* 2、为""<br>
*
* @param str 被检测的字符串
* @return 是否为空
*/
public static boolean isEmpty(CharSequence str) {
return str == null || str.length() == 0;
}
/**
* 编码字符串
*
* @param str 字符串
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
* @return 编码后的字节码
*/
public static byte[] bytes(CharSequence str, Charset charset) {
if (str == null) {
return null;
}
if (null == charset) {
return str.toString().getBytes();
}
return str.toString().getBytes(charset);
}
/**
* {@link CharSequence} 转为字符串null安全
*
* @param cs {@link CharSequence}
* @return 字符串
*/
public static String str(CharSequence cs) {
return null == cs ? null : cs.toString();
}
/**
* 解码字节码
*
* @param data 字符串
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
* @return 解码后的字符串
*/
public static String str(byte[] data, Charset charset) {
if (data == null) {
return null;
}
if (null == charset) {
return new String(data);
}
return new String(data, charset);
}
/**
* 改进JDK subString<br>
* index从0开始计算最后一个字符为-1<br>
* 如果from和to位置一样返回 "" <br>
* 如果from或to为负数则按照length从后向前数位置如果绝对值大于字符串长度则from归到0to归到length<br>
* 如果经过修正的index中from大于to则互换from和to example: <br>
* abcdefgh 2 3 =》 c <br>
* abcdefgh 2 -3 =》 cde <br>
*
* @param str String
* @param fromIndex 开始的index包括
* @param toIndex 结束的index不包括
* @return 字串
*/
public static String sub(CharSequence str, int fromIndex, int toIndex) {
if (isEmpty(str)) {
return str(str);
}
int len = str.length();
if (fromIndex < 0) {
fromIndex = len + fromIndex;
if (fromIndex < 0) {
fromIndex = 0;
}
} else if (fromIndex > len) {
fromIndex = len;
}
if (toIndex < 0) {
toIndex = len + toIndex;
if (toIndex < 0) {
toIndex = len;
}
} else if (toIndex > len) {
toIndex = len;
}
if (toIndex < fromIndex) {
int tmp = fromIndex;
fromIndex = toIndex;
toIndex = tmp;
}
if (fromIndex == toIndex) {
return EMPTY;
}
return str.toString().substring(fromIndex, toIndex);
}
/**
* 切割指定位置之前部分的字符串
*
* @param string 字符串
* @param toIndex 切割到的位置(不包括)
* @return 切割后的剩余的前半部分字符串
*/
public static String subPre(CharSequence string, int toIndex) {
return sub(string, 0, toIndex);
}
/**
* 切割指定位置之后部分的字符串
*
* @param string 字符串
* @param fromIndex 切割开始的位置(包括)
* @return 切割后后剩余的后半部分字符串
*/
public static String subSuf(CharSequence string, int fromIndex) {
if (isEmpty(string)) {
return null;
}
return sub(string, fromIndex, string.length());
}
/**
* 指定范围内查找指定字符
*
* @param str 字符串
* @param searchChar 被查找的字符
* @param start 起始位置如果小于0从0开始查找
* @param end 终止位置如果超过str.length()则默认查找到字符串末尾
* @return 位置
*/
public static int indexOf(final CharSequence str, char searchChar, int start, int end) {
final int len = str.length();
if (start < 0 || start > len) {
start = 0;
}
if (end > len || end < 0) {
end = len;
}
for (int i = start; i < end; i++) {
if (str.charAt(i) == searchChar) {
return i;
}
}
return -1;
}
/**
* 指定范围内查找指定字符
*
* @param str 字符串
* @param searchChar 被查找的字符
* @param start 起始位置如果小于0从0开始查找
* @return 位置
*/
public static int indexOf(final CharSequence str, char searchChar, int start) {
if (str instanceof String) {
return ((String) str).indexOf(searchChar, start);
} else {
return indexOf(str, searchChar, start, -1);
}
}
/**
* 指定范围内查找指定字符
*
* @param str 字符串
* @param searchChar 被查找的字符
* @return 位置
*/
public static int indexOf(final CharSequence str, char searchChar) {
return indexOf(str, searchChar, 0);
}
/**
* 如果字符串是<code>null</code>,则返回指定默认字符串,否则返回字符串本身。
*
* <pre>
* nullToDefault(null, &quot;default&quot;) = &quot;default&quot;
* nullToDefault(&quot;&quot;, &quot;default&quot;) = &quot;&quot;
* nullToDefault(&quot; &quot;, &quot;default&quot;) = &quot; &quot;
* nullToDefault(&quot;bat&quot;, &quot;default&quot;) = &quot;bat&quot;
* </pre>
*
* @param str 要转换的字符串
* @param defaultStr 默认字符串
*
* @return 字符串本身或指定的默认字符串
*/
public static String nullToDefault(CharSequence str, String defaultStr) {
return (str == null) ? defaultStr : str.toString();
}
/**
* 当给定字符串为null时转换为Empty
*
* @param str 被转换的字符串
* @return 转换后的字符串
*/
public static String nullToEmpty(CharSequence str) {
return nullToDefault(str, EMPTY);
}
}

View File

@@ -1,232 +0,0 @@
package cn.keking.hutool;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.BitSet;
/**
* URL编码数据内容的类型是 application/x-www-form-urlencoded。
*
* <pre>
* 1.字符"a"-"z""A"-"Z""0"-"9"".""-""*",和"_" 都不会被编码;
* 2.将空格转换为%20 ;
* 3.将非文本内容转换成"%xy"的形式,xy是两位16进制的数值;
* 4.在每个 name=value 对之间放置 &amp; 符号。
* </pre>
*
* @author looly,
*
*/
public class URLEncoder implements Serializable{
private static final long serialVersionUID = 1L;
// --------------------------------------------------------------------------------------------- Static method start
/**
* 默认{@link URLEncoder}<br>
* 默认的编码器针对URI路径编码定义如下
*
* <pre>
* pchar = unreserved不处理 / pct-encoded / sub-delims子分隔符 / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* sub-delims = "!" / "$" / "&amp;" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
* </pre>
*/
public static final URLEncoder DEFAULT = createDefault();
/**
* 用于查询语句的{@link URLEncoder}<br>
* 编码器针对URI路径编码定义如下
*
* <pre>
* 0x20 ' ' =》 '+'
* 0x2A, 0x2D, 0x2E, 0x30 to 0x39, 0x41 to 0x5A, 0x5F, 0x61 to 0x7A as-is
* '*', '-', '.', '0' to '9', 'A' to 'Z', '_', 'a' to 'z' Also '=' and '&amp;' 不编码
* 其它编码为 %nn 形式
* </pre>
*
* 详细见https://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
*/
public static final URLEncoder QUERY = createQuery();
/**
* 创建默认{@link URLEncoder}<br>
* 默认的编码器针对URI路径编码定义如下
*
* <pre>
* pchar = unreserved不处理 / pct-encoded / sub-delims子分隔符 / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* sub-delims = "!" / "$" / "&amp;" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
* </pre>
*
* @return {@link URLEncoder}
*/
public static URLEncoder createDefault() {
final URLEncoder encoder = new URLEncoder();
encoder.addSafeCharacter('-');
encoder.addSafeCharacter('.');
encoder.addSafeCharacter('_');
encoder.addSafeCharacter('~');
// Add the sub-delims
encoder.addSafeCharacter('!');
encoder.addSafeCharacter('$');
encoder.addSafeCharacter('&');
encoder.addSafeCharacter('\'');
encoder.addSafeCharacter('(');
encoder.addSafeCharacter(')');
encoder.addSafeCharacter('*');
encoder.addSafeCharacter('+');
encoder.addSafeCharacter(',');
encoder.addSafeCharacter(';');
encoder.addSafeCharacter('=');
// Add the remaining literals
encoder.addSafeCharacter(':');
encoder.addSafeCharacter('@');
// Add '/' so it isn't encoded when we encode a path
encoder.addSafeCharacter('/');
return encoder;
}
/**
* 创建用于查询语句的{@link URLEncoder}<br>
* 编码器针对URI路径编码定义如下
*
* <pre>
* 0x20 ' ' =》 '+'
* 0x2A, 0x2D, 0x2E, 0x30 to 0x39, 0x41 to 0x5A, 0x5F, 0x61 to 0x7A as-is
* '*', '-', '.', '0' to '9', 'A' to 'Z', '_', 'a' to 'z' Also '=' and '&amp;' 不编码
* 其它编码为 %nn 形式
* </pre>
*
* 详细见https://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
*
* @return {@link URLEncoder}
*/
public static URLEncoder createQuery() {
final URLEncoder encoder = new URLEncoder();
// Special encoding for space
encoder.setEncodeSpaceAsPlus(true);
// Alpha and digit are safe by default
// Add the other permitted characters
encoder.addSafeCharacter('*');
encoder.addSafeCharacter('-');
encoder.addSafeCharacter('.');
encoder.addSafeCharacter('_');
encoder.addSafeCharacter('=');
encoder.addSafeCharacter('&');
return encoder;
}
// --------------------------------------------------------------------------------------------- Static method end
/** 存放安全编码 */
private final BitSet safeCharacters;
/** 是否编码空格为+ */
private boolean encodeSpaceAsPlus = false;
/**
* 构造<br>
*
* [a-zA-Z0-9]默认不被编码
*/
public URLEncoder() {
this(new BitSet(256));
for (char i = 'a'; i <= 'z'; i++) {
addSafeCharacter(i);
}
for (char i = 'A'; i <= 'Z'; i++) {
addSafeCharacter(i);
}
for (char i = '0'; i <= '9'; i++) {
addSafeCharacter(i);
}
}
/**
* 构造
*
* @param safeCharacters 安全字符,安全字符不被编码
*/
private URLEncoder(BitSet safeCharacters) {
this.safeCharacters = safeCharacters;
}
/**
* 增加安全字符<br>
* 安全字符不被编码
*
* @param c 字符
*/
public void addSafeCharacter(char c) {
safeCharacters.set(c);
}
/**
* 移除安全字符<br>
* 安全字符不被编码
*
* @param c 字符
*/
public void removeSafeCharacter(char c) {
safeCharacters.clear(c);
}
/**
* 是否将空格编码为+
*
* @param encodeSpaceAsPlus 是否将空格编码为+
*/
public void setEncodeSpaceAsPlus(boolean encodeSpaceAsPlus) {
this.encodeSpaceAsPlus = encodeSpaceAsPlus;
}
/**
* 将URL中的字符串编码为%形式
*
* @param path 需要编码的字符串
* @param charset 编码
*
* @return 编码后的字符串
*/
public String encode(String path, Charset charset) {
int maxBytesPerChar = 10;
final StringBuilder rewrittenPath = new StringBuilder(path.length());
ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
OutputStreamWriter writer = new OutputStreamWriter(buf, charset);
int c;
for (int i = 0; i < path.length(); i++) {
c = path.charAt(i);
if (safeCharacters.get(c)) {
rewrittenPath.append((char) c);
} else if (encodeSpaceAsPlus && c == ' ') {
// 对于空格单独处理
rewrittenPath.append('+');
} else {
// convert to external encoding before hex conversion
try {
writer.write((char) c);
writer.flush();
} catch (IOException e) {
buf.reset();
continue;
}
byte[] ba = buf.toByteArray();
for (int j = 0; j < ba.length; j++) {
// Converting each byte in the buffer
byte toEncode = ba[j];
rewrittenPath.append('%');
HexUtil.appendHex(rewrittenPath, toEncode, false);
}
buf.reset();
}
}
return rewrittenPath.toString();
}
}

View File

@@ -1,74 +0,0 @@
package cn.keking.hutool;
import java.nio.charset.StandardCharsets;
/**
* 统一资源定位符相关工具类
*
* @author xiaoleilu
*
*/
public class URLUtil {
/**
* 标准化URL字符串包括
*
* <pre>
* 1. 多个/替换为一个
* </pre>
*
* @param url URL字符串
* @return 标准化后的URL字符串
*/
public static String normalize(String url) {
return normalize(url, false, false);
}
/**
* 标准化URL字符串包括
*
* <pre>
* 1. 多个/替换为一个
* </pre>
*
* @param url URL字符串
* @param isEncodeBody 是否对URL中body部分的中文和特殊字符做转义不包括http:和/
* @param isEncodeParam 是否对URL中参数部分的中文和特殊字符做转义
* @return 标准化后的URL字符串
* @since 4.4.1
*/
public static String normalize(String url, boolean isEncodeBody, boolean isEncodeParam) {
if (StrUtil.isBlank(url)) {
return url;
}
final int sepIndex = url.indexOf("://");
String pre;
String body;
if (sepIndex > 0) {
pre = StrUtil.subPre(url, sepIndex + 3);
body = StrUtil.subSuf(url, sepIndex + 3);
} else {
pre = "http://";
body = url;
}
final int paramsSepIndex = StrUtil.indexOf(body, '?');
String params = null;
if (paramsSepIndex > 0) {
params = StrUtil.subSuf(body, paramsSepIndex + 1);
body = StrUtil.subPre(body, paramsSepIndex);
}
// 去除开头的\或者/
body = body.replaceAll("^[\\\\/]+", StrUtil.EMPTY);
// 替换多个\或/为单个/
body = body.replace("\\", "/").replaceAll("//+", "/");
if (isEncodeBody) {
body = URLEncoder.DEFAULT.encode(body, StandardCharsets.UTF_8);
if (params != null) {
params = "?" + URLEncoder.DEFAULT.encode(params, StandardCharsets.UTF_8);
}
}
return pre + body + StrUtil.nullToEmpty(params);
}
}

View File

@@ -10,6 +10,7 @@ import java.util.Map;
* Content :文件类型文本office压缩包等等
*/
public enum FileType {
picture("pictureFilePreviewImpl"),
compress("compressFilePreviewImpl"),
office("officeFilePreviewImpl"),
@@ -19,12 +20,13 @@ public enum FileType {
media("mediaFilePreviewImpl"),
markdown("markdownFilePreviewImpl"),
xml("xmlFilePreviewImpl"),
flv("flvFilePreviewImpl"),
cad("cadFilePreviewImpl");
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[] 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[] SIMTEXT_TYPES = ConfigConstants.getSimText();
private static final String[] SSIM_TEXT_TYPES = ConfigConstants.getSimText();
private static final String[] MEDIA_TYPES = ConfigConstants.getMedia();
private static final Map<String, FileType> FILE_TYPE_MAPPER = new HashMap<>();
@@ -38,7 +40,7 @@ public enum FileType {
for (String archive : ARCHIVE_TYPES) {
FILE_TYPE_MAPPER.put(archive, FileType.compress);
}
for (String text : SIMTEXT_TYPES) {
for (String text : SSIM_TEXT_TYPES) {
FILE_TYPE_MAPPER.put(text, FileType.simText);
}
for (String media : MEDIA_TYPES) {
@@ -48,11 +50,30 @@ public enum FileType {
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);
}
public static FileType to(String fileType){
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);
}
private final String instanceName;

View File

@@ -4,11 +4,18 @@ 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. 成功
@@ -31,6 +38,30 @@ 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.FileUtils;
import cn.keking.utils.KkFileUtils;
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, FileUtils.getFileEncode(filePath));
ZipFile zipFile = new ZipFile(filePath, KkFileUtils.getFileEncode(filePath));
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
// 排序
entries = sortZipEntries(entries);
@@ -67,7 +67,7 @@ public class CompressFileReader {
}
String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName);
parentName = (level - 1) + "_" + parentName;
FileType type = fileHandlerService.typeFromUrl(childName);
FileType type = FileType.typeFromUrl(childName);
if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl + childName);
}
@@ -120,7 +120,7 @@ public class CompressFileReader {
headersToBeExtracted.add(Collections.singletonMap(childName, header));
}
String parentName = getLast2FileName(fullName, "\\", archiveFileName);
FileType type = fileHandlerService.typeFromUrl(childName);
FileType type = FileType.typeFromUrl(childName);
if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl + childName);
}
@@ -163,7 +163,7 @@ public class CompressFileReader {
}
String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName);
parentName = (level - 1) + "_" + parentName;
FileType type = fileHandlerService.typeFromUrl(childName);
FileType type = FileType.typeFromUrl(childName);
if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl + childName);
}
@@ -382,7 +382,7 @@ public class CompressFileReader {
} catch (IOException e) {
e.printStackTrace();
}
FileUtils.deleteFileByPath(filePath);
KkFileUtils.deleteFileByPath(filePath);
}
private void extractZipFile(String childName, InputStream zipFile) {
@@ -439,7 +439,7 @@ public class CompressFileReader {
} catch (IOException e) {
e.printStackTrace();
}
FileUtils.deleteFileByPath(filePath);
KkFileUtils.deleteFileByPath(filePath);
}
}
@@ -468,7 +468,7 @@ public class CompressFileReader {
} catch (IOException e) {
e.printStackTrace();
}
FileUtils.deleteFileByPath(filePath);
KkFileUtils.deleteFileByPath(filePath);
}
private void extractRarFile(String childName, FileHeader header, Archive archive) {

View File

@@ -10,6 +10,7 @@ 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.
@@ -70,7 +71,7 @@ public class FileConvertQueueTask {
}
} catch (Exception e) {
try {
Thread.sleep(1000*10);
TimeUnit.SECONDS.sleep(10);
} catch (Exception ex){
ex.printStackTrace();
}

View File

@@ -4,13 +4,28 @@ 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.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -21,10 +36,15 @@ 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;
}
@@ -51,35 +71,6 @@ 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);
}
/**
* 从路径中获取文件负
@@ -174,68 +165,87 @@ public class FileHandlerService {
}
/**
* 获取文件后缀
*
* @param url url
* @return 文件后缀
* pdf文件转换成jpg图片集
* @param pdfFilePath pdf文件路径
* @param pdfName pdf文件名称
* @param baseUrl 基础访问地址
* @return 图片访问集合
*/
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);
}
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(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);
private String suffixFromFileName(String fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1);
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;
}
/**
* 获取url中的参数
*
* @param url url
* @param name 参数名
* @return 参数值
* cad文件转pdf
* @param inputFilePath cad文件路径
* @param outputFilePath pdf输出文件路径
* @return 转换是否成功
*/
public String getUrlParameterReg(String url, String name) {
Map<String, String> mapRequest = new HashMap<>();
String strUrlParam = truncateUrlPage(url);
if (strUrlParam == null) {
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;
}
//每个键值为一组
String[] arrSplit = strUrlParam.split("[&]");
for (String strSplit : arrSplit) {
String[] arrSplitEqual = strSplit.split("[=]");
//解析出键值
if (arrSplitEqual.length > 1) {
//正确解析
mapRequest.put(arrSplitEqual[0], arrSplitEqual[1]);
} else if (!arrSplitEqual[0].equals("")) {
//只有参数没有值,不加入
mapRequest.put(arrSplitEqual[0], "");
}
}
return mapRequest.get(name);
}
/**
* 去掉url中的路径留下请求参数部分
*
* @param strURL url地址
* @return url请求参数部分
*/
private String truncateUrlPage(String strURL) {
String strAllParam = null;
strURL = strURL.trim();
String[] arrSplit = strURL.split("[?]");
if (strURL.length() > 1) {
if (arrSplit.length > 1) {
if (arrSplit[1] != null) {
strAllParam = arrSplit[1];
}
}
}
return strAllParam;
}
/**
@@ -249,15 +259,15 @@ public class FileHandlerService {
String suffix;
FileType type;
String fileName;
String fullFileName = this.getUrlParameterReg(url, "fullfilename");
String fullFileName = WebUtils.getUrlParameterReg(url, "fullfilename");
if (StringUtils.hasText(fullFileName)) {
fileName = fullFileName;
type = this.typeFromFileName(fullFileName);
suffix = suffixFromFileName(fullFileName);
type = FileType.typeFromFileName(fullFileName);
suffix = KkFileUtils.suffixFromFileName(fullFileName);
} else {
fileName = getFileNameFromURL(url);
type = typeFromUrl(url);
suffix = suffixFromUrl(url);
fileName = WebUtils.getFileNameFromURL(url);
type = FileType.typeFromUrl(url);
suffix = WebUtils.suffixFromUrl(url);
}
attribute.setType(type);
attribute.setName(fileName);
@@ -265,7 +275,7 @@ public class FileHandlerService {
attribute.setUrl(url);
if (req != null) {
String officePreviewType = req.getParameter("officePreviewType");
String fileKey = req.getParameter("fileKey");
String fileKey = WebUtils.getUrlParameterReg(url,"fileKey");
if (StringUtils.hasText(officePreviewType)) {
attribute.setOfficePreviewType(officePreviewType);
}

View File

@@ -8,5 +8,18 @@ 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 OFFICE_PICTURE_FILE_PREVIEW_PAGE = "officePicture";
String TXT_FILE_PREVIEW_PAGE = "txt";
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,4 +1,4 @@
package cn.keking.extend;
package cn.keking.service;
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 ControlDocumentFormatRegistry extends SimpleDocumentFormatRegistry {
public class OfficePluginExtendFormatRegistry extends SimpleDocumentFormatRegistry {
public ControlDocumentFormatRegistry() {
public OfficePluginExtendFormatRegistry() {
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

@@ -1,7 +1,6 @@
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;
@@ -72,7 +71,7 @@ public class OfficePluginManager {
}
public OfficeDocumentConverter getDocumentConverter() {
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager, new ControlDocumentFormatRegistry());
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager, new OfficePluginExtendFormatRegistry());
converter.setDefaultLoadProperties(getLoadProperties());
return converter;
}

View File

@@ -1,6 +1,8 @@
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;
@@ -10,6 +12,8 @@ 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) {
@@ -21,12 +25,11 @@ 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();
if (!outputFile.getParentFile().exists() && !outputFile.getParentFile().mkdirs()) {
logger.error("创建目录【{}】失败,请检查目录权限!",outputFilePath_end);
}
converter.convert(inputFile, outputFile);
}

View File

@@ -28,19 +28,11 @@ public class CacheServiceRedisImpl implements CacheService {
}
@Override
public void initPDFCachePool(Integer capacity) {
}
public void initPDFCachePool(Integer capacity) { }
@Override
public void initIMGCachePool(Integer capacity) {
}
public void initIMGCachePool(Integer capacity) { }
@Override
public void initPdfImagesCachePool(Integer capacity) {
}
public void initPdfImagesCachePool(Integer capacity) { }
@Override
public void putPDFCache(String key, String value) {

View File

@@ -31,11 +31,8 @@ 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;

View File

@@ -4,10 +4,8 @@ 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;
@@ -22,55 +20,38 @@ import static cn.keking.service.impl.OfficeFilePreviewImpl.getPreviewType;
@Service
public class CadFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final DownloadUtils downloadUtils;
private final CadUtils cadUtils;
private final PdfUtils pdfUtils;
public CadFilePreviewImpl(FileHandlerService fileHandlerService,
DownloadUtils downloadUtils,
CadUtils cadUtils,
PdfUtils pdfUtils) {
this.fileHandlerService = fileHandlerService;
this.downloadUtils = downloadUtils;
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 = model.asMap().get("officePreviewType") == null ? ConfigConstants.getOfficePreviewType() : model.asMap().get("officePreviewType").toString();
String baseUrl = BaseUrlFilter.getBaseUrl();
String suffix=fileAttribute.getSuffix();
String fileName=fileAttribute.getName();
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 (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
}
filePath = response.getContent();
if (StringUtils.hasText(outFilePath)) {
boolean convertResult = cadUtils.cadToPdf(filePath, outFilePath);
boolean convertResult = fileHandlerService.cadToPdf(filePath, outFilePath);
if (!convertResult) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", "cad文件转换异常请联系管理员");
return "fileNotSupported";
return otherFilePreview.notSupportedFile(model, fileAttribute, "cad文件转换异常请联系管理员");
}
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
@@ -79,10 +60,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, pdfUtils, OFFICE_PREVIEW_TYPE_IMAGE);
return getPreviewType(model, fileAttribute, officePreviewType, baseUrl, pdfName, outFilePath, fileHandlerService, OFFICE_PREVIEW_TYPE_IMAGE,otherFilePreview);
}
model.addAttribute("pdfUrl", pdfName);
return "pdf";
return PDF_FILE_PREVIEW_PAGE;
}

View File

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

View File

@@ -0,0 +1,27 @@
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,8 +5,6 @@ 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
@@ -23,7 +21,7 @@ public class MarkdownFilePreviewImpl implements FilePreview {
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
model.addAttribute(TEXT_TYPE,"markdown");
return simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
return MARKDOWN_FILE_PREVIEW_PAGE;
}
}

View File

@@ -8,6 +8,7 @@ import cn.keking.service.FileHandlerService;
import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
/**
* @author : kl
* @authorboke : kailing.pub
@@ -17,25 +18,21 @@ import org.springframework.ui.Model;
@Service
public class MediaFilePreviewImpl implements FilePreview {
private final DownloadUtils downloadUtils;
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
public MediaFilePreviewImpl(DownloadUtils downloadUtils,
FileHandlerService fileHandlerService) {
this.downloadUtils = downloadUtils;
public MediaFilePreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileAttribute.getName());
if (0 != response.getCode()) {
model.addAttribute("fileType", fileAttribute.getSuffix());
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileAttribute.getName());
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
} else {
model.addAttribute("mediaUrl", BaseUrlFilter.getBaseUrl() + fileHandlerService.getRelativePath(response.getContent()));
}
@@ -43,11 +40,7 @@ public class MediaFilePreviewImpl implements FilePreview {
model.addAttribute("mediaUrl", url);
}
model.addAttribute("mediaUrl", url);
String suffix=fileAttribute.getSuffix();
if ("flv".equalsIgnoreCase(suffix)) {
return "flv";
}
return "media";
return MEDIA_FILE_PREVIEW_PAGE;
}

View File

@@ -7,7 +7,6 @@ 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;
@@ -22,40 +21,36 @@ import java.util.List;
@Service
public class OfficeFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final PdfUtils pdfUtils;
private final DownloadUtils downloadUtils;
private final OfficeToPdfService officeToPdfService;
public OfficeFilePreviewImpl(FileHandlerService fileHandlerService, PdfUtils pdfUtils, DownloadUtils downloadUtils, OfficeToPdfService officeToPdfService) {
this.fileHandlerService = fileHandlerService;
this.pdfUtils = pdfUtils;
this.downloadUtils = downloadUtils;
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;
// 判断之前是否已转换过,如果转换过,直接返回,否则执行转换
if (!fileHandlerService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
String filePath;
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, null);
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
}
filePath = response.getContent();
if (StringUtils.hasText(outFilePath)) {
@@ -71,25 +66,23 @@ 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, pdfUtils, OFFICE_PREVIEW_TYPE_IMAGE);
return getPreviewType(model, fileAttribute, officePreviewType, baseUrl, pdfName, outFilePath, fileHandlerService, OFFICE_PREVIEW_TYPE_IMAGE, otherFilePreview);
}
model.addAttribute("pdfUrl", pdfName);
return isHtml ? "html" : "pdf";
return isHtml ? EXEL_FILE_PREVIEW_PAGE : PDF_FILE_PREVIEW_PAGE;
}
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);
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);
if (imageUrls == null || imageUrls.size() < 1) {
model.addAttribute("msg", "office转图片异常请联系管理员");
model.addAttribute("fileType",fileAttribute.getSuffix());
return "fileNotSupported";
return otherFilePreview.notSupportedFile(model, fileAttribute, "office转图片异常请联系管理员");
}
model.addAttribute("imgurls", imageUrls);
model.addAttribute("currentUrl", imageUrls.get(0));
if (officePreviewTypeImage.equals(officePreviewType)) {
return "officePicture";
return OFFICE_PICTURE_FILE_PREVIEW_PAGE;
} else {
return "picture";
return PICTURE_FILE_PREVIEW_PAGE;
}
}
}

View File

@@ -11,10 +11,41 @@ import org.springframework.ui.Model;
*/
@Service
public class OtherFilePreviewImpl implements FilePreview {
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
model.addAttribute("fileType",fileAttribute.getSuffix());
model.addAttribute("msg", "系统还不支持该格式文件的在线预览");
return "fileNotSupported";
return this.notSupportedFile(model, fileAttribute, "系统还不支持该格式文件的在线预览");
}
/**
* 通用的预览失败,导向到不支持的文件响应页面
*
* @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,7 +6,6 @@ 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;
@@ -21,25 +20,17 @@ import java.util.List;
public class PdfFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final PdfUtils pdfUtils;
private final DownloadUtils downloadUtils;
private final OtherFilePreviewImpl otherFilePreview;
private static final String FILE_DIR = ConfigConstants.getFileDir();
public PdfFilePreviewImpl(FileHandlerService fileHandlerService,
PdfUtils pdfUtils,
DownloadUtils downloadUtils) {
public PdfFilePreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService;
this.pdfUtils = pdfUtils;
this.downloadUtils = downloadUtils;
this.otherFilePreview = otherFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String suffix=fileAttribute.getSuffix();
String fileName=fileAttribute.getName();
String fileName = fileAttribute.getName();
String officePreviewType = fileAttribute.getOfficePreviewType();
String baseUrl = BaseUrlFilter.getBaseUrl();
String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + "pdf";
@@ -47,11 +38,9 @@ public class PdfFilePreviewImpl implements FilePreview {
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType)) {
//当文件不存在时,就去下载
if (!fileHandlerService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileName);
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
}
outFilePath = response.getContent();
if (ConfigConstants.isCacheEnabled()) {
@@ -59,28 +48,24 @@ public class PdfFilePreviewImpl implements FilePreview {
fileHandlerService.addConvertedFile(pdfName, fileHandlerService.getRelativePath(outFilePath));
}
}
List<String> imageUrls = pdfUtils.pdf2jpg(outFilePath, pdfName, baseUrl);
List<String> imageUrls = fileHandlerService.pdf2jpg(outFilePath, pdfName, baseUrl);
if (imageUrls == null || imageUrls.size() < 1) {
model.addAttribute("msg", "pdf转图片异常请联系管理员");
model.addAttribute("fileType",fileAttribute.getSuffix());
return "fileNotSupported";
return otherFilePreview.notSupportedFile(model, fileAttribute, "pdf转图片异常请联系管理员");
}
model.addAttribute("imgurls", imageUrls);
model.addAttribute("currentUrl", imageUrls.get(0));
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType)) {
return "officePicture";
return OFFICE_PICTURE_FILE_PREVIEW_PAGE;
} else {
return "picture";
return PICTURE_FILE_PREVIEW_PAGE;
}
} else {
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
if (!fileHandlerService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, pdfName);
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, pdfName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
}
model.addAttribute("pdfUrl", fileHandlerService.getRelativePath(response.getContent()));
if (ConfigConstants.isCacheEnabled()) {
@@ -94,6 +79,6 @@ public class PdfFilePreviewImpl implements FilePreview {
model.addAttribute("pdfUrl", url);
}
}
return "pdf";
return PDF_FILE_PREVIEW_PAGE;
}
}

View File

@@ -19,13 +19,11 @@ import java.util.List;
public class PictureFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
private final DownloadUtils downloadUtils;
public PictureFilePreviewImpl(FileHandlerService fileHandlerService,
DownloadUtils downloadUtils) {
public PictureFilePreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService;
this.downloadUtils = downloadUtils;
this.otherFilePreview = otherFilePreview;
}
@Override
@@ -39,22 +37,20 @@ public class PictureFilePreviewImpl implements FilePreview {
}
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, null);
if (0 != response.getCode()) {
model.addAttribute("fileType", fileAttribute.getSuffix());
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
} 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";
return PICTURE_FILE_PREVIEW_PAGE;
}
}

View File

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

View File

@@ -5,8 +5,6 @@ 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
@@ -20,10 +18,9 @@ public class XmlFilePreviewImpl implements FilePreview {
this.simTextFilePreview = simTextFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
model.addAttribute(TEXT_TYPE,"xml");
return simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
return XML_FILE_PREVIEW_PAGE;
}
}

View File

@@ -1,50 +0,0 @@
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

@@ -1,81 +1,59 @@
package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import cn.keking.hutool.URLUtil;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FileHandlerService;
import io.mola.galimatias.GalimatiasParseException;
import jodd.io.FileUtil;
import jodd.io.NetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
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
*/
@Component
public class DownloadUtils {
private final static Logger logger = LoggerFactory.getLogger(DownloadUtils.class);
private final String fileDir = ConfigConstants.getFileDir();
private static final String fileDir = ConfigConstants.getFileDir();
private static final String URL_PARAM_FTP_USERNAME = "ftp.username";
private static final String URL_PARAM_FTP_PASSWORD = "ftp.password";
private static final String URL_PARAM_FTP_CONTROL_ENCODING = "ftp.control.encoding";
private final FileHandlerService fileHandlerService;
public DownloadUtils(FileHandlerService fileHandlerService) {
this.fileHandlerService = fileHandlerService;
}
/**
* @param fileAttribute fileAttribute
* @param fileName 文件名
* @return 本地文件绝对路径
*/
public ReturnResponse<String> downLoad(FileAttribute fileAttribute, String fileName) {
public static ReturnResponse<String> downLoad(FileAttribute fileAttribute, String fileName) {
String urlStr = fileAttribute.getUrl();
String type = fileAttribute.getSuffix();
ReturnResponse<String> response = new ReturnResponse<>(0, "下载成功!!!", "");
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);
}
String realPath = DownloadUtils.getRelFilePath(fileName, fileAttribute);
try {
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 = fileHandlerService.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME);
String ftpPassword = fileHandlerService.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD);
String ftpControlEncoding = fileHandlerService.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING);
URL url = WebUtils.normalizedURL(urlStr);
if (isHttpUrl(url)) {
File realFile = new File(realPath);
NetUtil.downloadFile(url.toString(),realFile);
} else if (isFtpUrl(url)) {
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())) {
this.convertTextPlainFileCharsetToUtf8(realPath);
}
return response;
} catch (IOException e) {
} catch (IOException | GalimatiasParseException e) {
logger.error("文件下载失败url{}", urlStr, e);
response.setCode(1);
response.setContent(null);
@@ -88,82 +66,27 @@ public class DownloadUtils {
}
}
public 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);
}
}
return getBytesFromStream(is);
}
public void saveBytesToOutStream(byte[] b, OutputStream os) throws IOException {
os.write(b);
os.close();
}
private 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 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 文件路径
* @param fileName 文件
* @return 文件路径
*/
private 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());
}
}
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);
}
String realPath = fileDir + fileName;
File dirFile = new File(fileDir);
if (!dirFile.exists() && !dirFile.mkdirs()) {
logger.error("创建目录【{}】失败,可能是权限不够,请检查", fileDir);
}
return realPath;
}
}

View File

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

View File

@@ -1,79 +0,0 @@
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

@@ -0,0 +1,100 @@
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;
/**
* @author : kl
* 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中的参数
*
* @param url url
* @param name 参数名
* @return 参数值
*/
public static String getUrlParameterReg(String url, String name) {
Map<String, String> mapRequest = new HashMap<>();
String strUrlParam = truncateUrlPage(url);
if (strUrlParam == null) {
return "";
}
//每个键值为一组
String[] arrSplit = strUrlParam.split("[&]");
for (String strSplit : arrSplit) {
String[] arrSplitEqual = strSplit.split("[=]");
//解析出键值
if (arrSplitEqual.length > 1) {
//正确解析
mapRequest.put(arrSplitEqual[0], arrSplitEqual[1]);
} else if (!arrSplitEqual[0].equals("")) {
//只有参数没有值,不加入
mapRequest.put(arrSplitEqual[0], "");
}
}
return mapRequest.get(name);
}
/**
* 去掉url中的路径留下请求参数部分
*
* @param strURL url地址
* @return url请求参数部分
*/
private static String truncateUrlPage(String strURL) {
String strAllParam = null;
strURL = strURL.trim();
String[] arrSplit = strURL.split("[?]");
if (strURL.length() > 1) {
if (arrSplit.length > 1) {
if (arrSplit[1] != null) {
strAllParam = arrSplit[1];
}
}
}
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

@@ -28,9 +28,7 @@ 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)
@@ -49,19 +47,19 @@ public class FileController {
}
// 判断是否存在同名文件
if (existsFile(fileName)) {
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "存在同名文件,请先删除原有文件再次上传", null));
return new ObjectMapper().writeValueAsString(ReturnResponse.failure("存在同名文件,请先删除原有文件再次上传"));
}
File outFile = new File(fileDir + demoPath);
if (!outFile.exists()) {
outFile.mkdirs();
if (!outFile.exists() && !outFile.mkdirs()) {
logger.error("创建文件夹【{}】失败,请检查目录权限!",fileDir + demoPath);
}
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(new ReturnResponse<String>(0, "SUCCESS", null));
return new ObjectMapper().writeValueAsString(ReturnResponse.success(null));
} catch (IOException e) {
logger.error("文件上传失败", e);
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "FAILURE", null));
return new ObjectMapper().writeValueAsString(ReturnResponse.failure());
}
}
@@ -72,10 +70,10 @@ public class FileController {
}
File file = new File(fileDir + demoPath + fileName);
logger.info("删除文件:{}", file.getAbsolutePath());
if (file.exists()) {
file.delete();
if (file.exists() && !file.delete()) {
logger.error("删除文件【{}】失败,请检查目录权限!",file.getPath());
}
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(0, "SUCCESS", null));
return new ObjectMapper().writeValueAsString(ReturnResponse.success());
}
@RequestMapping(value = "listFiles", method = RequestMethod.GET)
@@ -84,7 +82,7 @@ public class FileController {
File file = new File(fileDir + demoPath);
if (file.exists()) {
Arrays.stream(Objects.requireNonNull(file.listFiles())).forEach(file1 -> {
Map<String, String> fileName = new HashMap();
Map<String, String> fileName = new HashMap<>();
fileName.put("fileName", demoDir + "/" + file1.getName());
list.add(fileName);
});

View File

@@ -5,12 +5,18 @@ import cn.keking.service.FilePreview;
import cn.keking.service.FilePreviewFactory;
import cn.keking.service.cache.CacheService;
import cn.keking.utils.DownloadUtils;
import cn.keking.service.impl.OtherFilePreviewImpl;
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 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.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@@ -18,75 +24,96 @@ import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
import java.net.URL;
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 DownloadUtils downloadUtils;
private final OtherFilePreviewImpl otherFilePreview;
public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService, DownloadUtils downloadUtils) {
public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService, OtherFilePreviewImpl otherFilePreview) {
this.previewFactory = filePreviewFactory;
this.fileHandlerService = fileHandlerService;
this.cacheService = cacheService;
this.downloadUtils = downloadUtils;
this.otherFilePreview = otherFilePreview;
}
@RequestMapping(value = "/onlinePreview")
public String onlinePreview(String url, Model model, HttpServletRequest req) {
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(url,req);
String fileUrl;
try {
fileUrl = new String(Base64.decodeBase64(url));
} 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);
FilePreview filePreview = previewFactory.get(fileAttribute);
logger.info("预览文件url{}previewType{}", url, fileAttribute.getType());
return filePreview.filePreviewHandle(url, model, fileAttribute);
logger.info("预览文件url{}previewType{}", fileUrl, fileAttribute.getType());
return filePreview.filePreviewHandle(fileUrl, model, fileAttribute);
}
@RequestMapping(value = "/picturesPreview")
public String picturesPreview(Model model, HttpServletRequest req) throws UnsupportedEncodingException {
String urls = req.getParameter("urls");
String currentUrl = req.getParameter("currentUrl");
logger.info("预览文件url{}urls{}", currentUrl, urls);
// 路径转码
String decodedUrl = URLDecoder.decode(urls, "utf-8");
String decodedCurrentUrl = URLDecoder.decode(currentUrl, "utf-8");
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[] imgs = decodedUrl.split("\\|");
List<String> imgUrls = Arrays.asList(imgs);
String[] images = fileUrls.split("\\|");
List<String> imgUrls = Arrays.asList(images);
model.addAttribute("imgUrls", imgUrls);
model.addAttribute("currentUrl",decodedCurrentUrl);
return "picture";
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;
}
/**
* 根据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 {
byte[] bytes = downloadUtils.getBytesFromUrl(urlPath);
downloadUtils.saveBytesToOutStream(bytes, response.getOutputStream());
} catch (IOException e) {
URL url = WebUtils.normalizedURL(urlPath);
byte[] bytes = NetUtil.downloadBytes(url.toString());
IOUtils.write(bytes, response.getOutputStream());
} catch (IOException | GalimatiasParseException e) {
logger.error("下载跨域pdf文件异常url{}", urlPath, e);
}
}
/**
* 通过api接口入队
*
* @param url 请编码后在入队
*/
@RequestMapping("/addTask")

View File

@@ -38,7 +38,7 @@ public class BaseUrlFilter implements Filter {
pathBuilder.append(request.getScheme()).append("://").append(request.getServerName()).append(":")
.append(request.getServerPort()).append(((HttpServletRequest) request).getContextPath()).append("/");
String baseUrlTmp = ConfigConstants.getBaseUrl();
if (baseUrlTmp != null && !ConfigConstants.DEFAULT_BASE_URL.equals(baseUrlTmp.toLowerCase())) {
if (baseUrlTmp != null && !ConfigConstants.DEFAULT_BASE_URL.equalsIgnoreCase(baseUrlTmp)) {
if (!baseUrlTmp.endsWith("/")) {
baseUrlTmp = baseUrlTmp.concat("/");
}

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,49 @@
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"/>
<script src="js/jquery-3.0.0.min.js" type="text/javascript"></script>
<script src="js/jquery.form.min.js" type="text/javascript"></script>
<script src="bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<script src="js/watermark.js" type="text/javascript"></script>
<script src="js/base64.min.js" type="text/javascript"></script>
<script>
/**
* 初始化水印
*/
function initWaterMark() {
let watermarkTxt = '${watermarkTxt}';
if (watermarkTxt !== '') {
watermark.init({
watermark_txt: '${watermarkTxt}',
watermark_x: 0,
watermark_y: 0,
watermark_rows: 0,
watermark_cols: 0,
watermark_x_space: ${watermarkXSpace},
watermark_y_space: ${watermarkYSpace},
watermark_font: '${watermarkFont}',
watermark_fontsize: '${watermarkFontsize}',
watermark_color: '${watermarkColor}',
watermark_alpha: ${watermarkAlpha},
watermark_width: ${watermarkWidth},
watermark_height: ${watermarkHeight},
watermark_angle: ${watermarkAngle},
});
}
}
</script>
<style>
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
}
</style>

View File

@@ -5,15 +5,8 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0">
<link href="css/zTreeStyle.css" rel="stylesheet" type="text/css">
<#include "*/commonHeader.ftl">
<style type="text/css">
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
}
body {
background-color: #404040;
}
@@ -23,9 +16,6 @@
h6 {font-weight: normal;font-size: 12px;letter-spacing: 1px;line-height: 24px;text-align: center;}
a {color:#3C6E31;text-decoration: underline;}
a:hover {background-color:#3C6E31;color:white;}
input.radio {margin: 0 2px 0 8px;}
input.radio.first {margin-left:0;}
input.empty {color: lightgray;}
code {color: #2f332a;}
div.zTreeDemoBackground {width:600px;text-align:center;margin: 0 auto;background-color: #ffffff;}
</style>
@@ -35,11 +25,11 @@
<div class="zTreeDemoBackground left">
<ul id="treeDemo" class="ztree"></ul>
</div>
<script src="js/watermark.js" type="text/javascript"></script>
<script type="text/javascript" src="js/jquery-3.0.0.min.js"></script>
<script type="text/javascript" src="js/jquery.ztree.core.js"></script>
<script type="text/javascript">
var data = JSON.parse('${fileTree}');
const data = JSON.parse('${fileTree}');
var baseUrl = "${baseUrl}";
var setting = {
view: {
fontCss : {"color":"blue"},
@@ -71,8 +61,8 @@
} else {
fulls += ",resizable"; // 对于不支持screen属性的浏览器可以手工进行最大化。 manually
}
window.open("onlinePreview?url="
+ encodeURIComponent("${baseUrl}" + treeNode.fileName)+"&fileKey="+ encodeURIComponent(treeNode.fileKey), "_blank",fulls);
var previewUrl = baseUrl + treeNode.fileName +"?fileKey="+ treeNode.fileKey;
window.open("onlinePreview?url=" + encodeURIComponent(Base64.encode(previewUrl)), "_blank",fulls);
}
}
}
@@ -87,25 +77,7 @@
/*初始化水印*/
window.onload = function() {
var watermarkTxt = '${watermarkTxt}';
if (watermarkTxt !== '') {
watermark.init({
watermark_txt: '${watermarkTxt}',
watermark_x: 0,
watermark_y: 0,
watermark_rows: 0,
watermark_cols: 0,
watermark_x_space: ${watermarkXSpace},
watermark_y_space: ${watermarkYSpace},
watermark_font: '${watermarkFont}',
watermark_fontsize: '${watermarkFontsize}',
watermark_color:'${watermarkColor}',
watermark_alpha: ${watermarkAlpha},
watermark_width: ${watermarkWidth},
watermark_height: ${watermarkHeight},
watermark_angle: ${watermarkAngle},
});
}
initWaterMark();
}
/**

View File

@@ -32,7 +32,7 @@
<div class="container">
<img src="images/sorry.jpg" />
<span>
该文件类型(${fileType})系统暂时不支持在线预览,<b>说明</b>
该文件类型(${file.suffix})系统暂时不支持在线预览,<b>说明</b>
<p style="color: red;">${msg}</p>
有任何疑问,请加&nbsp;<a href="https://jq.qq.com/?_wv=1027&k=5c0UAtu">官方QQ群613025121</a>&nbsp;咨询
</span>

View File

@@ -3,18 +3,10 @@
<head>
<meta charset="utf-8" />
<title>多媒体文件预览</title>
<script src="js/flv.min.js"type="text/javascript"></script>
<script src="js/watermark.js" type="text/javascript"></script>
<script src="js/flv.min.js" type="text/javascript"></script>
<#include "*/commonHeader.ftl">
</head>
<style>
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
}
body {
background-color: #404040;
}
@@ -40,25 +32,7 @@
}
/*初始化水印*/
window.onload = function() {
var watermarkTxt = '${watermarkTxt}';
if (watermarkTxt !== '') {
watermark.init({
watermark_txt: '${watermarkTxt}',
watermark_x: 0,
watermark_y: 0,
watermark_rows: 0,
watermark_cols: 0,
watermark_x_space: ${watermarkXSpace},
watermark_y_space: ${watermarkYSpace},
watermark_font: '${watermarkFont}',
watermark_fontsize: '${watermarkFontsize}',
watermark_color:'${watermarkColor}',
watermark_alpha: ${watermarkAlpha},
watermark_width: ${watermarkWidth},
watermark_height: ${watermarkHeight},
watermark_angle: ${watermarkAngle},
});
}
initWaterMark();
}
</script>
</body>

View File

@@ -4,21 +4,11 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0">
<style type="text/css">
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
}
</style>
<#include "*/commonHeader.ftl">
</head>
<body>
<iframe src="${pdfUrl}" width="100%" frameborder="0"></iframe>
</body>
<script src="js/watermark.js" type="text/javascript"></script>
<script type="text/javascript">
document.getElementsByTagName('iframe')[0].height = document.documentElement.clientHeight-10;
/**
@@ -30,25 +20,7 @@
}
/*初始化水印*/
window.onload = function() {
var watermarkTxt = '${watermarkTxt}';
if (watermarkTxt !== '') {
watermark.init({
watermark_txt: '${watermarkTxt}',
watermark_x: 0,
watermark_y: 0,
watermark_rows: 0,
watermark_cols: 0,
watermark_x_space: ${watermarkXSpace},
watermark_y_space: ${watermarkYSpace},
watermark_font: '${watermarkFont}',
watermark_fontsize: '${watermarkFontsize}',
watermark_color:'${watermarkColor}',
watermark_alpha: ${watermarkAlpha},
watermark_width: ${watermarkWidth},
watermark_height: ${watermarkHeight},
watermark_angle: ${watermarkAngle},
});
}
initWaterMark();
}
</script>
</html>

View File

@@ -2,50 +2,49 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0" />
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0"/>
<title>kkFileView演示首页</title>
<link rel="stylesheet" href="css/viewer.min.css" />
<link rel="stylesheet" href="css/loading.css" />
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" href="bootstrap-table/bootstrap-table.min.css" />
<link rel="stylesheet" href="gitalk/gitalk.css" />
<link rel="stylesheet" href="css/viewer.min.css"/>
<link rel="stylesheet" href="css/loading.css"/>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"/>
<link rel="stylesheet" href="bootstrap-table/bootstrap-table.min.css"/>
<link rel="stylesheet" href="gitalk/gitalk.css"/>
<script type="text/javascript" src="js/jquery-3.0.0.min.js"></script>
<script type="text/javascript" src="js/jquery.form.min.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="bootstrap-table/bootstrap-table.min.js"></script>
<script type="text/javascript" src="gitalk/gitalk.min.js"></script>
<script type="text/javascript" src="js/base64.min.js"></script>
</head>
<body>
<h1>文件预览项目接入和测试界面</h1>
<div class="panel-group" id="accordion">
<div class="panel-group container" id="accordion">
<h1>文件预览项目接入和测试界面</h1>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion"
href="#collapseOne">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne">
接入说明
</a>
</h4>
</div>
<div id="collapseOne" class="panel-collapse collapse">
<div class="panel-body">
<div>
如果你的项目需要接入文件预览项目达到对docx、excel、ppt、jpg等文件的预览效果那么通过在你的项目中加入下面的代码就可以
成功实现:
<pre style="background-color: #2f332a;color: #cccccc">
var url = 'http://127.0.0.1:8080/file/test.txt'; //要预览文件的访问地址
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(url));
</pre>
</div>
<div>
新增多图片同时预览功能,接口如下:
<pre style="background-color: #2f332a;color: #cccccc">
var fileUrl =url1+"|"+"url2";//多文件使用“|”字符隔开
window.open('http://127.0.0.1:8012/picturesPreview?urls='+encodeURIComponent(fileUrl));
</pre>
</div>
<div class="panel-body">
<div>
如果你的项目需要接入文件预览项目达到对docx、excel、ppt、jpg等文件的预览效果那么通过在你的项目中加入下面的代码就可以
成功实现:
<pre style="background-color: #2f332a;color: #cccccc">
var url = 'http://127.0.0.1:8080/file/test.txt'; //要预览文件的访问地址
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(base64Encode(url)));
</pre>
</div>
<div>
新增多图片同时预览功能,接口如下:
<pre style="background-color: #2f332a;color: #cccccc">
var fileUrl =url1+"|"+"url2";//多文件使用“|”字符隔开
window.open('http://127.0.0.1:8012/picturesPreview?urls='+encodeURIComponent(base64Encode(fileUrl)));
</pre>
</div>
</div>
</div>
@@ -58,86 +57,100 @@ window.open('http://127.0.0.1:8012/picturesPreview?urls='+encodeURIComponent(fil
</a>
</h4>
</div>
<div id="collapseTwo" class="panel-collapse collapse">
<div class="panel-body">
<div style="padding: 10px">
<form enctype="multipart/form-data" id="fileUpload">
<input type="file" name="file" />
<input type="button" id="btnsubmit" value=" " />
</form>
</div>
<div>
<table id="table" data-pagination="true"></table>
</div>
<div class="panel-body">
<div style="padding: 10px">
<form enctype="multipart/form-data" id="fileUpload">
<input type="file" name="file"/>
<input type="button" id="btnSubmit" value=" "/>
</form>
</div>
<div>
<table id="table" data-pagination="true"></table>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion"
href="#collapseThree">
更新记录
<a data-toggle="collapse" data-parent="#accordion" href="#collapseThree">
发版记录
</a>
</h4>
</div>
<div id="collapseThree" class="panel-collapse collapse in">
<div class="panel-body">
<div>
2020年05月20日 <br>
1. 新增支持全局水印,并支持通过参数动态改变水印内容<br>
2. 新增支持CAD文件预览<br>
3. 新增base.url配置支持使用nginx反向代理和使用context-path<br>
4. 支持所有配置项支持从环境变量里读取方便Docker镜像部署和集群中大规模使用<br>
5. 支持配置限信任站点(只能预览来自信任点的文件源),保护预览服务不被滥用<br>
6. 支持配置自定义缓存清理时间cron表达式<br>
7. 全部能识别的纯文本直接预览,不用再转跳下载,如.md .java .py等<br>
8. 支持配置限制转换后的PDF文件下载<br>
9. 优化maven打包配置解决 .sh 脚本可能出现换行符问题<br>
10. 将前端所有CDN依赖放到本地方便没有外网连接的用户使用<br>
11. 首页评论服务由搜狐畅言切换到Gitalk<br>
12. 修复url中包含特殊字符可能会引起的预览异常<br>
13. 修复转换文件队列addTask异常<br>
14. 修复其他已经问题<br>
15. 官网建设:<a href="https://kkfileview.keking.cn">https://kkfileview.keking.cn</a><br>
16. 官方Docker镜像仓库建设<a href="https://hub.docker.com/r/keking/kkfileview">https://hub.docker.com/r/keking/kkfileview</a><br><br>
<div class="panel-body">
<div>
2020年12月27日 <br>
2020年年终大版本更新架构全面设计代码全面重构代码质量全面提升二次开发更便捷欢迎拉源码品鉴提issue、pr共同建设<br>
1. 架构模块调整,大量的代码重构代码质量提升N个等级欢迎品鉴<br>
2. 增强XML文件预览效果新增XML文档数结构预览<br>
3. 新增markdown文件预览支持预览支持md渲染和源文本切换支持<br>
4. 切换底层web server为jetty解决这个issue<a href="https://github.com/kekingcn/kkFileView/issues/168">#issues/168</a><br>
5. 引入cpdetector解决文件编码识别问题<br>
6. url采用base64+urlencode双编码彻底解决各种奇葩文件名预览问题<br>
7. 新增配置项office.preview.switch.disabled控制offic文件预览切换开关<br>
8. 优化文本类型文件预览逻辑采用Base64传输内容避免预览时再次请求文件内容<br>
9. office预览图片模式禁用图片放大效果达到图片和pdf预览效果一致的体验<br>
10. 直接代码静态设置pdfbox兼容低版本jdk在IDEA中运行也不会有警告提示<br>
11. 移除guavahutool等非必须的工具包减少代码体积<br>
12. Office组件加载异步化提速应用启动速度最快到5秒内<br>
13. 合理设置预览消费队列的线程数<br>
14. 修复压缩包里文件再次预览失败的bug<br>
15. 修复图片预览的bug<br><br>
2019年06月18 <br>
1. 支持自动清理缓存及预览文件<br>
2. 支持http/https下载流url文件预览<br>
3. 支持FTP url文件预览<br>
4. 加入Docker构建<br><br>
2020年05月20 <br>
1. 新增支持全局水印并支持通过参数动态改变水印内容<br>
2. 新增支持CAD文件预览<br>
3. 新增base.url配置支持使用nginx反向代理和使用context-path<br>
4. 支持所有配置项支持从环境变量里读取方便Docker镜像部署和集群中大规模使用<br>
5. 支持配置限信任站点只能预览来自信任点的文件源保护预览服务不被滥用<br>
6. 支持配置自定义缓存清理时间cron表达式<br>
7. 全部能识别的纯文本直接预览不用再转跳下载.md .java .py等<br>
8. 支持配置限制转换后的PDF文件下载<br>
9. 优化maven打包配置解决 .sh 脚本可能出现换行符问题<br>
10. 将前端所有CDN依赖放到本地方便没有外网连接的用户使用<br>
11. 首页评论服务由搜狐畅言切换到Gitalk<br>
12. 修复url中包含特殊字符可能会引起的预览异常<br>
13. 修复转换文件队列addTask异常<br>
14. 修复其他已经问题<br>
15. 官网建设<a href="https://kkfileview.keking.cn">https://kkfileview.keking.cn</a><br>
16. 官方Docker镜像仓库建设<a href="https://hub.docker.com/r/keking/kkfileview">https://hub.docker.com/r/keking/kkfileview</a><br><br>
2019年04月08日 <br>
1. 缓存及队列实现抽象提供JDK和REDIS两种实现(REDIS成为可选依赖)<br>
2. 打包方式提供zip和tar.gz包并提供一键启动脚本<br><br>
2019年06月18日 <br>
1. 支持自动清理缓存及预览文件<br>
2. 支持http/https下载流url文件预览<br>
3. 支持FTP url文件预览<br>
4. 加入Docker构建<br><br>
2018年01月19 <br>
1. 大文件入队提前处理<br>
1. 新增addTask文件转换入队接口<br>
1. 采用redis队列支持kkFIleView接口和异构系统入队两种方式<br><br>
2019年04月08 <br>
1. 缓存及队列实现抽象提供JDK和REDIS两种实现(REDIS成为可选依赖)<br>
2. 打包方式提供zip和tar.gz包并提供一键启动脚本<br><br>
2018年01月15 <br>
1.首页新增社会化评论框<br><br>
2018年01月19 <br>
1. 大文件入队提前处理<br>
1. 新增addTask文件转换入队接口<br>
1. 采用redis队列支持kkFIleView接口和异构系统入队两种方式<br><br>
2018年01月12 <br>
1.新增多图片同时预览<br>
2.支持压缩包内图片轮番预览<br><br>
2018年01月15 <br>
1.首页新增社会化评论框<br><br>
2018年01月02日 <br>
1.修复txt等文本编码问题导致预览乱码<br>
2.修复项目模块依赖引入不到的问题<br>
3.新增spring boot profile支持多环境配置<br>
4.引入pdf.js预览doc等文件支持doc标题生成pdf预览菜单支持手机端预览<br><br>
2018年01月12日 <br>
1.新增多图片同时预览<br>
2.支持压缩包内图片轮番预览<br><br>
2017年12月12日<br>
1.项目gitee开源:<a href="https://gitee.com/kekingcn/file-online-preview" target="_blank">https://gitee.com/kekingcn/file-online-preview</a><br>
2.项目github开源:<a href="https://github.com/kekingcn/kkFileView" target="_blank">https://github.com/kekingcn/kkFileView</a>
</div>
2018年01月02日 <br>
1.修复txt等文本编码问题导致预览乱码<br>
2.修复项目模块依赖引入不到的问题<br>
3.新增spring boot profile支持多环境配置<br>
4.引入pdf.js预览doc等文件支持doc标题生成pdf预览菜单支持手机端预览<br><br>
2017年12月12日<br>
1.项目gitee开源:<a href="https://gitee.com/kekingcn/file-online-preview" target="_blank">https://gitee.com/kekingcn/file-online-preview</a><br>
2.项目github开源:<a href="https://github.com/kekingcn/kkFileView" target="_blank">https://github.com/kekingcn/kkFileView</a>
</div>
</div>
<div class="panel-body">
<div id = "comments"></div>
<div id="comments"></div>
</div>
</div>
@@ -171,9 +184,9 @@ window.open('http://127.0.0.1:8012/picturesPreview?urls='+encodeURIComponent(fil
url: '${baseUrl}deleteFile?fileName=' + encodeURIComponent(fileName),
success: function (data) {
// 删除完成刷新table
if (1 == data.code) {
if (1 === data.code) {
alert(data.msg);
} else{
} else {
$('#table').bootstrapTable('refresh', {});
}
},
@@ -182,6 +195,7 @@ window.open('http://127.0.0.1:8012/picturesPreview?urls='+encodeURIComponent(fil
}
})
}
$(function () {
$('#table').bootstrapTable({
url: 'listFiles',
@@ -192,14 +206,14 @@ window.open('http://127.0.0.1:8012/picturesPreview?urls='+encodeURIComponent(fil
field: 'action',
title: '操作'
},]
}).on('pre-body.bs.table', function (e,data) {
}).on('pre-body.bs.table', function (e, data) {
// 每个data添加一列用来操作
$(data).each(function (index, item) {
item.action = "<a class='btn btn-default' target='_blank' href='${baseUrl}onlinePreview?url="+ encodeURIComponent('${baseUrl}' + item.fileName) +"'>预览</a>" +
"<a class='btn btn-default' href='javascript:void(0);' onclick='deleteFile(\""+item.fileName+"\")'>删除</a>";
item.action = "<a class='btn btn-default' target='_blank' href='${baseUrl}onlinePreview?url=" + encodeURIComponent(Base64.encode('${baseUrl}' + item.fileName)) + "'>预览</a>" +
"<a class='btn btn-default' href='javascript:void(0);' onclick='deleteFile(\"" + item.fileName + "\")'>删除</a>";
});
return data;
}).on('post-body.bs.table', function (e,data) {
}).on('post-body.bs.table', function (e, data) {
return data;
});
@@ -209,12 +223,12 @@ window.open('http://127.0.0.1:8012/picturesPreview?urls='+encodeURIComponent(fil
$(".loading_container").css("height", height).show();
}
$("#btnsubmit").click(function () {
$("#btnSubmit").click(function () {
showLoadingDiv();
$("#fileUpload").ajaxSubmit({
success: function (data) {
// 上传完成刷新table
if (1 == data.code) {
if (1 === data.code) {
alert(data.msg);
} else {
$('#table').bootstrapTable('refresh', {});

View File

@@ -0,0 +1,73 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0">
<title>markdown文本预览</title>
<script src="js/marked.min.js" type="text/javascript"></script>
<#include "*/commonHeader.ftl">
</head>
<body>
<input hidden id="textData" value="${textData}"/>
<div class="container">
<div class="panel panel-default">
<div id="markdown_btn" class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne">
${file.name}
</a>
</h4>
</div>
<div id="text_btn" class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne">
${file.name}
</a>
</h4>
</div>
<div class="panel-body">
<div id="markdown"></div>
</div>
</div>
</div>
<script>
/**
* 初始化
*/
window.onload = function () {
$("#markdown_btn").hide()
initWaterMark();
loadMarkdown();
}
/**
* 加载markdown
*/
function loadMarkdown() {
var textData = Base64.decode($("#textData").val())
window.textPreData = "<pre style='background-color: #FFFFFF;border:none'>" + textData + "</pre>";
window.textMarkdownData = marked(textData);
$("#markdown").html(window.textMarkdownData);
}
$(function () {
$("#markdown_btn").click(function () {
$("#markdown").html(window.textMarkdownData);
$("#text_btn").show()
$("#markdown_btn").hide()
});
$("#text_btn").click(function () {
$("#markdown_btn").show()
$("#text_btn").hide();
$("#markdown").html(window.textPreData);
});
});
</script>
</body>
</html>

View File

@@ -1,23 +1,16 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<meta charset="utf-8"/>
<title>多媒体文件预览</title>
<link rel="stylesheet" href="plyr/plyr.css" />
<link rel="stylesheet" href="plyr/plyr.css"/>
<script type="text/javascript" src="plyr/plyr.js"></script>
<script type="text/javascript" src="js/watermark.js"></script>
<#include "*/commonHeader.ftl">
<style>
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
}
body {
background-color: #404040;
}
.m {
width: 1024px;
margin: 0 auto;
@@ -27,31 +20,13 @@
<body>
<div class="m">
<video>
<source src="${mediaUrl}" />
<source src="${mediaUrl}"/>
</video>
</div>
<script>
plyr.setup();
window.onload = function() {
var watermarkTxt = '${watermarkTxt}';
if (watermarkTxt !== '') {
watermark.init({
watermark_txt: '${watermarkTxt}',
watermark_x: 0,
watermark_y: 0,
watermark_rows: 0,
watermark_cols: 0,
watermark_x_space: ${watermarkXSpace},
watermark_y_space: ${watermarkYSpace},
watermark_font: '${watermarkFont}',
watermark_fontsize: '${watermarkFontsize}',
watermark_color:'${watermarkColor}',
watermark_alpha: ${watermarkAlpha},
watermark_width: ${watermarkWidth},
watermark_height: ${watermarkHeight},
watermark_angle: ${watermarkAngle},
});
}
window.onload = function () {
initWaterMark();
}
</script>
</body>

View File

@@ -4,15 +4,8 @@
<meta charset="utf-8" />
<title>PDF图片预览</title>
<script src="js/lazyload.js"></script>
<#include "*/commonHeader.ftl">
<style>
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
}
body {
background-color: #404040;
}
@@ -37,29 +30,10 @@
<#if "false" == switchDisabled>
<img src="images/pdf.svg" width="63" height="63" style="position: fixed; cursor: pointer; top: 40%; right: 48px; z-index: 999;" alt="使用PDF预览" title="使用PDF预览" onclick="changePreviewType('pdf')"/>
</#if>
<script src="js/watermark.js" type="text/javascript"></script>
<script>
window.onload = function () {
/*初始化水印*/
var watermarkTxt = '${watermarkTxt}';
if (watermarkTxt !== '') {
watermark.init({
watermark_txt: '${watermarkTxt}',
watermark_x: 0,
watermark_y: 0,
watermark_rows: 0,
watermark_cols: 0,
watermark_x_space: ${watermarkXSpace},
watermark_y_space: ${watermarkYSpace},
watermark_font: '${watermarkFont}',
watermark_fontsize: '${watermarkFontsize}',
watermark_color:'${watermarkColor}',
watermark_alpha: ${watermarkAlpha},
watermark_width: ${watermarkWidth},
watermark_height: ${watermarkHeight},
watermark_angle: ${watermarkAngle},
});
}
initWaterMark();
checkImgs();
};
window.onscroll = throttle(checkImgs);

View File

@@ -2,41 +2,33 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0">
<title>PDF预览</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
}
</style>
<#include "*/commonHeader.ftl">
</head>
<body>
<#if pdfUrl?contains("http://") || pdfUrl?contains("https://")>
<#assign finalUrl="${pdfUrl}">
<#else>
<#assign finalUrl="${baseUrl}${pdfUrl}">
</#if>
<iframe src="" width="100%" frameborder="0"></iframe>
<#if "false" == switchDisabled>
<img src="images/jpg.svg" width="63" height="63" style="position: fixed; cursor: pointer; top: 40%; right: 48px; z-index: 999;" alt="使用图片预览" title="使用图片预览" onclick="goForImage()"/>
</#if>
<#if pdfUrl?contains("http://") || pdfUrl?contains("https://")>
<#assign finalUrl="${pdfUrl}">
<#else>
<#assign finalUrl="${baseUrl}${pdfUrl}">
</#if>
<iframe src="" width="100%" frameborder="0"></iframe>
<#if "false" == switchDisabled>
<img src="images/jpg.svg" width="63" height="63"
style="position: fixed; cursor: pointer; top: 40%; right: 48px; z-index: 999;" alt="使用图片预览" title="使用图片预览"
onclick="goForImage()"/>
</#if>
</body>
<script src="js/watermark.js" type="text/javascript"></script>
<script type="text/javascript">
document.getElementsByTagName('iframe')[0].src = "${baseUrl}pdfjs/web/viewer.html?base=${baseUrl}&file="+encodeURIComponent('${finalUrl}')+"&disabledownload=${pdfDownloadDisable}";
document.getElementsByTagName('iframe')[0].height = document.documentElement.clientHeight-10;
document.getElementsByTagName('iframe')[0].src = "${baseUrl}pdfjs/web/viewer.html?base=${baseUrl}&file=" + encodeURIComponent('${finalUrl}') + "&disabledownload=${pdfDownloadDisable}";
document.getElementsByTagName('iframe')[0].height = document.documentElement.clientHeight - 10;
/**
* 页面变化调整高度
*/
window.onresize = function(){
window.onresize = function () {
var fm = document.getElementsByTagName("iframe")[0];
fm.height = window.document.documentElement.clientHeight-10;
fm.height = window.document.documentElement.clientHeight - 10;
}
function goForImage() {
@@ -46,29 +38,12 @@
} else {
url = url + "&officePreviewType=image";
}
window.location.href=url;
window.location.href = url;
}
/*初始化水印*/
window.onload = function() {
var watermarkTxt = '${watermarkTxt}';
if (watermarkTxt !== '') {
watermark.init({
watermark_txt: '${watermarkTxt}',
watermark_x: 0,
watermark_y: 0,
watermark_rows: 0,
watermark_cols: 0,
watermark_x_space: ${watermarkXSpace},
watermark_y_space: ${watermarkYSpace},
watermark_font: '${watermarkFont}',
watermark_fontsize: '${watermarkFontsize}',
watermark_color:'${watermarkColor}',
watermark_alpha: ${watermarkAlpha},
watermark_width: ${watermarkWidth},
watermark_height: ${watermarkHeight},
watermark_angle: ${watermarkAngle},
});
}
window.onload = function () {
initWaterMark();
}
</script>
</html>

View File

@@ -4,25 +4,20 @@
<meta charset="utf-8" />
<title>图片预览</title>
<link rel="stylesheet" href="css/viewer.min.css">
<script src="js/viewer.min.js"></script>
<#include "*/commonHeader.ftl">
<style>
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
}
body {
background-color: #404040;
}
#dowebok { width: 800px; margin: 0 auto; font-size: 0;}
#dowebok li { display: inline-block;width: 50px;height: 50px; margin-left: 1%; padding-top: 1%;}
#image { width: 800px; margin: 0 auto; font-size: 0;}
#image li { display: inline-block;width: 50px;height: 50px; margin-left: 1%; padding-top: 1%;}
/*#dowebok li img { width: 200%;}*/
</style>
</head>
<body>
<ul id="dowebok">
<ul id="image">
<#list imgUrls as img>
<#if img?contains("http://") || img?contains("https://")>
<#assign img="${img}">
@@ -32,11 +27,9 @@
<li><img id="${img}" url="${img}" src="${img}" width="1px" height="1px"></li>
</#list>
</ul>
<script src="js/jquery-3.0.0.min.js"></script>
<script src="js/viewer.min.js"></script>
<script src="js/watermark.js" type="text/javascript"></script>
<script>
var viewer = new Viewer(document.getElementById('dowebok'), {
var viewer = new Viewer(document.getElementById('image'), {
url: 'src',
navbar: false,
button: false,
@@ -44,61 +37,10 @@
loop : true
});
document.getElementById("${currentUrl}").click();
// 修改下一页按钮的样式和位置
$(function () {
var outHandler = function(){
$(this).css('background-color','rgba(0, 0, 0, 0)');
};
var overHandler = function(){
$(this).css('background-color','rgba(0, 0, 0, .5)');
};
var next = $("li[data-action=next]");
var prev = $("li[data-action=prev]");
var viewerToolBar = $(".viewer-footer");
// 覆盖按钮父类原始样式
viewerToolBar.css("overflow", "visible");
// 获取文档高度、宽度
var clientHeight = window.innerHeight;
var clientWidth = window.innerWidth;
// 调整样式
var styleCss = {},nextCss={},prevCss={};
styleCss.position = "absolute";
styleCss.top = -clientHeight;
styleCss.width = clientWidth*0.1;
styleCss.height = clientHeight + 52;
// 覆盖原始样式
styleCss.backgroundColor='rgba(0, 0, 0, 0)';
styleCss.borderRadius='inherit';
nextCss.right = "0";
prevCss.left = "0";
next.css($.extend(nextCss, styleCss));
prev.css($.extend(prevCss, styleCss));
next.on('mouseout',outHandler);
next.on('mouseover',overHandler);
prev.on('mouseout',outHandler);
prev.on('mouseover',overHandler);
});
/*初始化水印*/
window.onload = function() {
var watermarkTxt = '${watermarkTxt}';
if (watermarkTxt !== '') {
watermark.init({
watermark_txt: '${watermarkTxt}',
watermark_x: 0,
watermark_y: 0,
watermark_rows: 0,
watermark_cols: 0,
watermark_x_space: ${watermarkXSpace},
watermark_y_space: ${watermarkYSpace},
watermark_font: '${watermarkFont}',
watermark_fontsize: '${watermarkFontsize}',
watermark_color:'${watermarkColor}',
watermark_alpha: ${watermarkAlpha},
watermark_width: ${watermarkWidth},
watermark_height: ${watermarkHeight},
watermark_angle: ${watermarkAngle},
});
}
initWaterMark();
}
</script>
</body>

View File

@@ -4,144 +4,49 @@
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0">
<title>普通文本预览</title>
<#include "*/commonHeader.ftl">
</head>
<body>
<input hidden id="textType" value="${textType}"/>
<input hidden id="textData" value="${textData}"/>
<div class="container">
<#if textType?? && textType == "markdown">
<p>
<button id="markdown_btn" type="button" class="btn btn-primary">切换markdown</button>
<button id="text_btn" type="button" class="btn btn-primary">切换text</button>
</p>
<div id="markdown" style="padding: 18px;"></div>
<#elseif textType?? && textType == "xml" >
<div id="xml" style="padding: 18px;"></div>
<#else>
<div id="text"></div>
</#if>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne">
${file.name}
</a>
</h4>
</div>
<div class="panel-body">
<div id="text"></div>
</div>
</div>
</div>
<link rel="stylesheet" href="css/xmlTreeViewer.css"/>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"/>
<script src="js/jquery-3.0.0.min.js" type="text/javascript"></script>
<script src="js/jquery.form.min.js" type="text/javascript"></script>
<script src="bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<script src="js/watermark.js" type="text/javascript"></script>
<script src="js/marked.min.js" type="text/javascript"></script>
<script src="js/xmlTreeViewer.js" type="text/javascript"></script>
<script src="js/base64.min.js" type="text/javascript"></script>
<script>
/**
* 初始化
*/
window.onload = function () {
$("#markdown_btn").hide()
initWaterMark();
fetchData();
loadText();
loadXmlData()
loadMarkdown();
}
/**
* 初始化水印
*/
function initWaterMark() {
let watermarkTxt = '${watermarkTxt}';
if (watermarkTxt !== '') {
watermark.init({
watermark_txt: '${watermarkTxt}',
watermark_x: 0,
watermark_y: 0,
watermark_rows: 0,
watermark_cols: 0,
watermark_x_space: ${watermarkXSpace},
watermark_y_space: ${watermarkYSpace},
watermark_font: '${watermarkFont}',
watermark_fontsize: '${watermarkFontsize}',
watermark_color: '${watermarkColor}',
watermark_alpha: ${watermarkAlpha},
watermark_width: ${watermarkWidth},
watermark_height: ${watermarkHeight},
watermark_angle: ${watermarkAngle},
});
}
}
/**
* 获取文本数据
*/
function fetchData() {
window.textData = Base64.decode($("#textData").val())
window.textPreData = "<pre>" + window.textData + "</pre>";
}
/**
*加载普通文本
*/
function loadText() {
$("#text").html(window.textPreData);
var base64data = $("#textData").val()
var textData = Base64.decode(base64data);
var textPreData = "<xmp style='background-color: #FFFFFF;overflow-y: scroll;border:none'>" + textData + "</xmp>";
$("#text").append(textPreData);
}
/**
* 加载markdown
*/
function loadMarkdown() {
if ($("#textType").val() === "markdown") {
window.textMarkdownData = marked(window.textData);
$("#markdown").html(window.textMarkdownData);
}
}
/**
* 加载xml数据
*/
function loadXmlData() {
if ($("#textType").val() === "xml") {
var xmlNode = xmlTreeViewer.parseXML(window.textData);
var retNode = xmlTreeViewer.getXMLViewerNode(xmlNode.xml);
$("#xml").html(retNode);
}
}
$(function () {
$("#markdown_btn").click(function () {
$("#markdown").html(window.textMarkdownData);
$("#text_btn").show()
$("#markdown_btn").hide()
});
$("#text_btn").click(function () {
$("#markdown_btn").show()
$("#text_btn").hide();
$("#markdown").html(window.textPreData);
});
});
</script>
<style>
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
}
#markdown, #xml {
height: 97%;
max-height: 97%;
border: 1px solid #ece7e7;
overflow-y: scroll;
width: 100%;
}
</style>
</body>
</html>

View File

@@ -0,0 +1,73 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0">
<title>xml文本预览</title>
<#include "*/commonHeader.ftl">
<link rel="stylesheet" href="css/xmlTreeViewer.css"/>
<script src="js/xmlTreeViewer.js" type="text/javascript"></script>
</head>
<body>
<input hidden id="textData" value="${textData}"/>
<div class="container">
<div class="panel panel-default">
<div id="xml_btn" class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne">
${file.name}
</a>
</h4>
</div>
<div id="text_btn" class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne">
${file.name}
</a>
</h4>
</div>
<div class="panel-body">
<div id="xml"></div>
</div>
</div>
</div>
<script>
/**
* 初始化
*/
window.onload = function () {
$("#xml_btn").hide()
initWaterMark();
loadXmlData()
}
/**
* 加载xml数据
*/
function loadXmlData() {
var textData = Base64.decode($("#textData").val())
window.textPreData = "<xmp style='background-color: #FFFFFF;overflow-y: scroll;border:none'>" + textData + "</xmp>";
var xmlNode = xmlTreeViewer.parseXML(textData);
window.retNode = xmlTreeViewer.getXMLViewerNode(xmlNode.xml);
$("#xml").html(window.retNode);
}
$(function () {
$("#xml_btn").click(function () {
$("#xml").html(window.retNode);
$("#text_btn").show()
$("#xml_btn").hide()
});
$("#text_btn").click(function () {
$("#xml_btn").show()
$("#text_btn").hide();
$("#xml").html(window.textPreData);
});
});
</script>
</body>
</html>