Compare commits

...

36 Commits

Author SHA1 Message Date
b1fdbd26a3 新功能点:全部能识别的纯文本直接预览,不用再转跳下载(.md、.java、.py等浏览器不认识的后缀名) 2019-10-25 15:16:25 +08:00
fa7241bd4e 新功能点:所有配置项支持从环境变量里读取,方便Docker镜像部署 2019-10-25 15:16:25 +08:00
8fdf462c6c 新功能点:缓存清理时间cron表达式支持自定义 link #I1147X 2019-10-25 15:16:25 +08:00
f7c7411bcf fixup! 优化:目录调整,符合maven规范;maven编译指定.sh脚本换行符为unix换行 2019-10-25 15:16:25 +08:00
845cb2e657 修复:参数包含特殊字符时url解码失败 2019-10-25 15:16:25 +08:00
a4bfde68bd 优化:目录调整,符合maven规范;maven编译指定.sh脚本换行符为unix换行 2019-10-25 15:16:25 +08:00
19d1ba6cf9 优化:(内部)移除为pdf文档提供base64缩略图 2019-10-25 15:16:25 +08:00
1060bdd00f 新功能点:支持base url配置(主要用于nginx反向代理等) 2019-10-17 11:36:43 +08:00
8c2fb2bdee 优化:默认启用缓存 2019-10-17 11:36:43 +08:00
0fe75387eb 优化:启动脚本加入官网链接,点star推广等文案 close #I1148F 2019-09-16 11:49:49 +08:00
fbea49e54f 优化:加入查看日志脚本 2019-09-16 11:49:49 +08:00
41a72798d9 优化:去除可能导致文件不更新的缓存 2019-09-16 11:49:49 +08:00
03cc185085 修复:压缩包中文件名有空格异常 2019-09-16 11:49:49 +08:00
bfbd8ee25e 新功能点:(内部)为pdf文档提供base64缩略图 2019-09-10 18:24:06 +08:00
f3f36169ff 优化:压缩文件名支持有特殊字符 2019-09-10 18:24:06 +08:00
2df88544d3 修复:macOS下office组件默认路径错误 2019-09-10 18:24:06 +08:00
6b744d77c7 修复:RocksDB缓存实现压缩包图片url缓存失效 2019-09-10 18:24:06 +08:00
ba57dedebb "加入是否启用缓存配置项"后,excel转成的html文件不再转换编码,修复因此出现的乱码问题 2019-08-26 11:17:37 +08:00
affd5b3057 图片和pdf预览模式切换按钮大小调整 2019-08-23 18:18:37 +08:00
30c3128995 修复压缩文件中文fileKey未编码 link #I111PD 2019-08-23 18:18:37 +08:00
b003a05775 加入是否启用缓存配置项 2019-08-23 18:18:37 +08:00
98ec3d7dab 首页预览打开新页面 2019-08-23 18:18:37 +08:00
69e23dbb99 shutdown脚本更新 2019-08-23 18:18:37 +08:00
47bda1023a 修复Chrome76+删除弹出新窗口 2019-08-23 18:18:37 +08:00
fd538a74af 中文语言环境 2019-08-23 18:18:37 +08:00
300d213a7a cdn资源不指定http/https 2019-08-23 18:18:37 +08:00
4c0a70f300 2.2.0迭代 2019-08-23 18:18:37 +08:00
8798d344b6 2.1.2版 2019-07-30 16:31:53 +08:00
63e62ab57b pdf.js使用里面一个bug 2019-07-30 16:31:53 +08:00
9a027674ac 演示首页兼容IE 2019-07-30 16:31:53 +08:00
e4407467dd IE兼容性问题,目前已兼容到IE9 2019-07-17 09:03:32 +08:00
551eeb0390 2.1.1版 2019-07-09 10:46:21 +08:00
11d6ad1ed3 修复文件下载流URL参数中包含中文URL编码不正确导致HTTP-400异常 2019-07-09 10:46:21 +08:00
e57db6925c 修复config.js 404问题 2019-07-09 10:46:21 +08:00
87096364d8 首页示例修改、首页更新记录 2019-07-09 10:46:21 +08:00
ad8027a7d0 2.1.1迭代 2019-07-09 10:46:21 +08:00
32 changed files with 327 additions and 194 deletions

View File

@ -1,15 +1,17 @@
FROM centos:centos7.6.1810
MAINTAINER chenjh "842761733@qq.com"
ADD jodconverter-web/target/kkFileView-*.tar.gz /opt/
COPY fonts/* /usr/share/fonts/
COPY fonts/* /usr/share/fonts/chienes/
RUN yum install -y kde-l10n-Chinese &&\
yum install -y glibc-common &&\
yum install -y fontconfig &&\
yum install -y mkfontscale &&\
localedef -c -f UTF-8 -i zh_CN zh_CN.utf8 &&\
echo "LANG=zh_CN.UTF-8" > /etc/locale.conf &&\
source /etc/locale.conf &&\
export LANG=zh_CN.UTF-8 &&\
echo "export LANG=zh_CN.UTF-8" >> /etc/locale.conf &&\
LANG="zh_CN.UTF-8" &&\
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\
yum install -y java-1.8.0-openjdk.x86_64 &&\
yum install -y wget &&\
yum install -y libXext.x86_64 &&\
@ -22,10 +24,11 @@ RUN yum install -y kde-l10n-Chinese &&\
rpm -Uvih desktop-integration/openoffice4.1.6-redhat-menus-4.1.6-9790.noarch.rpm &&\
rm -f /tmp/openoffice_rpm.tar.gz &&\
rm -rf /tmp/zh-CN &&\
cd /usr/share/fonts &&\
mkfontscale &&\
cd /usr/share/fonts/chienes &&\
mkfontscale &&\
mkfontdir &&\
fc-cache -fv
ENV LANG zh_CN.UTF-8
ENV LC_ALL zh_CN.UTF-8
ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-2.1.0/bin
ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider","-Dspring.config.location=/opt/kkFileView-2.1.0/conf/application.properties","-jar","/opt/kkFileView-2.1.0/bin/kkFileView-2.1.0.jar"]
ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-2.2.0-SNAPSHOT/bin
ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider","-Dspring.config.location=/opt/kkFileView-2.2.0-SNAPSHOT/config/application.properties","-jar","/opt/kkFileView-2.2.0-SNAPSHOT/bin/kkFileView-2.2.0-SNAPSHOT.jar"]

View File

@ -15,6 +15,7 @@ package org.artofsolving.jodconverter.office;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
@ -26,6 +27,8 @@ import com.sun.star.uno.UnoRuntime;
public class OfficeUtils {
public static final String SERVICE_DESKTOP = "com.sun.star.frame.Desktop";
public static final String OFFICE_HOME_KEY = "office.home";
public static final String DEFAULT_OFFICE_HOME_VALUE = "default";
private OfficeUtils() {
throw new AssertionError("utility class must not be instantiated");
@ -69,9 +72,11 @@ public class OfficeUtils {
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader(customizedConfigPath));
properties.load(bufferedReader);
restorePropertiesFromEnvFormat(properties);
} catch (Exception e) {}
if (properties.getProperty("office.home") != null) {
return new File(properties.getProperty("office.home"));
String officeHome = properties.getProperty(OFFICE_HOME_KEY);
if (officeHome != null && !DEFAULT_OFFICE_HOME_VALUE.equals(officeHome)) {
return new File(officeHome);
}
if (PlatformUtils.isWindows()) {
// %ProgramFiles(x86)% on 64-bit machines; %ProgramFiles% on 32-bit ones
@ -116,7 +121,7 @@ public class OfficeUtils {
public static File getOfficeExecutable(File officeHome) {
if (PlatformUtils.isMac()) {
return new File(officeHome, "MacOS/soffice.bin");
return new File(officeHome, "MacOS/soffice");
} else {
return new File(officeHome, "program/soffice.bin");
}
@ -143,8 +148,36 @@ public class OfficeUtils {
public static String getCustomizedConfigPath() {
String homePath = OfficeUtils.getHomePath();
String separator = java.io.File.separator;
String configFilePath = homePath + separator + "conf" + separator + "application.properties";
String configFilePath = homePath + separator + "config" + separator + "application.properties";
return configFilePath;
}
/**
* SpringBoot application.properties 支持从环境变量获取值
* @param properties
*/
public synchronized static void restorePropertiesFromEnvFormat(Properties properties) {
Iterator<Map.Entry<Object, Object>> iterator = properties.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Object, Object> entry = iterator.next();
String key = entry.getKey().toString();
String value = entry.getValue().toString();
if (value.trim().startsWith("${") && value.trim().endsWith("}")) {
int beginIndex = value.indexOf(":");
if (beginIndex < 0) {
beginIndex = value.length() - 1;
}
int endIndex = value.length() - 1;
String envKey = value.substring(2, beginIndex);
String envValue = System.getenv(envKey);
if (envValue == null || "".equals(envValue.trim())) {
value = value.substring(beginIndex + 1, endIndex);
} else {
value = envValue;
}
properties.setProperty(key, value);
}
}
}
}

View File

@ -12,7 +12,7 @@
<groupId>cn.keking</groupId>
<artifactId>kkFileView</artifactId>
<version>2.1.0</version>
<version>2.2.0-SNAPSHOT</version>
<properties>
@ -194,7 +194,7 @@
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/conf</directory>
<directory>src/main/config</directory>
<excludes>
<exclude>${build.exclude.resource}</exclude>
</excludes>
@ -210,7 +210,7 @@
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/main/resources/assembly.xml</descriptor>
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>

View File

@ -11,18 +11,24 @@
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>src/main/conf</directory>
<outputDirectory>${file.separator}conf</outputDirectory>
<directory>src/main/bin</directory>
<outputDirectory>${file.separator}bin</outputDirectory>
<includes>
<include>*.sh</include>
</includes>
<fileMode>755</fileMode>
<lineEnding>unix</lineEnding>
</fileSet>
<fileSet>
<directory>src/main/bin</directory>
<outputDirectory>${file.separator}bin</outputDirectory>
<fileMode>755</fileMode>
<includes>
<include>*.bat</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/script</directory>
<outputDirectory>${file.separator}script</outputDirectory>
<fileMode>755</fileMode>
<directory>src/main/config</directory>
<outputDirectory>${file.separator}config</outputDirectory>
</fileSet>
<fileSet>
<directory>src/main/log</directory>

View File

@ -0,0 +1,2 @@
#!/bin/bash
tail -fn 300 ../log/kkFileView.log

View File

@ -1,2 +1,2 @@
#!/bin/bash
kill 15 `ps -ef|grep kkFileView|awk '{print $2}'`
kill -15 `ps -ef|grep kkFileView|awk 'NR==1{print $2}'`

View File

@ -3,5 +3,7 @@ set "KKFILEVIEW_BIN_FOLDER=%cd%"
cd "%KKFILEVIEW_BIN_FOLDER%"
echo Using KKFILEVIEW_BIN_FOLDER %KKFILEVIEW_BIN_FOLDER%
echo Starting kkFileView...
echo Please check log file for more information
java -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=..\conf\application.properties -jar kkFileView-2.1.0.jar -> ..\log\kkFileView.log
echo Please check log file in ../log/kkFileView.log for more information
echo You can get help in our official homesite: https://kkFileView.keking.cn
echo If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers
java -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=..\config\application.properties -jar kkFileView-2.2.0-SNAPSHOT.jar -> ..\log\kkFileView.log

View File

@ -6,7 +6,7 @@ KKFILEVIEW_BIN_FOLDER=$(cd "$(dirname "$0")";pwd)
export KKFILEVIEW_BIN_FOLDER=$KKFILEVIEW_BIN_FOLDER
cd $KKFILEVIEW_BIN_FOLDER
echo "Using KKFILEVIEW_BIN_FOLDER $KKFILEVIEW_BIN_FOLDER"
grep 'office\.home' ../conf/application.properties | grep '!^#'
grep 'office\.home' ../config/application.properties | grep '!^#'
if [ $? -eq 0 ]; then
echo "Using customized office.home"
else
@ -20,11 +20,13 @@ else
done
if [ ! -n "${FLAG}" ]; then
echo "Installing OpenOffice"
sh ../script/install.sh
sh ./install.sh
else
echo "Detected office component has been installed in $OFFICE_HOME"
fi
fi
echo "Starting kkFileView..."
echo "Please check log file for more information"
nohup java -Dfile.encoding=UTF-8 -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=../conf/application.properties -jar kkFileView-2.1.0.jar > ../log/kkFileView.log 2>&1 &
echo "Please execute ./showlog.sh to check log for more information"
echo "You can get help in our official homesite: https://kkFileView.keking.cn"
echo "If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers"
nohup java -Dfile.encoding=UTF-8 -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=../config/application.properties -jar kkFileView-2.2.0-SNAPSHOT.jar > ../log/kkFileView.log 2>&1 &

View File

@ -1,5 +1,5 @@
#######################################不可动态配置,需要重启生效#######################################
server.port = 8012
server.port = ${KK_SERVER_PORT:8012}
spring.http.encoding.charset = utf8
## Freemarker 配置
spring.freemarker.template-loader-path = classpath:/web/
@ -19,30 +19,39 @@ spring.http.multipart.max-file-size=100MB
#文件资源路径默认为打包根路径下的file目录下
#file.dir = D:\\kkFileview\\
file.dir = ${KK_FILE_DIR:default}
#openoffice home路径
#office.home = C:\\Program Files (x86)\\OpenOffice 4
office.home = ${KK_OFFICE_HOME:default}
#缓存实现类型不配默认为内嵌RocksDB实现可配置为redis(type = redis)实现需要配置spring.redisson.address等参数和 JDK 内置对象实现type = jdk,
#cache.type = redis
#redis连接
#spring.redisson.address = 192.168.1.204:6379
#spring.redisson.password = xxx
#缓存自动清理(每晚3点自动清理) true 为开启,注释掉或其他值都为关闭
cache.clean = true
#缓存实现类型不配默认为内嵌RocksDB(type = default)实现可配置为redis(type = redis)实现需要配置spring.redisson.address等参数和 JDK 内置对象实现type = jdk,
cache.type = ${KK_CACHE_TYPE:default}
#redis连接只有当cache.type = redis时才有用
spring.redisson.address = ${KK_SPRING_REDISSON_ADDRESS:127.0.0.1:6379}
spring.redisson.password = ${KK_SPRING_REDISSON_PASSWORD:123456}
#缓存是否自动清理 true 为开启,注释掉或其他值都为关闭
cache.clean.enabled = ${KK_CACHE_CLEAN_ENABLED:true}
#缓存自动清理时间cache.clean.enabled = true时才有用cron表达式基于Quartz cron
cache.clean.cron = ${KK_CACHE_CLEAN_CRON:0 0 3 * * ?}
#######################################可在运行时动态配置#######################################
#提供预览服务的地址默认从请求url读如果使用nginx等反向代理需要手动设置
#base.url = https://file.keking.cn
base.url = ${KK_BASE_URL:default}
#是否启用缓存
cache.enabled = ${KK_CACHE_ENABLED:true}
#文本类型,默认如下,可自定义添加
#simText = txt,html,xml,properties,md,java,py,c,cpp,sql
simText = ${KK_SIMTEXT:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd}
#多媒体类型,默认如下,可自定义添加
#media = mp3,wav,mp4,flv
#文件转换编码,默认根据操作系统获取
#converted.file.charset = GBK
media = ${KK_MEDIA:mp3,wav,mp4,flv}
#office类型文档(word ppt)样式,默认为图片(image)可配置为pdf预览时也有按钮切换
#office.preview.type = pdf
office.preview.type = ${KK_OFFICE_PREVIEW_TYPE:image}
#预览源为FTP时 FTP用户名可在ftp url后面加参数ftp.username=ftpuser指定不指定默认用配置的
ftp.username = ftpuser
ftp.username = ${KK_FTP_USERNAME:ftpuser}
#预览源为FTP时 FTP密码可在ftp url后面加参数ftp.password=123456指定不指定默认用配置的
ftp.password = 123456
ftp.password = ${KK_FTP_PASSWORD:123456}
#预览源为FTP时, FTP连接默认ControlEncoding(根据FTP服务器操作系统选择Linux一般为UTF-8Windows一般为GBK)可在ftp url后面加参数ftp.control.encoding=UTF-8指定不指定默认用配置的
ftp.control.encoding = UTF-8
ftp.control.encoding = ${KK_FTP_CONTROL_ENCODING:UTF-8}

View File

@ -14,14 +14,25 @@ import java.io.File;
@Component
public class ConfigConstants {
private static Boolean cacheEnabled;
private static String[] simText = {};
private static String[] media = {};
private static String convertedFileCharset;
private static String officePreviewType;
private static String ftpUsername;
private static String ftpPassword;
private static String ftpControlEncoding;
private static String fileDir = OfficeUtils.getHomePath() + File.separator + "file" + File.separator;
private static String baseUrl;
public static final String DEFAULT_FILE_DIR_VALUE = "default";
public static Boolean isCacheEnabled() {
return cacheEnabled;
}
public static void setCacheEnabled(Boolean cacheEnabled) {
ConfigConstants.cacheEnabled = cacheEnabled;
}
public static String[] getSimText() {
return simText;
@ -39,14 +50,6 @@ public class ConfigConstants {
ConfigConstants.media = media;
}
public static String getConvertedFileCharset() {
return convertedFileCharset;
}
public static void setConvertedFileCharset(String convertedFileCharset) {
ConfigConstants.convertedFileCharset = convertedFileCharset;
}
public static String getOfficePreviewType() {
return officePreviewType;
}
@ -83,9 +86,17 @@ public class ConfigConstants {
return fileDir;
}
public static String getBaseUrl() {
return baseUrl;
}
public static void setBaseUrl(String baseUrl) {
ConfigConstants.baseUrl = baseUrl;
}
@Value("${file.dir:default}")
public void setFileDir(String fileDir) {
if (!"default".equals(fileDir)) {
if (!DEFAULT_FILE_DIR_VALUE.equals(fileDir.toLowerCase())) {
if (!fileDir.endsWith(File.separator)) {
fileDir = fileDir + File.separator;
}

View File

@ -22,13 +22,14 @@ public class ConfigRefreshComponent {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigRefreshComponent.class);
public static final String DEFAULT_TXT_TYPE = "txt,html,xml,properties,md,java,py,c,cpp,sql";
public static final String DEFAULT_CACHE_ENABLED = "true";
public static final String DEFAULT_TXT_TYPE = "txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd";
public static final String DEFAULT_MEDIA_TYPE = "mp3,wav,mp4,flv";
public static final String DEFAULT_CONVERTER_CHARSET = System.getProperty("sun.jnu.encoding");
public static final String DEFAULT_FTP_USERNAME = null;
public static final String DEFAULT_FTP_PASSWORD = null;
public static final String DEFAULT_FTP_CONTROL_ENCODING = "UTF-8";
public static final String DEFAULT_BASE_URL = "default";
@PostConstruct
void refresh() {
@ -43,34 +44,38 @@ public class ConfigRefreshComponent {
Properties properties = new Properties();
String text;
String media;
Boolean cacheEnabled;
String[] textArray;
String[] mediaArray;
String convertedFileCharset;
String officePreviewType;
String ftpUsername;
String ftpPassword;
String ftpControlEncoding;
String configFilePath = OfficeUtils.getCustomizedConfigPath();
String baseUlr;
while (true) {
FileReader fileReader = new FileReader(configFilePath);
BufferedReader bufferedReader = new BufferedReader(fileReader);
properties.load(bufferedReader);
OfficeUtils.restorePropertiesFromEnvFormat(properties);
cacheEnabled = new Boolean(properties.getProperty("cache.enabled", DEFAULT_CACHE_ENABLED));
text = properties.getProperty("simText", DEFAULT_TXT_TYPE);
media = properties.getProperty("media", DEFAULT_MEDIA_TYPE);
convertedFileCharset = properties.getProperty("converted.file.charset", DEFAULT_CONVERTER_CHARSET);
officePreviewType = properties.getProperty("office.preview.type", OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE);
ftpUsername = properties.getProperty("ftp.username", DEFAULT_FTP_USERNAME);
ftpPassword = properties.getProperty("ftp.password", DEFAULT_FTP_PASSWORD);
ftpControlEncoding = properties.getProperty("ftp.control.encoding", DEFAULT_FTP_CONTROL_ENCODING);
textArray = text.split(",");
mediaArray = media.split(",");
baseUlr = properties.getProperty("base.url", DEFAULT_BASE_URL);
ConfigConstants.setCacheEnabled(cacheEnabled);
ConfigConstants.setSimText(textArray);
ConfigConstants.setMedia(mediaArray);
ConfigConstants.setConvertedFileCharset(convertedFileCharset);
ConfigConstants.setOfficePreviewType(officePreviewType);
ConfigConstants.setFtpUsername(ftpUsername);
ConfigConstants.setFtpPassword(ftpPassword);
ConfigConstants.setFtpControlEncoding(ftpControlEncoding);
ConfigConstants.setBaseUrl(baseUlr);
bufferedReader.close();
fileReader.close();
Thread.sleep(1000L);

View File

@ -1,5 +1,8 @@
package cn.keking.filters;
import cn.keking.config.ConfigConstants;
import cn.keking.config.ConfigRefreshComponent;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@ -19,10 +22,20 @@ public class ChinesePathFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(request.getScheme()).append("://").append(request.getServerName()).append(":")
.append(request.getServerPort()).append(((HttpServletRequest)request).getContextPath()).append("/");
request.setAttribute("baseUrl", pathBuilder.toString());
String baseUrl;
String baseUrlTmp = ConfigConstants.getBaseUrl();
if (baseUrlTmp != null && !ConfigRefreshComponent.DEFAULT_BASE_URL.equals(baseUrlTmp.toLowerCase())) {
if (!baseUrlTmp.endsWith("/")) {
baseUrlTmp = baseUrlTmp.concat("/");
}
baseUrl = baseUrlTmp;
} else {
StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(request.getScheme()).append("://").append(request.getServerName()).append(":")
.append(request.getServerPort()).append(((HttpServletRequest) request).getContextPath()).append("/");
baseUrl = pathBuilder.toString();
}
request.setAttribute("baseUrl", baseUrl);
chain.doFilter(request, response);
}

View File

@ -92,7 +92,7 @@ public class CacheServiceRocksDBImpl implements CacheService {
try {
Map<String, List<String>> imgCacheItem = getImgCache();
imgCacheItem.put(key, value);
db.put(REDIS_FILE_PREVIEW_PDF_KEY.getBytes(), toByteArray(imgCacheItem));
db.put(REDIS_FILE_PREVIEW_IMGS_KEY.getBytes(), toByteArray(imgCacheItem));
} catch (RocksDBException | IOException e) {
LOGGER.error("Put into RocksDB Exception" + e);
}

View File

@ -1,5 +1,6 @@
package cn.keking.service.impl;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
@ -33,7 +34,7 @@ public class CompressFilePreviewImpl implements FilePreview{
String suffix=fileAttribute.getSuffix();
String fileTree = null;
// 判断文件名是否存在(redis缓存读取)
if (!StringUtils.hasText(fileUtils.getConvertedFile(fileName))) {
if (!StringUtils.hasText(fileUtils.getConvertedFile(fileName)) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileName);
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
@ -48,7 +49,7 @@ public class CompressFilePreviewImpl implements FilePreview{
} else if ("7z".equalsIgnoreCase(suffix)) {
fileTree = zipReader.read7zFile(filePath, fileName);
}
if (fileTree != null && !"null".equals(fileTree)) {
if (fileTree != null && !"null".equals(fileTree) && ConfigConstants.isCacheEnabled()) {
fileUtils.addConvertedFile(fileName, fileTree);
}
} else {

View File

@ -52,29 +52,25 @@ public class OfficeFilePreviewImpl implements FilePreview {
String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + (isHtml ? "html" : "pdf");
String outFilePath = fileDir + pdfName;
// 判断之前是否已转换过,如果转换过,直接返回,否则执行转换
if (!fileUtils.listConvertedFiles().containsKey(pdfName)) {
if (!fileUtils.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
String filePath = fileDir + fileName;
if (!new File(filePath).exists()) {
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();
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)) {
officeToPdf.openOfficeToPDF(filePath, outFilePath);
File f = new File(filePath);
if (f.exists()) {
f.delete();
}
if (isHtml) {
// 对转换后的文件进行操作(改变编码方式)
fileUtils.doActionConvertedFile(outFilePath);
}
// 加入缓存
fileUtils.addConvertedFile(pdfName, fileUtils.getRelativePath(outFilePath));
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
fileUtils.addConvertedFile(pdfName, fileUtils.getRelativePath(outFilePath));
}
}
}
if (!isHtml && originUrl != null && (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OFFICE_PREVIEW_TYPE_ALLIMAGES.equals(officePreviewType))) {

View File

@ -44,15 +44,13 @@ public class PdfFilePreviewImpl implements FilePreview{
String outFilePath = fileDir + pdfName;
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_ALLIMAGES.equals(officePreviewType)) {
//当文件不存在时,就去下载
if (!new File(outFilePath).exists()) {
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();
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();
List<String> imageUrls = pdfUtils.pdf2jpg(outFilePath, pdfName, originUrl);
if (imageUrls == null || imageUrls.size() < 1) {
model.addAttribute("msg", "pdf转图片异常请联系管理员");

View File

@ -9,6 +9,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
/**
* Created by kl on 2018/1/17.
* Content :处理文本文件
@ -32,7 +36,16 @@ public class SimTextFilePreviewImpl implements FilePreview{
model.addAttribute("fileType",fileAttribute.getSuffix());
return "fileNotSupported";
}
model.addAttribute("ordinaryUrl", response.getMsg());
try {
File originFile = new File(response.getContent());
File previewFile = new File(response.getContent() + ".txt");
Files.copy(originFile.toPath(), previewFile.toPath());
} catch (IOException e) {
model.addAttribute("msg", e.getLocalizedMessage());
model.addAttribute("fileType",fileAttribute.getSuffix());
return "fileNotSupported";
}
model.addAttribute("ordinaryUrl", response.getMsg() + ".txt");
return "txt";
}

View File

@ -46,7 +46,9 @@ public class DownloadUtils {
urlAddress = replacePlusMark(urlAddress);
urlAddress = encodeUrlParam(urlAddress);
// 因为tomcat不能处理'+'号,所以讲'+'号替换成'%20%'
// 也不能处理空格
urlAddress = urlAddress.replaceAll("\\+", "%20");
urlAddress = urlAddress.replaceAll(" ", "%20");
url = new URL(urlAddress);
} catch (MalformedURLException e) {
e.printStackTrace();
@ -123,30 +125,35 @@ public class DownloadUtils {
* 对最有一个路径进行转码
* @param urlAddress
* http://192.168.2.111:8013/demo/Handle中文.zip
* http://192.168.2.111:8013/download?id=1&filename=中文.zip
* @return
*/
private String encodeUrlParam(String urlAddress) {
String newUrl = "";
try {
String path = "";
String param = "";
if (urlAddress.contains("?")) {
path = urlAddress.substring(0, urlAddress.indexOf("?"));
param = urlAddress.substring(urlAddress.indexOf("?"));
}else {
path = urlAddress;
private String encodeUrlParam(String urlAddress){
StringBuffer sb = new StringBuffer();
for (int i = 0; i < urlAddress.length(); i++) {
char c = urlAddress.charAt(i);
if (c >= 0 && c <= 255) {
sb.append(c);
} else {
byte[] b;
try {
//指定需要的编码类型
b = String.valueOf(c).getBytes("utf-8");
} catch (Exception ex) {
System.out.println(ex);
b = new byte[0];
}
for (int j = 0; j < b.length; j++) {
int k = b[j];
if (k < 0) {
k += 256;
}
sb.append("%" + Integer.toHexString(k).toUpperCase());
}
}
String lastPath = path.substring(path.lastIndexOf("/") + 1);
String leftPath = path.substring(0, path.lastIndexOf("/") + 1);
String encodeLastPath = URLEncoder.encode(lastPath, "UTF-8");
newUrl += leftPath + encodeLastPath;
if (urlAddress.contains("?")) {
newUrl += param;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return newUrl;
return sb.toString();
}

View File

@ -12,7 +12,6 @@ import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
@ -26,6 +25,9 @@ import java.util.Map;
*/
@Component
public class FileUtils {
public static final String DEFAULT_CONVERTER_CHARSET = System.getProperty("sun.jnu.encoding");
Logger log= LoggerFactory.getLogger(getClass());
@Autowired
@ -234,9 +236,8 @@ public class FileUtils {
*/
public void doActionConvertedFile(String outFilePath) {
StringBuffer sb = new StringBuffer();
String charset = ConfigConstants.getConvertedFileCharset();
try (InputStream inputStream = new FileInputStream(outFilePath);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charset))){
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, DEFAULT_CONVERTER_CHARSET))){
String line;
while(null != (line = reader.readLine())){
if (line.contains("charset=gb2312")) {
@ -247,7 +248,7 @@ public class FileUtils {
// 添加sheet控制头
sb.append("<script src=\"js/jquery-3.0.0.min.js\" type=\"text/javascript\"></script>");
sb.append("<script src=\"js/excel.header.js\" type=\"text/javascript\"></script>");
sb.append("<link rel=\"stylesheet\" href=\"http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css\">");
sb.append("<link rel=\"stylesheet\" href=\"//cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css\">");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
@ -328,26 +329,19 @@ public class FileUtils {
public FileAttribute getFileAttribute(String url) {
String decodedUrl = null;
try {
decodedUrl = URLDecoder.decode(url, "utf-8");
} catch (UnsupportedEncodingException e){
log.error("url解码失败");
}
String fileName;
FileType type;
String suffix;
String fullFileName = getUrlParameterReg(decodedUrl, "fullfilename");
String fullFileName = getUrlParameterReg(url, "fullfilename");
if (!StringUtils.isEmpty(fullFileName)) {
fileName = fullFileName;
type = typeFromFileName(fileName);
suffix = suffixFromFileName(fileName);
} else {
fileName = getFileNameFromURL(decodedUrl);
fileName = getFileNameFromURL(url);
type = typeFromUrl(url);
suffix = suffixFromUrl(url);
}
return new FileAttribute(type,suffix,fileName,url,decodedUrl);
return new FileAttribute(type,suffix,fileName,url,url);
}
}

View File

@ -14,7 +14,7 @@ import org.springframework.stereotype.Component;
* @since: 2019/6/11 7:45
*/
@Component
@ConditionalOnExpression("'${cache.clean:false}'.equals('true')")
@ConditionalOnExpression("'${cache.clean.enabled:false}'.equals('true')")
public class ShedulerClean {
private static final Logger LOGGER = LoggerFactory.getLogger(ShedulerClean.class);
@ -24,7 +24,8 @@ public class ShedulerClean {
private String fileDir = ConfigConstants.getFileDir();
@Scheduled(cron = "0 0 3 * * ?") //每晚3点执行一次
//默认每晚3点执行一次
@Scheduled(cron = "${cache.clean.cron:0 0 3 * * ?}")
public void clean() {
LOGGER.info("Cache clean start");
cacheService.cleanCache();

View File

@ -37,7 +37,19 @@ public class FileController {
@RequestMapping(value = "fileUpload", method = RequestMethod.POST)
public String fileUpload(@RequestParam("file") MultipartFile file,
HttpServletRequest request) throws JsonProcessingException {
// 获取文件名
String fileName = file.getOriginalFilename();
//判断是否为IE浏览器的文件名IE浏览器下文件名会带有盘符信息
// Check for Unix-style path
int unixSep = fileName.lastIndexOf('/');
// Check for Windows-style path
int winSep = fileName.lastIndexOf('\\');
// Cut off at latest possible point
int pos = (winSep > unixSep ? winSep : unixSep);
if (pos != -1) {
fileName = fileName.substring(pos + 1);
}
// 判断该文件类型是否有上传过,如果上传过则提示不允许再次上传
if (existsTypeFile(fileName)) {
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "每一种类型只可以上传一个文件,请先删除原有文件再次上传", null));

View File

@ -1,5 +1,6 @@
package cn.keking.web.controller;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.service.FilePreview;
import cn.keking.service.FilePreviewFactory;
@ -41,6 +42,8 @@ public class OnlinePreviewController {
@Autowired
private FileUtils fileUtils;
private String fileDir = ConfigConstants.getFileDir();
/**
* @param url
* @param model

View File

@ -1 +0,0 @@
app.id=file-preview

View File

@ -1,41 +1,65 @@
function isInSight(el) {
const bound = el.getBoundingClientRect();
const clientHeight = window.innerHeight;
//只考虑向下滚动加载
//const clientWidth=window.innerWeight;
return bound.top <= clientHeight + 100;
var bound = el.getBoundingClientRect();
var clientHeight = window.innerHeight;
//只考虑向下滚动加载
//const clientWidth=window.innerWeight;
return bound.top <= clientHeight + 100;
}
let index = 0;
var index = 0;
function checkImgs() {
const imgs = document.querySelectorAll('.my-photo');
for (let i = index; i < imgs.length; i++) {
if (isInSight(imgs[i])) {
loadImg(imgs[i]);
index = i;
var imgs = document.querySelectorAll('.my-photo');
for (var i = index; i < imgs.length; i++) {
if (isInSight(imgs[i])) {
loadImg(imgs[i]);
index = i;
}
}
}
}
function loadImg(el) {
const source = el.dataset.src;
el.src = source;
var source = el.getAttribute("data-src");
el.src = source;
}
// var mustRun = 500
// function throttle(fn, mustRun) {
// var timer = null;
// var previous = null;
// return function() {
// var now = new Date();
// var context = this;
// var args = arguments;
// if (!previous) {
// previous = now;
// }
// var remaining = now - previous;
// if (mustRun && remaining >= mustRun) {
// fn.apply(context, args);
// previous = now;
// }
// }
// }
function throttle(fn) {
var timer = null;
var previous = null;
return function () {
var now = new Date();
var context = this;
var args = arguments;
if (!previous) {
previous = now;
}
var remaining = now - previous;
setTimeout(refresh(fn, remaining, context, args, previous, now));
}
}
function throttle(fn, mustRun = 500) {
const timer = null;
let previous = null;
return function() {
const now = new Date();
const context = this;
const args = arguments;
if (!previous) {
previous = now;
function refresh(fn, remaining, context, args, previous, now) {
if (remaining >= 500) {
fn.apply(context, args);
previous = now;
}
const remaining = now - previous;
if (mustRun && remaining >= mustRun) {
fn.apply(context, args);
previous = now;
}
}
}

View File

@ -27,17 +27,9 @@ See https://github.com/adobe-type-tools/cmap-resources
<meta name="google" content="notranslate">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>PDF.js viewer</title>
<link rel="stylesheet" href="viewer.css">
<!-- This snippet is used in production (included from viewer.html) -->
<link rel="resource" type="application/l10n" href="locale/locale.properties">
<script src="../build/pdf.js"></script>
<script src="../../config.js"></script>
<link rel="resource" type="application/l10n" href="locale/locale.properties">
<script src="../build/pdf.js"></script>
<script src="viewer.js"></script>
</head>

View File

@ -1897,7 +1897,7 @@ var validateFileURL = void 0;
}
var fileOrigin = new URL(file, window.location.href).origin;
if (fileOrigin !== viewerOrigin) {
return '/getCorsFile?urlPath=' + file;
return '/getCorsFile?urlPath=' + encodeURIComponent(file);
}
} catch (ex) {
var message = ex && ex.message;

View File

@ -68,7 +68,7 @@
fulls += ",resizable"; // 对于不支持screen属性的浏览器可以手工进行最大化。 manually
}
window.open("onlinePreview?url="
+ encodeURIComponent("${baseUrl}" + treeNode.fileName)+"&fileKey="+treeNode.fileKey, "_blank",fulls);
+ encodeURIComponent("${baseUrl}" + treeNode.fileName)+"&fileKey="+ encodeURIComponent(treeNode.fileKey), "_blank",fulls);
}
}
}

View File

@ -2,11 +2,11 @@
<html lang="en">
<head>
<title>图片预览图</title>
<title>kkFileView演示首页</title>
<link rel="stylesheet" href="css/viewer.min.css">
<link rel="stylesheet" href="css/loading.css">
<link rel="stylesheet" href="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.11.1/bootstrap-table.css" />
<link rel="stylesheet" href="//cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.11.1/bootstrap-table.css" />
<style type="text/css">
</style>
</head>
@ -29,22 +29,15 @@
如果你的项目需要接入文件预览项目达到对docx、excel、ppt、jpg等文件的预览效果那么通过在你的项目中加入下面的代码就可以
成功实现:
<pre style="background-color: #2f332a;color: #cccccc">
$scope.openWin = function (fileUrl) {
var url = configuration.previewUrl + encodeURIComponent(fileUrl);
var winHeight = window.document.documentElement.clientHeight-10;
$window.open(url, "_blank", "height=" + winHeight
+ ",top=80,left=80,toolbar=no, menubar=no, scrollbars=yes, resizable=yes");
};
var url = 'http://127.0.0.1:8080/file/test.txt'; //要预览文件的访问地址
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(url));
</pre>
</div>
<div>
新增多图片同时预览功能,接口如下:
<pre style="background-color: #2f332a;color: #cccccc">
var fileUrl =url1+"|"+"url2";//多文件使用“|”字符隔开
var url = "http://localhost:8012/picturesPreview?urls" + encodeURIComponent(fileUrl);
var winHeight = window.document.documentElement.clientHeight-10;
$window.open(url, "_blank", "height=" + winHeight
+ ",top=80,left=80,toolbar=no, menubar=no, scrollbars=yes, resizable=yes");
var fileUrl =url1+"|"+"url2";//多文件使用“|”字符隔开
window.open('http://127.0.0.1:8012/picturesPreview?urls='+encodeURIComponent(fileUrl));
</pre>
</div>
</div>
@ -86,8 +79,23 @@
<div id="collapseThree" class="panel-collapse collapse in">
<div class="panel-body">
<div>
2019年06月18日 <br>
1. 支持自动清理缓存及预览文件<br>
2. 支持http/https下载流url文件预览<br>
3. 支持FTP url文件预览<br>
4. 加入Docker构建<br><br>
2019年04月08日 <br>
1. 缓存及队列实现抽象提供JDK和REDIS两种实现(REDIS成为可选依赖)<br>
2. 打包方式提供zip和tar.gz包并提供一键启动脚本<br><br>
2018年01月19日 <br>
1. 大文件入队提前处理<br>
1. 新增addTask文件转换入队接口<br>
1. 采用redis队列支持kkFIleView接口和异构系统入队两种方式<br><br>
2018年01月15日 <br>
1.首页新增社会化评论框<br><br>
1.首页新增社会化评论框<br><br>
2018年01月12日 <br>
1.新增多图片同时预览<br>
@ -100,7 +108,7 @@
4.引入pdf.js预览doc等文件支持doc标题生成pdf预览菜单支持手机端预览<br><br>
2017年12月12日<br>
1.项目osc开源:<a href="https://gitee.com/kekingcn/file-online-preview" target="_blank">https://gitee.com/kekingcn/file-online-preview</a><br>
1.项目gitee开源:<a href="https://gitee.com/kekingcn/file-online-preview" target="_blank">https://gitee.com/kekingcn/file-online-preview</a><br>
2.项目github开源:<a href="https://github.com/kekingcn/kkFileView" target="_blank">https://github.com/kekingcn/kkFileView</a>
</div>
</div>
@ -109,7 +117,7 @@
<div style="width: 80%">
<!-- 多说评论框 start -->
<div id="SOHUCS" sid="kkfileView"></div>
<script charset="utf-8" type="text/javascript" src="https://changyan.sohu.com/upload/changyan.js" ></script>
<script charset="utf-8" type="text/javascript" src="//changyan.sohu.com/upload/changyan.js" ></script>
<script type="text/javascript">
window.changyan.api.config({
appid: 'cytx6wU4N',
@ -146,9 +154,9 @@
</div>
</div>
<script src="js/jquery-3.0.0.min.js" type="text/javascript"></script>
<script src="https://cdn.bootcss.com/jquery.form/3.09/jquery.form.min.js" type="text/javascript"></script>
<script src="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.11.1/bootstrap-table.js"></script>
<script src="//cdn.bootcss.com/jquery.form/3.09/jquery.form.min.js" type="text/javascript"></script>
<script src="//cdn.static.runoob.com/libs/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.11.1/bootstrap-table.js"></script>
<script>
function deleteFile(fileName) {
$.ajax({
@ -157,7 +165,7 @@
// 删除完成刷新table
if (1 == data.code) {
alert(data.msg);
}else{
} else{
$('#table').bootstrapTable('refresh', {});
}
},
@ -179,9 +187,8 @@
}).on('pre-body.bs.table', function (e,data) {
// 每个data添加一列用来操作
$(data).each(function (index, item) {
item.action = "<a class='btn btn-default' target='_blank' href='${baseUrl}onlinePreview?url="
+ encodeURIComponent('${baseUrl}' + item.fileName ) +"'>预览</a>" +
"<a class='btn btn-default' target='_blank' href='javascript:void(0);' onclick='deleteFile(\""+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;
}).on('post-body.bs.table', function (e,data) {

View File

@ -25,7 +25,7 @@
</#list>
</div>
<#--<img src="images/right.png" style="position: fixed; cursor: pointer; top: 40%; right: 60px; z-index: 999;" alt="使用PDF预览" title="使用PDF预览" onclick="changePreviewType('pdf')"/>-->
<span class="fa fa-file-pdf-o fa-5x" style="position: fixed; cursor: pointer; top: 40%; right: 50px; z-index: 999;" title="使用PDF预览" onclick="changePreviewType('pdf')"></span>
<span class="fa fa-file-pdf-o fa-4x" style="position: fixed; cursor: pointer; top: 40%; right: 50px; z-index: 999;" title="使用PDF预览" onclick="changePreviewType('pdf')"></span>
<script>
window.onload=checkImgs;
window.onscroll = throttle(checkImgs);

View File

@ -20,7 +20,7 @@
<iframe src="" width="100%" frameborder="0"></iframe>
<#-- <img src="images/left.png" style="position: fixed; cursor: pointer; top: 40%; right: 60px; z-index: 999;" alt="使用图片预览" title="使用图片预览" onclick="goForImage()"/>-->
<span class="fa fa-file-image-o fa-5x" style="position: fixed; cursor: pointer; top: 40%; right: 50px; z-index: 999;" title="使用图片预览" onclick="goForImage()"></span>
<span class="fa fa-file-image-o fa-4x" style="position: fixed; cursor: pointer; top: 40%; right: 50px; z-index: 999;" title="使用图片预览" onclick="goForImage()"></span>
</body>

View File

@ -5,7 +5,7 @@
<groupId>cn.keking</groupId>
<artifactId>filepreview</artifactId>
<version>0.0.1-SNAPSHOT</version>
<version>2.2.0-SNAPSHOT</version>
<modules>
<module>jodconverter-core</module>
<module>jodconverter-web</module>