mirror of
https://gitee.com/kekingcn/file-online-preview.git
synced 2026-04-28 19:16:43 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3414844bfb | ||
|
|
656c6748e9 | ||
|
|
12120d076e | ||
|
|
eb00385d76 | ||
|
|
986f562266 | ||
|
|
11d0441ed4 | ||
|
|
ef46e2c51e | ||
|
|
0a3c03f18b | ||
|
|
7a7e1a1855 | ||
|
|
f530f441d5 | ||
|
|
10160e8104 | ||
|
|
602e80ee9e | ||
|
|
9c83860e1b | ||
|
|
1f1970232b | ||
|
|
594bd895ec | ||
|
|
486c09b24a | ||
|
|
aaf396fbc8 | ||
|
|
4e01d6f5f3 | ||
|
|
342c391a9b | ||
|
|
f2d929e6fa | ||
|
|
41cdc227b3 | ||
|
|
0f4f1d580b | ||
|
|
37c37868a3 | ||
|
|
01218e4a5c | ||
|
|
f6d54902e9 | ||
|
|
5a559aa868 | ||
|
|
9b0f381c06 | ||
|
|
c1802b2487 | ||
|
|
d4b11a4056 | ||
|
|
da1553920b | ||
|
|
d787813bc6 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -17,7 +17,6 @@ target/
|
|||||||
|
|
||||||
### NetBeans ###
|
### NetBeans ###
|
||||||
nbproject/private/
|
nbproject/private/
|
||||||
build/
|
|
||||||
nbbuild/
|
nbbuild/
|
||||||
dist/
|
dist/
|
||||||
nbdist/
|
nbdist/
|
||||||
@@ -27,5 +26,5 @@ nbdist/
|
|||||||
### VS Code ###
|
### VS Code ###
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
jodconverter-web/src/main/cache/
|
server/src/main/cache/
|
||||||
jodconverter-web/src/main/file/
|
server/src/main/file/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
FROM ubuntu:20.04
|
FROM ubuntu:20.04
|
||||||
MAINTAINER chenjh "842761733@qq.com"
|
MAINTAINER chenjh "842761733@qq.com"
|
||||||
ADD jodconverter-web/target/kkFileView-*.tar.gz /opt/
|
ADD server/target/kkFileView-*.tar.gz /opt/
|
||||||
COPY fonts/* /usr/share/fonts/chienes/
|
COPY fonts/* /usr/share/fonts/chienes/
|
||||||
RUN echo "deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse" > /etc/apt/sources.list &&\
|
RUN echo "deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse" > /etc/apt/sources.list &&\
|
||||||
apt-get clean && apt-get update &&\
|
apt-get clean && apt-get update &&\
|
||||||
@@ -28,5 +28,5 @@ ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
|
|||||||
ENV PATH $PATH:$JAVA_HOME/bin
|
ENV PATH $PATH:$JAVA_HOME/bin
|
||||||
ENV LANG zh_CN.UTF-8
|
ENV LANG zh_CN.UTF-8
|
||||||
ENV LC_ALL zh_CN.UTF-8
|
ENV LC_ALL zh_CN.UTF-8
|
||||||
ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-2.2.1/bin
|
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-2.2.1/config/application.properties","-jar","/opt/kkFileView-2.2.1/bin/kkFileView-2.2.1.jar"]
|
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"]
|
||||||
20
README.md
20
README.md
@@ -109,6 +109,26 @@ pdf预览模式预览效果如下
|
|||||||
|
|
||||||
### 历史更新记录
|
### 历史更新记录
|
||||||
|
|
||||||
|
> 2020年12月27日 :
|
||||||
|
|
||||||
|
2020年年终大版本更新,架构全面设计,代码全面重构,代码质量全面提升,二次开发更便捷,欢迎拉源码品鉴,提issue、pr共同建设
|
||||||
|
|
||||||
|
1. 架构模块调整,大量的代码重构,代码质量提升N个等级,欢迎品鉴
|
||||||
|
2. 增强XML文件预览效果,新增XML文档数结构预览
|
||||||
|
3. 新增markdown文件预览支持,预览支持md渲染和源文本切换支持
|
||||||
|
4. 切换底层web server为jetty,解决这个issue:https://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. 移除guava、hutool等非必须的工具包,减少代码体积
|
||||||
|
12. Office组件加载异步化,提速应用启动速度最快到5秒内
|
||||||
|
13. 合理设置预览消费队列的线程数
|
||||||
|
14. 修复压缩包里文件再次预览失败的bug
|
||||||
|
15. 修复图片预览的bug
|
||||||
|
|
||||||
> 2020年05月20日 :
|
> 2020年05月20日 :
|
||||||
1. 新增支持全局水印,并支持通过参数动态改变水印内容
|
1. 新增支持全局水印,并支持通过参数动态改变水印内容
|
||||||
2. 新增支持CAD文件预览
|
2. 新增支持CAD文件预览
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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归到0,to归到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, "default") = "default"
|
|
||||||
* nullToDefault("", "default") = ""
|
|
||||||
* nullToDefault(" ", "default") = " "
|
|
||||||
* nullToDefault("bat", "default") = "bat"
|
|
||||||
* </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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 对之间放置 & 符号。
|
|
||||||
* </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 = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
|
|
||||||
* </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 '&' 不编码
|
|
||||||
* 其它编码为 %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 = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
|
|
||||||
* </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 '&' 不编码
|
|
||||||
* 其它编码为 %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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package cn.keking.model;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by kl on 2018/1/17.
|
|
||||||
* Content :文件类型,文本,office,压缩包等等
|
|
||||||
*/
|
|
||||||
public enum FileType {
|
|
||||||
picture("picturefilepreviewimpl"),
|
|
||||||
compress("compressFilePreviewImpl"),
|
|
||||||
office("officeFilePreviewImpl"),
|
|
||||||
simText("simTextFilePreviewImpl"),
|
|
||||||
pdf("pdfFilePreviewImpl"),
|
|
||||||
other("otherFilePreviewImpl"),
|
|
||||||
media("mediaFilePreviewImpl"),
|
|
||||||
markdown("markdownFilePreviewImpl"),
|
|
||||||
cad("cadFilePreviewImpl");
|
|
||||||
|
|
||||||
|
|
||||||
private final String instanceName;
|
|
||||||
|
|
||||||
FileType(String instanceName){
|
|
||||||
this.instanceName=instanceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getInstanceName() {
|
|
||||||
return instanceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package cn.keking.service;
|
|
||||||
|
|
||||||
import cn.keking.model.FileAttribute;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by kl on 2018/1/17.
|
|
||||||
* Content :
|
|
||||||
*/
|
|
||||||
public interface FilePreview {
|
|
||||||
String filePreviewHandle(String url, Model model, FileAttribute fileAttribute);
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
package cn.keking.service.impl;
|
|
||||||
|
|
||||||
import cn.keking.config.ConfigConstants;
|
|
||||||
import cn.keking.model.FileAttribute;
|
|
||||||
import cn.keking.model.ReturnResponse;
|
|
||||||
import cn.keking.service.FilePreview;
|
|
||||||
import cn.keking.utils.DownloadUtils;
|
|
||||||
import cn.keking.utils.FileUtils;
|
|
||||||
import cn.keking.utils.ZipReader;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by kl on 2018/1/17.
|
|
||||||
* Content :处理压缩包文件
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class CompressFilePreviewImpl implements FilePreview {
|
|
||||||
|
|
||||||
private final FileUtils fileUtils;
|
|
||||||
|
|
||||||
private final DownloadUtils downloadUtils;
|
|
||||||
|
|
||||||
private final ZipReader zipReader;
|
|
||||||
|
|
||||||
public CompressFilePreviewImpl(FileUtils fileUtils,
|
|
||||||
DownloadUtils downloadUtils,
|
|
||||||
ZipReader zipReader) {
|
|
||||||
this.fileUtils = fileUtils;
|
|
||||||
this.downloadUtils = downloadUtils;
|
|
||||||
this.zipReader = zipReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
|
|
||||||
String fileName=fileAttribute.getName();
|
|
||||||
String suffix=fileAttribute.getSuffix();
|
|
||||||
String fileTree = null;
|
|
||||||
// 判断文件名是否存在(redis缓存读取)
|
|
||||||
if (!StringUtils.hasText(fileUtils.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";
|
|
||||||
}
|
|
||||||
String filePath = response.getContent();
|
|
||||||
if ("zip".equalsIgnoreCase(suffix) || "jar".equalsIgnoreCase(suffix) || "gzip".equalsIgnoreCase(suffix)) {
|
|
||||||
fileTree = zipReader.readZipFile(filePath, fileName);
|
|
||||||
} else if ("rar".equalsIgnoreCase(suffix)) {
|
|
||||||
fileTree = zipReader.unRar(filePath, fileName);
|
|
||||||
} else if ("7z".equalsIgnoreCase(suffix)) {
|
|
||||||
fileTree = zipReader.read7zFile(filePath, fileName);
|
|
||||||
}
|
|
||||||
if (fileTree != null && !"null".equals(fileTree) && ConfigConstants.isCacheEnabled()) {
|
|
||||||
fileUtils.addConvertedFile(fileName, fileTree);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fileTree = fileUtils.getConvertedFile(fileName);
|
|
||||||
}
|
|
||||||
if (fileTree != null && !"null".equals(fileTree)) {
|
|
||||||
model.addAttribute("fileTree", fileTree);
|
|
||||||
return "compress";
|
|
||||||
} else {
|
|
||||||
model.addAttribute("fileType", suffix);
|
|
||||||
model.addAttribute("msg", "压缩文件类型不受支持,尝试在压缩的时候选择RAR4格式");
|
|
||||||
return "fileNotSupported";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package cn.keking.service.impl;
|
|
||||||
|
|
||||||
import cn.keking.model.FileAttribute;
|
|
||||||
import cn.keking.service.FilePreview;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by kl on 2018/1/17.
|
|
||||||
* Content :其他文件
|
|
||||||
*/
|
|
||||||
@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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
package cn.keking.service.impl;
|
|
||||||
|
|
||||||
import cn.keking.config.ConfigConstants;
|
|
||||||
import cn.keking.model.FileAttribute;
|
|
||||||
import cn.keking.model.ReturnResponse;
|
|
||||||
import cn.keking.service.FilePreview;
|
|
||||||
import cn.keking.utils.DownloadUtils;
|
|
||||||
import cn.keking.utils.FileUtils;
|
|
||||||
import cn.keking.utils.PdfUtils;
|
|
||||||
import cn.keking.web.filter.BaseUrlFilter;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by kl on 2018/1/17.
|
|
||||||
* Content :处理pdf文件
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class PdfFilePreviewImpl implements FilePreview {
|
|
||||||
|
|
||||||
private final FileUtils fileUtils;
|
|
||||||
|
|
||||||
private final PdfUtils pdfUtils;
|
|
||||||
|
|
||||||
private final DownloadUtils downloadUtils;
|
|
||||||
|
|
||||||
private static final String FILE_DIR = ConfigConstants.getFileDir();
|
|
||||||
|
|
||||||
public PdfFilePreviewImpl(FileUtils fileUtils,
|
|
||||||
PdfUtils pdfUtils,
|
|
||||||
DownloadUtils downloadUtils) {
|
|
||||||
this.fileUtils = fileUtils;
|
|
||||||
this.pdfUtils = pdfUtils;
|
|
||||||
this.downloadUtils = downloadUtils;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
|
|
||||||
String suffix=fileAttribute.getSuffix();
|
|
||||||
String fileName=fileAttribute.getName();
|
|
||||||
String officePreviewType = model.asMap().get("officePreviewType") == null ? ConfigConstants.getOfficePreviewType() : model.asMap().get("officePreviewType").toString();
|
|
||||||
String baseUrl = BaseUrlFilter.getBaseUrl();
|
|
||||||
String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + "pdf";
|
|
||||||
String outFilePath = FILE_DIR + pdfName;
|
|
||||||
model.addAttribute("switchDisabled", ConfigConstants.getOfficePreviewSwitchDisabled());
|
|
||||||
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType)) {
|
|
||||||
//当文件不存在时,就去下载
|
|
||||||
if (!fileUtils.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";
|
|
||||||
}
|
|
||||||
outFilePath = response.getContent();
|
|
||||||
if (ConfigConstants.isCacheEnabled()) {
|
|
||||||
// 加入缓存
|
|
||||||
fileUtils.addConvertedFile(pdfName, fileUtils.getRelativePath(outFilePath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
List<String> imageUrls = pdfUtils.pdf2jpg(outFilePath, pdfName, baseUrl);
|
|
||||||
if (imageUrls == null || imageUrls.size() < 1) {
|
|
||||||
model.addAttribute("msg", "pdf转图片异常,请联系管理员");
|
|
||||||
model.addAttribute("fileType",fileAttribute.getSuffix());
|
|
||||||
return "fileNotSupported";
|
|
||||||
}
|
|
||||||
model.addAttribute("imgurls", imageUrls);
|
|
||||||
model.addAttribute("currentUrl", imageUrls.get(0));
|
|
||||||
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType)) {
|
|
||||||
return "officePicture";
|
|
||||||
} else {
|
|
||||||
return "picture";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 不是http开头,浏览器不能直接访问,需下载到本地
|
|
||||||
if (url != null && !url.toLowerCase().startsWith("http")) {
|
|
||||||
if (!fileUtils.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";
|
|
||||||
}
|
|
||||||
model.addAttribute("pdfUrl", fileUtils.getRelativePath(response.getContent()));
|
|
||||||
if (ConfigConstants.isCacheEnabled()) {
|
|
||||||
// 加入缓存
|
|
||||||
fileUtils.addConvertedFile(pdfName, fileUtils.getRelativePath(outFilePath));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
model.addAttribute("pdfUrl", pdfName);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
model.addAttribute("pdfUrl", url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "pdf";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package cn.keking.service.impl;
|
|
||||||
|
|
||||||
import cn.keking.config.ConfigConstants;
|
|
||||||
import cn.keking.model.FileAttribute;
|
|
||||||
import cn.keking.model.ReturnResponse;
|
|
||||||
import cn.keking.service.FilePreview;
|
|
||||||
import cn.keking.utils.DownloadUtils;
|
|
||||||
import cn.keking.utils.FileUtils;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by kl on 2018/1/17.
|
|
||||||
* Content :图片文件处理
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class PictureFilePreviewImpl implements FilePreview {
|
|
||||||
|
|
||||||
private final FileUtils fileUtils;
|
|
||||||
|
|
||||||
private final DownloadUtils downloadUtils;
|
|
||||||
|
|
||||||
public PictureFilePreviewImpl(FileUtils fileUtils,
|
|
||||||
DownloadUtils downloadUtils) {
|
|
||||||
this.fileUtils = fileUtils;
|
|
||||||
this.downloadUtils = downloadUtils;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
|
|
||||||
String fileKey = (String) RequestContextHolder.currentRequestAttributes().getAttribute("fileKey",0);
|
|
||||||
List<String> imgUrls = Lists.newArrayList(url);
|
|
||||||
model.addAttribute("switchDisabled", ConfigConstants.getOfficePreviewSwitchDisabled());
|
|
||||||
try {
|
|
||||||
imgUrls.clear();
|
|
||||||
imgUrls.addAll(fileUtils.getImgCache(fileKey));
|
|
||||||
} catch (Exception e){
|
|
||||||
imgUrls = Lists.newArrayList(url);
|
|
||||||
}
|
|
||||||
// 不是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";
|
|
||||||
} else {
|
|
||||||
String file = fileUtils.getRelativePath(response.getContent());
|
|
||||||
model.addAttribute("imgurls", Lists.newArrayList(file));
|
|
||||||
model.addAttribute("currentUrl", file);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
model.addAttribute("imgurls", imgUrls);
|
|
||||||
model.addAttribute("currentUrl", url);
|
|
||||||
}
|
|
||||||
return "picture";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
package cn.keking.service.impl;
|
|
||||||
|
|
||||||
import cn.keking.model.FileAttribute;
|
|
||||||
import cn.keking.model.ReturnResponse;
|
|
||||||
import cn.keking.service.FilePreview;
|
|
||||||
import cn.keking.utils.DownloadUtils;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by kl on 2018/1/17.
|
|
||||||
* Content :处理文本文件
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class SimTextFilePreviewImpl implements FilePreview {
|
|
||||||
|
|
||||||
private final DownloadUtils downloadUtils;
|
|
||||||
|
|
||||||
public SimTextFilePreviewImpl(DownloadUtils downloadUtils) {
|
|
||||||
this.downloadUtils = downloadUtils;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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";
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
File originFile = new File(response.getContent());
|
|
||||||
File previewFile = new File(response.getContent() + ".txt");
|
|
||||||
if (previewFile.exists()) {
|
|
||||||
previewFile.delete();
|
|
||||||
}
|
|
||||||
Files.copy(originFile.toPath(), previewFile.toPath());
|
|
||||||
} catch (IOException e) {
|
|
||||||
model.addAttribute("msg", e.getLocalizedMessage());
|
|
||||||
model.addAttribute("fileType",fileAttribute.getSuffix());
|
|
||||||
return "fileNotSupported";
|
|
||||||
}
|
|
||||||
model.addAttribute("ordinaryUrl", response.getMsg());
|
|
||||||
return "txt";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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("PDFFileNotFoundException,inputFilePath:{}", inputFilePath, e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
package cn.keking.utils;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.mozilla.intl.chardet.nsDetector;
|
|
||||||
import org.mozilla.intl.chardet.nsICharsetDetectionObserver;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文本文件编码探测工具类
|
|
||||||
*
|
|
||||||
* @author HWliao
|
|
||||||
* @date 2017-12-24
|
|
||||||
*/
|
|
||||||
public class FileCharsetDetector {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 传入一个文件(File)对象,检查文件编码
|
|
||||||
*
|
|
||||||
* @param file File对象实例
|
|
||||||
* @return 文件编码,若无,则返回null
|
|
||||||
* @throws FileNotFoundException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public static Observer guessFileEncoding(File file)
|
|
||||||
throws FileNotFoundException, IOException {
|
|
||||||
return guessFileEncoding(file, new nsDetector());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <pre>
|
|
||||||
* 获取文件的编码
|
|
||||||
* @param file
|
|
||||||
* File对象实例
|
|
||||||
* @param languageHint
|
|
||||||
* 语言提示区域代码 @see #nsPSMDetector ,取值如下:
|
|
||||||
* 1 : Japanese
|
|
||||||
* 2 : Chinese
|
|
||||||
* 3 : Simplified Chinese
|
|
||||||
* 4 : Traditional Chinese
|
|
||||||
* 5 : Korean
|
|
||||||
* 6 : Dont know(default)
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @return 文件编码,eg:UTF-8,GBK,GB2312形式(不确定的时候,返回可能的字符编码序列);若无,则返回null
|
|
||||||
* @throws FileNotFoundException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public static Observer guessFileEncoding(File file, int languageHint)
|
|
||||||
throws FileNotFoundException, IOException {
|
|
||||||
return guessFileEncoding(file, new nsDetector(languageHint));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取文件的编码
|
|
||||||
*
|
|
||||||
* @param file
|
|
||||||
* @param det
|
|
||||||
* @return
|
|
||||||
* @throws FileNotFoundException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
private static Observer guessFileEncoding(File file, nsDetector det)
|
|
||||||
throws FileNotFoundException, IOException {
|
|
||||||
// new Observer
|
|
||||||
Observer observer = new Observer();
|
|
||||||
// set Observer
|
|
||||||
// The Notify() will be called when a matching charset is found.
|
|
||||||
det.Init(observer);
|
|
||||||
|
|
||||||
BufferedInputStream imp = new BufferedInputStream(new FileInputStream(
|
|
||||||
file));
|
|
||||||
byte[] buf = new byte[1024];
|
|
||||||
int len;
|
|
||||||
boolean done = false;
|
|
||||||
boolean isAscii = false;
|
|
||||||
|
|
||||||
while ((len = imp.read(buf, 0, buf.length)) != -1) {
|
|
||||||
// Check if the stream is only ascii.
|
|
||||||
isAscii = det.isAscii(buf, len);
|
|
||||||
if (isAscii) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// DoIt if non-ascii and not done yet.
|
|
||||||
done = det.DoIt(buf, len, false);
|
|
||||||
if (done) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
imp.close();
|
|
||||||
det.DataEnd();
|
|
||||||
|
|
||||||
if (isAscii) {
|
|
||||||
observer.encoding = "ASCII";
|
|
||||||
observer.found = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!observer.isFound()) {
|
|
||||||
String[] prob = det.getProbableCharsets();
|
|
||||||
// // 这里将可能的字符集组合起来返回
|
|
||||||
// for (int i = 0; i < prob.length; i++) {
|
|
||||||
// if (i == 0) {
|
|
||||||
// encoding = prob[i];
|
|
||||||
// } else {
|
|
||||||
// encoding += "," + prob[i];
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
if (prob.length > 0) {
|
|
||||||
// 在没有发现情况下,去第一个可能的编码
|
|
||||||
observer.encoding = prob[0];
|
|
||||||
} else {
|
|
||||||
observer.encoding = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return observer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author liaohongwei
|
|
||||||
* @Description: 文件字符编码观察者, 但判断出字符编码时候调用
|
|
||||||
* @date 2016年6月20日 下午2:27:06
|
|
||||||
*/
|
|
||||||
public static class Observer implements nsICharsetDetectionObserver {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Fields encoding : 字符编码
|
|
||||||
*/
|
|
||||||
private String encoding = null;
|
|
||||||
/**
|
|
||||||
* @Fields found : 是否找到字符集
|
|
||||||
*/
|
|
||||||
private boolean found = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void Notify(String charset) {
|
|
||||||
this.encoding = charset;
|
|
||||||
this.found = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEncoding() {
|
|
||||||
return encoding;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFound() {
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Observer [encoding=" + encoding + ", found=" + found + "]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,339 +0,0 @@
|
|||||||
package cn.keking.utils;
|
|
||||||
|
|
||||||
import cn.keking.config.ConfigConstants;
|
|
||||||
import cn.keking.model.FileAttribute;
|
|
||||||
import cn.keking.model.FileType;
|
|
||||||
import cn.keking.service.cache.CacheService;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author yudian-it
|
|
||||||
* @date 2017/11/13
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class FileUtils {
|
|
||||||
|
|
||||||
private static final String DEFAULT_CONVERTER_CHARSET = System.getProperty("sun.jnu.encoding");
|
|
||||||
|
|
||||||
private final String fileDir = ConfigConstants.getFileDir();
|
|
||||||
private final CacheService cacheService;
|
|
||||||
|
|
||||||
public FileUtils(CacheService cacheService) {
|
|
||||||
this.cacheService = cacheService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 已转换过的文件集合(缓存)
|
|
||||||
*/
|
|
||||||
public Map<String, String> listConvertedFiles() {
|
|
||||||
return cacheService.getPDFCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 已转换过的文件,根据文件名获取
|
|
||||||
*/
|
|
||||||
public String getConvertedFile(String key) {
|
|
||||||
return cacheService.getPDFCache(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param key pdf本地路径
|
|
||||||
* @return 已将pdf转换成图片的图片本地相对路径
|
|
||||||
*/
|
|
||||||
public Integer getConvertedPdfImage(String key) {
|
|
||||||
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 typeFromFileName(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FileType typeFromFileName(String fileName) {
|
|
||||||
String[] simText = ConfigConstants.getSimText();
|
|
||||||
String[] media = ConfigConstants.getMedia();
|
|
||||||
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
|
|
||||||
if (listPictureTypes().contains(fileType.toLowerCase())) {
|
|
||||||
return FileType.picture;
|
|
||||||
}
|
|
||||||
if (listArchiveTypes().contains(fileType.toLowerCase())) {
|
|
||||||
return FileType.compress;
|
|
||||||
}
|
|
||||||
if (listOfficeTypes().contains(fileType.toLowerCase())) {
|
|
||||||
return FileType.office;
|
|
||||||
}
|
|
||||||
if("md".equalsIgnoreCase(fileType)){
|
|
||||||
return FileType.markdown;
|
|
||||||
}
|
|
||||||
if (Arrays.asList(simText).contains(fileType.toLowerCase())) {
|
|
||||||
return FileType.simText;
|
|
||||||
}
|
|
||||||
if (Arrays.asList(media).contains(fileType.toLowerCase())) {
|
|
||||||
return FileType.media;
|
|
||||||
}
|
|
||||||
if ("pdf".equalsIgnoreCase(fileType)) {
|
|
||||||
return FileType.pdf;
|
|
||||||
}
|
|
||||||
if ("dwg".equalsIgnoreCase(fileType)) {
|
|
||||||
return FileType.cad;
|
|
||||||
}
|
|
||||||
return FileType.other;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 从url中剥离出文件名
|
|
||||||
* @param url
|
|
||||||
* 格式如:http://keking.ufile.ucloud.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从路径中获取文件负
|
|
||||||
* @param path
|
|
||||||
* 类似这种:C:\Users\yudian-it\Downloads
|
|
||||||
* @return 文件名
|
|
||||||
*/
|
|
||||||
public String getFileNameFromPath(String path) {
|
|
||||||
return path.substring(path.lastIndexOf(File.separator) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> listPictureTypes(){
|
|
||||||
List<String> list = Lists.newArrayList();
|
|
||||||
list.add("jpg");
|
|
||||||
list.add("jpeg");
|
|
||||||
list.add("png");
|
|
||||||
list.add("gif");
|
|
||||||
list.add("bmp");
|
|
||||||
list.add("ico");
|
|
||||||
list.add("RAW");
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> listArchiveTypes(){
|
|
||||||
List<String> list = Lists.newArrayList();
|
|
||||||
list.add("rar");
|
|
||||||
list.add("zip");
|
|
||||||
list.add("jar");
|
|
||||||
list.add("7-zip");
|
|
||||||
list.add("tar");
|
|
||||||
list.add("gzip");
|
|
||||||
list.add("7z");
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> listOfficeTypes() {
|
|
||||||
List<String> list = Lists.newArrayList();
|
|
||||||
list.add("docx");
|
|
||||||
list.add("doc");
|
|
||||||
list.add("xls");
|
|
||||||
list.add("xlsx");
|
|
||||||
list.add("ppt");
|
|
||||||
list.add("pptx");
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取相对路径
|
|
||||||
* @param absolutePath 绝对路径
|
|
||||||
* @return 相对路径
|
|
||||||
*/
|
|
||||||
public String getRelativePath(String absolutePath) {
|
|
||||||
return absolutePath.substring(fileDir.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加转换后PDF缓存
|
|
||||||
* @param fileName pdf文件名
|
|
||||||
* @param value 缓存相对路径
|
|
||||||
*/
|
|
||||||
public void addConvertedFile(String fileName, String value){
|
|
||||||
cacheService.putPDFCache(fileName, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加转换后图片组缓存
|
|
||||||
* @param pdfFilePath pdf文件绝对路径
|
|
||||||
* @param num 图片张数
|
|
||||||
*/
|
|
||||||
public void addConvertedPdfImage(String pdfFilePath, int num){
|
|
||||||
cacheService.putPdfImageCache(pdfFilePath, num);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取redis中压缩包内图片文件
|
|
||||||
* @param fileKey fileKey
|
|
||||||
* @return 图片文件访问url列表
|
|
||||||
*/
|
|
||||||
public List<String> getImgCache(String fileKey){
|
|
||||||
return cacheService.getImgCache(fileKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置redis中压缩包内图片文件
|
|
||||||
* @param fileKey fileKey
|
|
||||||
* @param imgs 图片文件访问url列表
|
|
||||||
*/
|
|
||||||
public void putImgCache(String fileKey,List<String> imgs){
|
|
||||||
cacheService.putImgCache(fileKey, imgs);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 判断文件编码格式
|
|
||||||
* @param path 绝对路径
|
|
||||||
* @return 编码格式
|
|
||||||
*/
|
|
||||||
public String getFileEncodeUTFGBK(String path){
|
|
||||||
String enc = Charset.forName("GBK").name();
|
|
||||||
File file = new File(path);
|
|
||||||
InputStream in;
|
|
||||||
try {
|
|
||||||
in = new FileInputStream(file);
|
|
||||||
byte[] b = new byte[3];
|
|
||||||
in.read(b);
|
|
||||||
in.close();
|
|
||||||
if (b[0] == -17 && b[1] == -69 && b[2] == -65) {
|
|
||||||
enc = StandardCharsets.UTF_8.name();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
System.out.println("文件编码格式为:" + enc);
|
|
||||||
return enc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对转换后的文件进行操作(改变编码方式)
|
|
||||||
* @param outFilePath 文件绝对路径
|
|
||||||
*/
|
|
||||||
public void doActionConvertedFile(String outFilePath) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
try (InputStream inputStream = new FileInputStream(outFilePath);
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, DEFAULT_CONVERTER_CHARSET))){
|
|
||||||
String line;
|
|
||||||
while(null != (line = reader.readLine())){
|
|
||||||
if (line.contains("charset=gb2312")) {
|
|
||||||
line = line.replace("charset=gb2312", "charset=utf-8");
|
|
||||||
}
|
|
||||||
sb.append(line);
|
|
||||||
}
|
|
||||||
// 添加sheet控制头
|
|
||||||
sb.append("<script src=\"js/jquery-3.0.0.min.js\" type=\"text/javascript\"></script>");
|
|
||||||
sb.append("<script src=\"js/excel.header.js\" type=\"text/javascript\"></script>");
|
|
||||||
sb.append("<link rel=\"stylesheet\" href=\"bootstrap/css/bootstrap.min.css\">");
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
// 重新写入文件
|
|
||||||
try(FileOutputStream fos = new FileOutputStream(outFilePath);
|
|
||||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8))) {
|
|
||||||
writer.write(sb.toString());
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 获取文件后缀
|
|
||||||
* @param url url
|
|
||||||
* @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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String suffixFromFileName(String fileName) {
|
|
||||||
return fileName.substring(fileName.lastIndexOf(".") + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取url中的参数
|
|
||||||
* @param url url
|
|
||||||
* @param name 参数名
|
|
||||||
* @return 参数值
|
|
||||||
*/
|
|
||||||
public 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 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取文件属性
|
|
||||||
* @param url url
|
|
||||||
* @return 文件属性
|
|
||||||
*/
|
|
||||||
public FileAttribute getFileAttribute(String url) {
|
|
||||||
String fileName;
|
|
||||||
FileType type;
|
|
||||||
String suffix;
|
|
||||||
String fullFileName = getUrlParameterReg(url, "fullfilename");
|
|
||||||
if (StringUtils.hasText(fullFileName)) {
|
|
||||||
fileName = fullFileName;
|
|
||||||
type = typeFromFileName(fileName);
|
|
||||||
suffix = suffixFromFileName(fileName);
|
|
||||||
} else {
|
|
||||||
fileName = getFileNameFromURL(url);
|
|
||||||
type = typeFromUrl(url);
|
|
||||||
suffix = suffixFromUrl(url);
|
|
||||||
}
|
|
||||||
return new FileAttribute(type,suffix,fileName,url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
package cn.keking.utils;
|
|
||||||
|
|
||||||
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 FileUtils fileUtils;
|
|
||||||
|
|
||||||
@Value("${server.tomcat.uri-encoding:UTF-8}")
|
|
||||||
private String uriEncoding;
|
|
||||||
|
|
||||||
public PdfUtils(FileUtils fileUtils) {
|
|
||||||
this.fileUtils = fileUtils;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> pdf2jpg(String pdfFilePath, String pdfName, String baseUrl) {
|
|
||||||
List<String> imageUrls = new ArrayList<>();
|
|
||||||
Integer imageCount = fileUtils.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();
|
|
||||||
fileUtils.addConvertedPdfImage(pdfFilePath, pageCount);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("Convert pdf to jpg exception, pdfFilePath:{}", pdfFilePath, e);
|
|
||||||
}
|
|
||||||
return imageUrls;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
package cn.keking.web.controller;
|
|
||||||
|
|
||||||
import cn.keking.config.ConfigConstants;
|
|
||||||
import cn.keking.model.FileAttribute;
|
|
||||||
import cn.keking.service.FilePreview;
|
|
||||||
import cn.keking.service.FilePreviewFactory;
|
|
||||||
|
|
||||||
import cn.keking.service.cache.CacheService;
|
|
||||||
import cn.keking.utils.DownloadUtils;
|
|
||||||
import cn.keking.utils.FileUtils;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
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.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author yudian-it
|
|
||||||
*/
|
|
||||||
@Controller
|
|
||||||
public class OnlinePreviewController {
|
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(OnlinePreviewController.class);
|
|
||||||
|
|
||||||
private final FilePreviewFactory previewFactory;
|
|
||||||
private final CacheService cacheService;
|
|
||||||
private final FileUtils fileUtils;
|
|
||||||
private final DownloadUtils downloadUtils;
|
|
||||||
|
|
||||||
public OnlinePreviewController(FilePreviewFactory filePreviewFactory,
|
|
||||||
FileUtils fileUtils,
|
|
||||||
CacheService cacheService,
|
|
||||||
DownloadUtils downloadUtils) {
|
|
||||||
this.previewFactory = filePreviewFactory;
|
|
||||||
this.fileUtils = fileUtils;
|
|
||||||
this.cacheService = cacheService;
|
|
||||||
this.downloadUtils = downloadUtils;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping(value = "/onlinePreview")
|
|
||||||
public String onlinePreview(String url, Model model, HttpServletRequest req) {
|
|
||||||
FileAttribute fileAttribute = fileUtils.getFileAttribute(url);
|
|
||||||
req.setAttribute("fileKey", req.getParameter("fileKey"));
|
|
||||||
model.addAttribute("pdfDownloadDisable", ConfigConstants.getPdfDownloadDisable());
|
|
||||||
model.addAttribute("officePreviewType", req.getParameter("officePreviewType"));
|
|
||||||
FilePreview filePreview = previewFactory.get(fileAttribute);
|
|
||||||
logger.info("预览文件url:{},previewType:{}", url, fileAttribute.getType());
|
|
||||||
return filePreview.filePreviewHandle(url, 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");
|
|
||||||
// 抽取文件并返回文件列表
|
|
||||||
String[] imgs = decodedUrl.split("\\|");
|
|
||||||
List imgurls = Arrays.asList(imgs);
|
|
||||||
model.addAttribute("imgurls", imgurls);
|
|
||||||
model.addAttribute("currentUrl",decodedCurrentUrl);
|
|
||||||
return "picture";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据url获取文件内容
|
|
||||||
* 当pdfjs读取存在跨域问题的文件时将通过此接口读取
|
|
||||||
*
|
|
||||||
* @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) {
|
|
||||||
logger.error("下载跨域pdf文件异常,url:{}", urlPath, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通过api接口入队
|
|
||||||
* @param url 请编码后在入队
|
|
||||||
*/
|
|
||||||
@RequestMapping("/addTask")
|
|
||||||
@ResponseBody
|
|
||||||
public String addQueueTask(String url) {
|
|
||||||
logger.info("添加转码队列url:{}", url);
|
|
||||||
cacheService.addQueueTask(url);
|
|
||||||
return "success";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,247 +0,0 @@
|
|||||||
<!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>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" />
|
|
||||||
<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>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<h1>文件预览项目接入和测试界面</h1>
|
|
||||||
<div class="panel-group" id="accordion">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h4 class="panel-title">
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h4 class="panel-title">
|
|
||||||
<a data-toggle="collapse" data-parent="#accordion"
|
|
||||||
href="#collapseTwo">
|
|
||||||
预览测试
|
|
||||||
</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>
|
|
||||||
</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>
|
|
||||||
</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>
|
|
||||||
|
|
||||||
2019年06月18日 :<br>
|
|
||||||
1. 支持自动清理缓存及预览文件<br>
|
|
||||||
2. 支持http/https下载流url文件预览<br>
|
|
||||||
3. 支持FTP url文件预览<br>
|
|
||||||
4. 加入Docker构建<br><br>
|
|
||||||
|
|
||||||
2019年04月08日 :<br>
|
|
||||||
1. 缓存及队列实现抽象,提供JDK和REDIS两种实现(REDIS成为可选依赖)<br>
|
|
||||||
2. 打包方式提供zip和tar.gz包,并提供一键启动脚本<br><br>
|
|
||||||
|
|
||||||
2018年01月19日 :<br>
|
|
||||||
1. 大文件入队提前处理<br>
|
|
||||||
1. 新增addTask文件转换入队接口<br>
|
|
||||||
1. 采用redis队列,支持kkFIleView接口和异构系统入队两种方式<br><br>
|
|
||||||
|
|
||||||
2018年01月15日 :<br>
|
|
||||||
1.首页新增社会化评论框<br><br>
|
|
||||||
|
|
||||||
2018年01月12日 :<br>
|
|
||||||
1.新增多图片同时预览<br>
|
|
||||||
2.支持压缩包内图片轮番预览<br><br>
|
|
||||||
|
|
||||||
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>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div id = "comments"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="loading_container">
|
|
||||||
<div class="spinner">
|
|
||||||
<div class="spinner-container container1">
|
|
||||||
<div class="circle1"></div>
|
|
||||||
<div class="circle2"></div>
|
|
||||||
<div class="circle3"></div>
|
|
||||||
<div class="circle4"></div>
|
|
||||||
</div>
|
|
||||||
<div class="spinner-container container2">
|
|
||||||
<div class="circle1"></div>
|
|
||||||
<div class="circle2"></div>
|
|
||||||
<div class="circle3"></div>
|
|
||||||
<div class="circle4"></div>
|
|
||||||
</div>
|
|
||||||
<div class="spinner-container container3">
|
|
||||||
<div class="circle1"></div>
|
|
||||||
<div class="circle2"></div>
|
|
||||||
<div class="circle3"></div>
|
|
||||||
<div class="circle4"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
function deleteFile(fileName) {
|
|
||||||
$.ajax({
|
|
||||||
url: '${baseUrl}deleteFile?fileName=' + encodeURIComponent(fileName),
|
|
||||||
success: function (data) {
|
|
||||||
// 删除完成,刷新table
|
|
||||||
if (1 == data.code) {
|
|
||||||
alert(data.msg);
|
|
||||||
} else{
|
|
||||||
$('#table').bootstrapTable('refresh', {});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function (data) {
|
|
||||||
console.log(data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
$(function () {
|
|
||||||
$('#table').bootstrapTable({
|
|
||||||
url: 'listFiles',
|
|
||||||
columns: [{
|
|
||||||
field: 'fileName',
|
|
||||||
title: '文件名'
|
|
||||||
}, {
|
|
||||||
field: 'action',
|
|
||||||
title: '操作'
|
|
||||||
},]
|
|
||||||
}).on('pre-body.bs.table', function (e,data) {
|
|
||||||
// 每个data添加一列用来操作
|
|
||||||
$(data).each(function (index, item) {
|
|
||||||
item.action = "<a class='btn btn-default' target='_blank' href='${baseUrl}onlinePreview?url="+ encodeURIComponent('${baseUrl}' + item.fileName) +"'>预览</a>" +
|
|
||||||
"<a class='btn btn-default' href='javascript:void(0);' onclick='deleteFile(\""+item.fileName+"\")'>删除</a>";
|
|
||||||
});
|
|
||||||
return data;
|
|
||||||
}).on('post-body.bs.table', function (e,data) {
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
function showLoadingDiv() {
|
|
||||||
var height = window.document.documentElement.clientHeight - 1;
|
|
||||||
$(".loading_container").css("height", height).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#btnsubmit").click(function () {
|
|
||||||
showLoadingDiv();
|
|
||||||
$("#fileUpload").ajaxSubmit({
|
|
||||||
success: function (data) {
|
|
||||||
// 上传完成,刷新table
|
|
||||||
if (1 == data.code) {
|
|
||||||
alert(data.msg);
|
|
||||||
} else {
|
|
||||||
$('#table').bootstrapTable('refresh', {});
|
|
||||||
}
|
|
||||||
$(".loading_container").hide();
|
|
||||||
},
|
|
||||||
error: function () {
|
|
||||||
alert('上传失败,请联系管理员');
|
|
||||||
$(".loading_container").hide();
|
|
||||||
},
|
|
||||||
url: 'fileUpload', /*设置post提交到的页面*/
|
|
||||||
type: "post", /*设置表单以post方法提交*/
|
|
||||||
dataType: "json" /*设置返回值类型为文本*/
|
|
||||||
});
|
|
||||||
});
|
|
||||||
var gitalk = new Gitalk({
|
|
||||||
clientID: '525d7f16e17aab08cef5',
|
|
||||||
clientSecret: 'd1154e3aee5c8f1cbdc918b5c97a4f4157e0bfd9',
|
|
||||||
repo: 'kkFileView',
|
|
||||||
owner: 'kekingcn',
|
|
||||||
admin: ['kekingcn,klboke,gitchenjh'],
|
|
||||||
language: 'zh-CN',
|
|
||||||
id: location.pathname,
|
|
||||||
distractionFreeMode: false
|
|
||||||
})
|
|
||||||
gitalk.render((document.getElementById('comments')))
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>cn.keking</groupId>
|
<groupId>cn.keking</groupId>
|
||||||
<artifactId>jodconverter-core</artifactId>
|
<artifactId>office-plugin</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<properties>
|
<properties>
|
||||||
@@ -19,6 +19,7 @@ import java.util.Iterator;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.artofsolving.jodconverter.util.ConfigUtils;
|
||||||
import org.artofsolving.jodconverter.util.PlatformUtils;
|
import org.artofsolving.jodconverter.util.PlatformUtils;
|
||||||
|
|
||||||
import com.sun.star.beans.PropertyValue;
|
import com.sun.star.beans.PropertyValue;
|
||||||
@@ -55,7 +56,7 @@ public class OfficeUtils {
|
|||||||
Map<String,Object> subProperties = (Map<String,Object>) value;
|
Map<String,Object> subProperties = (Map<String,Object>) value;
|
||||||
value = toUnoProperties(subProperties);
|
value = toUnoProperties(subProperties);
|
||||||
}
|
}
|
||||||
propertyValues[i++] = property((String) entry.getKey(), value);
|
propertyValues[i++] = property(entry.getKey(), value);
|
||||||
}
|
}
|
||||||
return propertyValues;
|
return propertyValues;
|
||||||
}
|
}
|
||||||
@@ -68,7 +69,7 @@ public class OfficeUtils {
|
|||||||
|
|
||||||
public static File getDefaultOfficeHome() {
|
public static File getDefaultOfficeHome() {
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
String customizedConfigPath = getCustomizedConfigPath();
|
String customizedConfigPath = ConfigUtils.getCustomizedConfigPath();
|
||||||
try {
|
try {
|
||||||
BufferedReader bufferedReader = new BufferedReader(new FileReader(customizedConfigPath));
|
BufferedReader bufferedReader = new BufferedReader(new FileReader(customizedConfigPath));
|
||||||
properties.load(bufferedReader);
|
properties.load(bufferedReader);
|
||||||
@@ -80,7 +81,7 @@ public class OfficeUtils {
|
|||||||
}
|
}
|
||||||
if (PlatformUtils.isWindows()) {
|
if (PlatformUtils.isWindows()) {
|
||||||
// %ProgramFiles(x86)% on 64-bit machines; %ProgramFiles% on 32-bit ones
|
// %ProgramFiles(x86)% on 64-bit machines; %ProgramFiles% on 32-bit ones
|
||||||
String homePath = OfficeUtils.getHomePath();
|
String homePath = ConfigUtils.getHomePath();
|
||||||
String programFiles = System.getenv("ProgramFiles(x86)");
|
String programFiles = System.getenv("ProgramFiles(x86)");
|
||||||
if (programFiles == null) {
|
if (programFiles == null) {
|
||||||
programFiles = System.getenv("ProgramFiles");
|
programFiles = System.getenv("ProgramFiles");
|
||||||
@@ -127,30 +128,7 @@ public class OfficeUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getHomePath() {
|
|
||||||
String userDir = System.getenv("KKFILEVIEW_BIN_FOLDER");
|
|
||||||
if (userDir == null) {
|
|
||||||
userDir = System.getProperty("user.dir");
|
|
||||||
}
|
|
||||||
if (userDir.endsWith("bin")) {
|
|
||||||
userDir = userDir.substring(0, userDir.length() - 4);
|
|
||||||
} else {
|
|
||||||
String separator = File.separator;
|
|
||||||
if (userDir.contains("jodconverter-web")) {
|
|
||||||
userDir = userDir + separator + "src" + separator + "main";
|
|
||||||
} else {
|
|
||||||
userDir = userDir + separator + "jodconverter-web" + separator + "src" + separator + "main";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return userDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getCustomizedConfigPath() {
|
|
||||||
String homePath = OfficeUtils.getHomePath();
|
|
||||||
String separator = java.io.File.separator;
|
|
||||||
String configFilePath = homePath + separator + "config" + separator + "application.properties";
|
|
||||||
return configFilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SpringBoot application.properties 支持从环境变量获取值
|
* SpringBoot application.properties 支持从环境变量获取值
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user