Compare commits

..

1 Commits

Author SHA1 Message Date
24f876a6ee 忽略log目录 2020-12-26 19:27:10 +08:00
38 changed files with 1446 additions and 465 deletions

View File

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

View File

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

View File

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

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

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

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

View File

@ -10,7 +10,6 @@ import java.util.Map;
* Content :文件类型文本office压缩包等等
*/
public enum FileType {
picture("pictureFilePreviewImpl"),
compress("compressFilePreviewImpl"),
office("officeFilePreviewImpl"),
@ -20,13 +19,12 @@ public enum FileType {
media("mediaFilePreviewImpl"),
markdown("markdownFilePreviewImpl"),
xml("xmlFilePreviewImpl"),
flv("flvFilePreviewImpl"),
cad("cadFilePreviewImpl");
private static final String[] OFFICE_TYPES = {"docx", "doc", "xls", "xlsx", "ppt", "pptx"};
private static final String[] PICTURE_TYPES = {"jpg", "jpeg", "png", "gif", "bmp", "ico", "RAW"};
private static final String[] ARCHIVE_TYPES = {"rar", "zip", "jar", "7-zip", "tar", "gzip", "7z"};
private static final String[] SSIM_TEXT_TYPES = ConfigConstants.getSimText();
private static final String[] SIMTEXT_TYPES = ConfigConstants.getSimText();
private static final String[] MEDIA_TYPES = ConfigConstants.getMedia();
private static final Map<String, FileType> FILE_TYPE_MAPPER = new HashMap<>();
@ -40,7 +38,7 @@ public enum FileType {
for (String archive : ARCHIVE_TYPES) {
FILE_TYPE_MAPPER.put(archive, FileType.compress);
}
for (String text : SSIM_TEXT_TYPES) {
for (String text : SIMTEXT_TYPES) {
FILE_TYPE_MAPPER.put(text, FileType.simText);
}
for (String media : MEDIA_TYPES) {
@ -50,29 +48,11 @@ public enum FileType {
FILE_TYPE_MAPPER.put("xml", FileType.xml);
FILE_TYPE_MAPPER.put("pdf", FileType.pdf);
FILE_TYPE_MAPPER.put("dwg", FileType.cad);
FILE_TYPE_MAPPER.put("flv", FileType.flv);
}
private static FileType to(String fileType){
public static FileType to(String fileType){
return FILE_TYPE_MAPPER.getOrDefault(fileType,other);
}
/**
* 查看文件类型(防止参数中存在.点号或者其他特殊字符,所以先抽取文件名,然后再获取文件类型)
*
* @param url url
* @return 文件类型
*/
public static FileType typeFromUrl(String url) {
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return typeFromFileName(fileName);
}
public static FileType typeFromFileName(String fileName) {
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
return FileType.to(fileType);
}
private final String instanceName;

View File

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

View File

@ -67,7 +67,7 @@ public class CompressFileReader {
}
String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName);
parentName = (level - 1) + "_" + parentName;
FileType type = FileType.typeFromUrl(childName);
FileType type = fileHandlerService.typeFromUrl(childName);
if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl + childName);
}
@ -120,7 +120,7 @@ public class CompressFileReader {
headersToBeExtracted.add(Collections.singletonMap(childName, header));
}
String parentName = getLast2FileName(fullName, "\\", archiveFileName);
FileType type = FileType.typeFromUrl(childName);
FileType type = fileHandlerService.typeFromUrl(childName);
if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl + childName);
}
@ -163,7 +163,7 @@ public class CompressFileReader {
}
String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName);
parentName = (level - 1) + "_" + parentName;
FileType type = FileType.typeFromUrl(childName);
FileType type = fileHandlerService.typeFromUrl(childName);
if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl + childName);
}

View File

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

View File

@ -10,7 +10,6 @@ import org.springframework.ui.ExtendedModelMap;
import javax.annotation.PostConstruct;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* Created by kl on 2018/1/19.
@ -71,7 +70,7 @@ public class FileConvertQueueTask {
}
} catch (Exception e) {
try {
TimeUnit.SECONDS.sleep(10);
Thread.sleep(1000*10);
} catch (Exception ex){
ex.printStackTrace();
}

View File

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

View File

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

View File

@ -1,6 +1,7 @@
package cn.keking.service;
import com.sun.star.document.UpdateDocMode;
import cn.keking.extend.ControlDocumentFormatRegistry;
import org.apache.commons.lang3.StringUtils;
import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.artofsolving.jodconverter.office.DefaultOfficeManagerConfiguration;
@ -71,7 +72,7 @@ public class OfficePluginManager {
}
public OfficeDocumentConverter getDocumentConverter() {
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager, new OfficePluginExtendFormatRegistry());
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager, new ControlDocumentFormatRegistry());
converter.setDefaultLoadProperties(getLoadProperties());
return converter;
}

View File

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

View File

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

View File

@ -31,8 +31,11 @@ public class CacheServiceRocksDBImpl implements CacheService {
}
private static final String DB_PATH = ConfigUtils.getHomePath() + File.separator + "cache";
private static final int QUEUE_SIZE = 500000;
private static final Logger LOGGER = LoggerFactory.getLogger(CacheServiceRocksDBImpl.class);
private final BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(QUEUE_SIZE);
private RocksDB db;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,30 +23,34 @@ public class SimTextFilePreviewImpl implements FilePreview {
public static final String TEXT_TYPE = "textType";
public static final String DEFAULT_TEXT_TYPE = "simText";
private final OtherFilePreviewImpl otherFilePreview;
private final DownloadUtils downloadUtils;
public SimTextFilePreviewImpl(OtherFilePreviewImpl otherFilePreview) {
this.otherFilePreview = otherFilePreview;
public SimTextFilePreviewImpl(DownloadUtils downloadUtils) {
this.downloadUtils = downloadUtils;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String fileName = fileAttribute.getName();
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileName);
if (0 != response.getCode()) {
model.addAttribute("msg", response.getMsg());
model.addAttribute("fileType", fileAttribute.getSuffix());
return "fileNotSupported";
}
try {
File originFile = new File(response.getContent());
String xmlString = FileUtils.readFileToString(originFile, StandardCharsets.UTF_8);
model.addAttribute("textData", Base64Utils.encodeToString(xmlString.getBytes()));
} catch (IOException e) {
return otherFilePreview.notSupportedFile(model, fileAttribute, e.getLocalizedMessage());
model.addAttribute("msg", e.getLocalizedMessage());
model.addAttribute("fileType", fileAttribute.getSuffix());
return "fileNotSupported";
}
if (!model.containsAttribute(TEXT_TYPE)) {
model.addAttribute(TEXT_TYPE, DEFAULT_TEXT_TYPE);
}
return TXT_FILE_PREVIEW_PAGE;
return "txt";
}
}

View File

@ -20,6 +20,7 @@ public class XmlFilePreviewImpl implements FilePreview {
this.simTextFilePreview = simTextFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
model.addAttribute(TEXT_TYPE,"xml");

View File

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

View File

@ -1,11 +1,14 @@
package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import cn.keking.hutool.URLUtil;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FileHandlerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.*;
@ -15,20 +18,27 @@ import java.util.UUID;
/**
* @author yudian-it
*/
@Component
public class DownloadUtils {
private final static Logger logger = LoggerFactory.getLogger(DownloadUtils.class);
private static final String fileDir = ConfigConstants.getFileDir();
private final String fileDir = ConfigConstants.getFileDir();
private static final String URL_PARAM_FTP_USERNAME = "ftp.username";
private static final String URL_PARAM_FTP_PASSWORD = "ftp.password";
private static final String URL_PARAM_FTP_CONTROL_ENCODING = "ftp.control.encoding";
private final FileHandlerService fileHandlerService;
public DownloadUtils(FileHandlerService fileHandlerService) {
this.fileHandlerService = fileHandlerService;
}
/**
* @param fileAttribute fileAttribute
* @param fileName 文件名
* @return 本地文件绝对路径
*/
public static ReturnResponse<String> downLoad(FileAttribute fileAttribute, String fileName) {
public ReturnResponse<String> downLoad(FileAttribute fileAttribute, String fileName) {
String urlStr = fileAttribute.getUrl();
String type = fileAttribute.getSuffix();
ReturnResponse<String> response = new ReturnResponse<>(0, "下载成功!!!", "");
@ -50,9 +60,9 @@ public class DownloadUtils {
OutputStream os = new FileOutputStream(realPath);
saveBytesToOutStream(bytes, os);
} else if (url.getProtocol() != null && "ftp".equalsIgnoreCase(url.getProtocol())) {
String ftpUsername = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME);
String ftpPassword = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD);
String ftpControlEncoding = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING);
String ftpUsername = fileHandlerService.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME);
String ftpPassword = fileHandlerService.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD);
String ftpControlEncoding = fileHandlerService.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING);
FtpUtils.download(fileAttribute.getUrl(), realPath, ftpUsername, ftpPassword, ftpControlEncoding);
} else {
response.setCode(1);
@ -62,7 +72,7 @@ public class DownloadUtils {
response.setContent(realPath);
response.setMsg(fileName);
if (FileType.simText.equals(fileAttribute.getType())) {
convertTextPlainFileCharsetToUtf8(realPath);
this.convertTextPlainFileCharsetToUtf8(realPath);
}
return response;
} catch (IOException e) {
@ -78,10 +88,10 @@ public class DownloadUtils {
}
}
public static byte[] getBytesFromUrl(String urlStr) throws IOException {
public byte[] getBytesFromUrl(String urlStr) throws IOException {
InputStream is = getInputStreamFromUrl(urlStr);
if (is == null) {
// urlStr = URLUtil.normalize(urlStr, true, true);
urlStr = URLUtil.normalize(urlStr, true, true);
is = getInputStreamFromUrl(urlStr);
if (is == null) {
logger.error("文件下载异常url{}", urlStr);
@ -91,12 +101,12 @@ public class DownloadUtils {
return getBytesFromStream(is);
}
public static void saveBytesToOutStream(byte[] b, OutputStream os) throws IOException {
public void saveBytesToOutStream(byte[] b, OutputStream os) throws IOException {
os.write(b);
os.close();
}
private static InputStream getInputStreamFromUrl(String urlStr) {
private InputStream getInputStreamFromUrl(String urlStr) {
try {
URL url = new URL(urlStr);
URLConnection connection = url.openConnection();
@ -110,7 +120,7 @@ public class DownloadUtils {
}
}
private static byte[] getBytesFromStream(InputStream is) throws IOException {
private byte[] getBytesFromStream(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
@ -129,7 +139,7 @@ public class DownloadUtils {
*
* @param filePath 文件路径
*/
private static void convertTextPlainFileCharsetToUtf8(String filePath) throws IOException {
private void convertTextPlainFileCharsetToUtf8(String filePath) throws IOException {
File sourceFile = new File(filePath);
if (sourceFile.exists() && sourceFile.isFile() && sourceFile.canRead()) {
String encoding = FileUtils.getFileEncode(filePath);

View File

@ -57,16 +57,6 @@ public class FileUtils {
}
}
/**
* 通过文件名获取文件后缀
* @param fileName 文件名称
* @return 文件后缀
*/
public static String suffixFromFileName(String fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
/**
* 根据文件路径删除文件
*

View File

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

View File

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

View File

@ -1,86 +0,0 @@
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;
}
/**
* 从url中剥离出文件名
*
* @param url 格式如http://www.com.cn/20171113164107_月度绩效表模板(新).xls?UCloudPublicKey=ucloudtangshd@weifenf.com14355492830001993909323&Expires=&Signature=I D1NOFtAJSPT16E6imv6JWuq0k=
* @return 文件名
*/
public static String getFileNameFromURL(String url) {
// 因为url的参数中可能会存在/的情况所以直接url.lastIndexOf("/")会有问题
// 所以先从处将url截断然后运用url.lastIndexOf("/")获取文件名
String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
return noQueryUrl.substring(noQueryUrl.lastIndexOf("/") + 1);
}
/**
* 从url中获取文件后缀
*
* @param url url
* @return 文件后缀
*/
public static String suffixFromUrl(String url) {
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return FileUtils.suffixFromFileName(fileName);
}
}

View File

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

View File

@ -11,7 +11,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@ -19,6 +18,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.List;
@ -33,34 +33,37 @@ public class OnlinePreviewController {
private final FilePreviewFactory previewFactory;
private final CacheService cacheService;
private final FileHandlerService fileHandlerService;
private final DownloadUtils downloadUtils;
public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService) {
public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService, DownloadUtils downloadUtils) {
this.previewFactory = filePreviewFactory;
this.fileHandlerService = fileHandlerService;
this.cacheService = cacheService;
this.downloadUtils = downloadUtils;
}
@RequestMapping(value = "/onlinePreview")
public String onlinePreview(String url, Model model, HttpServletRequest req) {
String fileUrl = new String(Base64Utils.decodeFromString(url));
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(fileUrl, req);
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(url,req);
FilePreview filePreview = previewFactory.get(fileAttribute);
logger.info("预览文件url{}previewType{}", fileUrl, fileAttribute.getType());
return filePreview.filePreviewHandle(fileUrl, model, fileAttribute);
logger.info("预览文件url{}previewType{}", url, fileAttribute.getType());
return filePreview.filePreviewHandle(url, model, fileAttribute);
}
@RequestMapping(value = "/picturesPreview")
public String picturesPreview(String urls, Model model, HttpServletRequest req) throws UnsupportedEncodingException {
String fileUrls = new String(Base64Utils.decodeFromString(urls));
public String picturesPreview(Model model, HttpServletRequest req) throws UnsupportedEncodingException {
String urls = req.getParameter("urls");
String currentUrl = req.getParameter("currentUrl");
logger.info("预览文件url{}urls{}", fileUrls, urls);
logger.info("预览文件url{}urls{}", currentUrl, urls);
// 路径转码
String decodedCurrentUrl = new String(Base64Utils.decodeFromString(currentUrl));
String decodedUrl = URLDecoder.decode(urls, "utf-8");
String decodedCurrentUrl = URLDecoder.decode(currentUrl, "utf-8");
// 抽取文件并返回文件列表
String[] imgs = fileUrls.split("\\|");
String[] imgs = decodedUrl.split("\\|");
List<String> imgUrls = Arrays.asList(imgs);
model.addAttribute("imgUrls", imgUrls);
model.addAttribute("currentUrl", decodedCurrentUrl);
model.addAttribute("currentUrl",decodedCurrentUrl);
return "picture";
}
@ -68,15 +71,15 @@ public class OnlinePreviewController {
* 根据url获取文件内容
* 当pdfjs读取存在跨域问题的文件时将通过此接口读取
*
* @param urlPath url
* @param urlPath url
* @param response response
*/
@RequestMapping(value = "/getCorsFile", method = RequestMethod.GET)
public void getCorsFile(String urlPath, HttpServletResponse response) {
logger.info("下载跨域pdf文件url{}", urlPath);
try {
byte[] bytes = DownloadUtils.getBytesFromUrl(urlPath);
DownloadUtils.saveBytesToOutStream(bytes, response.getOutputStream());
byte[] bytes = downloadUtils.getBytesFromUrl(urlPath);
downloadUtils.saveBytesToOutStream(bytes, response.getOutputStream());
} catch (IOException e) {
logger.error("下载跨域pdf文件异常url{}", urlPath, e);
}
@ -84,7 +87,6 @@ public class OnlinePreviewController {
/**
* 通过api接口入队
*
* @param url 请编码后在入队
*/
@RequestMapping("/addTask")

View File

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

View File

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

View File

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