Compare commits

...

5 Commits

Author SHA1 Message Date
chenkailing
2910544c26 修复压缩包里文件再次预览失败的bug 2020-12-27 12:35:41 +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
chenkailing
4e01d6f5f3 忽略file目录 2020-12-26 19:23:00 +08:00
chenkailing
342c391a9b 引入cpdetector解决文件编码识别问题 2020-12-26 19:23:00 +08:00
56 changed files with 334 additions and 3681 deletions

5
.gitignore vendored
View File

@@ -27,5 +27,6 @@ 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/
server/src/main/log

Binary file not shown.

View File

@@ -193,6 +193,14 @@
<scope>system</scope> <scope>system</scope>
<systemPath>${basedir}/lib/aspose-cad-19.9.jar</systemPath> <systemPath>${basedir}/lib/aspose-cad-19.9.jar</systemPath>
</dependency> </dependency>
<!-- 编码识别 -->
<dependency>
<groupId>cpdetector</groupId>
<artifactId>cpdetector</artifactId>
<version>1.04</version>
<scope>system</scope>
<systemPath>${basedir}/lib/cpdetector-1.04.jar</systemPath>
</dependency>
</dependencies> </dependencies>
<build> <build>
<resources> <resources>

View File

@@ -1,5 +1,7 @@
[#ftl] [#ftl]
[#-- @implicitly included --] [#-- @implicitly included --]
[#-- @ftlvariable name="fileTree" type="java.lang.String" --]
[#-- @ftlvariable name="baseUrl" type="java.lang.String" --]
[#-- @ftlvariable name="imgUrls" type="String" --] [#-- @ftlvariable name="imgUrls" type="String" --]
[#-- @ftlvariable name="textData" type="java.lang.String" --] [#-- @ftlvariable name="textData" type="java.lang.String" --]
[#-- @ftlvariable name="xmlContent" type="java.lang.String" --] [#-- @ftlvariable name="xmlContent" type="java.lang.String" --]

View File

@@ -1,28 +0,0 @@
This page lists all active maintainers of this repository. If you were a
maintainer and would like to add your name to the Emeritus list, please send us a
PR.
See [GOVERNANCE.md](https://github.com/grpc/grpc-community/blob/master/governance.md)
for governance guidelines and how to become a maintainer.
See [CONTRIBUTING.md](https://github.com/grpc/grpc-community/blob/master/CONTRIBUTING.md)
for general contribution guidelines.
## Maintainers (in alphabetical order)
- [creamsoup](https://github.com/creamsoup), Google LLC
- [dapengzhang0](https://github.com/dapengzhang0), Google LLC
- [ejona86](https://github.com/ejona86), Google LLC
- [ericgribkoff](https://github.com/ericgribkoff), Google LLC
- [jiangtaoli2016](https://github.com/jiangtaoli2016), Google LLC
- [ran-su](https://github.com/ran-su), Google LLC
- [sanjaypujare](https://github.com/sanjaypujare), Google LLC
- [srini100](https://github.com/srini100), Google LLC
- [voidzcy](https://github.com/voidzcy), Google LLC
- [zhangkun83](https://github.com/zhangkun83), Google LLC
## Emeritus Maintainers (in alphabetical order)
- [carl-mastrangelo](https://github.com/carl-mastrangelo), Google LLC
- [jtattermusch](https://github.com/jtattermusch), Google LLC
- [louiscryan](https://github.com/louiscryan), Google LLC
- [nicolasnoble](https://github.com/nicolasnoble), Google LLC
- [nmittler](https://github.com/nmittler), Google LLC
- [zpencer](https://github.com/zpencer), Google LLC

View File

@@ -1,28 +0,0 @@
This page lists all active maintainers of this repository. If you were a
maintainer and would like to add your name to the Emeritus list, please send us a
PR.
See [GOVERNANCE.md](https://github.com/grpc/grpc-community/blob/master/governance.md)
for governance guidelines and how to become a maintainer.
See [CONTRIBUTING.md](https://github.com/grpc/grpc-community/blob/master/CONTRIBUTING.md)
for general contribution guidelines.
## Maintainers (in alphabetical order)
- [creamsoup](https://github.com/creamsoup), Google LLC
- [dapengzhang0](https://github.com/dapengzhang0), Google LLC
- [ejona86](https://github.com/ejona86), Google LLC
- [ericgribkoff](https://github.com/ericgribkoff), Google LLC
- [jiangtaoli2016](https://github.com/jiangtaoli2016), Google LLC
- [ran-su](https://github.com/ran-su), Google LLC
- [sanjaypujare](https://github.com/sanjaypujare), Google LLC
- [srini100](https://github.com/srini100), Google LLC
- [voidzcy](https://github.com/voidzcy), Google LLC
- [zhangkun83](https://github.com/zhangkun83), Google LLC
## Emeritus Maintainers (in alphabetical order)
- [carl-mastrangelo](https://github.com/carl-mastrangelo), Google LLC
- [jtattermusch](https://github.com/jtattermusch), Google LLC
- [louiscryan](https://github.com/louiscryan), Google LLC
- [nicolasnoble](https://github.com/nicolasnoble), Google LLC
- [nmittler](https://github.com/nmittler), Google LLC
- [zpencer](https://github.com/zpencer), Google LLC

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 323 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 387 KiB

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

@@ -1,8 +1,8 @@
package cn.keking.utils; package cn.keking.service;
import cn.keking.config.ConfigConstants; import cn.keking.config.ConfigConstants;
import cn.keking.model.FileType; import cn.keking.model.FileType;
import cn.keking.service.FilePreviewCommonService; import cn.keking.utils.FileUtils;
import cn.keking.web.filter.BaseUrlFilter; import cn.keking.web.filter.BaseUrlFilter;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@@ -26,37 +26,34 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
*
* @author yudian-it * @author yudian-it
* @date 2017/11/27 * create 2017/11/27
*/ */
@Component @Component
public class ZipReader { public class CompressFileReader {
static Pattern pattern = Pattern.compile("^\\d+");
private final FilePreviewCommonService filePreviewCommonService;
private static final Pattern pattern = Pattern.compile("^\\d+");
private final FileHandlerService fileHandlerService;
private final String fileDir = ConfigConstants.getFileDir(); private final String fileDir = ConfigConstants.getFileDir();
private final ExecutorService executors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); private final ExecutorService executors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public ZipReader(FilePreviewCommonService filePreviewCommonService) { public CompressFileReader(FileHandlerService fileHandlerService) {
this.filePreviewCommonService = filePreviewCommonService; this.fileHandlerService = fileHandlerService;
} }
public String readZipFile(String filePath,String fileKey) { public String readZipFile(String filePath, String fileKey) {
String archiveSeparator = "/"; String archiveSeparator = "/";
Map<String, FileNode> appender = new HashMap<>(); Map<String, FileNode> appender = new HashMap<>();
List<String> imgUrls = new LinkedList<>(); List<String> imgUrls = new LinkedList<>();
String baseUrl = BaseUrlFilter.getBaseUrl(); String baseUrl = BaseUrlFilter.getBaseUrl();
String archiveFileName = filePreviewCommonService.getFileNameFromPath(filePath); String archiveFileName = fileHandlerService.getFileNameFromPath(filePath);
try { try {
ZipFile zipFile = new ZipFile(filePath, filePreviewCommonService.getFileEncodeUTFGBK(filePath)); ZipFile zipFile = new ZipFile(filePath, FileUtils.getFileEncode(filePath));
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries(); Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
// 排序 // 排序
entries = sortZipEntries(entries); entries = sortZipEntries(entries);
List<Map<String, ZipArchiveEntry>> entriesToBeExtracted = new LinkedList<>(); List<Map<String, ZipArchiveEntry>> entriesToBeExtracted = new LinkedList<>();
while (entries.hasMoreElements()){ while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement(); ZipArchiveEntry entry = entries.nextElement();
String fullName = entry.getName(); String fullName = entry.getName();
int level = fullName.split(archiveSeparator).length; int level = fullName.split(archiveSeparator).length;
@@ -69,10 +66,10 @@ public class ZipReader {
entriesToBeExtracted.add(Collections.singletonMap(childName, entry)); entriesToBeExtracted.add(Collections.singletonMap(childName, entry));
} }
String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName); String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName);
parentName = (level-1) + "_" + parentName; parentName = (level - 1) + "_" + parentName;
FileType type= filePreviewCommonService.typeFromUrl(childName); FileType type = fileHandlerService.typeFromUrl(childName);
if (type.equals(FileType.picture)){//添加图片文件到图片列表 if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl+childName); imgUrls.add(baseUrl + childName);
} }
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey); FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey);
addNodes(appender, parentName, node); addNodes(appender, parentName, node);
@@ -80,7 +77,7 @@ public class ZipReader {
} }
// 开启新的线程处理文件解压 // 开启新的线程处理文件解压
executors.submit(new ZipExtractorWorker(entriesToBeExtracted, zipFile, filePath)); executors.submit(new ZipExtractorWorker(entriesToBeExtracted, zipFile, filePath));
filePreviewCommonService.putImgCache(fileKey,imgUrls); fileHandlerService.putImgCache(fileKey, imgUrls);
return new ObjectMapper().writeValueAsString(appender.get("")); return new ObjectMapper().writeValueAsString(appender.get(""));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@@ -90,28 +87,28 @@ public class ZipReader {
private Enumeration<ZipArchiveEntry> sortZipEntries(Enumeration<ZipArchiveEntry> entries) { private Enumeration<ZipArchiveEntry> sortZipEntries(Enumeration<ZipArchiveEntry> entries) {
List<ZipArchiveEntry> sortedEntries = new LinkedList<>(); List<ZipArchiveEntry> sortedEntries = new LinkedList<>();
while(entries.hasMoreElements()){ while (entries.hasMoreElements()) {
sortedEntries.add(entries.nextElement()); sortedEntries.add(entries.nextElement());
} }
sortedEntries.sort(Comparator.comparingInt(o -> o.getName().length())); sortedEntries.sort(Comparator.comparingInt(o -> o.getName().length()));
return Collections.enumeration(sortedEntries); return Collections.enumeration(sortedEntries);
} }
public String unRar(String filePath,String fileKey){ public String unRar(String filePath, String fileKey) {
Map<String, FileNode> appender = new HashMap<>(); Map<String, FileNode> appender = new HashMap<>();
List<String> imgUrls = new ArrayList<>(); List<String> imgUrls = new ArrayList<>();
String baseUrl = BaseUrlFilter.getBaseUrl(); String baseUrl = BaseUrlFilter.getBaseUrl();
try { try {
Archive archive = new Archive(new FileInputStream(new File(filePath))); Archive archive = new Archive(new FileInputStream(filePath));
List<FileHeader> headers = archive.getFileHeaders(); List<FileHeader> headers = archive.getFileHeaders();
headers = sortedHeaders(headers); headers = sortedHeaders(headers);
String archiveFileName = filePreviewCommonService.getFileNameFromPath(filePath); String archiveFileName = fileHandlerService.getFileNameFromPath(filePath);
List<Map<String, FileHeader>> headersToBeExtracted =new ArrayList<>(); List<Map<String, FileHeader>> headersToBeExtracted = new ArrayList<>();
for (FileHeader header : headers) { for (FileHeader header : headers) {
String fullName; String fullName;
if (header.isUnicode()) { if (header.isUnicode()) {
fullName = header.getFileNameW(); fullName = header.getFileNameW();
}else { } else {
fullName = header.getFileNameString(); fullName = header.getFileNameString();
} }
// 展示名 // 展示名
@@ -123,16 +120,16 @@ public class ZipReader {
headersToBeExtracted.add(Collections.singletonMap(childName, header)); headersToBeExtracted.add(Collections.singletonMap(childName, header));
} }
String parentName = getLast2FileName(fullName, "\\", archiveFileName); String parentName = getLast2FileName(fullName, "\\", archiveFileName);
FileType type = filePreviewCommonService.typeFromUrl(childName); FileType type = fileHandlerService.typeFromUrl(childName);
if (type.equals(FileType.picture)){//添加图片文件到图片列表 if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl+childName); imgUrls.add(baseUrl + childName);
} }
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey); FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey);
addNodes(appender, parentName, node); addNodes(appender, parentName, node);
appender.put(childName, node); appender.put(childName, node);
} }
executors.submit(new RarExtractorWorker(headersToBeExtracted, archive, filePath)); executors.submit(new RarExtractorWorker(headersToBeExtracted, archive, filePath));
filePreviewCommonService.putImgCache(fileKey,imgUrls); fileHandlerService.putImgCache(fileKey, imgUrls);
return new ObjectMapper().writeValueAsString(appender.get("")); return new ObjectMapper().writeValueAsString(appender.get(""));
} catch (RarException | IOException e) { } catch (RarException | IOException e) {
e.printStackTrace(); e.printStackTrace();
@@ -140,19 +137,19 @@ public class ZipReader {
return null; return null;
} }
public String read7zFile(String filePath,String fileKey) { public String read7zFile(String filePath, String fileKey) {
String archiveSeparator = "/"; String archiveSeparator = "/";
Map<String, FileNode> appender = new HashMap<>(); Map<String, FileNode> appender = new HashMap<>();
List<String> imgUrls = new ArrayList<>(); List<String> imgUrls = new ArrayList<>();
String baseUrl= BaseUrlFilter.getBaseUrl(); String baseUrl = BaseUrlFilter.getBaseUrl();
String archiveFileName = filePreviewCommonService.getFileNameFromPath(filePath); String archiveFileName = fileHandlerService.getFileNameFromPath(filePath);
try { try {
SevenZFile zipFile = new SevenZFile(new File(filePath)); SevenZFile zipFile = new SevenZFile(new File(filePath));
Iterable<SevenZArchiveEntry> entries = zipFile.getEntries(); Iterable<SevenZArchiveEntry> entries = zipFile.getEntries();
// 排序 // 排序
Enumeration<SevenZArchiveEntry> newEntries = sortSevenZEntries(entries); Enumeration<SevenZArchiveEntry> newEntries = sortSevenZEntries(entries);
List<Map<String, SevenZArchiveEntry>> entriesToBeExtracted = new ArrayList<>(); List<Map<String, SevenZArchiveEntry>> entriesToBeExtracted = new ArrayList<>();
while (newEntries.hasMoreElements()){ while (newEntries.hasMoreElements()) {
SevenZArchiveEntry entry = newEntries.nextElement(); SevenZArchiveEntry entry = newEntries.nextElement();
String fullName = entry.getName(); String fullName = entry.getName();
int level = fullName.split(archiveSeparator).length; int level = fullName.split(archiveSeparator).length;
@@ -165,10 +162,10 @@ public class ZipReader {
entriesToBeExtracted.add(Collections.singletonMap(childName, entry)); entriesToBeExtracted.add(Collections.singletonMap(childName, entry));
} }
String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName); String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName);
parentName = (level-1) + "_" + parentName; parentName = (level - 1) + "_" + parentName;
FileType type= filePreviewCommonService.typeFromUrl(childName); FileType type = fileHandlerService.typeFromUrl(childName);
if (type.equals(FileType.picture)){//添加图片文件到图片列表 if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl+childName); imgUrls.add(baseUrl + childName);
} }
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey); FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey);
addNodes(appender, parentName, node); addNodes(appender, parentName, node);
@@ -176,7 +173,7 @@ public class ZipReader {
} }
// 开启新的线程处理文件解压 // 开启新的线程处理文件解压
executors.submit(new SevenZExtractorWorker(entriesToBeExtracted, filePath)); executors.submit(new SevenZExtractorWorker(entriesToBeExtracted, filePath));
filePreviewCommonService.putImgCache(fileKey,imgUrls); fileHandlerService.putImgCache(fileKey, imgUrls);
return new ObjectMapper().writeValueAsString(appender.get("")); return new ObjectMapper().writeValueAsString(appender.get(""));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@@ -210,7 +207,7 @@ public class ZipReader {
List<FileHeader> sortedHeaders = new ArrayList<>(); List<FileHeader> sortedHeaders = new ArrayList<>();
Map<Integer, FileHeader> mapHeaders = new TreeMap<>(); Map<Integer, FileHeader> mapHeaders = new TreeMap<>();
headers.forEach(header -> mapHeaders.put(new Integer(0).equals(header.getFileNameW().length()) ? header.getFileNameString().length() : header.getFileNameW().length(), header)); headers.forEach(header -> mapHeaders.put(new Integer(0).equals(header.getFileNameW().length()) ? header.getFileNameString().length() : header.getFileNameW().length(), header));
for (Map.Entry<Integer, FileHeader> entry : mapHeaders.entrySet()){ for (Map.Entry<Integer, FileHeader> entry : mapHeaders.entrySet()) {
for (FileHeader header : headers) { for (FileHeader header : headers) {
if (entry.getKey().equals(new Integer(0).equals(header.getFileNameW().length()) ? header.getFileNameString().length() : header.getFileNameW().length())) { if (entry.getKey().equals(new Integer(0).equals(header.getFileNameW().length()) ? header.getFileNameString().length() : header.getFileNameW().length())) {
sortedHeaders.add(header); sortedHeaders.add(header);
@@ -222,7 +219,7 @@ public class ZipReader {
private static String getLast2FileName(String fullName, String seperator, String rootName) { private static String getLast2FileName(String fullName, String seperator, String rootName) {
if (fullName.endsWith(seperator)) { if (fullName.endsWith(seperator)) {
fullName = fullName.substring(0, fullName.length()-1); fullName = fullName.substring(0, fullName.length() - 1);
} }
// 1.获取剩余部分 // 1.获取剩余部分
int endIndex = fullName.lastIndexOf(seperator); int endIndex = fullName.lastIndexOf(seperator);
@@ -237,7 +234,7 @@ public class ZipReader {
private static String getLastFileName(String fullName, String seperator) { private static String getLastFileName(String fullName, String seperator) {
if (fullName.endsWith(seperator)) { if (fullName.endsWith(seperator)) {
fullName = fullName.substring(0, fullName.length()-1); fullName = fullName.substring(0, fullName.length() - 1);
} }
String newName = fullName; String newName = fullName;
if (fullName.contains(seperator)) { if (fullName.contains(seperator)) {
@@ -248,10 +245,11 @@ public class ZipReader {
public static Comparator<FileNode> sortComparator = new Comparator<FileNode>() { public static Comparator<FileNode> sortComparator = new Comparator<FileNode>() {
final Collator cmp = Collator.getInstance(Locale.US); final Collator cmp = Collator.getInstance(Locale.US);
@Override @Override
public int compare(FileNode o1, FileNode o2) { public int compare(FileNode o1, FileNode o2) {
// 判断两个对比对象是否是开头包含数字如果包含数字则获取数字并按数字真正大小进行排序 // 判断两个对比对象是否是开头包含数字如果包含数字则获取数字并按数字真正大小进行排序
BigDecimal num1,num2; BigDecimal num1, num2;
if (null != (num1 = isStartNumber(o1)) if (null != (num1 = isStartNumber(o1))
&& null != (num2 = isStartNumber(o2))) { && null != (num2 = isStartNumber(o2))) {
return num1.subtract(num2).intValue(); return num1.subtract(num2).intValue();
@@ -287,14 +285,16 @@ public class ZipReader {
this.childList = childList; this.childList = childList;
this.directory = directory; this.directory = directory;
} }
public FileNode(String originName, String fileName, String parentFileName, List<FileNode> childList, boolean directory,String fileKey) {
public FileNode(String originName, String fileName, String parentFileName, List<FileNode> childList, boolean directory, String fileKey) {
this.originName = originName; this.originName = originName;
this.fileName = fileName; this.fileName = fileName;
this.parentFileName = parentFileName; this.parentFileName = parentFileName;
this.childList = childList; this.childList = childList;
this.directory = directory; this.directory = directory;
this.fileKey=fileKey; this.fileKey = fileKey;
} }
public String getFileKey() { public String getFileKey() {
return fileKey; return fileKey;
} }
@@ -382,17 +382,15 @@ public class ZipReader {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
if (new File(filePath).exists()) { FileUtils.deleteFileByPath(filePath);
new File(filePath).delete();
}
} }
private void extractZipFile(String childName, InputStream zipFile) { private void extractZipFile(String childName, InputStream zipFile) {
String outPath = fileDir + childName; String outPath = fileDir + childName;
try (OutputStream ot = new FileOutputStream(outPath)){ try (OutputStream ot = new FileOutputStream(outPath)) {
byte[] inByte = new byte[1024]; byte[] inByte = new byte[1024];
int len; int len;
while ((-1 != (len = zipFile.read(inByte)))){ while ((-1 != (len = zipFile.read(inByte)))) {
ot.write(inByte, 0, len); ot.write(inByte, 0, len);
} }
} catch (IOException e) { } catch (IOException e) {
@@ -441,10 +439,7 @@ public class ZipReader {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
FileUtils.deleteFileByPath(filePath);
if (new File(filePath).exists()) {
new File(filePath).delete();
}
} }
} }
@@ -473,14 +468,12 @@ public class ZipReader {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
if (new File(filePath).exists()) { FileUtils.deleteFileByPath(filePath);
new File(filePath).delete();
}
} }
private void extractRarFile(String childName, FileHeader header, Archive archive) { private void extractRarFile(String childName, FileHeader header, Archive archive) {
String outPath = fileDir + childName; String outPath = fileDir + childName;
try(OutputStream ot = new FileOutputStream(outPath)) { try (OutputStream ot = new FileOutputStream(outPath)) {
archive.extractFile(header, ot); archive.extractFile(header, ot);
} catch (IOException | RarException e) { } catch (IOException | RarException e) {
e.printStackTrace(); e.printStackTrace();

View File

@@ -21,18 +21,18 @@ public class FileConvertQueueTask {
private final Logger logger = LoggerFactory.getLogger(getClass()); private final Logger logger = LoggerFactory.getLogger(getClass());
private final FilePreviewFactory previewFactory; private final FilePreviewFactory previewFactory;
private final CacheService cacheService; private final CacheService cacheService;
private final FilePreviewCommonService filePreviewCommonService; private final FileHandlerService fileHandlerService;
public FileConvertQueueTask(FilePreviewFactory previewFactory, CacheService cacheService, FilePreviewCommonService filePreviewCommonService) { public FileConvertQueueTask(FilePreviewFactory previewFactory, CacheService cacheService, FileHandlerService fileHandlerService) {
this.previewFactory = previewFactory; this.previewFactory = previewFactory;
this.cacheService = cacheService; this.cacheService = cacheService;
this.filePreviewCommonService = filePreviewCommonService; this.fileHandlerService = fileHandlerService;
} }
@PostConstruct @PostConstruct
public void startTask(){ public void startTask(){
ExecutorService executorService = Executors.newFixedThreadPool(1); ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(new ConvertTask(previewFactory, cacheService, filePreviewCommonService)); executorService.submit(new ConvertTask(previewFactory, cacheService, fileHandlerService));
logger.info("队列处理文件转换任务启动完成 "); logger.info("队列处理文件转换任务启动完成 ");
} }
@@ -41,14 +41,14 @@ public class FileConvertQueueTask {
private final Logger logger = LoggerFactory.getLogger(ConvertTask.class); private final Logger logger = LoggerFactory.getLogger(ConvertTask.class);
private final FilePreviewFactory previewFactory; private final FilePreviewFactory previewFactory;
private final CacheService cacheService; private final CacheService cacheService;
private final FilePreviewCommonService filePreviewCommonService; private final FileHandlerService fileHandlerService;
public ConvertTask(FilePreviewFactory previewFactory, public ConvertTask(FilePreviewFactory previewFactory,
CacheService cacheService, CacheService cacheService,
FilePreviewCommonService filePreviewCommonService) { FileHandlerService fileHandlerService) {
this.previewFactory = previewFactory; this.previewFactory = previewFactory;
this.cacheService = cacheService; this.cacheService = cacheService;
this.filePreviewCommonService = filePreviewCommonService; this.fileHandlerService = fileHandlerService;
} }
@Override @Override
@@ -58,7 +58,7 @@ public class FileConvertQueueTask {
try { try {
url = cacheService.takeQueueTask(); url = cacheService.takeQueueTask();
if(url != null){ if(url != null){
FileAttribute fileAttribute = filePreviewCommonService.getFileAttribute(url,null); FileAttribute fileAttribute = fileHandlerService.getFileAttribute(url,null);
FileType fileType = fileAttribute.getType(); FileType fileType = fileAttribute.getType();
logger.info("正在处理预览转换任务url{},预览类型:{}", url, fileType); logger.info("正在处理预览转换任务url{},预览类型:{}", url, fileType);
if(fileType.equals(FileType.compress) || fileType.equals(FileType.office) || fileType.equals(FileType.cad)) { if(fileType.equals(FileType.compress) || fileType.equals(FileType.office) || fileType.equals(FileType.cad)) {

View File

@@ -4,28 +4,28 @@ import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute; import cn.keking.model.FileAttribute;
import cn.keking.model.FileType; import cn.keking.model.FileType;
import cn.keking.service.cache.CacheService; import cn.keking.service.cache.CacheService;
import cn.keking.utils.WebUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.io.*; import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.List;
import java.util.Map;
/** /**
* @author yudian-it * @author yudian-it
* @date 2017/11/13 * @date 2017/11/13
*/ */
@Component @Component
public class FilePreviewCommonService { public class FileHandlerService {
private static final String DEFAULT_CONVERTER_CHARSET = System.getProperty("sun.jnu.encoding"); private static final String DEFAULT_CONVERTER_CHARSET = System.getProperty("sun.jnu.encoding");
private final String fileDir = ConfigConstants.getFileDir(); private final String fileDir = ConfigConstants.getFileDir();
private final CacheService cacheService; private final CacheService cacheService;
public FilePreviewCommonService(CacheService cacheService) { public FileHandlerService(CacheService cacheService) {
this.cacheService = cacheService; this.cacheService = cacheService;
} }
@@ -141,31 +141,6 @@ public class FilePreviewCommonService {
cacheService.putImgCache(fileKey, 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;
}
/** /**
* 对转换后的文件进行操作(改变编码方式) * 对转换后的文件进行操作(改变编码方式)
* *
@@ -214,55 +189,6 @@ public class FilePreviewCommonService {
return fileName.substring(fileName.lastIndexOf(".") + 1); 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;
}
/** /**
* 获取文件属性 * 获取文件属性
* *
@@ -274,7 +200,7 @@ public class FilePreviewCommonService {
String suffix; String suffix;
FileType type; FileType type;
String fileName; String fileName;
String fullFileName = this.getUrlParameterReg(url, "fullfilename"); String fullFileName = WebUtils.getUrlParameterReg(url, "fullfilename");
if (StringUtils.hasText(fullFileName)) { if (StringUtils.hasText(fullFileName)) {
fileName = fullFileName; fileName = fullFileName;
type = this.typeFromFileName(fullFileName); type = this.typeFromFileName(fullFileName);
@@ -290,11 +216,11 @@ public class FilePreviewCommonService {
attribute.setUrl(url); attribute.setUrl(url);
if (req != null) { if (req != null) {
String officePreviewType = req.getParameter("officePreviewType"); String officePreviewType = req.getParameter("officePreviewType");
String fileKey = req.getParameter("fileKey"); String fileKey = req.getParameter("fileKey");
if(StringUtils.hasText(officePreviewType)){ if (StringUtils.hasText(officePreviewType)) {
attribute.setOfficePreviewType(officePreviewType); attribute.setOfficePreviewType(officePreviewType);
} }
if(StringUtils.hasText(fileKey)){ if (StringUtils.hasText(fileKey)) {
attribute.setFileKey(fileKey); attribute.setFileKey(fileKey);
} }
} }

View File

@@ -1,6 +1,5 @@
package cn.keking.service; package cn.keking.service;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute; import cn.keking.model.FileAttribute;
import org.springframework.ui.Model; import org.springframework.ui.Model;
@@ -9,9 +8,5 @@ import org.springframework.ui.Model;
* Content : * Content :
*/ */
public interface FilePreview { public interface FilePreview {
String TEXT_TYPE = "textType";
String DEFAULT_TEXT_TYPE = "simText";
String filePreviewHandle(String url, Model model, FileAttribute fileAttribute); String filePreviewHandle(String url, Model model, FileAttribute fileAttribute);
} }

View File

@@ -32,9 +32,9 @@ import java.util.Properties;
*/ */
@Component @Component
@Order(Ordered.HIGHEST_PRECEDENCE) @Order(Ordered.HIGHEST_PRECEDENCE)
public class OfficeProcessManager { public class OfficePluginManager {
private final Logger logger = LoggerFactory.getLogger(OfficeProcessManager.class); private final Logger logger = LoggerFactory.getLogger(OfficePluginManager.class);
private OfficeManager officeManager; private OfficeManager officeManager;

View File

@@ -10,10 +10,10 @@ import java.io.File;
*/ */
@Component @Component
public class OfficeToPdfService { public class OfficeToPdfService {
private final OfficeProcessManager officeProcessManager; private final OfficePluginManager officePluginManager;
public OfficeToPdfService(OfficeProcessManager officeProcessManager) { public OfficeToPdfService(OfficePluginManager officePluginManager) {
this.officeProcessManager = officeProcessManager; this.officePluginManager = officePluginManager;
} }
public void openOfficeToPDF(String inputFilePath, String outputFilePath) { public void openOfficeToPDF(String inputFilePath, String outputFilePath) {
@@ -33,7 +33,7 @@ public class OfficeToPdfService {
public void office2pdf(String inputFilePath, String outputFilePath) { public void office2pdf(String inputFilePath, String outputFilePath) {
OfficeDocumentConverter converter = officeProcessManager.getDocumentConverter(); OfficeDocumentConverter converter = officePluginManager.getDocumentConverter();
if (null != inputFilePath) { if (null != inputFilePath) {
File inputFile = new File(inputFilePath); File inputFile = new File(inputFilePath);
// 判断目标文件路径是否为空 // 判断目标文件路径是否为空

View File

@@ -6,7 +6,7 @@ import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview; import cn.keking.service.FilePreview;
import cn.keking.utils.CadUtils; import cn.keking.utils.CadUtils;
import cn.keking.utils.DownloadUtils; import cn.keking.utils.DownloadUtils;
import cn.keking.service.FilePreviewCommonService; import cn.keking.service.FileHandlerService;
import cn.keking.utils.PdfUtils; import cn.keking.utils.PdfUtils;
import cn.keking.web.filter.BaseUrlFilter; import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -22,20 +22,12 @@ import static cn.keking.service.impl.OfficeFilePreviewImpl.getPreviewType;
@Service @Service
public class CadFilePreviewImpl implements FilePreview { public class CadFilePreviewImpl implements FilePreview {
private final FilePreviewCommonService filePreviewCommonService; private final FileHandlerService fileHandlerService;
private final DownloadUtils downloadUtils;
private final CadUtils cadUtils; private final CadUtils cadUtils;
private final PdfUtils pdfUtils; private final PdfUtils pdfUtils;
public CadFilePreviewImpl(FilePreviewCommonService filePreviewCommonService, public CadFilePreviewImpl(FileHandlerService fileHandlerService, CadUtils cadUtils, PdfUtils pdfUtils) {
DownloadUtils downloadUtils, this.fileHandlerService = fileHandlerService;
CadUtils cadUtils,
PdfUtils pdfUtils) {
this.filePreviewCommonService = filePreviewCommonService;
this.downloadUtils = downloadUtils;
this.cadUtils = cadUtils; this.cadUtils = cadUtils;
this.pdfUtils = pdfUtils; this.pdfUtils = pdfUtils;
@@ -56,9 +48,9 @@ public class CadFilePreviewImpl implements FilePreview {
String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + "pdf"; String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + "pdf";
String outFilePath = FILE_DIR + pdfName; String outFilePath = FILE_DIR + pdfName;
// 判断之前是否已转换过,如果转换过,直接返回,否则执行转换 // 判断之前是否已转换过,如果转换过,直接返回,否则执行转换
if (!filePreviewCommonService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) { if (!fileHandlerService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
String filePath; String filePath;
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, null); ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null);
if (0 != response.getCode()) { if (0 != response.getCode()) {
model.addAttribute("fileType", suffix); model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg()); model.addAttribute("msg", response.getMsg());
@@ -74,7 +66,7 @@ public class CadFilePreviewImpl implements FilePreview {
} }
if (ConfigConstants.isCacheEnabled()) { if (ConfigConstants.isCacheEnabled()) {
// 加入缓存 // 加入缓存
filePreviewCommonService.addConvertedFile(pdfName, filePreviewCommonService.getRelativePath(outFilePath)); fileHandlerService.addConvertedFile(pdfName, fileHandlerService.getRelativePath(outFilePath));
} }
} }
} }

View File

@@ -5,8 +5,8 @@ import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse; import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview; import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils; import cn.keking.utils.DownloadUtils;
import cn.keking.service.FilePreviewCommonService; import cn.keking.service.FileHandlerService;
import cn.keking.utils.ZipReader; import cn.keking.service.CompressFileReader;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@@ -18,18 +18,12 @@ import org.springframework.util.StringUtils;
@Service @Service
public class CompressFilePreviewImpl implements FilePreview { public class CompressFilePreviewImpl implements FilePreview {
private final FilePreviewCommonService filePreviewCommonService; private final FileHandlerService fileHandlerService;
private final CompressFileReader compressFileReader;
private final DownloadUtils downloadUtils; public CompressFilePreviewImpl(FileHandlerService fileHandlerService, CompressFileReader compressFileReader) {
this.fileHandlerService = fileHandlerService;
private final ZipReader zipReader; this.compressFileReader = compressFileReader;
public CompressFilePreviewImpl(FilePreviewCommonService filePreviewCommonService,
DownloadUtils downloadUtils,
ZipReader zipReader) {
this.filePreviewCommonService = filePreviewCommonService;
this.downloadUtils = downloadUtils;
this.zipReader = zipReader;
} }
@Override @Override
@@ -38,8 +32,8 @@ public class CompressFilePreviewImpl implements FilePreview {
String suffix=fileAttribute.getSuffix(); String suffix=fileAttribute.getSuffix();
String fileTree = null; String fileTree = null;
// 判断文件名是否存在(redis缓存读取) // 判断文件名是否存在(redis缓存读取)
if (!StringUtils.hasText(filePreviewCommonService.getConvertedFile(fileName)) || !ConfigConstants.isCacheEnabled()) { if (!StringUtils.hasText(fileHandlerService.getConvertedFile(fileName)) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileName); ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (0 != response.getCode()) { if (0 != response.getCode()) {
model.addAttribute("fileType", suffix); model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg()); model.addAttribute("msg", response.getMsg());
@@ -47,17 +41,17 @@ public class CompressFilePreviewImpl implements FilePreview {
} }
String filePath = response.getContent(); String filePath = response.getContent();
if ("zip".equalsIgnoreCase(suffix) || "jar".equalsIgnoreCase(suffix) || "gzip".equalsIgnoreCase(suffix)) { if ("zip".equalsIgnoreCase(suffix) || "jar".equalsIgnoreCase(suffix) || "gzip".equalsIgnoreCase(suffix)) {
fileTree = zipReader.readZipFile(filePath, fileName); fileTree = compressFileReader.readZipFile(filePath, fileName);
} else if ("rar".equalsIgnoreCase(suffix)) { } else if ("rar".equalsIgnoreCase(suffix)) {
fileTree = zipReader.unRar(filePath, fileName); fileTree = compressFileReader.unRar(filePath, fileName);
} else if ("7z".equalsIgnoreCase(suffix)) { } else if ("7z".equalsIgnoreCase(suffix)) {
fileTree = zipReader.read7zFile(filePath, fileName); fileTree = compressFileReader.read7zFile(filePath, fileName);
} }
if (fileTree != null && !"null".equals(fileTree) && ConfigConstants.isCacheEnabled()) { if (fileTree != null && !"null".equals(fileTree) && ConfigConstants.isCacheEnabled()) {
filePreviewCommonService.addConvertedFile(fileName, fileTree); fileHandlerService.addConvertedFile(fileName, fileTree);
} }
} else { } else {
fileTree = filePreviewCommonService.getConvertedFile(fileName); fileTree = fileHandlerService.getConvertedFile(fileName);
} }
if (fileTree != null && !"null".equals(fileTree)) { if (fileTree != null && !"null".equals(fileTree)) {
model.addAttribute("fileTree", fileTree); model.addAttribute("fileTree", fileTree);

View File

@@ -5,6 +5,8 @@ import cn.keking.service.FilePreview;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import static com.sun.glass.ui.Clipboard.TEXT_TYPE;
/** /**
* @author kl (http://kailing.pub) * @author kl (http://kailing.pub)
* @since 2020/12/25 * @since 2020/12/25

View File

@@ -4,7 +4,7 @@ import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse; import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview; import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils; import cn.keking.utils.DownloadUtils;
import cn.keking.service.FilePreviewCommonService; import cn.keking.service.FileHandlerService;
import cn.keking.web.filter.BaseUrlFilter; import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.ui.Model; import org.springframework.ui.Model;
@@ -17,27 +17,23 @@ import org.springframework.ui.Model;
@Service @Service
public class MediaFilePreviewImpl implements FilePreview { public class MediaFilePreviewImpl implements FilePreview {
private final DownloadUtils downloadUtils; private final FileHandlerService fileHandlerService;
private final FilePreviewCommonService filePreviewCommonService; public MediaFilePreviewImpl(FileHandlerService fileHandlerService) {
this.fileHandlerService = fileHandlerService;
public MediaFilePreviewImpl(DownloadUtils downloadUtils,
FilePreviewCommonService filePreviewCommonService) {
this.downloadUtils = downloadUtils;
this.filePreviewCommonService = filePreviewCommonService;
} }
@Override @Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) { public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
// 不是http开头浏览器不能直接访问需下载到本地 // 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) { if (url != null && !url.toLowerCase().startsWith("http")) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileAttribute.getName()); ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileAttribute.getName());
if (0 != response.getCode()) { if (0 != response.getCode()) {
model.addAttribute("fileType", fileAttribute.getSuffix()); model.addAttribute("fileType", fileAttribute.getSuffix());
model.addAttribute("msg", response.getMsg()); model.addAttribute("msg", response.getMsg());
return "fileNotSupported"; return "fileNotSupported";
} else { } else {
model.addAttribute("mediaUrl", BaseUrlFilter.getBaseUrl() + filePreviewCommonService.getRelativePath(response.getContent())); model.addAttribute("mediaUrl", BaseUrlFilter.getBaseUrl() + fileHandlerService.getRelativePath(response.getContent()));
} }
} else { } else {
model.addAttribute("mediaUrl", url); model.addAttribute("mediaUrl", url);

View File

@@ -5,7 +5,7 @@ import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse; import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview; import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils; import cn.keking.utils.DownloadUtils;
import cn.keking.service.FilePreviewCommonService; import cn.keking.service.FileHandlerService;
import cn.keking.service.OfficeToPdfService; import cn.keking.service.OfficeToPdfService;
import cn.keking.utils.PdfUtils; import cn.keking.utils.PdfUtils;
import cn.keking.web.filter.BaseUrlFilter; import cn.keking.web.filter.BaseUrlFilter;
@@ -22,15 +22,13 @@ import java.util.List;
@Service @Service
public class OfficeFilePreviewImpl implements FilePreview { public class OfficeFilePreviewImpl implements FilePreview {
private final FilePreviewCommonService filePreviewCommonService; private final FileHandlerService fileHandlerService;
private final PdfUtils pdfUtils; private final PdfUtils pdfUtils;
private final DownloadUtils downloadUtils;
private final OfficeToPdfService officeToPdfService; private final OfficeToPdfService officeToPdfService;
public OfficeFilePreviewImpl(FilePreviewCommonService filePreviewCommonService, PdfUtils pdfUtils, DownloadUtils downloadUtils, OfficeToPdfService officeToPdfService) { public OfficeFilePreviewImpl(FileHandlerService fileHandlerService, PdfUtils pdfUtils, OfficeToPdfService officeToPdfService) {
this.filePreviewCommonService = filePreviewCommonService; this.fileHandlerService = fileHandlerService;
this.pdfUtils = pdfUtils; this.pdfUtils = pdfUtils;
this.downloadUtils = downloadUtils;
this.officeToPdfService = officeToPdfService; this.officeToPdfService = officeToPdfService;
} }
@@ -49,9 +47,9 @@ public class OfficeFilePreviewImpl implements FilePreview {
String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + (isHtml ? "html" : "pdf"); String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + (isHtml ? "html" : "pdf");
String outFilePath = FILE_DIR + pdfName; String outFilePath = FILE_DIR + pdfName;
// 判断之前是否已转换过,如果转换过,直接返回,否则执行转换 // 判断之前是否已转换过,如果转换过,直接返回,否则执行转换
if (!filePreviewCommonService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) { if (!fileHandlerService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
String filePath; String filePath;
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, null); ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null);
if (0 != response.getCode()) { if (0 != response.getCode()) {
model.addAttribute("fileType", suffix); model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg()); model.addAttribute("msg", response.getMsg());
@@ -62,11 +60,11 @@ public class OfficeFilePreviewImpl implements FilePreview {
officeToPdfService.openOfficeToPDF(filePath, outFilePath); officeToPdfService.openOfficeToPDF(filePath, outFilePath);
if (isHtml) { if (isHtml) {
// 对转换后的文件进行操作(改变编码方式) // 对转换后的文件进行操作(改变编码方式)
filePreviewCommonService.doActionConvertedFile(outFilePath); fileHandlerService.doActionConvertedFile(outFilePath);
} }
if (ConfigConstants.isCacheEnabled()) { if (ConfigConstants.isCacheEnabled()) {
// 加入缓存 // 加入缓存
filePreviewCommonService.addConvertedFile(pdfName, filePreviewCommonService.getRelativePath(outFilePath)); fileHandlerService.addConvertedFile(pdfName, fileHandlerService.getRelativePath(outFilePath));
} }
} }
} }

View File

@@ -5,7 +5,7 @@ import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse; import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview; import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils; import cn.keking.utils.DownloadUtils;
import cn.keking.service.FilePreviewCommonService; import cn.keking.service.FileHandlerService;
import cn.keking.utils.PdfUtils; import cn.keking.utils.PdfUtils;
import cn.keking.web.filter.BaseUrlFilter; import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -20,20 +20,13 @@ import java.util.List;
@Service @Service
public class PdfFilePreviewImpl implements FilePreview { public class PdfFilePreviewImpl implements FilePreview {
private final FilePreviewCommonService filePreviewCommonService; private final FileHandlerService fileHandlerService;
private final PdfUtils pdfUtils; private final PdfUtils pdfUtils;
private final DownloadUtils downloadUtils;
private static final String FILE_DIR = ConfigConstants.getFileDir(); private static final String FILE_DIR = ConfigConstants.getFileDir();
public PdfFilePreviewImpl(FilePreviewCommonService filePreviewCommonService, public PdfFilePreviewImpl(FileHandlerService fileHandlerService, PdfUtils pdfUtils) {
PdfUtils pdfUtils, this.fileHandlerService = fileHandlerService;
DownloadUtils downloadUtils) {
this.filePreviewCommonService = filePreviewCommonService;
this.pdfUtils = pdfUtils; this.pdfUtils = pdfUtils;
this.downloadUtils = downloadUtils;
} }
@Override @Override
@@ -46,8 +39,8 @@ public class PdfFilePreviewImpl implements FilePreview {
String outFilePath = FILE_DIR + pdfName; String outFilePath = FILE_DIR + pdfName;
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType)) { if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType)) {
//当文件不存在时,就去下载 //当文件不存在时,就去下载
if (!filePreviewCommonService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) { if (!fileHandlerService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileName); ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (0 != response.getCode()) { if (0 != response.getCode()) {
model.addAttribute("fileType", suffix); model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg()); model.addAttribute("msg", response.getMsg());
@@ -56,7 +49,7 @@ public class PdfFilePreviewImpl implements FilePreview {
outFilePath = response.getContent(); outFilePath = response.getContent();
if (ConfigConstants.isCacheEnabled()) { if (ConfigConstants.isCacheEnabled()) {
// 加入缓存 // 加入缓存
filePreviewCommonService.addConvertedFile(pdfName, filePreviewCommonService.getRelativePath(outFilePath)); fileHandlerService.addConvertedFile(pdfName, fileHandlerService.getRelativePath(outFilePath));
} }
} }
List<String> imageUrls = pdfUtils.pdf2jpg(outFilePath, pdfName, baseUrl); List<String> imageUrls = pdfUtils.pdf2jpg(outFilePath, pdfName, baseUrl);
@@ -75,17 +68,17 @@ public class PdfFilePreviewImpl implements FilePreview {
} else { } else {
// 不是http开头浏览器不能直接访问需下载到本地 // 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) { if (url != null && !url.toLowerCase().startsWith("http")) {
if (!filePreviewCommonService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) { if (!fileHandlerService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, pdfName); ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, pdfName);
if (0 != response.getCode()) { if (0 != response.getCode()) {
model.addAttribute("fileType", suffix); model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg()); model.addAttribute("msg", response.getMsg());
return "fileNotSupported"; return "fileNotSupported";
} }
model.addAttribute("pdfUrl", filePreviewCommonService.getRelativePath(response.getContent())); model.addAttribute("pdfUrl", fileHandlerService.getRelativePath(response.getContent()));
if (ConfigConstants.isCacheEnabled()) { if (ConfigConstants.isCacheEnabled()) {
// 加入缓存 // 加入缓存
filePreviewCommonService.addConvertedFile(pdfName, filePreviewCommonService.getRelativePath(outFilePath)); fileHandlerService.addConvertedFile(pdfName, fileHandlerService.getRelativePath(outFilePath));
} }
} else { } else {
model.addAttribute("pdfUrl", pdfName); model.addAttribute("pdfUrl", pdfName);

View File

@@ -4,7 +4,7 @@ import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse; import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview; import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils; import cn.keking.utils.DownloadUtils;
import cn.keking.service.FilePreviewCommonService; import cn.keking.service.FileHandlerService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@@ -18,14 +18,10 @@ import java.util.List;
@Service @Service
public class PictureFilePreviewImpl implements FilePreview { public class PictureFilePreviewImpl implements FilePreview {
private final FilePreviewCommonService filePreviewCommonService; private final FileHandlerService fileHandlerService;
private final DownloadUtils downloadUtils; public PictureFilePreviewImpl(FileHandlerService fileHandlerService) {
this.fileHandlerService = fileHandlerService;
public PictureFilePreviewImpl(FilePreviewCommonService filePreviewCommonService,
DownloadUtils downloadUtils) {
this.filePreviewCommonService = filePreviewCommonService;
this.downloadUtils = downloadUtils;
} }
@Override @Override
@@ -33,19 +29,19 @@ public class PictureFilePreviewImpl implements FilePreview {
List<String> imgUrls = new ArrayList<>(); List<String> imgUrls = new ArrayList<>();
imgUrls.add(url); imgUrls.add(url);
String fileKey = fileAttribute.getFileKey(); String fileKey = fileAttribute.getFileKey();
List<String> zipImgUrls = filePreviewCommonService.getImgCache(fileKey); List<String> zipImgUrls = fileHandlerService.getImgCache(fileKey);
if (!CollectionUtils.isEmpty(zipImgUrls)) { if (!CollectionUtils.isEmpty(zipImgUrls)) {
imgUrls.addAll(zipImgUrls); imgUrls.addAll(zipImgUrls);
} }
// 不是http开头浏览器不能直接访问需下载到本地 // 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) { if (url != null && !url.toLowerCase().startsWith("http")) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, null); ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null);
if (0 != response.getCode()) { if (0 != response.getCode()) {
model.addAttribute("fileType", fileAttribute.getSuffix()); model.addAttribute("fileType", fileAttribute.getSuffix());
model.addAttribute("msg", response.getMsg()); model.addAttribute("msg", response.getMsg());
return "fileNotSupported"; return "fileNotSupported";
} else { } else {
String file = filePreviewCommonService.getRelativePath(response.getContent()); String file = fileHandlerService.getRelativePath(response.getContent());
imgUrls.clear(); imgUrls.clear();
imgUrls.add(file); imgUrls.add(file);
model.addAttribute("imgurls", imgUrls); model.addAttribute("imgurls", imgUrls);

View File

@@ -1,7 +1,6 @@
package cn.keking.service.impl; package cn.keking.service.impl;
import cn.keking.model.FileAttribute; import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.model.ReturnResponse; import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview; import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils; import cn.keking.utils.DownloadUtils;
@@ -13,7 +12,6 @@ import org.springframework.util.Base64Utils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
/** /**
* Created by kl on 2018/1/17. * Created by kl on 2018/1/17.
@@ -22,17 +20,13 @@ import java.nio.file.Files;
@Service @Service
public class SimTextFilePreviewImpl implements FilePreview { public class SimTextFilePreviewImpl implements FilePreview {
private final DownloadUtils downloadUtils; public static final String TEXT_TYPE = "textType";
public static final String DEFAULT_TEXT_TYPE = "simText";
public SimTextFilePreviewImpl(DownloadUtils downloadUtils) {
this.downloadUtils = downloadUtils;
}
@Override @Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) { public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String fileName = fileAttribute.getName(); String fileName = fileAttribute.getName();
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileName); ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (0 != response.getCode()) { if (0 != response.getCode()) {
model.addAttribute("msg", response.getMsg()); model.addAttribute("msg", response.getMsg());
model.addAttribute("fileType", fileAttribute.getSuffix()); model.addAttribute("fileType", fileAttribute.getSuffix());

View File

@@ -5,6 +5,8 @@ import cn.keking.service.FilePreview;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import static com.sun.glass.ui.Clipboard.TEXT_TYPE;
/** /**
* @author kl (http://kailing.pub) * @author kl (http://kailing.pub)
* @since 2020/12/25 * @since 2020/12/25

View File

@@ -1,14 +1,11 @@
package cn.keking.utils; package cn.keking.utils;
import cn.keking.config.ConfigConstants; import cn.keking.config.ConfigConstants;
import cn.keking.hutool.URLUtil;
import cn.keking.model.FileAttribute; import cn.keking.model.FileAttribute;
import cn.keking.model.FileType; import cn.keking.model.FileType;
import cn.keking.model.ReturnResponse; import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreviewCommonService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
@@ -18,53 +15,44 @@ import java.util.UUID;
/** /**
* @author yudian-it * @author yudian-it
*/ */
@Component
public class DownloadUtils { public class DownloadUtils {
private final Logger logger = LoggerFactory.getLogger(DownloadUtils.class); private final static Logger logger = LoggerFactory.getLogger(DownloadUtils.class);
private static final String fileDir = ConfigConstants.getFileDir();
private final String fileDir = ConfigConstants.getFileDir();
private final FilePreviewCommonService filePreviewCommonService;
public DownloadUtils(FilePreviewCommonService filePreviewCommonService) {
this.filePreviewCommonService = filePreviewCommonService;
}
private static final String URL_PARAM_FTP_USERNAME = "ftp.username"; 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_PASSWORD = "ftp.password";
private static final String URL_PARAM_FTP_CONTROL_ENCODING = "ftp.control.encoding"; private static final String URL_PARAM_FTP_CONTROL_ENCODING = "ftp.control.encoding";
/** /**
* @param fileAttribute fileAttribute * @param fileAttribute fileAttribute
* @param fileName 文件名 * @param fileName 文件名
* @return 本地文件绝对路径 * @return 本地文件绝对路径
*/ */
public ReturnResponse<String> downLoad(FileAttribute fileAttribute, String fileName) { public static ReturnResponse<String> downLoad(FileAttribute fileAttribute, String fileName) {
String urlStr = fileAttribute.getUrl(); String urlStr = fileAttribute.getUrl();
String type = fileAttribute.getSuffix(); String type = fileAttribute.getSuffix();
ReturnResponse<String> response = new ReturnResponse<>(0, "下载成功!!!", ""); ReturnResponse<String> response = new ReturnResponse<>(0, "下载成功!!!", "");
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
if (null == fileName) { if (null == fileName) {
fileName = uuid+ "."+type; fileName = uuid + "." + type;
} else { // 文件后缀不一致时以type为准(针对simText【将类txt文件转为txt】) } else { // 文件后缀不一致时以type为准(针对simText【将类txt文件转为txt】)
fileName = fileName.replace(fileName.substring(fileName.lastIndexOf(".") + 1), type); fileName = fileName.replace(fileName.substring(fileName.lastIndexOf(".") + 1), type);
} }
String realPath = fileDir + fileName; String realPath = fileDir + fileName;
File dirFile = new File(fileDir); File dirFile = new File(fileDir);
if (!dirFile.exists()) { if (!dirFile.exists() && !dirFile.mkdirs()) {
dirFile.mkdirs(); logger.error("创建目录【{}】失败,可能是权限不够,请检查", fileDir);
} }
try { try {
URL url = new URL(urlStr); URL url = new URL(urlStr);
if (url.getProtocol() != null && (url.getProtocol().toLowerCase().startsWith("file")||url.getProtocol().toLowerCase().startsWith("http"))) { if (url.getProtocol() != null && (url.getProtocol().toLowerCase().startsWith("file") || url.getProtocol().toLowerCase().startsWith("http"))) {
byte[] bytes = getBytesFromUrl(urlStr); byte[] bytes = getBytesFromUrl(urlStr);
OutputStream os = new FileOutputStream(realPath); OutputStream os = new FileOutputStream(realPath);
saveBytesToOutStream(bytes, os); saveBytesToOutStream(bytes, os);
} else if (url.getProtocol() != null && "ftp".equalsIgnoreCase(url.getProtocol())) { } else if (url.getProtocol() != null && "ftp".equalsIgnoreCase(url.getProtocol())) {
String ftpUsername = filePreviewCommonService.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME); String ftpUsername = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME);
String ftpPassword = filePreviewCommonService.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD); String ftpPassword = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD);
String ftpControlEncoding = filePreviewCommonService.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING); String ftpControlEncoding = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING);
FtpUtils.download(fileAttribute.getUrl(), realPath, ftpUsername, ftpPassword, ftpControlEncoding); FtpUtils.download(fileAttribute.getUrl(), realPath, ftpUsername, ftpPassword, ftpControlEncoding);
} else { } else {
response.setCode(1); response.setCode(1);
@@ -73,7 +61,7 @@ public class DownloadUtils {
} }
response.setContent(realPath); response.setContent(realPath);
response.setMsg(fileName); response.setMsg(fileName);
if(FileType.simText.equals(fileAttribute.getType())){ if (FileType.simText.equals(fileAttribute.getType())) {
convertTextPlainFileCharsetToUtf8(realPath); convertTextPlainFileCharsetToUtf8(realPath);
} }
return response; return response;
@@ -90,27 +78,25 @@ public class DownloadUtils {
} }
} }
public byte[] getBytesFromUrl(String urlStr) throws IOException { public static byte[] getBytesFromUrl(String urlStr) throws IOException {
InputStream is = getInputStreamFromUrl(urlStr); InputStream is = getInputStreamFromUrl(urlStr);
if (is != null) { if (is == null) {
return getBytesFromStream(is); // urlStr = URLUtil.normalize(urlStr, true, true);
} else {
urlStr = URLUtil.normalize(urlStr, true, true);
is = getInputStreamFromUrl(urlStr); is = getInputStreamFromUrl(urlStr);
if (is == null) { if (is == null) {
logger.error("文件下载异常url{}", urlStr); logger.error("文件下载异常url{}", urlStr);
throw new IOException("文件下载异常url" + urlStr); throw new IOException("文件下载异常url" + urlStr);
} }
return getBytesFromStream(is);
} }
return getBytesFromStream(is);
} }
public void saveBytesToOutStream(byte[] b, OutputStream os) throws IOException { public static void saveBytesToOutStream(byte[] b, OutputStream os) throws IOException {
os.write(b); os.write(b);
os.close(); os.close();
} }
private InputStream getInputStreamFromUrl(String urlStr) { private static InputStream getInputStreamFromUrl(String urlStr) {
try { try {
URL url = new URL(urlStr); URL url = new URL(urlStr);
URLConnection connection = url.openConnection(); URLConnection connection = url.openConnection();
@@ -124,10 +110,10 @@ public class DownloadUtils {
} }
} }
private byte[] getBytesFromStream(InputStream is) throws IOException { private static byte[] getBytesFromStream(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
int len = 0; int len;
while ((len = is.read(buffer)) != -1) { while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len); baos.write(buffer, 0, len);
} }
@@ -137,41 +123,37 @@ public class DownloadUtils {
return b; return b;
} }
/** /**
* 转换文本文件编码为utf8 * 转换文本文件编码为utf8
* 探测源文件编码,探测到编码切不为utf8则进行转码 * 探测源文件编码,探测到编码切不为utf8则进行转码
* @param filePath 文件路径 *
*/ * @param filePath 文件路径
private static void convertTextPlainFileCharsetToUtf8(String filePath) throws IOException { */
File sourceFile = new File(filePath); private static void convertTextPlainFileCharsetToUtf8(String filePath) throws IOException {
if(sourceFile.exists() && sourceFile.isFile() && sourceFile.canRead()) { File sourceFile = new File(filePath);
String encoding = null; if (sourceFile.exists() && sourceFile.isFile() && sourceFile.canRead()) {
try { String encoding = FileUtils.getFileEncode(filePath);
FileCharsetDetector.Observer observer = FileCharsetDetector.guessFileEncoding(sourceFile); if (!FileUtils.DEFAULT_FILE_ENCODING.equals(encoding)) {
// 为准确探测到编码,不适用猜测的编 // 不为utf8,进行转
encoding = observer.isFound()?observer.getEncoding():null; File tmpUtf8File = new File(filePath + ".utf8");
// 为准确探测到编码,可以考虑使用GBK 大部分文件都是windows系统产生的 Writer writer = new OutputStreamWriter(new FileOutputStream(tmpUtf8File), StandardCharsets.UTF_8);
} catch (IOException e) { Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile), encoding));
// 编码探测失败, char[] buf = new char[1024];
e.printStackTrace(); int read;
} while ((read = reader.read(buf)) > 0) {
if(encoding != null && !"UTF-8".equals(encoding)){ writer.write(buf, 0, read);
// 不为utf8,进行转码 }
File tmpUtf8File = new File(filePath+".utf8"); reader.close();
Writer writer = new OutputStreamWriter(new FileOutputStream(tmpUtf8File), StandardCharsets.UTF_8); writer.close();
Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile),encoding)); // 删除源文件
char[] buf = new char[1024]; if (!sourceFile.delete()) {
int read; logger.error("源文件【{}】删除失败,请检查文件目录权限!", filePath);
while ((read = reader.read(buf)) > 0){ }
writer.write(buf, 0, read); // 重命名
if (tmpUtf8File.renameTo(sourceFile)) {
logger.error("临时文件【{}】重命名失败,请检查文件路径权限!", tmpUtf8File.getPath());
}
}
} }
reader.close();
writer.close();
// 删除源文件
sourceFile.delete();
// 重命名
tmpUtf8File.renameTo(sourceFile);
}
} }
}
} }

View File

@@ -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 文件编码egUTF-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 + "]";
}
}
}

View File

@@ -1,14 +1,18 @@
package cn.keking.utils; package cn.keking.utils;
import cpdetector.CharsetPrinter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.Objects; import java.util.Objects;
public class DeleteFileUtil { public class FileUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(DeleteFileUtil.class); private static final Logger LOGGER = LoggerFactory.getLogger(FileUtils.class);
public static final String DEFAULT_FILE_ENCODING = "UTF-8";
/** /**
* 删除单个文件 * 删除单个文件
@@ -17,7 +21,7 @@ public class DeleteFileUtil {
* 要删除的文件的文件名 * 要删除的文件的文件名
* @return 单个文件删除成功返回true否则返回false * @return 单个文件删除成功返回true否则返回false
*/ */
public static boolean deleteFile(String fileName) { public static boolean deleteFileByName(String fileName) {
File file = new File(fileName); File file = new File(fileName);
// 如果文件路径所对应的文件存在并且是一个文件则直接删除 // 如果文件路径所对应的文件存在并且是一个文件则直接删除
if (file.exists() && file.isFile()) { if (file.exists() && file.isFile()) {
@@ -34,6 +38,36 @@ public class DeleteFileUtil {
} }
} }
/**
* 判断文件编码格式
*
* @param filePath 绝对路径
* @return 编码格式
*/
public static String getFileEncode(String filePath) {
File file = new File(filePath);
CharsetPrinter cp = new CharsetPrinter();
try {
String encoding = cp.guessEncoding(file);
LOGGER.info("检测到文件【{}】编码: {}", filePath, encoding);
return encoding;
} catch (IOException e) {
LOGGER.warn("文件编码获取失败采用默认的编码格式UTF-8", e);
return DEFAULT_FILE_ENCODING;
}
}
/**
* 根据文件路径删除文件
*
* @param filePath 绝对路径
*/
public static void deleteFileByPath(String filePath) {
File file = new File(filePath);
if (file.exists() && !file.delete()) {
LOGGER.warn("压缩包源文件删除失败:{}", filePath);
}
}
/** /**
* 删除目录及目录下的文件 * 删除目录及目录下的文件
@@ -59,20 +93,20 @@ public class DeleteFileUtil {
for (int i = 0; i < Objects.requireNonNull(files).length; i++) { for (int i = 0; i < Objects.requireNonNull(files).length; i++) {
// 删除子文件 // 删除子文件
if (files[i].isFile()) { if (files[i].isFile()) {
flag = DeleteFileUtil.deleteFile(files[i].getAbsolutePath()); flag = FileUtils.deleteFileByName(files[i].getAbsolutePath());
if (!flag) { if (!flag) {
break; break;
} }
} else if (files[i].isDirectory()) { } else if (files[i].isDirectory()) {
// 删除子目录 // 删除子目录
flag = DeleteFileUtil.deleteDirectory(files[i].getAbsolutePath()); flag = FileUtils.deleteDirectory(files[i].getAbsolutePath());
if (!flag) { if (!flag) {
break; break;
} }
} }
} }
dirFile.delete();
if (!flag) { if (!dirFile.delete() || !flag) {
LOGGER.info("删除目录失败!"); LOGGER.info("删除目录失败!");
return false; return false;
} }

View File

@@ -1,6 +1,6 @@
package cn.keking.utils; package cn.keking.utils;
import cn.keking.service.FilePreviewCommonService; import cn.keking.service.FileHandlerService;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType; import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.rendering.PDFRenderer;
@@ -23,18 +23,18 @@ public class PdfUtils {
private final Logger logger = LoggerFactory.getLogger(PdfUtils.class); private final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
private final FilePreviewCommonService filePreviewCommonService; private final FileHandlerService fileHandlerService;
@Value("${server.tomcat.uri-encoding:UTF-8}") @Value("${server.tomcat.uri-encoding:UTF-8}")
private String uriEncoding; private String uriEncoding;
public PdfUtils(FilePreviewCommonService filePreviewCommonService) { public PdfUtils(FileHandlerService fileHandlerService) {
this.filePreviewCommonService = filePreviewCommonService; this.fileHandlerService = fileHandlerService;
} }
public List<String> pdf2jpg(String pdfFilePath, String pdfName, String baseUrl) { public List<String> pdf2jpg(String pdfFilePath, String pdfName, String baseUrl) {
List<String> imageUrls = new ArrayList<>(); List<String> imageUrls = new ArrayList<>();
Integer imageCount = filePreviewCommonService.getConvertedPdfImage(pdfFilePath); Integer imageCount = fileHandlerService.getConvertedPdfImage(pdfFilePath);
String imageFileSuffix = ".jpg"; String imageFileSuffix = ".jpg";
String pdfFolder = pdfName.substring(0, pdfName.length() - 4); String pdfFolder = pdfName.substring(0, pdfName.length() - 4);
String urlPrefix = null; String urlPrefix = null;
@@ -70,7 +70,7 @@ public class PdfUtils {
imageUrls.add(urlPrefix + "/" + pageIndex + imageFileSuffix); imageUrls.add(urlPrefix + "/" + pageIndex + imageFileSuffix);
} }
doc.close(); doc.close();
filePreviewCommonService.addConvertedPdfImage(pdfFilePath, pageCount); fileHandlerService.addConvertedPdfImage(pdfFilePath, pageCount);
} catch (IOException e) { } catch (IOException e) {
logger.error("Convert pdf to jpg exception, pdfFilePath{}", pdfFilePath, e); logger.error("Convert pdf to jpg exception, pdfFilePath{}", pdfFilePath, e);
} }

View File

@@ -31,7 +31,7 @@ public class ShedulerClean {
public void clean() { public void clean() {
logger.info("Cache clean start"); logger.info("Cache clean start");
cacheService.cleanCache(); cacheService.cleanCache();
DeleteFileUtil.deleteDirectory(fileDir); FileUtils.deleteDirectory(fileDir);
logger.info("Cache clean end"); logger.info("Cache clean end");
} }
} }

View File

@@ -0,0 +1,60 @@
package cn.keking.utils;
import java.util.HashMap;
import java.util.Map;
/**
* @author : kl
* create : 2020-12-27 1:30 上午
**/
public class WebUtils {
/**
* 获取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;
}
}

View File

@@ -52,8 +52,8 @@ public class FileController {
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "存在同名文件,请先删除原有文件再次上传", null)); return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "存在同名文件,请先删除原有文件再次上传", null));
} }
File outFile = new File(fileDir + demoPath); File outFile = new File(fileDir + demoPath);
if (!outFile.exists()) { if (!outFile.exists() && !outFile.mkdirs()) {
outFile.mkdirs(); logger.error("创建文件夹【{}】失败,请检查目录权限!",fileDir + demoPath);
} }
logger.info("上传文件:{}", fileDir + demoPath + fileName); logger.info("上传文件:{}", fileDir + demoPath + fileName);
try(InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream(fileDir + demoPath + fileName)) { try(InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream(fileDir + demoPath + fileName)) {
@@ -72,8 +72,8 @@ public class FileController {
} }
File file = new File(fileDir + demoPath + fileName); File file = new File(fileDir + demoPath + fileName);
logger.info("删除文件:{}", file.getAbsolutePath()); logger.info("删除文件:{}", file.getAbsolutePath());
if (file.exists()) { if (file.exists() && !file.delete()) {
file.delete(); logger.error("删除文件【{}】失败,请检查目录权限!",file.getPath());
} }
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(0, "SUCCESS", null)); return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(0, "SUCCESS", null));
} }
@@ -84,7 +84,7 @@ public class FileController {
File file = new File(fileDir + demoPath); File file = new File(fileDir + demoPath);
if (file.exists()) { if (file.exists()) {
Arrays.stream(Objects.requireNonNull(file.listFiles())).forEach(file1 -> { 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()); fileName.put("fileName", demoDir + "/" + file1.getName());
list.add(fileName); list.add(fileName);
}); });

View File

@@ -6,11 +6,13 @@ import cn.keking.service.FilePreviewFactory;
import cn.keking.service.cache.CacheService; import cn.keking.service.cache.CacheService;
import cn.keking.utils.DownloadUtils; import cn.keking.utils.DownloadUtils;
import cn.keking.service.FilePreviewCommonService; import cn.keking.service.FileHandlerService;
import com.thoughtworks.xstream.core.util.Base64JavaUtilCodec;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@@ -32,23 +34,21 @@ public class OnlinePreviewController {
private final FilePreviewFactory previewFactory; private final FilePreviewFactory previewFactory;
private final CacheService cacheService; private final CacheService cacheService;
private final FilePreviewCommonService filePreviewCommonService; private final FileHandlerService fileHandlerService;
private final DownloadUtils downloadUtils;
public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FilePreviewCommonService filePreviewCommonService, CacheService cacheService, DownloadUtils downloadUtils) { public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService) {
this.previewFactory = filePreviewFactory; this.previewFactory = filePreviewFactory;
this.filePreviewCommonService = filePreviewCommonService; this.fileHandlerService = fileHandlerService;
this.cacheService = cacheService; this.cacheService = cacheService;
this.downloadUtils = downloadUtils;
} }
@RequestMapping(value = "/onlinePreview") @RequestMapping(value = "/onlinePreview")
public String onlinePreview(String url, Model model, HttpServletRequest req) { public String onlinePreview(String url, Model model, HttpServletRequest req) {
FileAttribute fileAttribute = filePreviewCommonService.getFileAttribute(url,req); String fileUrl = new String(Base64Utils.decodeFromString(url));
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(fileUrl,req);
FilePreview filePreview = previewFactory.get(fileAttribute); FilePreview filePreview = previewFactory.get(fileAttribute);
logger.info("预览文件url{}previewType{}", url, fileAttribute.getType()); logger.info("预览文件url{}previewType{}", fileUrl, fileAttribute.getType());
return filePreview.filePreviewHandle(url, model, fileAttribute); return filePreview.filePreviewHandle(fileUrl, model, fileAttribute);
} }
@RequestMapping(value = "/picturesPreview") @RequestMapping(value = "/picturesPreview")
@@ -78,8 +78,8 @@ public class OnlinePreviewController {
public void getCorsFile(String urlPath, HttpServletResponse response) { public void getCorsFile(String urlPath, HttpServletResponse response) {
logger.info("下载跨域pdf文件url{}", urlPath); logger.info("下载跨域pdf文件url{}", urlPath);
try { try {
byte[] bytes = downloadUtils.getBytesFromUrl(urlPath); byte[] bytes = DownloadUtils.getBytesFromUrl(urlPath);
downloadUtils.saveBytesToOutStream(bytes, response.getOutputStream()); DownloadUtils.saveBytesToOutStream(bytes, response.getOutputStream());
} catch (IOException e) { } catch (IOException e) {
logger.error("下载跨域pdf文件异常url{}", urlPath, e); logger.error("下载跨域pdf文件异常url{}", urlPath, e);
} }

View File

@@ -38,7 +38,7 @@ public class BaseUrlFilter implements Filter {
pathBuilder.append(request.getScheme()).append("://").append(request.getServerName()).append(":") pathBuilder.append(request.getScheme()).append("://").append(request.getServerName()).append(":")
.append(request.getServerPort()).append(((HttpServletRequest) request).getContextPath()).append("/"); .append(request.getServerPort()).append(((HttpServletRequest) request).getContextPath()).append("/");
String baseUrlTmp = ConfigConstants.getBaseUrl(); 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("/")) { if (!baseUrlTmp.endsWith("/")) {
baseUrlTmp = baseUrlTmp.concat("/"); baseUrlTmp = baseUrlTmp.concat("/");
} }

View File

@@ -23,9 +23,6 @@
h6 {font-weight: normal;font-size: 12px;letter-spacing: 1px;line-height: 24px;text-align: center;} h6 {font-weight: normal;font-size: 12px;letter-spacing: 1px;line-height: 24px;text-align: center;}
a {color:#3C6E31;text-decoration: underline;} a {color:#3C6E31;text-decoration: underline;}
a:hover {background-color:#3C6E31;color:white;} 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;} code {color: #2f332a;}
div.zTreeDemoBackground {width:600px;text-align:center;margin: 0 auto;background-color: #ffffff;} div.zTreeDemoBackground {width:600px;text-align:center;margin: 0 auto;background-color: #ffffff;}
</style> </style>
@@ -38,8 +35,11 @@
<script src="js/watermark.js" type="text/javascript"></script> <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-3.0.0.min.js"></script>
<script type="text/javascript" src="js/jquery.ztree.core.js"></script> <script type="text/javascript" src="js/jquery.ztree.core.js"></script>
<script type="text/javascript" src="js/base64.min.js" ></script>
<script type="text/javascript"> <script type="text/javascript">
var data = JSON.parse('${fileTree}'); const data = JSON.parse('${fileTree}');
var baseUrl = "${baseUrl}";
var setting = { var setting = {
view: { view: {
fontCss : {"color":"blue"}, fontCss : {"color":"blue"},
@@ -71,8 +71,8 @@
} else { } else {
fulls += ",resizable"; // 对于不支持screen属性的浏览器可以手工进行最大化。 manually fulls += ",resizable"; // 对于不支持screen属性的浏览器可以手工进行最大化。 manually
} }
window.open("onlinePreview?url=" var previewUrl = baseUrl + treeNode.fileName +"?fileKey="+ treeNode.fileKey;
+ encodeURIComponent("${baseUrl}" + treeNode.fileName)+"&fileKey="+ encodeURIComponent(treeNode.fileKey), "_blank",fulls); window.open("onlinePreview?url=" + encodeURIComponent(Base64.encode(previewUrl)), "_blank",fulls);
} }
} }
} }

View File

@@ -15,6 +15,7 @@
<script type="text/javascript" src="bootstrap/js/bootstrap.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="bootstrap-table/bootstrap-table.min.js"></script>
<script type="text/javascript" src="gitalk/gitalk.min.js"></script> <script type="text/javascript" src="gitalk/gitalk.min.js"></script>
<script type="text/javascript" src="js/base64.min.js" ></script>
</head> </head>
<body> <body>
@@ -36,14 +37,14 @@
成功实现: 成功实现:
<pre style="background-color: #2f332a;color: #cccccc"> <pre style="background-color: #2f332a;color: #cccccc">
var url = 'http://127.0.0.1:8080/file/test.txt'; //要预览文件的访问地址 var url = 'http://127.0.0.1:8080/file/test.txt'; //要预览文件的访问地址
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(url)); window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(base64Encode(url)));
</pre> </pre>
</div> </div>
<div> <div>
新增多图片同时预览功能,接口如下: 新增多图片同时预览功能,接口如下:
<pre style="background-color: #2f332a;color: #cccccc"> <pre style="background-color: #2f332a;color: #cccccc">
var fileUrl =url1+"|"+"url2";//多文件使用“|”字符隔开 var fileUrl =url1+"|"+"url2";//多文件使用“|”字符隔开
window.open('http://127.0.0.1:8012/picturesPreview?urls='+encodeURIComponent(fileUrl)); window.open('http://127.0.0.1:8012/picturesPreview?urls='+encodeURIComponent(base64Encode(fileUrl)));
</pre> </pre>
</div> </div>
</div> </div>
@@ -195,7 +196,7 @@ window.open('http://127.0.0.1:8012/picturesPreview?urls='+encodeURIComponent(fil
}).on('pre-body.bs.table', function (e,data) { }).on('pre-body.bs.table', function (e,data) {
// 每个data添加一列用来操作 // 每个data添加一列用来操作
$(data).each(function (index, item) { $(data).each(function (index, item) {
item.action = "<a class='btn btn-default' target='_blank' href='${baseUrl}onlinePreview?url="+ encodeURIComponent('${baseUrl}' + 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>"; "<a class='btn btn-default' href='javascript:void(0);' onclick='deleteFile(\""+item.fileName+"\")'>删除</a>";
}); });
return data; return data;