Compare commits

..

1 Commits
io ... utils

Author SHA1 Message Date
chenkailing
38429b476e 精简util模块,ReturenResponse重构 2020-12-27 14:06:06 +08:00
37 changed files with 413 additions and 84025 deletions

2
.gitignore vendored
View File

@@ -17,6 +17,7 @@ target/
### NetBeans ### ### NetBeans ###
nbproject/private/ nbproject/private/
build/
nbbuild/ nbbuild/
dist/ dist/
nbdist/ nbdist/
@@ -28,3 +29,4 @@ 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-3.3.0/bin ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-2.2.1/bin
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"] 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"]

View File

@@ -109,26 +109,6 @@ 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>3.3.0</version> <version>2.2.1</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>3.3.0</version> <version>2.2.1</version>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -201,12 +201,6 @@
<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-3.3.0.jar -> ..\log\kkFileView.log java -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=..\config\application.properties -jar kkFileView-2.2.1.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-3.3.0.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-2.2.1.jar > ../log/kkFileView.log 2>&1 &

View File

@@ -1,7 +1,5 @@
[#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,7 @@
package cn.keking.config; package cn.keking.config;
import cn.keking.service.cache.CacheService; import cn.keking.service.cache.CacheService;
import cn.keking.utils.KkFileUtils; import cn.keking.utils.FileUtils;
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;
@@ -31,7 +31,7 @@ public class SchedulerCleanConfig {
public void clean() { public void clean() {
logger.info("Cache clean start"); logger.info("Cache clean start");
cacheService.cleanCache(); cacheService.cleanCache();
KkFileUtils.deleteDirectory(fileDir); FileUtils.deleteDirectory(fileDir);
logger.info("Cache clean end"); logger.info("Cache clean end");
} }
} }

View File

@@ -10,7 +10,6 @@ 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"),
@@ -20,13 +19,12 @@ 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[] SSIM_TEXT_TYPES = ConfigConstants.getSimText(); private static final String[] SIMTEXT_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<>();
@@ -40,7 +38,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 : SSIM_TEXT_TYPES) { for (String text : SIMTEXT_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) {
@@ -50,8 +48,6 @@ 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);
} }
private static FileType to(String fileType){ private static FileType to(String fileType){

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.KkFileUtils; import cn.keking.utils.FileUtils;
import cn.keking.web.filter.BaseUrlFilter; import cn.keking.web.filter.BaseUrlFilter;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@@ -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, KkFileUtils.getFileEncode(filePath)); ZipFile zipFile = new ZipFile(filePath, FileUtils.getFileEncode(filePath));
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries(); Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
// 排序 // 排序
entries = sortZipEntries(entries); entries = sortZipEntries(entries);
@@ -382,7 +382,7 @@ public class CompressFileReader {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
KkFileUtils.deleteFileByPath(filePath); FileUtils.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();
} }
KkFileUtils.deleteFileByPath(filePath); FileUtils.deleteFileByPath(filePath);
} }
} }
@@ -468,7 +468,7 @@ public class CompressFileReader {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
KkFileUtils.deleteFileByPath(filePath); FileUtils.deleteFileByPath(filePath);
} }
private void extractRarFile(String childName, FileHeader header, Archive archive) { private void extractRarFile(String childName, FileHeader header, Archive archive) {

View File

@@ -4,7 +4,7 @@ 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.FileUtils;
import cn.keking.utils.WebUtils; import cn.keking.utils.WebUtils;
import com.aspose.cad.Color; import com.aspose.cad.Color;
import com.aspose.cad.fileformats.cad.CadDrawTypeMode; import com.aspose.cad.fileformats.cad.CadDrawTypeMode;
@@ -263,7 +263,7 @@ public class FileHandlerService {
if (StringUtils.hasText(fullFileName)) { if (StringUtils.hasText(fullFileName)) {
fileName = fullFileName; fileName = fullFileName;
type = FileType.typeFromFileName(fullFileName); type = FileType.typeFromFileName(fullFileName);
suffix = KkFileUtils.suffixFromFileName(fullFileName); suffix = FileUtils.suffixFromFileName(fullFileName);
} else { } else {
fileName = WebUtils.getFileNameFromURL(url); fileName = WebUtils.getFileNameFromURL(url);
type = FileType.typeFromUrl(url); type = FileType.typeFromUrl(url);

View File

@@ -8,18 +8,5 @@ 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

@@ -25,11 +25,9 @@ public class CadFilePreviewImpl implements FilePreview {
private static final String FILE_DIR = ConfigConstants.getFileDir(); private static final String FILE_DIR = ConfigConstants.getFileDir();
private final FileHandlerService fileHandlerService; private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
public CadFilePreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) { public CadFilePreviewImpl(FileHandlerService fileHandlerService) {
this.fileHandlerService = fileHandlerService; this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
} }
@Override @Override
@@ -37,6 +35,7 @@ public class CadFilePreviewImpl implements FilePreview {
// 预览Type参数传了就取参数的没传取系统默认 // 预览Type参数传了就取参数的没传取系统默认
String officePreviewType = model.asMap().get("officePreviewType") == null ? ConfigConstants.getOfficePreviewType() : model.asMap().get("officePreviewType").toString(); String officePreviewType = model.asMap().get("officePreviewType") == null ? ConfigConstants.getOfficePreviewType() : model.asMap().get("officePreviewType").toString();
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;
@@ -45,13 +44,17 @@ public class CadFilePreviewImpl implements FilePreview {
String filePath; String filePath;
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null); ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null);
if (response.isFailure()) { if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg()); model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
} }
filePath = response.getContent(); filePath = response.getContent();
if (StringUtils.hasText(outFilePath)) { if (StringUtils.hasText(outFilePath)) {
boolean convertResult = fileHandlerService.cadToPdf(filePath, outFilePath); boolean convertResult = fileHandlerService.cadToPdf(filePath, outFilePath);
if (!convertResult) { if (!convertResult) {
return otherFilePreview.notSupportedFile(model, fileAttribute, "cad文件转换异常请联系管理员"); model.addAttribute("fileType", suffix);
model.addAttribute("msg", "cad文件转换异常请联系管理员");
return "fileNotSupported";
} }
if (ConfigConstants.isCacheEnabled()) { if (ConfigConstants.isCacheEnabled()) {
// 加入缓存 // 加入缓存
@@ -60,10 +63,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, fileHandlerService, OFFICE_PREVIEW_TYPE_IMAGE,otherFilePreview); return getPreviewType(model, fileAttribute, officePreviewType, baseUrl, pdfName, outFilePath, fileHandlerService, OFFICE_PREVIEW_TYPE_IMAGE);
} }
model.addAttribute("pdfUrl", pdfName); model.addAttribute("pdfUrl", pdfName);
return PDF_FILE_PREVIEW_PAGE; return "pdf";
} }

View File

@@ -20,12 +20,10 @@ 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, OtherFilePreviewImpl otherFilePreview) { public CompressFilePreviewImpl(FileHandlerService fileHandlerService, CompressFileReader compressFileReader) {
this.fileHandlerService = fileHandlerService; this.fileHandlerService = fileHandlerService;
this.compressFileReader = compressFileReader; this.compressFileReader = compressFileReader;
this.otherFilePreview = otherFilePreview;
} }
@Override @Override
@@ -37,7 +35,9 @@ public class CompressFilePreviewImpl implements FilePreview {
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 (response.isFailure()) { if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg()); model.addAttribute("fileType", suffix);
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,9 +55,11 @@ 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_FILE_PREVIEW_PAGE; return "compress";
} else { } else {
return otherFilePreview.notSupportedFile(model, fileAttribute, "压缩文件类型不受支持尝试在压缩的时候选择RAR4格式"); model.addAttribute("fileType", suffix);
model.addAttribute("msg", "压缩文件类型不受支持尝试在压缩的时候选择RAR4格式");
return "fileNotSupported";
} }
} }
} }

View File

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

View File

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

View File

@@ -19,11 +19,9 @@ 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, OtherFilePreviewImpl otherFilePreview) { public MediaFilePreviewImpl(FileHandlerService fileHandlerService) {
this.fileHandlerService = fileHandlerService; this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
} }
@Override @Override
@@ -31,8 +29,10 @@ 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 (response.isFailure()) { if (!response.isSuccess()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg()); model.addAttribute("fileType", fileAttribute.getSuffix());
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()));
} }
@@ -40,7 +40,11 @@ public class MediaFilePreviewImpl implements FilePreview {
model.addAttribute("mediaUrl", url); model.addAttribute("mediaUrl", url);
} }
model.addAttribute("mediaUrl", url); model.addAttribute("mediaUrl", url);
return MEDIA_FILE_PREVIEW_PAGE; String suffix = fileAttribute.getSuffix();
if ("flv".equalsIgnoreCase(suffix)) {
return "flv";
}
return "media";
} }

View File

@@ -27,12 +27,10 @@ public class OfficeFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService; private final FileHandlerService fileHandlerService;
private final OfficeToPdfService officeToPdfService; private final OfficeToPdfService officeToPdfService;
private final OtherFilePreviewImpl otherFilePreview;
public OfficeFilePreviewImpl(FileHandlerService fileHandlerService, OfficeToPdfService officeToPdfService, OtherFilePreviewImpl otherFilePreview) { public OfficeFilePreviewImpl(FileHandlerService fileHandlerService, OfficeToPdfService officeToPdfService) {
this.fileHandlerService = fileHandlerService; this.fileHandlerService = fileHandlerService;
this.officeToPdfService = officeToPdfService; this.officeToPdfService = officeToPdfService;
this.otherFilePreview = otherFilePreview;
} }
@Override @Override
@@ -40,8 +38,8 @@ public class OfficeFilePreviewImpl implements FilePreview {
// 预览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,7 +48,9 @@ public class OfficeFilePreviewImpl implements FilePreview {
String filePath; String filePath;
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null); ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null);
if (response.isFailure()) { if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg()); model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
} }
filePath = response.getContent(); filePath = response.getContent();
if (StringUtils.hasText(outFilePath)) { if (StringUtils.hasText(outFilePath)) {
@@ -66,23 +66,25 @@ 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, fileHandlerService, OFFICE_PREVIEW_TYPE_IMAGE, otherFilePreview); return getPreviewType(model, fileAttribute, officePreviewType, baseUrl, pdfName, outFilePath, fileHandlerService, OFFICE_PREVIEW_TYPE_IMAGE);
} }
model.addAttribute("pdfUrl", pdfName); model.addAttribute("pdfUrl", pdfName);
return isHtml ? EXEL_FILE_PREVIEW_PAGE : PDF_FILE_PREVIEW_PAGE; return isHtml ? "html" : "pdf";
} }
static String getPreviewType(Model model, FileAttribute fileAttribute, String officePreviewType, String baseUrl, String pdfName, String outFilePath, FileHandlerService fileHandlerService, String officePreviewTypeImage, OtherFilePreviewImpl otherFilePreview) { static String getPreviewType(Model model, FileAttribute fileAttribute, String officePreviewType, String baseUrl, String pdfName, String outFilePath, FileHandlerService fileHandlerService, String officePreviewTypeImage) {
List<String> imageUrls = fileHandlerService.pdf2jpg(outFilePath, pdfName, baseUrl); List<String> imageUrls = fileHandlerService.pdf2jpg(outFilePath, pdfName, baseUrl);
if (imageUrls == null || imageUrls.size() < 1) { if (imageUrls == null || imageUrls.size() < 1) {
return otherFilePreview.notSupportedFile(model, fileAttribute, "office转图片异常请联系管理员"); model.addAttribute("msg", "office转图片异常请联系管理员");
model.addAttribute("fileType",fileAttribute.getSuffix());
return "fileNotSupported";
} }
model.addAttribute("imgurls", imageUrls); model.addAttribute("imgurls", imageUrls);
model.addAttribute("currentUrl", imageUrls.get(0)); model.addAttribute("currentUrl", imageUrls.get(0));
if (officePreviewTypeImage.equals(officePreviewType)) { if (officePreviewTypeImage.equals(officePreviewType)) {
return OFFICE_PICTURE_FILE_PREVIEW_PAGE; return "officePicture";
} else { } else {
return PICTURE_FILE_PREVIEW_PAGE; return "picture";
} }
} }
} }

View File

@@ -11,41 +11,10 @@ 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) {
return this.notSupportedFile(model, fileAttribute, "系统还不支持该格式文件的在线预览"); model.addAttribute("fileType",fileAttribute.getSuffix());
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

@@ -20,17 +20,16 @@ import java.util.List;
public class PdfFilePreviewImpl implements FilePreview { public class PdfFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService; private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
private static final String FILE_DIR = ConfigConstants.getFileDir(); private static final String FILE_DIR = ConfigConstants.getFileDir();
public PdfFilePreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) { public PdfFilePreviewImpl(FileHandlerService fileHandlerService) {
this.fileHandlerService = fileHandlerService; 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) {
String fileName = fileAttribute.getName(); String suffix=fileAttribute.getSuffix();
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";
@@ -40,7 +39,9 @@ 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 (response.isFailure()) { if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg()); model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
} }
outFilePath = response.getContent(); outFilePath = response.getContent();
if (ConfigConstants.isCacheEnabled()) { if (ConfigConstants.isCacheEnabled()) {
@@ -50,14 +51,16 @@ public class PdfFilePreviewImpl implements FilePreview {
} }
List<String> imageUrls = fileHandlerService.pdf2jpg(outFilePath, pdfName, baseUrl); List<String> imageUrls = fileHandlerService.pdf2jpg(outFilePath, pdfName, baseUrl);
if (imageUrls == null || imageUrls.size() < 1) { if (imageUrls == null || imageUrls.size() < 1) {
return otherFilePreview.notSupportedFile(model, fileAttribute, "pdf转图片异常请联系管理员"); model.addAttribute("msg", "pdf转图片异常请联系管理员");
model.addAttribute("fileType",fileAttribute.getSuffix());
return "fileNotSupported";
} }
model.addAttribute("imgurls", imageUrls); model.addAttribute("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 OFFICE_PICTURE_FILE_PREVIEW_PAGE; return "officePicture";
} else { } else {
return PICTURE_FILE_PREVIEW_PAGE; return "picture";
} }
} else { } else {
// 不是http开头浏览器不能直接访问需下载到本地 // 不是http开头浏览器不能直接访问需下载到本地
@@ -65,7 +68,9 @@ 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, pdfName); ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, pdfName);
if (response.isFailure()) { if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg()); model.addAttribute("fileType", suffix);
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()) {
@@ -79,6 +84,6 @@ public class PdfFilePreviewImpl implements FilePreview {
model.addAttribute("pdfUrl", url); model.addAttribute("pdfUrl", url);
} }
} }
return PDF_FILE_PREVIEW_PAGE; return "pdf";
} }
} }

View File

@@ -19,11 +19,9 @@ 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, OtherFilePreviewImpl otherFilePreview) { public PictureFilePreviewImpl(FileHandlerService fileHandlerService) {
this.fileHandlerService = fileHandlerService; this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
} }
@Override @Override
@@ -39,18 +37,20 @@ public class PictureFilePreviewImpl implements FilePreview {
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 (response.isFailure()) { if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg()); model.addAttribute("fileType", fileAttribute.getSuffix());
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_FILE_PREVIEW_PAGE; return "picture";
} }
} }

View File

@@ -4,7 +4,6 @@ import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse; import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview; import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils; import cn.keking.utils.DownloadUtils;
import cn.keking.utils.KkFileUtils;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.ui.Model; import org.springframework.ui.Model;
@@ -12,6 +11,7 @@ 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,28 +20,31 @@ import java.io.IOException;
@Service @Service
public class SimTextFilePreviewImpl implements FilePreview { public class SimTextFilePreviewImpl implements FilePreview {
private final OtherFilePreviewImpl otherFilePreview; public static final String TEXT_TYPE = "textType";
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 (response.isFailure()) { if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg()); model.addAttribute("msg", response.getMsg());
model.addAttribute("fileType", fileAttribute.getSuffix());
return "fileNotSupported";
} }
try { try {
File originFile = new File(response.getContent()); File originFile = new File(response.getContent());
String charset = KkFileUtils.getFileEncode(originFile); String xmlString = FileUtils.readFileToString(originFile, StandardCharsets.UTF_8);
String xmlString = FileUtils.readFileToString(originFile, charset); model.addAttribute("textData", Base64Utils.encodeToString(xmlString.getBytes()));
model.addAttribute("textData", Base64Utils.encodeToString(xmlString.getBytes(charset)));
} catch (IOException e) { } catch (IOException e) {
return otherFilePreview.notSupportedFile(model, fileAttribute, e.getLocalizedMessage()); model.addAttribute("msg", e.getLocalizedMessage());
model.addAttribute("fileType", fileAttribute.getSuffix());
return "fileNotSupported";
} }
return TXT_FILE_PREVIEW_PAGE; if (!model.containsAttribute(TEXT_TYPE)) {
model.addAttribute(TEXT_TYPE, DEFAULT_TEXT_TYPE);
}
return "txt";
} }
} }

View File

@@ -5,6 +5,8 @@ import cn.keking.service.FilePreview;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import static com.sun.glass.ui.Clipboard.TEXT_TYPE;
/** /**
* @author kl (http://kailing.pub) * @author kl (http://kailing.pub)
* @since 2020/12/25 * @since 2020/12/25
@@ -18,9 +20,10 @@ 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) {
simTextFilePreview.filePreviewHandle(url, model, fileAttribute); model.addAttribute(TEXT_TYPE,"xml");
return XML_FILE_PREVIEW_PAGE; return simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
} }
} }

View File

@@ -2,19 +2,16 @@ 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 org.apache.commons.io.FileUtils;
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
*/ */
@@ -33,26 +30,42 @@ 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, "下载成功!!!", "");
String realPath = DownloadUtils.getRelFilePath(fileName, fileAttribute); UUID uuid = UUID.randomUUID();
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 = WebUtils.normalizedURL(urlStr); URL url = new URL(urlStr);
if (isHttpUrl(url)) { if (url.getProtocol() != null && (url.getProtocol().toLowerCase().startsWith("file") || url.getProtocol().toLowerCase().startsWith("http"))) {
File realFile = new File(realPath); byte[] bytes = getBytesFromUrl(urlStr);
FileUtils.copyURLToFile(url, realFile); OutputStream os = new FileOutputStream(realPath);
} else if (isFtpUrl(url)) { saveBytesToOutStream(bytes, os);
} 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 | GalimatiasParseException e) { } catch (IOException e) {
logger.error("文件下载失败url{}", urlStr, e); logger.error("文件下载失败url{}", urlStr, e);
response.setCode(1); response.setCode(1);
response.setContent(null); response.setContent(null);
@@ -65,27 +78,82 @@ public class DownloadUtils {
} }
} }
public static byte[] getBytesFromUrl(String urlStr) throws IOException {
/** InputStream is = getInputStreamFromUrl(urlStr);
* 获取真实文件绝对路径 if (is == null) {
* // urlStr = URLUtil.normalize(urlStr, true, true);
* @param fileName 文件名 is = getInputStreamFromUrl(urlStr);
* @return 文件路径 if (is == null) {
*/ logger.error("文件下载异常url{}", urlStr);
private static String getRelFilePath(String fileName, FileAttribute fileAttribute) { throw new IOException("文件下载异常url" + urlStr);
String type = fileAttribute.getSuffix(); }
if (null == fileName) {
UUID uuid = UUID.randomUUID();
fileName = uuid + "." + type;
} else { // 文件后缀不一致时以type为准(针对simText【将类txt文件转为txt】)
fileName = fileName.replace(fileName.substring(fileName.lastIndexOf(".") + 1), type);
} }
String realPath = fileDir + fileName; return getBytesFromStream(is);
File dirFile = new File(fileDir);
if (!dirFile.exists() && !dirFile.mkdirs()) {
logger.error("创建目录【{}】失败,可能是权限不够,请检查", fileDir);
}
return realPath;
} }
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 文件路径
*/
private static void convertTextPlainFileCharsetToUtf8(String filePath) throws IOException {
File sourceFile = new File(filePath);
if (sourceFile.exists() && sourceFile.isFile() && sourceFile.canRead()) {
String encoding = FileUtils.getFileEncode(filePath);
if (!FileUtils.DEFAULT_FILE_ENCODING.equals(encoding)) {
// 不为utf8,进行转码
File tmpUtf8File = new File(filePath + ".utf8");
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());
}
}
}
}
} }

View File

@@ -6,39 +6,19 @@ 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 KkFileUtils { public class FileUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(KkFileUtils.class); private static final Logger LOGGER = LoggerFactory.getLogger(FileUtils.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) {
@@ -59,26 +39,17 @@ public class KkFileUtils {
} }
/** /**
* 检测文件编码格式 * 判断文件编码格式
* *
* @param filePath 绝对路径 * @param filePath 绝对路径
* @return 编码格式 * @return 编码格式
*/ */
public static String getFileEncode(String filePath) { public static String getFileEncode(String filePath) {
return getFileEncode(new File(filePath)); File file = 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("检测到文件【{}】编码: {}", file.getAbsolutePath(), encoding); LOGGER.info("检测到文件【{}】编码: {}", filePath, encoding);
return encoding; return encoding;
} catch (IOException e) { } catch (IOException e) {
LOGGER.warn("文件编码获取失败采用默认的编码格式UTF-8", e); LOGGER.warn("文件编码获取失败采用默认的编码格式UTF-8", e);
@@ -88,7 +59,6 @@ public class KkFileUtils {
/** /**
* 通过文件名获取文件后缀 * 通过文件名获取文件后缀
*
* @param fileName 文件名称 * @param fileName 文件名称
* @return 文件后缀 * @return 文件后缀
*/ */
@@ -112,7 +82,8 @@ public class KkFileUtils {
/** /**
* 删除目录及目录下的文件 * 删除目录及目录下的文件
* *
* @param dir 要删除的目录的文件路径 * @param dir
* 要删除的目录的文件路径
* @return 目录删除成功返回true否则返回false * @return 目录删除成功返回true否则返回false
*/ */
public static boolean deleteDirectory(String dir) { public static boolean deleteDirectory(String dir) {
@@ -132,13 +103,13 @@ public class KkFileUtils {
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 = KkFileUtils.deleteFileByName(files[i].getAbsolutePath()); flag = FileUtils.deleteFileByName(files[i].getAbsolutePath());
if (!flag) { if (!flag) {
break; break;
} }
} else if (files[i].isDirectory()) { } else if (files[i].isDirectory()) {
// 删除子目录 // 删除子目录
flag = KkFileUtils.deleteDirectory(files[i].getAbsolutePath()); flag = FileUtils.deleteDirectory(files[i].getAbsolutePath());
if (!flag) { if (!flag) {
break; break;
} }

View File

@@ -1,9 +1,5 @@
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;
@@ -12,16 +8,6 @@ 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中的参数
* *
@@ -95,6 +81,6 @@ public class WebUtils {
public static String suffixFromUrl(String url) { public static String suffixFromUrl(String url) {
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length()); String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1); String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return KkFileUtils.suffixFromFileName(fileName); return FileUtils.suffixFromFileName(fileName);
} }
} }

View File

@@ -5,17 +5,14 @@ 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.service.impl.OtherFilePreviewImpl; import cn.keking.utils.DownloadUtils;
import cn.keking.service.FileHandlerService; import cn.keking.service.FileHandlerService;
import cn.keking.utils.WebUtils; import com.thoughtworks.xstream.core.util.Base64JavaUtilCodec;
import io.mola.galimatias.GalimatiasParseException;
import org.apache.commons.io.IOUtils;
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.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;
@@ -23,96 +20,73 @@ 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.URL; import java.net.URLDecoder;
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, OtherFilePreviewImpl otherFilePreview) { public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService) {
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; String fileUrl = new String(Base64Utils.decodeFromString(url));
try { FileAttribute fileAttribute = fileHandlerService.getFileAttribute(fileUrl,req);
fileUrl = new String(Base64Utils.decodeFromString(url));
} 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(String urls, Model model, HttpServletRequest req) throws UnsupportedEncodingException { public String picturesPreview(Model model, HttpServletRequest req) throws UnsupportedEncodingException {
String fileUrls; String urls = req.getParameter("urls");
try {
fileUrls = new String(Base64Utils.decodeFromString(urls));
} catch (Exception ex) {
String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "urls");
return otherFilePreview.notSupportedFile(model, errorMsg);
}
logger.info("预览文件url{}urls{}", fileUrls, urls);
// 抽取文件并返回文件列表
String[] images = fileUrls.split("\\|");
List<String> imgUrls = Arrays.asList(images);
model.addAttribute("imgUrls", imgUrls);
String currentUrl = req.getParameter("currentUrl"); String currentUrl = req.getParameter("currentUrl");
if (StringUtils.hasText(currentUrl)) { logger.info("预览文件url{}urls{}", currentUrl, urls);
String decodedCurrentUrl = new String(Base64Utils.decodeFromString(currentUrl)); // 路径转码
model.addAttribute("currentUrl", decodedCurrentUrl); String decodedUrl = URLDecoder.decode(urls, "utf-8");
} else { String decodedCurrentUrl = URLDecoder.decode(currentUrl, "utf-8");
model.addAttribute("currentUrl", imgUrls.get(0)); // 抽取文件并返回文件列表
} String[] imgs = decodedUrl.split("\\|");
return PICTURE_FILE_PREVIEW_PAGE; List<String> imgUrls = Arrays.asList(imgs);
model.addAttribute("imgUrls", imgUrls);
model.addAttribute("currentUrl",decodedCurrentUrl);
return "picture";
} }
/** /**
* 根据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 {
URL url = WebUtils.normalizedURL(urlPath); byte[] bytes = DownloadUtils.getBytesFromUrl(urlPath);
byte[] bytes = IOUtils.toByteArray(url); DownloadUtils.saveBytesToOutStream(bytes, response.getOutputStream());
IOUtils.write(bytes, response.getOutputStream()); } catch (IOException e) {
} 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

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

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

@@ -1,118 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0">
<title>普通文本预览</title>
</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>
<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/base64.min.js" type="text/javascript"></script>
<script>
/**
* 初始化
*/
window.onload = function () {
$("#markdown_btn").hide()
initWaterMark();
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},
});
}
}
/**
* 加载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>
<style>
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
}
</style>
</body>
</html>

View File

@@ -6,29 +6,32 @@
<title>普通文本预览</title> <title>普通文本预览</title>
</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">
<div class="panel panel-default"> <#if textType?? && textType == "markdown">
<div class="panel-heading"> <p>
<h4 class="panel-title"> <button id="markdown_btn" type="button" class="btn btn-primary">切换markdown</button>
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne"> <button id="text_btn" type="button" class="btn btn-primary">切换text</button>
${file.name} </p>
</a> <div id="markdown" style="padding: 18px;"></div>
</h4> <#elseif textType?? && textType == "xml" >
</div> <div id="xml" style="padding: 18px;"></div>
<div class="panel-body"> <#else>
<div id="text"></div> <div id="text"></div>
</div> </#if>
</div>
</div> </div>
<link rel="stylesheet" href="css/xmlTreeViewer.css"/>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.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-3.0.0.min.js" type="text/javascript"></script>
<script src="js/jquery.form.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="bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<script src="js/watermark.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 src="js/base64.min.js" type="text/javascript"></script>
<script> <script>
@@ -36,8 +39,12 @@
* 初始化 * 初始化
*/ */
window.onload = function () { window.onload = function () {
$("#markdown_btn").hide()
initWaterMark(); initWaterMark();
fetchData();
loadText(); loadText();
loadXmlData()
loadMarkdown();
} }
/** /**
@@ -65,16 +72,56 @@
} }
} }
/**
* 获取文本数据
*/
function fetchData() {
window.textData = Base64.decode($("#textData").val())
window.textPreData = "<pre>" + window.textData + "</pre>";
}
/** /**
*加载普通文本 *加载普通文本
*/ */
function loadText() { function loadText() {
var textData = Base64.decode($("#textData").val()) $("#text").html(window.textPreData);
var textPreData = "<pre style='background-color: #FFFFFF;border:none'>" + textData + "</pre>";
$("#text").html(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> <style>
* { * {
@@ -87,6 +134,13 @@
width: 100%; width: 100%;
} }
#markdown, #xml {
height: 97%;
max-height: 97%;
border: 1px solid #ece7e7;
overflow-y: scroll;
width: 100%;
}
</style> </style>
</body> </body>

View File

@@ -1,97 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0">
<title>普通文本预览</title>
</head>
<body>
<input hidden id="textData" value="${textData}"/>
<div class="container">
<div class="panel panel-default">
<div 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>
<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>
/**
* 初始化
*/
window.onload = function () {
initWaterMark();
loadXmlData()
}
/**
* 初始化水印
*/
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},
});
}
}
/**
* 加载xml数据
*/
function loadXmlData() {
var textData = Base64.decode($("#textData").val())
var xmlNode = xmlTreeViewer.parseXML(textData);
var retNode = xmlTreeViewer.getXMLViewerNode(xmlNode.xml);
$("#xml").html(retNode);
}
</script>
<style>
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
}
</style>
</body>
</html>