Compare commits

...

32 Commits
utils ... xss

Author SHA1 Message Date
chenkailing
50dd7c1b83 移除 Apache-common-text 包,采用 spring 内置的 HtmlUtils 处理 xss 问题 2021-01-23 13:12:31 +08:00
hunterale
2dd067170b escaping of dangerous characters to prevent reflected xss 2021-01-22 12:08:06 +08:00
ale
e635ca86c5 escaping of dangerous characters 2021-01-22 12:08:06 +08:00
gitchenjh
8bd36e37a3 Merge pull request #204 from gitchenjh/master
修复:dwg文件预览时无法在jpg和pdf两种类型之间切换
2021-01-21 15:48:27 +08:00
陈精华
b3b2f7c407 修复:dwg文件预览时无法在jpg和pdf两种类型之间切换 2021-01-21 15:47:13 +08:00
dependabot[bot]
f1ad3d44ff Bump poi from 3.12 to 3.17 in /server
Bumps poi from 3.12 to 3.17.

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

2
.gitignore vendored
View File

@@ -17,7 +17,6 @@ target/
### NetBeans ### ### NetBeans ###
nbproject/private/ nbproject/private/
build/
nbbuild/ nbbuild/
dist/ dist/
nbdist/ nbdist/
@@ -29,4 +28,3 @@ nbdist/
server/src/main/cache/ server/src/main/cache/
server/src/main/file/ server/src/main/file/
server/src/main/log

View File

@@ -28,5 +28,5 @@ ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH $PATH:$JAVA_HOME/bin ENV PATH $PATH:$JAVA_HOME/bin
ENV LANG zh_CN.UTF-8 ENV LANG zh_CN.UTF-8
ENV LC_ALL zh_CN.UTF-8 ENV LC_ALL zh_CN.UTF-8
ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-2.2.1/bin ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-3.3.0/bin
ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider","-Dspring.config.location=/opt/kkFileView-2.2.1/config/application.properties","-jar","/opt/kkFileView-2.2.1/bin/kkFileView-2.2.1.jar"] ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider","-Dspring.config.location=/opt/kkFileView-3.3.0/config/application.properties","-jar","/opt/kkFileView-3.3.0/bin/kkFileView-3.3.0.jar"]

View File

@@ -109,6 +109,26 @@ pdf预览模式预览效果如下
### 历史更新记录 ### 历史更新记录
> 2020年12月27日
2020年年终大版本更新架构全面设计代码全面重构代码质量全面提升二次开发更便捷欢迎拉源码品鉴提issuepr共同建设
1. 架构模块调整,大量的代码重构代码质量提升N个等级欢迎品鉴
2. 增强XML文件预览效果新增XML文档数结构预览
3. 新增markdown文件预览支持预览支持md渲染和源文本切换支持
4. 切换底层web server为jetty解决这个issuehttps://github.com/kekingcn/kkFileView/issues/168
5. 引入cpdetector解决文件编码识别问题
6. url采用base64+urlencode双编码彻底解决各种奇葩文件名预览问题
7. 新增配置项office.preview.switch.disabled控制offic文件预览切换开关
8. 优化文本类型文件预览逻辑采用Base64传输内容避免预览时再次请求文件内容
9. office预览图片模式禁用图片放大效果达到图片和pdf预览效果一致的体验
10. 直接代码静态设置pdfbox兼容低版本jdk在IDEA中运行也不会有警告提示
11. 移除guavahutool等非必须的工具包减少代码体积
12. Office组件加载异步化提速应用启动速度最快到5秒内
13. 合理设置预览消费队列的线程数
14. 修复压缩包里文件再次预览失败的bug
15. 修复图片预览的bug
> 2020年05月20日 > 2020年05月20日
1. 新增支持全局水印并支持通过参数动态改变水印内容 1. 新增支持全局水印并支持通过参数动态改变水印内容
2. 新增支持CAD文件预览 2. 新增支持CAD文件预览

View File

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

View File

@@ -12,7 +12,7 @@
<groupId>cn.keking</groupId> <groupId>cn.keking</groupId>
<artifactId>kkFileView</artifactId> <artifactId>kkFileView</artifactId>
<version>2.2.1</version> <version>3.3.0</version>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -56,12 +56,6 @@
<groupId>cn.keking</groupId> <groupId>cn.keking</groupId>
<artifactId>office-plugin</artifactId> <artifactId>office-plugin</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<artifactId>commons-io</artifactId>
<groupId>commons-io</groupId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
@@ -77,7 +71,7 @@
<dependency> <dependency>
<groupId>org.apache.poi</groupId> <groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId> <artifactId>poi</artifactId>
<version>3.12</version> <version>3.17</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.poi</groupId> <groupId>org.apache.poi</groupId>
@@ -105,11 +99,6 @@
<artifactId>fr.opensagres.xdocreport.document</artifactId> <artifactId>fr.opensagres.xdocreport.document</artifactId>
<version>1.0.5</version> <version>1.0.5</version>
</dependency> </dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<!-- 解压(apache) --> <!-- 解压(apache) -->
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
@@ -201,6 +190,12 @@
<scope>system</scope> <scope>system</scope>
<systemPath>${basedir}/lib/cpdetector-1.04.jar</systemPath> <systemPath>${basedir}/lib/cpdetector-1.04.jar</systemPath>
</dependency> </dependency>
<!-- url 规范化 -->
<dependency>
<groupId>io.mola.galimatias</groupId>
<artifactId>galimatias</artifactId>
<version>0.2.1</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
<resources> <resources>

View File

@@ -6,4 +6,4 @@ echo Starting kkFileView...
echo Please check log file in ../log/kkFileView.log for more information 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 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 echo If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers
java -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=..\config\application.properties -jar kkFileView-2.2.1.jar -> ..\log\kkFileView.log java -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=..\config\application.properties -jar kkFileView-3.3.0.jar -> ..\log\kkFileView.log

View File

@@ -29,4 +29,4 @@ echo "Starting kkFileView..."
echo "Please execute ./showlog.sh to check log for more information" echo "Please execute ./showlog.sh to check log for more information"
echo "You can get help in our official homesite: https://kkFileView.keking.cn" 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" echo "If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers"
nohup java -Dfile.encoding=UTF-8 -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=../config/application.properties -jar kkFileView-2.2.1.jar > ../log/kkFileView.log 2>&1 & nohup java -Dfile.encoding=UTF-8 -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=../config/application.properties -jar kkFileView-3.3.0.jar > ../log/kkFileView.log 2>&1 &

View File

@@ -1,5 +1,7 @@
[#ftl] [#ftl]
[#-- @implicitly included --] [#-- @implicitly included --]
[#-- @ftlvariable name="file" type="cn.keking.model.FileAttribute" --]
[#-- @ftlvariable name="fileName" type="java.lang.String" --]
[#-- @ftlvariable name="fileTree" type="java.lang.String" --] [#-- @ftlvariable name="fileTree" type="java.lang.String" --]
[#-- @ftlvariable name="baseUrl" type="java.lang.String" --] [#-- @ftlvariable name="baseUrl" type="java.lang.String" --]
[#-- @ftlvariable name="imgUrls" type="String" --] [#-- @ftlvariable name="imgUrls" type="String" --]

View File

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

View File

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

View File

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

View File

@@ -4,11 +4,18 @@ import java.io.Serializable;
/** /**
* 接口返回值结构 * 接口返回值结构
*
* @author yudian-it * @author yudian-it
* @date 2017/11/17 * @date 2017/11/17
*/ */
public class ReturnResponse<T> implements Serializable{ public class ReturnResponse<T> implements Serializable {
private static final long serialVersionUID = 313975329998789878L; 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. 成功 * 0. 成功
@@ -31,6 +38,30 @@ public class ReturnResponse<T> implements Serializable{
this.content = content; 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() { public int getCode() {
return code; return code;
} }

View File

@@ -2,7 +2,7 @@ package cn.keking.service;
import cn.keking.config.ConfigConstants; import cn.keking.config.ConfigConstants;
import cn.keking.model.FileType; import cn.keking.model.FileType;
import cn.keking.utils.FileUtils; import cn.keking.utils.KkFileUtils;
import cn.keking.web.filter.BaseUrlFilter; import cn.keking.web.filter.BaseUrlFilter;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@@ -48,7 +48,7 @@ public class CompressFileReader {
String baseUrl = BaseUrlFilter.getBaseUrl(); String baseUrl = BaseUrlFilter.getBaseUrl();
String archiveFileName = fileHandlerService.getFileNameFromPath(filePath); String archiveFileName = fileHandlerService.getFileNameFromPath(filePath);
try { try {
ZipFile zipFile = new ZipFile(filePath, FileUtils.getFileEncode(filePath)); ZipFile zipFile = new ZipFile(filePath, KkFileUtils.getFileEncode(filePath));
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries(); Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
// 排序 // 排序
entries = sortZipEntries(entries); entries = sortZipEntries(entries);
@@ -67,7 +67,7 @@ public class CompressFileReader {
} }
String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName); String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName);
parentName = (level - 1) + "_" + parentName; parentName = (level - 1) + "_" + parentName;
FileType type = fileHandlerService.typeFromUrl(childName); FileType type = FileType.typeFromUrl(childName);
if (type.equals(FileType.picture)) {//添加图片文件到图片列表 if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl + childName); imgUrls.add(baseUrl + childName);
} }
@@ -120,7 +120,7 @@ public class CompressFileReader {
headersToBeExtracted.add(Collections.singletonMap(childName, header)); headersToBeExtracted.add(Collections.singletonMap(childName, header));
} }
String parentName = getLast2FileName(fullName, "\\", archiveFileName); String parentName = getLast2FileName(fullName, "\\", archiveFileName);
FileType type = fileHandlerService.typeFromUrl(childName); FileType type = FileType.typeFromUrl(childName);
if (type.equals(FileType.picture)) {//添加图片文件到图片列表 if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl + childName); imgUrls.add(baseUrl + childName);
} }
@@ -163,7 +163,7 @@ public class CompressFileReader {
} }
String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName); String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName);
parentName = (level - 1) + "_" + parentName; parentName = (level - 1) + "_" + parentName;
FileType type = fileHandlerService.typeFromUrl(childName); FileType type = FileType.typeFromUrl(childName);
if (type.equals(FileType.picture)) {//添加图片文件到图片列表 if (type.equals(FileType.picture)) {//添加图片文件到图片列表
imgUrls.add(baseUrl + childName); imgUrls.add(baseUrl + childName);
} }
@@ -382,7 +382,7 @@ public class CompressFileReader {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
FileUtils.deleteFileByPath(filePath); KkFileUtils.deleteFileByPath(filePath);
} }
private void extractZipFile(String childName, InputStream zipFile) { private void extractZipFile(String childName, InputStream zipFile) {
@@ -439,7 +439,7 @@ public class CompressFileReader {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
FileUtils.deleteFileByPath(filePath); KkFileUtils.deleteFileByPath(filePath);
} }
} }
@@ -468,7 +468,7 @@ public class CompressFileReader {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
FileUtils.deleteFileByPath(filePath); KkFileUtils.deleteFileByPath(filePath);
} }
private void extractRarFile(String childName, FileHeader header, Archive archive) { private void extractRarFile(String childName, FileHeader header, Archive archive) {

View File

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

View File

@@ -4,13 +4,28 @@ import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute; import cn.keking.model.FileAttribute;
import cn.keking.model.FileType; import cn.keking.model.FileType;
import cn.keking.service.cache.CacheService; import cn.keking.service.cache.CacheService;
import cn.keking.utils.KkFileUtils;
import cn.keking.utils.WebUtils; 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.stereotype.Component;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.*; import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -21,10 +36,15 @@ import java.util.Map;
@Component @Component
public class FileHandlerService { public class FileHandlerService {
private final Logger logger = LoggerFactory.getLogger(FileHandlerService.class);
private static final String DEFAULT_CONVERTER_CHARSET = System.getProperty("sun.jnu.encoding"); private static final String DEFAULT_CONVERTER_CHARSET = System.getProperty("sun.jnu.encoding");
private final String fileDir = ConfigConstants.getFileDir(); private final String fileDir = ConfigConstants.getFileDir();
private final CacheService cacheService; private final CacheService cacheService;
@Value("${server.tomcat.uri-encoding:UTF-8}")
private String uriEncoding;
public FileHandlerService(CacheService cacheService) { public FileHandlerService(CacheService cacheService) {
this.cacheService = cacheService; this.cacheService = cacheService;
} }
@@ -51,35 +71,6 @@ public class FileHandlerService {
return cacheService.getPdfImageCache(key); return cacheService.getPdfImageCache(key);
} }
/**
* 查看文件类型(防止参数中存在.点号或者其他特殊字符,所以先抽取文件名,然后再获取文件类型)
*
* @param url url
* @return 文件类型
*/
public FileType typeFromUrl(String url) {
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return this.typeFromFileName(fileName);
}
private FileType typeFromFileName(String fileName) {
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
return FileType.to(fileType);
}
/**
* 从url中剥离出文件名
*
* @param url 格式如http://www.com.cn/20171113164107_月度绩效表模板(新).xls?UCloudPublicKey=ucloudtangshd@weifenf.com14355492830001993909323&Expires=&Signature=I D1NOFtAJSPT16E6imv6JWuq0k=
* @return 文件名
*/
public String getFileNameFromURL(String url) {
// 因为url的参数中可能会存在/的情况所以直接url.lastIndexOf("/")会有问题
// 所以先从处将url截断然后运用url.lastIndexOf("/")获取文件名
String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
return noQueryUrl.substring(noQueryUrl.lastIndexOf("/") + 1);
}
/** /**
* 从路径中获取文件负 * 从路径中获取文件负
@@ -174,19 +165,87 @@ public class FileHandlerService {
} }
/** /**
* 获取文件后缀 * pdf文件转换成jpg图片集
* * @param pdfFilePath pdf文件路径
* @param url url * @param pdfName pdf文件名称
* @return 文件后缀 * @param baseUrl 基础访问地址
* @return 图片访问集合
*/ */
private String suffixFromUrl(String url) { public List<String> pdf2jpg(String pdfFilePath, String pdfName, String baseUrl) {
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length()); List<String> imageUrls = new ArrayList<>();
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1); Integer imageCount = this.getConvertedPdfImage(pdfFilePath);
return suffixFromFileName(fileName); 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);
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 转换是否成功
*/
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;
}
} }
/** /**
@@ -203,12 +262,12 @@ public class FileHandlerService {
String fullFileName = WebUtils.getUrlParameterReg(url, "fullfilename"); String fullFileName = WebUtils.getUrlParameterReg(url, "fullfilename");
if (StringUtils.hasText(fullFileName)) { if (StringUtils.hasText(fullFileName)) {
fileName = fullFileName; fileName = fullFileName;
type = this.typeFromFileName(fullFileName); type = FileType.typeFromFileName(fullFileName);
suffix = suffixFromFileName(fullFileName); suffix = KkFileUtils.suffixFromFileName(fullFileName);
} else { } else {
fileName = getFileNameFromURL(url); fileName = WebUtils.getFileNameFromURL(url);
type = typeFromUrl(url); type = FileType.typeFromUrl(url);
suffix = suffixFromUrl(url); suffix = WebUtils.suffixFromUrl(url);
} }
attribute.setType(type); attribute.setType(type);
attribute.setName(fileName); attribute.setName(fileName);
@@ -216,7 +275,7 @@ public class FileHandlerService {
attribute.setUrl(url); attribute.setUrl(url);
if (req != null) { if (req != null) {
String officePreviewType = req.getParameter("officePreviewType"); String officePreviewType = req.getParameter("officePreviewType");
String fileKey = req.getParameter("fileKey"); String fileKey = WebUtils.getUrlParameterReg(url,"fileKey");
if (StringUtils.hasText(officePreviewType)) { if (StringUtils.hasText(officePreviewType)) {
attribute.setOfficePreviewType(officePreviewType); attribute.setOfficePreviewType(officePreviewType);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,8 +5,6 @@ import cn.keking.service.FilePreview;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import static com.sun.glass.ui.Clipboard.TEXT_TYPE;
/** /**
* @author kl (http://kailing.pub) * @author kl (http://kailing.pub)
* @since 2020/12/25 * @since 2020/12/25
@@ -23,7 +21,7 @@ public class MarkdownFilePreviewImpl implements FilePreview {
@Override @Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) { public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
model.addAttribute(TEXT_TYPE,"markdown"); simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
return simTextFilePreview.filePreviewHandle(url, model, fileAttribute); return MARKDOWN_FILE_PREVIEW_PAGE;
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,8 +5,6 @@ import cn.keking.service.FilePreview;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import static com.sun.glass.ui.Clipboard.TEXT_TYPE;
/** /**
* @author kl (http://kailing.pub) * @author kl (http://kailing.pub)
* @since 2020/12/25 * @since 2020/12/25
@@ -20,10 +18,9 @@ public class XmlFilePreviewImpl implements FilePreview {
this.simTextFilePreview = simTextFilePreview; this.simTextFilePreview = simTextFilePreview;
} }
@Override @Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) { public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
model.addAttribute(TEXT_TYPE,"xml"); simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
return simTextFilePreview.filePreviewHandle(url, model, fileAttribute); return XML_FILE_PREVIEW_PAGE;
} }
} }

View File

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

View File

@@ -2,16 +2,19 @@ package cn.keking.utils;
import cn.keking.config.ConfigConstants; import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute; import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.model.ReturnResponse; import cn.keking.model.ReturnResponse;
import io.mola.galimatias.GalimatiasParseException;
import jodd.io.NetUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.UUID; import java.util.UUID;
import static cn.keking.utils.KkFileUtils.isFtpUrl;
import static cn.keking.utils.KkFileUtils.isHttpUrl;
/** /**
* @author yudian-it * @author yudian-it
*/ */
@@ -30,42 +33,26 @@ public class DownloadUtils {
*/ */
public static ReturnResponse<String> downLoad(FileAttribute fileAttribute, String fileName) { public static ReturnResponse<String> downLoad(FileAttribute fileAttribute, String fileName) {
String urlStr = fileAttribute.getUrl(); String urlStr = fileAttribute.getUrl();
String type = fileAttribute.getSuffix();
ReturnResponse<String> response = new ReturnResponse<>(0, "下载成功!!!", ""); ReturnResponse<String> response = new ReturnResponse<>(0, "下载成功!!!", "");
UUID uuid = UUID.randomUUID(); String realPath = DownloadUtils.getRelFilePath(fileName, fileAttribute);
if (null == fileName) {
fileName = uuid + "." + type;
} else { // 文件后缀不一致时以type为准(针对simText【将类txt文件转为txt】)
fileName = fileName.replace(fileName.substring(fileName.lastIndexOf(".") + 1), type);
}
String realPath = fileDir + fileName;
File dirFile = new File(fileDir);
if (!dirFile.exists() && !dirFile.mkdirs()) {
logger.error("创建目录【{}】失败,可能是权限不够,请检查", fileDir);
}
try { try {
URL url = new URL(urlStr); URL url = WebUtils.normalizedURL(urlStr);
if (url.getProtocol() != null && (url.getProtocol().toLowerCase().startsWith("file") || url.getProtocol().toLowerCase().startsWith("http"))) { if (isHttpUrl(url)) {
byte[] bytes = getBytesFromUrl(urlStr); File realFile = new File(realPath);
OutputStream os = new FileOutputStream(realPath); NetUtil.downloadFile(url.toString(),realFile);
saveBytesToOutStream(bytes, os); } else if (isFtpUrl(url)) {
} else if (url.getProtocol() != null && "ftp".equalsIgnoreCase(url.getProtocol())) {
String ftpUsername = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME); String ftpUsername = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME);
String ftpPassword = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD); String ftpPassword = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD);
String ftpControlEncoding = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING); String ftpControlEncoding = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING);
FtpUtils.download(fileAttribute.getUrl(), realPath, ftpUsername, ftpPassword, ftpControlEncoding); FtpUtils.download(fileAttribute.getUrl(), realPath, ftpUsername, ftpPassword, ftpControlEncoding);
} else { } else {
response.setCode(1); response.setCode(1);
response.setContent(null);
response.setMsg("url不能识别url" + urlStr); response.setMsg("url不能识别url" + urlStr);
} }
response.setContent(realPath); response.setContent(realPath);
response.setMsg(fileName); response.setMsg(fileName);
if (FileType.simText.equals(fileAttribute.getType())) {
convertTextPlainFileCharsetToUtf8(realPath);
}
return response; return response;
} catch (IOException e) { } catch (IOException | GalimatiasParseException e) {
logger.error("文件下载失败url{}", urlStr, e); logger.error("文件下载失败url{}", urlStr, e);
response.setCode(1); response.setCode(1);
response.setContent(null); response.setContent(null);
@@ -78,82 +65,27 @@ public class DownloadUtils {
} }
} }
public static byte[] getBytesFromUrl(String urlStr) throws IOException {
InputStream is = getInputStreamFromUrl(urlStr);
if (is == null) {
// urlStr = URLUtil.normalize(urlStr, true, true);
is = getInputStreamFromUrl(urlStr);
if (is == null) {
logger.error("文件下载异常url{}", urlStr);
throw new IOException("文件下载异常url" + urlStr);
}
}
return getBytesFromStream(is);
}
public static void saveBytesToOutStream(byte[] b, OutputStream os) throws IOException {
os.write(b);
os.close();
}
private static InputStream getInputStreamFromUrl(String urlStr) {
try {
URL url = new URL(urlStr);
URLConnection connection = url.openConnection();
if (connection instanceof HttpURLConnection) {
connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
}
return connection.getInputStream();
} catch (IOException e) {
logger.warn("连接url异常url{}", urlStr);
return null;
}
}
private static byte[] getBytesFromStream(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
byte[] b = baos.toByteArray();
is.close();
baos.close();
return b;
}
/** /**
* 转换文本文件编码为utf8 * 获取真实文件绝对路径
* 探测源文件编码,探测到编码切不为utf8则进行转码
* *
* @param filePath 文件路径 * @param fileName 文件
* @return 文件路径
*/ */
private static void convertTextPlainFileCharsetToUtf8(String filePath) throws IOException { private static String getRelFilePath(String fileName, FileAttribute fileAttribute) {
File sourceFile = new File(filePath); String type = fileAttribute.getSuffix();
if (sourceFile.exists() && sourceFile.isFile() && sourceFile.canRead()) { if (null == fileName) {
String encoding = FileUtils.getFileEncode(filePath); UUID uuid = UUID.randomUUID();
if (!FileUtils.DEFAULT_FILE_ENCODING.equals(encoding)) { fileName = uuid + "." + type;
// 不为utf8,进行转码 } else { // 文件后缀不一致时以type为准(针对simText【将类txt文件转为txt】)
File tmpUtf8File = new File(filePath + ".utf8"); fileName = fileName.replace(fileName.substring(fileName.lastIndexOf(".") + 1), type);
Writer writer = new OutputStreamWriter(new FileOutputStream(tmpUtf8File), StandardCharsets.UTF_8);
Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile), encoding));
char[] buf = new char[1024];
int read;
while ((read = reader.read(buf)) > 0) {
writer.write(buf, 0, read);
}
reader.close();
writer.close();
// 删除源文件
if (!sourceFile.delete()) {
logger.error("源文件【{}】删除失败,请检查文件目录权限!", filePath);
}
// 重命名
if (tmpUtf8File.renameTo(sourceFile)) {
logger.error("临时文件【{}】重命名失败,请检查文件路径权限!", tmpUtf8File.getPath());
}
}
} }
String realPath = fileDir + fileName;
File dirFile = new File(fileDir);
if (!dirFile.exists() && !dirFile.mkdirs()) {
logger.error("创建目录【{}】失败,可能是权限不够,请检查", fileDir);
}
return realPath;
} }
} }

View File

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

View File

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

View File

@@ -1,5 +1,9 @@
package cn.keking.utils; package cn.keking.utils;
import io.mola.galimatias.GalimatiasParseException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -8,6 +12,16 @@ import java.util.Map;
* create : 2020-12-27 1:30 上午 * create : 2020-12-27 1:30 上午
**/ **/
public class WebUtils { public class WebUtils {
/**
* 获取标准的URL
* @param urlStr url
* @return 标准的URL
*/
public static URL normalizedURL(String urlStr) throws GalimatiasParseException, MalformedURLException {
return io.mola.galimatias.URL.parse(urlStr).toJavaURL();
}
/** /**
* 获取url中的参数 * 获取url中的参数
* *
@@ -57,4 +71,30 @@ public class WebUtils {
} }
return strAllParam; return strAllParam;
} }
/**
* 从url中剥离出文件名
*
* @param url 格式如http://www.com.cn/20171113164107_月度绩效表模板(新).xls?UCloudPublicKey=ucloudtangshd@weifenf.com14355492830001993909323&Expires=&Signature=I D1NOFtAJSPT16E6imv6JWuq0k=
* @return 文件名
*/
public static String getFileNameFromURL(String url) {
// 因为url的参数中可能会存在/的情况所以直接url.lastIndexOf("/")会有问题
// 所以先从处将url截断然后运用url.lastIndexOf("/")获取文件名
String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
return noQueryUrl.substring(noQueryUrl.lastIndexOf("/") + 1);
}
/**
* 从url中获取文件后缀
*
* @param url url
* @return 文件后缀
*/
public static String suffixFromUrl(String url) {
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return KkFileUtils.suffixFromFileName(fileName);
}
} }

View File

@@ -15,7 +15,9 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import org.springframework.web.util.HtmlUtils;
/** /**
* *
@@ -28,9 +30,7 @@ public class FileController {
private final Logger logger = LoggerFactory.getLogger(FileController.class); private final Logger logger = LoggerFactory.getLogger(FileController.class);
private final String fileDir = ConfigConstants.getFileDir(); private final String fileDir = ConfigConstants.getFileDir();
private final String demoDir = "demo"; private final String demoDir = "demo";
private final String demoPath = demoDir + File.separator; private final String demoPath = demoDir + File.separator;
@RequestMapping(value = "fileUpload", method = RequestMethod.POST) @RequestMapping(value = "fileUpload", method = RequestMethod.POST)
@@ -38,6 +38,10 @@ public class FileController {
// 获取文件名 // 获取文件名
String fileName = file.getOriginalFilename(); String fileName = file.getOriginalFilename();
//判断是否为IE浏览器的文件名IE浏览器下文件名会带有盘符信息 //判断是否为IE浏览器的文件名IE浏览器下文件名会带有盘符信息
// escaping dangerous characters to prevent XSS
fileName = HtmlUtils.htmlEscape(fileName, StandardCharsets.UTF_8.name());
// Check for Unix-style path // Check for Unix-style path
int unixSep = fileName.lastIndexOf('/'); int unixSep = fileName.lastIndexOf('/');
// Check for Windows-style path // Check for Windows-style path
@@ -49,7 +53,7 @@ public class FileController {
} }
// 判断是否存在同名文件 // 判断是否存在同名文件
if (existsFile(fileName)) { if (existsFile(fileName)) {
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "存在同名文件,请先删除原有文件再次上传", null)); return new ObjectMapper().writeValueAsString(ReturnResponse.failure("存在同名文件,请先删除原有文件再次上传"));
} }
File outFile = new File(fileDir + demoPath); File outFile = new File(fileDir + demoPath);
if (!outFile.exists() && !outFile.mkdirs()) { if (!outFile.exists() && !outFile.mkdirs()) {
@@ -58,10 +62,10 @@ public class FileController {
logger.info("上传文件:{}", fileDir + demoPath + fileName); logger.info("上传文件:{}", fileDir + demoPath + fileName);
try(InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream(fileDir + demoPath + fileName)) { try(InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream(fileDir + demoPath + fileName)) {
StreamUtils.copy(in, out); StreamUtils.copy(in, out);
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(0, "SUCCESS", null)); return new ObjectMapper().writeValueAsString(ReturnResponse.success(null));
} catch (IOException e) { } catch (IOException e) {
logger.error("文件上传失败", e); logger.error("文件上传失败", e);
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "FAILURE", null)); return new ObjectMapper().writeValueAsString(ReturnResponse.failure());
} }
} }
@@ -75,7 +79,7 @@ public class FileController {
if (file.exists() && !file.delete()) { if (file.exists() && !file.delete()) {
logger.error("删除文件【{}】失败,请检查目录权限!",file.getPath()); logger.error("删除文件【{}】失败,请检查目录权限!",file.getPath());
} }
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(0, "SUCCESS", null)); return new ObjectMapper().writeValueAsString(ReturnResponse.success());
} }
@RequestMapping(value = "listFiles", method = RequestMethod.GET) @RequestMapping(value = "listFiles", method = RequestMethod.GET)

View File

@@ -5,14 +5,18 @@ import cn.keking.service.FilePreview;
import cn.keking.service.FilePreviewFactory; import cn.keking.service.FilePreviewFactory;
import cn.keking.service.cache.CacheService; import cn.keking.service.cache.CacheService;
import cn.keking.utils.DownloadUtils; import cn.keking.service.impl.OtherFilePreviewImpl;
import cn.keking.service.FileHandlerService; import cn.keking.service.FileHandlerService;
import com.thoughtworks.xstream.core.util.Base64JavaUtilCodec; import cn.keking.utils.WebUtils;
import fr.opensagres.xdocreport.core.io.IOUtils;
import io.mola.galimatias.GalimatiasParseException;
import jodd.io.NetUtil;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.util.Base64Utils; import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@@ -20,73 +24,97 @@ import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.*; import java.io.*;
import java.net.URLDecoder; import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import static cn.keking.service.FilePreview.PICTURE_FILE_PREVIEW_PAGE;
/** /**
* @author yudian-it * @author yudian-it
*/ */
@Controller @Controller
public class OnlinePreviewController { public class OnlinePreviewController {
public static final String BASE64_DECODE_ERROR_MSG = "Base64解码失败请检查你的 %s 是否采用 Base64 + urlEncode 双重编码了!";
private final Logger logger = LoggerFactory.getLogger(OnlinePreviewController.class); private final Logger logger = LoggerFactory.getLogger(OnlinePreviewController.class);
private final FilePreviewFactory previewFactory; private final FilePreviewFactory previewFactory;
private final CacheService cacheService; private final CacheService cacheService;
private final FileHandlerService fileHandlerService; private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService) { public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService, OtherFilePreviewImpl otherFilePreview) {
this.previewFactory = filePreviewFactory; this.previewFactory = filePreviewFactory;
this.fileHandlerService = fileHandlerService; this.fileHandlerService = fileHandlerService;
this.cacheService = cacheService; this.cacheService = cacheService;
this.otherFilePreview = otherFilePreview;
} }
@RequestMapping(value = "/onlinePreview") @RequestMapping(value = "/onlinePreview")
public String onlinePreview(String url, Model model, HttpServletRequest req) { public String onlinePreview(String url, Model model, HttpServletRequest req) {
String fileUrl = new String(Base64Utils.decodeFromString(url)); String fileUrl;
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(fileUrl,req); try {
fileUrl = new String(Base64.decodeBase64(url), StandardCharsets.UTF_8);
} catch (Exception ex) {
String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "url");
return otherFilePreview.notSupportedFile(model, errorMsg);
}
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(fileUrl, req);
model.addAttribute("file", fileAttribute);
FilePreview filePreview = previewFactory.get(fileAttribute); FilePreview filePreview = previewFactory.get(fileAttribute);
logger.info("预览文件url{}previewType{}", fileUrl, fileAttribute.getType()); logger.info("预览文件url{}previewType{}", fileUrl, fileAttribute.getType());
return filePreview.filePreviewHandle(fileUrl, model, fileAttribute); return filePreview.filePreviewHandle(fileUrl, model, fileAttribute);
} }
@RequestMapping(value = "/picturesPreview") @RequestMapping(value = "/picturesPreview")
public String picturesPreview(Model model, HttpServletRequest req) throws UnsupportedEncodingException { public String picturesPreview(String urls, Model model, HttpServletRequest req) throws UnsupportedEncodingException {
String urls = req.getParameter("urls"); String fileUrls;
String currentUrl = req.getParameter("currentUrl"); try {
logger.info("预览文件url{}urls{}", currentUrl, urls); fileUrls = new String(Base64.decodeBase64(urls));
// 路径转码 } catch (Exception ex) {
String decodedUrl = URLDecoder.decode(urls, "utf-8"); String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "urls");
String decodedCurrentUrl = URLDecoder.decode(currentUrl, "utf-8"); return otherFilePreview.notSupportedFile(model, errorMsg);
}
logger.info("预览文件url{}urls{}", fileUrls, urls);
// 抽取文件并返回文件列表 // 抽取文件并返回文件列表
String[] imgs = decodedUrl.split("\\|"); String[] images = fileUrls.split("\\|");
List<String> imgUrls = Arrays.asList(imgs); List<String> imgUrls = Arrays.asList(images);
model.addAttribute("imgUrls", imgUrls); model.addAttribute("imgUrls", imgUrls);
model.addAttribute("currentUrl",decodedCurrentUrl);
return "picture"; String currentUrl = req.getParameter("currentUrl");
if (StringUtils.hasText(currentUrl)) {
String decodedCurrentUrl = new String(Base64.decodeBase64(currentUrl));
model.addAttribute("currentUrl", decodedCurrentUrl);
} else {
model.addAttribute("currentUrl", imgUrls.get(0));
}
return PICTURE_FILE_PREVIEW_PAGE;
} }
/** /**
* 根据url获取文件内容 * 根据url获取文件内容
* 当pdfjs读取存在跨域问题的文件时将通过此接口读取 * 当pdfjs读取存在跨域问题的文件时将通过此接口读取
* *
* @param urlPath url * @param urlPath url
* @param response response * @param response response
*/ */
@RequestMapping(value = "/getCorsFile", method = RequestMethod.GET) @RequestMapping(value = "/getCorsFile", method = RequestMethod.GET)
public void getCorsFile(String urlPath, HttpServletResponse response) { public void getCorsFile(String urlPath, HttpServletResponse response) {
logger.info("下载跨域pdf文件url{}", urlPath); logger.info("下载跨域pdf文件url{}", urlPath);
try { try {
byte[] bytes = DownloadUtils.getBytesFromUrl(urlPath); URL url = WebUtils.normalizedURL(urlPath);
DownloadUtils.saveBytesToOutStream(bytes, response.getOutputStream()); byte[] bytes = NetUtil.downloadBytes(url.toString());
} catch (IOException e) { IOUtils.write(bytes, response.getOutputStream());
} catch (IOException | GalimatiasParseException e) {
logger.error("下载跨域pdf文件异常url{}", urlPath, e); logger.error("下载跨域pdf文件异常url{}", urlPath, e);
} }
} }
/** /**
* 通过api接口入队 * 通过api接口入队
*
* @param url 请编码后在入队 * @param url 请编码后在入队
*/ */
@RequestMapping("/addTask") @RequestMapping("/addTask")

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -5,15 +5,8 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0"> <meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0">
<link href="css/zTreeStyle.css" rel="stylesheet" type="text/css"> <link href="css/zTreeStyle.css" rel="stylesheet" type="text/css">
<#include "*/commonHeader.ftl">
<style type="text/css"> <style type="text/css">
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
}
body { body {
background-color: #404040; background-color: #404040;
} }
@@ -32,10 +25,7 @@
<div class="zTreeDemoBackground left"> <div class="zTreeDemoBackground left">
<ul id="treeDemo" class="ztree"></ul> <ul id="treeDemo" class="ztree"></ul>
</div> </div>
<script src="js/watermark.js" type="text/javascript"></script>
<script type="text/javascript" src="js/jquery-3.0.0.min.js"></script>
<script type="text/javascript" src="js/jquery.ztree.core.js"></script> <script type="text/javascript" src="js/jquery.ztree.core.js"></script>
<script type="text/javascript" src="js/base64.min.js" ></script>
<script type="text/javascript"> <script type="text/javascript">
const data = JSON.parse('${fileTree}'); const data = JSON.parse('${fileTree}');
@@ -87,25 +77,7 @@
/*初始化水印*/ /*初始化水印*/
window.onload = function() { window.onload = function() {
var watermarkTxt = '${watermarkTxt}'; initWaterMark();
if (watermarkTxt !== '') {
watermark.init({
watermark_txt: '${watermarkTxt}',
watermark_x: 0,
watermark_y: 0,
watermark_rows: 0,
watermark_cols: 0,
watermark_x_space: ${watermarkXSpace},
watermark_y_space: ${watermarkYSpace},
watermark_font: '${watermarkFont}',
watermark_fontsize: '${watermarkFontsize}',
watermark_color:'${watermarkColor}',
watermark_alpha: ${watermarkAlpha},
watermark_width: ${watermarkWidth},
watermark_height: ${watermarkHeight},
watermark_angle: ${watermarkAngle},
});
}
} }
/** /**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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