From 5ff1b4c9bd64f3e2a51a8a400a0f0ebe6241555f Mon Sep 17 00:00:00 2001 From: devbean Date: Tue, 26 Nov 2024 10:45:07 +0800 Subject: [PATCH 1/4] Add SSL ignore for restTemplate. --- .../java/cn/keking/utils/DownloadUtils.java | 15 ++++---- .../main/java/cn/keking/utils/SslUtils.java | 21 ++++++++++++ .../controller/OnlinePreviewController.java | 34 +++++++++---------- 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/server/src/main/java/cn/keking/utils/DownloadUtils.java b/server/src/main/java/cn/keking/utils/DownloadUtils.java index 5f8914bf..ded54a0e 100644 --- a/server/src/main/java/cn/keking/utils/DownloadUtils.java +++ b/server/src/main/java/cn/keking/utils/DownloadUtils.java @@ -6,9 +6,6 @@ import cn.keking.model.ReturnResponse; import com.fasterxml.jackson.databind.ObjectMapper; import io.mola.galimatias.GalimatiasParseException; import org.apache.commons.io.FileUtils; -import org.apache.http.client.HttpClient; -import org.apache.http.impl.client.DefaultRedirectStrategy; -import org.apache.http.impl.client.HttpClientBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; @@ -22,6 +19,9 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URL; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Map; import java.util.UUID; @@ -93,8 +93,7 @@ public class DownloadUtils { factory.setConnectionRequestTimeout(2000); //设置超时时间 factory.setConnectTimeout(10000); factory.setReadTimeout(72000); - HttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new DefaultRedirectStrategy()).build(); - factory.setHttpClient(httpClient); //加入重定向方法 + factory.setHttpClient(SslUtils.getIgnoreSslHttpClient()); //加入重定向方法 restTemplate.setRequestFactory(factory); RequestCallback requestCallback = request -> { request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL)); @@ -128,7 +127,11 @@ public class DownloadUtils { response.setContent(realPath); response.setMsg(fileName); return response; - } catch (IOException | GalimatiasParseException e) { + } catch (IOException + | GalimatiasParseException + | NoSuchAlgorithmException + | KeyStoreException + | KeyManagementException e) { logger.error("文件下载失败,url:{}", urlStr); response.setCode(1); response.setContent(null); diff --git a/server/src/main/java/cn/keking/utils/SslUtils.java b/server/src/main/java/cn/keking/utils/SslUtils.java index d42b88ab..b22675bc 100644 --- a/server/src/main/java/cn/keking/utils/SslUtils.java +++ b/server/src/main/java/cn/keking/utils/SslUtils.java @@ -1,6 +1,16 @@ package cn.keking.utils; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultRedirectStrategy; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContextBuilder; + import javax.net.ssl.*; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -39,4 +49,15 @@ public class SslUtils { HttpsURLConnection.setDefaultHostnameVerifier(hv); } + public static CloseableHttpClient getIgnoreSslHttpClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { + SSLContext sslContext = SSLContextBuilder.create() + .loadTrustMaterial((chain, authType) -> true) + .build(); + SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); + return HttpClients.custom() + .setSSLSocketFactory(socketFactory) + .setRedirectStrategy(new DefaultRedirectStrategy()) + .build(); + } + } diff --git a/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java b/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java index bd324d57..c0a40d3e 100644 --- a/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java +++ b/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java @@ -7,13 +7,11 @@ import cn.keking.service.FilePreviewFactory; import cn.keking.service.cache.CacheService; import cn.keking.service.impl.OtherFilePreviewImpl; import cn.keking.utils.KkFileUtils; +import cn.keking.utils.SslUtils; import cn.keking.utils.WebUtils; import com.fasterxml.jackson.databind.ObjectMapper; import fr.opensagres.xdocreport.core.io.IOUtils; import org.apache.commons.codec.binary.Base64; -import org.apache.http.client.HttpClient; -import org.apache.http.impl.client.DefaultRedirectStrategy; -import org.apache.http.impl.client.HttpClientBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; @@ -33,6 +31,9 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -53,7 +54,7 @@ public class OnlinePreviewController { private final FileHandlerService fileHandlerService; private final OtherFilePreviewImpl otherFilePreview; private static final RestTemplate restTemplate = new RestTemplate(); - private static final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); + private static final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); private static final ObjectMapper mapper = new ObjectMapper(); public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService, OtherFilePreviewImpl otherFilePreview) { @@ -63,7 +64,7 @@ public class OnlinePreviewController { this.otherFilePreview = otherFilePreview; } - @GetMapping( "/onlinePreview") + @GetMapping("/onlinePreview") public String onlinePreview(String url, Model model, HttpServletRequest req) { String fileUrl; @@ -77,14 +78,14 @@ public class OnlinePreviewController { model.addAttribute("file", fileAttribute); FilePreview filePreview = previewFactory.get(fileAttribute); logger.info("预览文件url:{},previewType:{}", fileUrl, fileAttribute.getType()); - fileUrl =WebUtils.urlEncoderencode(fileUrl); + fileUrl = WebUtils.urlEncoderencode(fileUrl); if (ObjectUtils.isEmpty(fileUrl)) { return otherFilePreview.notSupportedFile(model, "非法路径,不允许访问"); } return filePreview.filePreviewHandle(fileUrl, model, fileAttribute); //统一在这里处理 url } - @GetMapping( "/picturesPreview") + @GetMapping("/picturesPreview") public String picturesPreview(String urls, Model model, HttpServletRequest req) { String fileUrls; try { @@ -103,7 +104,7 @@ public class OnlinePreviewController { String currentUrl = req.getParameter("currentUrl"); if (StringUtils.hasText(currentUrl)) { String decodedCurrentUrl = new String(Base64.decodeBase64(currentUrl)); - decodedCurrentUrl = KkFileUtils.htmlEscape(decodedCurrentUrl); // 防止XSS攻击 + decodedCurrentUrl = KkFileUtils.htmlEscape(decodedCurrentUrl); // 防止XSS攻击 model.addAttribute("currentUrl", decodedCurrentUrl); } else { model.addAttribute("currentUrl", imgUrls.get(0)); @@ -119,13 +120,13 @@ public class OnlinePreviewController { * @param response response */ @GetMapping("/getCorsFile") - public void getCorsFile(String urlPath, HttpServletResponse response,FileAttribute fileAttribute) throws IOException { + public void getCorsFile(String urlPath, HttpServletResponse response, FileAttribute fileAttribute) throws IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { URL url; try { urlPath = WebUtils.decodeUrl(urlPath); url = WebUtils.normalizedURL(urlPath); } catch (Exception ex) { - logger.error(String.format(BASE64_DECODE_ERROR_MSG, urlPath),ex); + logger.error(String.format(BASE64_DECODE_ERROR_MSG, urlPath), ex); return; } assert urlPath != null; @@ -139,14 +140,13 @@ public class OnlinePreviewController { factory.setConnectionRequestTimeout(2000); factory.setConnectTimeout(10000); factory.setReadTimeout(72000); - HttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new DefaultRedirectStrategy()).build(); - factory.setHttpClient(httpClient); + factory.setHttpClient(SslUtils.getIgnoreSslHttpClient()); restTemplate.setRequestFactory(factory); RequestCallback requestCallback = request -> { request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL)); String proxyAuthorization = fileAttribute.getKkProxyAuthorization(); - if(StringUtils.hasText(proxyAuthorization)){ - Map proxyAuthorizationMap = mapper.readValue(proxyAuthorization, Map.class); + if (StringUtils.hasText(proxyAuthorization)) { + Map proxyAuthorizationMap = mapper.readValue(proxyAuthorization, Map.class); proxyAuthorizationMap.forEach((key, value) -> request.getHeaders().set(key, value)); } }; @@ -155,12 +155,12 @@ public class OnlinePreviewController { IOUtils.copy(fileResponse.getBody(), response.getOutputStream()); return null; }); - } catch (Exception e) { + } catch (Exception e) { System.out.println(e); } - }else{ + } else { try { - if(urlPath.contains(".svg")) { + if (urlPath.contains(".svg")) { response.setContentType("image/svg+xml"); } inputStream = (url).openStream(); -- Gitee From 9533586e745f1cd53b6fbee5891522e92f838af8 Mon Sep 17 00:00:00 2001 From: Cheng Liang <531964946@qq.com> Date: Thu, 11 Sep 2025 13:33:14 +0800 Subject: [PATCH 2/4] Add home.enabled parameter --- Dockerfile | 6 +- README.cn.md | 100 ++- docker/kkfileview-base/Dockerfile | 3 - pom.xml | 2 +- server/pom.xml | 2 +- server/src/main/bin/install.sh | 4 +- server/src/main/bin/startup.bat | 2 +- server/src/main/bin/startup.sh | 6 +- server/src/main/config/application.properties | 6 +- .../cn/keking/config/ConfigConstants.java | 15 + .../keking/config/ConfigRefreshComponent.java | 3 + .../java/cn/keking/utils/DownloadUtils.java | 3 + .../cn/keking/utils/LocalOfficeUtils.java | 3 + .../keking/web/controller/FileController.java | 140 ---- .../controller/OnlinePreviewController.java | 8 +- .../keking/web/filter/AttributeSetFilter.java | 1 + server/src/main/resources/banner.txt | 2 +- .../src/main/resources/web/commonHeader.ftl | 20 +- server/src/main/resources/web/main/index.ftl | 465 ++++++----- .../main/resources/web/main/integrated.ftl | 100 +-- server/src/main/resources/web/main/record.ftl | 773 ++++++++++-------- .../src/main/resources/web/main/sponsor.ftl | 88 +- 22 files changed, 903 insertions(+), 849 deletions(-) diff --git a/Dockerfile b/Dockerfile index ca8a9719..448f6100 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM keking/kkfileview-base:4.4.0 +FROM keking/kkfileview-base:4.5.0 ADD server/target/kkFileView-*.tar.gz /opt/ -ENV KKFILEVIEW_BIN_FOLDER=/opt/kkFileView-4.4.0-beta/bin -ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dspring.config.location=/opt/kkFileView-4.4.0-beta/config/application.properties","-jar","/opt/kkFileView-4.4.0-beta/bin/kkFileView-4.4.0-beta.jar"] +ENV KKFILEVIEW_BIN_FOLDER=/opt/kkFileView-4.5.0/bin +ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dspring.config.location=/opt/kkFileView-4.5.0/config/application.properties","-jar","/opt/kkFileView-4.5.0/bin/kkFileView-4.5.0.jar"] diff --git a/README.cn.md b/README.cn.md index ad4a2d25..9636c06b 100644 --- a/README.cn.md +++ b/README.cn.md @@ -53,80 +53,81 @@ #### 1. 文本预览 支持所有类型的文本文档预览, 由于文本文档类型过多,无法全部枚举,默认开启的类型如下 txt,html,htm,asp,jsp,xml,xbrl,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd 文本预览效果如下 -![文本预览效果如下](https://kkview.cn/img/preview/preview-text.png) +![文本预览效果如下](./doc/img/preview/preview-text.png) #### 2. 图片预览 支持jpg,jpeg,png,gif等图片预览(翻转,缩放,镜像),预览效果如下 -![图片预览](https://kkview.cn/img/preview/preview-image.png) +![图片预览](./doc/img/preview/preview-image.png) #### 3. word文档预览 支持doc,docx文档预览,word预览有两种模式:一种是每页word转为图片预览,另一种是整个word文档转成pdf,再预览pdf。两种模式的适用场景如下 * 图片预览:word文件大,前台加载整个pdf过慢 * pdf预览:内网访问,加载pdf快 图片预览模式预览效果如下 -![word文档预览1](https://kkview.cn/img/preview/preview-doc-image.png) +![word文档预览1](./doc/img/preview/preview-doc-image.png) pdf预览模式预览效果如下 -![word文档预览2](https://kkview.cn/img/preview/preview-doc-pdf.png) +![word文档预览2](./doc/img/preview/preview-doc-pdf.png) #### 4. ppt文档预览 支持ppt,pptx文档预览,和word文档一样,有两种预览模式 图片预览模式预览效果如下 -![ppt文档预览1](https://kkview.cn/img/preview/preview-ppt-image.png) +![ppt文档预览1](./doc/img/preview/preview-ppt-image.png) pdf预览模式预览效果如下 -![ppt文档预览2](https://kkview.cn/img/preview/preview-ppt-pdf.png) +![ppt文档预览2](./doc/img/preview/preview-ppt-pdf.png) #### 5. pdf文档预览 支持pdf文档预览,和word文档一样,有两种预览模式 图片预览模式预览效果如下 -![pdf文档预览1](https://kkview.cn/img/preview/preview-pdf-image.png) +![pdf文档预览1](./doc/img/preview/preview-pdf-image.png) pdf预览模式预览效果如下 -![pdf文档预览2](https://kkview.cn/img/preview/preview-pdf-pdf.png) +![pdf文档预览2](./doc/img/preview/preview-pdf-pdf.png) #### 6. excel文档预览 支持xls,xlsx文档预览,预览效果如下 -![excel文档预览](https://kkview.cn/img/preview/preview-xls.png) +![excel文档预览](./doc/img/preview/preview-xls.png) #### 7. 压缩文件预览 支持zip,rar,jar,tar,gzip等压缩包,预览效果如下 -![压缩文件预览1](https://kkview.cn/img/preview/preview-zip.png) +![压缩文件预览1](./doc/img/preview/preview-zip.png) 可点击压缩包中的文件名,直接预览文件,预览效果如下 -![压缩文件预览2](https://kkview.cn/img/preview/preview-zip-inner.png) +![压缩文件预览2](./doc/img/preview/preview-zip-inner.png) #### 8. 多媒体文件预览 理论上支持所有的视频、音频文件,由于无法枚举所有文件格式,默认开启的类型如下 mp3,wav,mp4,flv 视频预览效果如下 -![多媒体文件预览1](https://kkview.cn/img/preview/preview-video.png) +![多媒体文件预览1](./doc/img/preview/preview-video.png) 音频预览效果如下 -![多媒体文件预览2](https://kkview.cn/img/preview/preview-audio.png) +![多媒体文件预览2](./doc/img/preview/preview-audio.png) #### 9. CAD文档预览 支持CAD dwg文档预览,和word文档一样,有两种预览模式 图片预览模式预览效果如下 -![cad文档预览1](https://kkview.cn/img/preview/preview-cad-image.png) +![cad文档预览1](./doc/img/preview/preview-cad-image.png) pdf预览模式预览效果如下 -![cad文档预览2](https://kkview.cn/img/preview/preview-cad-pdf.png) -考虑说明篇幅原因,就不贴其他格式文件的预览效果了,感兴趣的可以参考下面的实例搭建下 +![cad文档预览2](./doc/img/preview/preview-cad-pdf.png) #### 10. Excel文件纯前端渲染效果 -![Excel文件纯前端渲染效果](https://kkview.cn/img/preview/preview-xlsx-web.png) +![Excel文件纯前端渲染效果](./doc/img/preview/preview-xlsx-web.png) #### 11. 流程图bpmn文件预览效果 -![流程图bpmn文件预览效果](https://kkview.cn/img/preview/preview-bpmn.png) +![流程图bpmn文件预览效果](./doc/img/preview/preview-bpmn.png) #### 12. 3D模型文件预览效果: -![3D模型文件预览效果](https://kkview.cn/img/preview/preview-3ds.png) +![3D模型文件预览效果](./doc/img/preview/preview-3ds.png) #### 13. dcm医疗数位影像文件预览效果: -![dcm医疗数位影像文件预览效果](https://kkview.cn/img/preview/preview-dcm.png) +![dcm医疗数位影像文件预览效果](./doc/img/preview/preview-dcm.png) #### 14. drawio流程图预览效果: -![dcdrawio流程图预览效果](https://kkview.cn/img/preview/preview-drawio.png) +![drawio流程图预览效果](./doc/img/preview/preview-drawio.png) + +考虑说明篇幅原因,就不贴其他格式文件的预览效果了,感兴趣的可以参考下面的实例搭建下 ### 快速开始 > 项目使用技术 @@ -148,7 +149,62 @@ pdf预览模式预览效果如下 ### 历史更新记录 -#### > 2023年07月05日,v4.3 版本发布 : +#### > 2025年09月10日,v4.5.0 版本发布: + +### 新增功能 +1. 增加开启首页功能参数,关闭后,首页预览将被禁用 +2. 增加开启首页文件上传参数,关闭后,首页上传文件功能将被禁用 + +#### > 2025年01月16日,v4.4.0 版本发布 : + +### 新增功能 +1. xlsx 新增支持打印功能 +2. 配置文件新增启用 GZIP 压缩 +3. CAD 格式新增支持转换成 SVG 和 TIF 格式,新增超时结束、线程管理 +4. 新增删除文件使用验证码校验 +5. 新增 xbrl 格式预览支持 +6. PDF 预览新增控制签名、绘图、插图控制、搜索定位页码、定义显示内容等功能 +7. 新增 CSV 格式前端解析支持 +8. 新增 ARM64 下的 Docker 镜像支持 +9. 新增 Office 预览支持转换超时属性设置功能 +10. 新增预览文件 host 黑名单机制 + +### 优化 +1. 优化 OFD 移动端预览 页面不自适应 +2. 更新 xlsx 前端解析组件,加速解析速度 +3. 升级 CAD 组件 +4. office 功能调整,支持批注、转换页码限制、生成水印等功能 +5. 升级 markdown 组件 +6. 升级 dcm 解析组件 +7. 升级 PDF.JS 解析组件 +8. 更换视频播放插件为 ckplayer +9. tif 解析更加智能化,支持被修改的图片格式 +10. 针对大小文本文件检测字符编码的正确率,处理并发隐患 +11. 重构下载文件的代码,新增通用的文件服务器认证访问的设计 +12. 更新 bootstrap 组件,并精简掉不需要的文件 +13. 更新 epub 版本,优化 epub 显示效果 +14. 解决定时清除缓存时,对于多媒体类型文件,只删除了磁盘缓存文件 +15. 自动检测已安装 Office 组件,增加 LibreOffice 7.5 & 7.6 版本默认路径 +16. 修改 drawio 默认为预览模式 +17. 新增 PDF 线程管理、超时管理、内存缓存管理,更新 PDF 解析组件版本 +18. 优化 Dockerfile,支持真正的跨平台构建镜像 + +### 修复 +1. 修复 forceUpdatedCache 属性设置,但本地缓存文件不更新的问题 +2. 修复 PDF 解密加密文件转换成功后后台报错的问题 +3. 修复 BPMN 不支持跨域的问题 +4. 修复压缩包二级反代特殊符号错误问题 +5. 修复视频跨域配置导致视频无法预览的问题 +6. 修复 TXT 文本类分页二次加载问题 +7. 修复 Drawio 缺少 Base64 组件的问题 +8. 修复 Markdown 被转义问题 +9. 修复 EPUB 跨域报错问题 +10. 修复 URL 特殊符号问题 +11. 修复压缩包穿越漏洞 +12. 修复压缩获取路径错误、图片合集路径错误、水印问题等 BUG +13. 修复前端解析 XLSX 包含 EMF 格式文件错误问题 + +#### > 2023年07月05日,v4.3.0 版本发布 : #### 新增功能: 1. 新增dcm等医疗数位影像预 diff --git a/docker/kkfileview-base/Dockerfile b/docker/kkfileview-base/Dockerfile index 6aeca8c7..8ebdbbec 100644 --- a/docker/kkfileview-base/Dockerfile +++ b/docker/kkfileview-base/Dockerfile @@ -10,11 +10,8 @@ RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.aliyun.com@g' /etc/apt/sources.li ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\ localedef -i zh_CN -c -f UTF-8 -A /usr/share/locale/locale.alias zh_CN.UTF-8 &&\ locale-gen zh_CN.UTF-8 &&\ -# 安装微软字体 apt-get install -y --no-install-recommends ttf-mscorefonts-installer &&\ -# 安装文泉驿字体 apt-get install -y --no-install-recommends ttf-wqy-microhei ttf-wqy-zenhei xfonts-wqy &&\ -# 清理临时文件 apt-get autoremove -y &&\ apt-get clean &&\ rm -rf /var/lib/apt/lists/* diff --git a/pom.xml b/pom.xml index 26e874ed..eed7c8bd 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ cn.keking kkFileView-parent - 4.4.0-beta + 4.5.0 1.8 diff --git a/server/pom.xml b/server/pom.xml index 64746215..0356bed1 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -6,7 +6,7 @@ kkFileView-parent cn.keking - 4.4.0-beta + 4.5.0 kkFileView diff --git a/server/src/main/bin/install.sh b/server/src/main/bin/install.sh index 96dd5d4d..6f27e0e1 100644 --- a/server/src/main/bin/install.sh +++ b/server/src/main/bin/install.sh @@ -8,7 +8,7 @@ install_redhat() { yum install -y libSM.x86_64 libXrender.x86_64 libXext.x86_64 yum groupinstall -y "X Window System" yum localinstall -y *.rpm - echo 'install finshed...' + echo 'install finished...' else echo 'download package error...' fi @@ -20,7 +20,7 @@ install_ubuntu() { if [ $? -eq 0 ];then apt-get install -y libxinerama1 libcairo2 libcups2 libx11-xcb1 dpkg -i *.deb - echo 'install finshed...' + echo 'install finished...' else echo 'download package error...' fi diff --git a/server/src/main/bin/startup.bat b/server/src/main/bin/startup.bat index 13a1452d..03ace87a 100644 --- a/server/src/main/bin/startup.bat +++ b/server/src/main/bin/startup.bat @@ -7,4 +7,4 @@ echo Please check log file in ../log/kkFileView.log for more information echo You can get help in our official home site: https://kkview.cn echo If you need further help, please join our kk opensource community: https://t.zsxq.com/09ZHSXbsQ echo If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers -java -Dspring.config.location=..\config\application.properties -jar kkFileView-4.4.0-beta.jar -> ..\log\kkFileView.log +java -Dspring.config.location=..\config\application.properties -jar kkFileView-4.5.0.jar -> ..\log\kkFileView.log diff --git a/server/src/main/bin/startup.sh b/server/src/main/bin/startup.sh index 1a7f129d..823c8253 100644 --- a/server/src/main/bin/startup.sh +++ b/server/src/main/bin/startup.sh @@ -9,7 +9,7 @@ # Description: v1.1:修改进程启动机制为pid形式。 ############################# # -DIR_HOME=("/opt/openoffice.org3" "/opt/libreoffice" "/opt/libreoffice6.1" "/opt/libreoffice7.0" "/opt/libreoffice7.1" "/opt/libreoffice7.2" "/opt/libreoffice7.3" "/opt/libreoffice7.4" "/opt/libreoffice7.5" "/opt/libreoffice7.6" "/opt/openoffice4" "/usr/lib/openoffice" "/usr/lib/libreoffice") +DIR_HOME=("/opt/openoffice.org3" "/opt/libreoffice" "/opt/libreoffice6.1" "/opt/libreoffice7.0" "/opt/libreoffice7.1" "/opt/libreoffice7.2" "/opt/libreoffice7.3" "/opt/libreoffice7.4" "/opt/libreoffice7.5" "/opt/libreoffice7.6" "/opt/libreoffice24.2" "/opt/libreoffice24.8" "/opt/libreoffice25.2" "/opt/openoffice4" "/usr/lib/openoffice" "/usr/lib/libreoffice") FLAG= OFFICE_HOME= KKFILEVIEW_BIN_FOLDER=$(cd "$(dirname "$0")" || exit 1 ;pwd) @@ -29,7 +29,7 @@ if [ -s "${PID_FILE}" ]; then else cd "$KKFILEVIEW_BIN_FOLDER" || exit 1 echo "Using KKFILEVIEW_BIN_FOLDER $KKFILEVIEW_BIN_FOLDER" - grep 'office\.home' ../config/application.properties | grep '!^#' + grep 'office\.home' ../config/application.properties | grep -v '^#' | grep -v 'default' if [ $? -eq 0 ]; then echo "Using customized office.home" else @@ -51,7 +51,7 @@ else ## 启动kkFileView echo "Starting kkFileView..." - nohup java -Dfile.encoding=UTF-8 -Dspring.config.location=../config/application.properties -jar kkFileView-4.4.0-beta.jar > ../log/kkFileView.log 2>&1 & + nohup java -Dfile.encoding=UTF-8 -Dspring.config.location=../config/application.properties -jar kkFileView-4.5.0.jar > ../log/kkFileView.log 2>&1 & echo "Please execute ./showlog.sh to check log for more information" echo "You can get help in our official home site: https://kkview.cn" echo "If you need further help, please join our kk opensource community: https://t.zsxq.com/09ZHSXbsQ" diff --git a/server/src/main/config/application.properties b/server/src/main/config/application.properties index c8801b46..ef4d6c99 100644 --- a/server/src/main/config/application.properties +++ b/server/src/main/config/application.properties @@ -3,7 +3,7 @@ server.port = ${KK_SERVER_PORT:8012} server.servlet.context-path= ${KK_CONTEXT_PATH:/} server.servlet.encoding.charset = utf-8 #启用GZIP压缩功能 -server.compression.enable= true +server.compression.enabled = true #允许压缩的响应缓冲区最小字节数,默认2048 server.compression.min-response-size = 2048 #压缩格式 @@ -161,7 +161,9 @@ watermark.angle = ${WATERMARK_ANGLE:10} #首页功能设置 -#是否禁用首页文件上传 +# 是否开启首页功能,默认关闭 +home.enabled = ${KK_HOME_ENABLED:false} +# 是否开启文件上传功能,默认开启 file.upload.disable = ${KK_FILE_UPLOAD_DISABLE:false} # 备案信息,默认为空 beian = ${KK_BEIAN:default} diff --git a/server/src/main/java/cn/keking/config/ConfigConstants.java b/server/src/main/java/cn/keking/config/ConfigConstants.java index 432ea2dc..36b7aa8e 100644 --- a/server/src/main/java/cn/keking/config/ConfigConstants.java +++ b/server/src/main/java/cn/keking/config/ConfigConstants.java @@ -63,6 +63,7 @@ public class ConfigConstants { private static Boolean officeDocumentOpenPasswords; private static String cadTimeout; private static int cadThread; + private static Boolean homeEnabled; private static String homePageNumber; private static String homePagination; private static String homePageSize; @@ -107,6 +108,7 @@ public class ConfigConstants { public static final String DEFAULT_OFFICE_EXPORTBOOKMARKS = "true"; public static final String DEFAULT_OFFICE_EXPORTNOTES = "true"; public static final String DEFAULT_OFFICE_EOCUMENTOPENPASSWORDS = "true"; + public static final String DEFAULT_HOME_ENABLED = "false"; public static final String DEFAULT_HOME_PAGENUMBER = "1"; public static final String DEFAULT_HOME_PAGINATION = "true"; public static final String DEFAULT_HOME_PAGSIZE = "15"; @@ -746,6 +748,19 @@ public class ConfigConstants { * 以下为首页显示 */ + public static Boolean getHomeEnabled() { + return homeEnabled; + } + + @Value("${home.enabled:true}") + public void setHomeEnabled(Boolean homeEnabled) { + setHomeEnabledValue(homeEnabled); + } + + public static void setHomeEnabledValue(Boolean enabled) { + ConfigConstants.homeEnabled = enabled; + } + public static String getBeian() { return beian; } diff --git a/server/src/main/java/cn/keking/config/ConfigRefreshComponent.java b/server/src/main/java/cn/keking/config/ConfigRefreshComponent.java index 3482c514..66c17050 100644 --- a/server/src/main/java/cn/keking/config/ConfigRefreshComponent.java +++ b/server/src/main/java/cn/keking/config/ConfigRefreshComponent.java @@ -74,6 +74,7 @@ public class ConfigRefreshComponent { boolean officeDocumentOpenPasswords; String cadTimeout; int cadThread; + boolean homeEnabled; String homePageNumber; String homePagination; String homePageSize; @@ -125,6 +126,7 @@ public class ConfigRefreshComponent { officeExportNotes = Boolean.parseBoolean(properties.getProperty("office.exportnotes", ConfigConstants.DEFAULT_OFFICE_EXPORTNOTES)); officeDocumentOpenPasswords = Boolean.parseBoolean(properties.getProperty("office.documentopenpasswords", ConfigConstants.DEFAULT_OFFICE_EOCUMENTOPENPASSWORDS)); cadTimeout = properties.getProperty("cad.timeout", ConfigConstants.DEFAULT_CAD_TIMEOUT); + homeEnabled = Boolean.parseBoolean(properties.getProperty("home.enabled", ConfigConstants.DEFAULT_HOME_ENABLED)); homePageNumber = properties.getProperty("home.pagenumber", ConfigConstants.DEFAULT_HOME_PAGENUMBER); homePagination = properties.getProperty("home.pagination", ConfigConstants.DEFAULT_HOME_PAGINATION); homePageSize = properties.getProperty("home.pagesize", ConfigConstants.DEFAULT_HOME_PAGSIZE); @@ -173,6 +175,7 @@ public class ConfigRefreshComponent { ConfigConstants.setDeleteCaptchaValue(deleteCaptcha); ConfigConstants.setCadTimeoutValue(cadTimeout); ConfigConstants.setCadThreadValue(cadThread); + ConfigConstants.setHomeEnabledValue(homeEnabled); ConfigConstants.setHomePageNumberValue(homePageNumber); ConfigConstants.setHomePaginationValue(homePagination); ConfigConstants.setHomePageSizeValue(homePageSize); diff --git a/server/src/main/java/cn/keking/utils/DownloadUtils.java b/server/src/main/java/cn/keking/utils/DownloadUtils.java index ded54a0e..ddcdc6bd 100644 --- a/server/src/main/java/cn/keking/utils/DownloadUtils.java +++ b/server/src/main/java/cn/keking/utils/DownloadUtils.java @@ -6,6 +6,9 @@ import cn.keking.model.ReturnResponse; import com.fasterxml.jackson.databind.ObjectMapper; import io.mola.galimatias.GalimatiasParseException; import org.apache.commons.io.FileUtils; +import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.DefaultRedirectStrategy; +import org.apache.http.impl.client.HttpClientBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; diff --git a/server/src/main/java/cn/keking/utils/LocalOfficeUtils.java b/server/src/main/java/cn/keking/utils/LocalOfficeUtils.java index e1a23da0..17bc1d46 100644 --- a/server/src/main/java/cn/keking/utils/LocalOfficeUtils.java +++ b/server/src/main/java/cn/keking/utils/LocalOfficeUtils.java @@ -86,6 +86,9 @@ public class LocalOfficeUtils { "/opt/libreoffice7.4", "/opt/libreoffice7.5", "/opt/libreoffice7.6", + "/opt/libreoffice24.2", + "/opt/libreoffice24.8", + "/opt/libreoffice25.2", "/usr/lib64/libreoffice", "/usr/lib/libreoffice", "/usr/local/lib64/libreoffice", diff --git a/server/src/main/java/cn/keking/web/controller/FileController.java b/server/src/main/java/cn/keking/web/controller/FileController.java index 0ea6a914..6743b555 100644 --- a/server/src/main/java/cn/keking/web/controller/FileController.java +++ b/server/src/main/java/cn/keking/web/controller/FileController.java @@ -53,77 +53,6 @@ public class FileController { private final String demoPath = demoDir + File.separator; public static final String BASE64_DECODE_ERROR_MSG = "Base64解码失败,请检查你的 %s 是否采用 Base64 + urlEncode 双重编码了!"; - @PostMapping("/fileUpload") - public ReturnResponse fileUpload(@RequestParam("file") MultipartFile file) { - ReturnResponse checkResult = this.fileUploadCheck(file); - if (checkResult.isFailure()) { - return checkResult; - } - File outFile = new File(fileDir + demoPath); - if (!outFile.exists() && !outFile.mkdirs()) { - logger.error("创建文件夹【{}】失败,请检查目录权限!", fileDir + demoPath); - } - String fileName = checkResult.getContent().toString(); - logger.info("上传文件:{}{}{}", fileDir, demoPath, fileName); - try (InputStream in = file.getInputStream(); OutputStream out = Files.newOutputStream(Paths.get(fileDir + demoPath + fileName))) { - StreamUtils.copy(in, out); - return ReturnResponse.success(null); - } catch (IOException e) { - logger.error("文件上传失败", e); - return ReturnResponse.failure(); - } - } - - @GetMapping("/deleteFile") - public ReturnResponse deleteFile(HttpServletRequest request, String fileName, String password) { - ReturnResponse checkResult = this.deleteFileCheck(request, fileName, password); - if (checkResult.isFailure()) { - return checkResult; - } - fileName = checkResult.getContent().toString(); - File file = new File(fileDir + demoPath + fileName); - logger.info("删除文件:{}", file.getAbsolutePath()); - if (file.exists() && !file.delete()) { - String msg = String.format("删除文件【%s】失败,请检查目录权限!", file.getPath()); - logger.error(msg); - return ReturnResponse.failure(msg); - } - WebUtils.removeSessionAttr(request, CAPTCHA_CODE); //删除缓存验证码 - return ReturnResponse.success(); - } - - /** - * 验证码方法 - */ - @RequestMapping("/deleteFile/captcha") - public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception { - if (!ConfigConstants.getDeleteCaptcha()) { - return; - } - - response.setContentType("image/jpeg"); - response.setHeader("Pragma", "no-cache"); - response.setHeader("Cache-Control", "no-cache"); - response.setDateHeader("Expires", -1); - String captchaCode = WebUtils.getSessionAttr(request, CAPTCHA_CODE); - long captchaGenerateTime = WebUtils.getLongSessionAttr(request, CAPTCHA_GENERATE_TIME); - long timeDifference = DateUtils.calculateCurrentTimeDifference(captchaGenerateTime); - - // 验证码为空,且生成验证码超过50秒,重新生成验证码 - if (timeDifference > 50 && ObjectUtils.isEmpty(captchaCode)) { - captchaCode = CaptchaUtil.generateCaptchaCode(); - // 更新验证码 - WebUtils.setSessionAttr(request, CAPTCHA_CODE, captchaCode); - WebUtils.setSessionAttr(request, CAPTCHA_GENERATE_TIME, DateUtils.getCurrentSecond()); - } else { - captchaCode = ObjectUtils.isEmpty(captchaCode) ? "wait" : captchaCode; - } - - ServletOutputStream outputStream = response.getOutputStream(); - ImageIO.write(CaptchaUtil.generateCaptchaPic(captchaCode), "jpeg", outputStream); - outputStream.close(); - } - @GetMapping("/listFiles") public List> getFiles() { List> list = new ArrayList<>(); @@ -140,70 +69,6 @@ public class FileController { return list; } - /** - * 上传文件前校验 - * - * @param file 文件 - * @return 校验结果 - */ - private ReturnResponse fileUploadCheck(MultipartFile file) { - if (ConfigConstants.getFileUploadDisable()) { - return ReturnResponse.failure("文件传接口已禁用"); - } - String fileName = WebUtils.getFileNameFromMultipartFile(file); - if (fileName.lastIndexOf(".") == -1) { - return ReturnResponse.failure("不允许上传的类型"); - } - if (!KkFileUtils.isAllowedUpload(fileName)) { - return ReturnResponse.failure("不允许上传的文件类型: " + fileName); - } - if (KkFileUtils.isIllegalFileName(fileName)) { - return ReturnResponse.failure("不允许上传的文件名: " + fileName); - } - // 判断是否存在同名文件 - if (existsFile(fileName)) { - return ReturnResponse.failure("存在同名文件,请先删除原有文件再次上传"); - } - return ReturnResponse.success(fileName); - } - - - /** - * 删除文件前校验 - * - * @param fileName 文件名 - * @return 校验结果 - */ - private ReturnResponse deleteFileCheck(HttpServletRequest request, String fileName, String password) { - if (ObjectUtils.isEmpty(fileName)) { - return ReturnResponse.failure("文件名为空,删除失败!"); - } - try { - fileName = WebUtils.decodeUrl(fileName); - } catch (Exception ex) { - String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, fileName); - return ReturnResponse.failure(errorMsg + "删除失败!"); - } - assert fileName != null; - if (fileName.contains("/")) { - fileName = fileName.substring(fileName.lastIndexOf("/") + 1); - } - if (KkFileUtils.isIllegalFileName(fileName)) { - return ReturnResponse.failure("非法文件名,删除失败!"); - } - if (ObjectUtils.isEmpty(password)) { - return ReturnResponse.failure("密码 or 验证码为空,删除失败!"); - } - - String expectedPassword = ConfigConstants.getDeleteCaptcha() ? WebUtils.getSessionAttr(request, CAPTCHA_CODE) : ConfigConstants.getPassword(); - - if (!password.equalsIgnoreCase(expectedPassword)) { - logger.error("删除文件【{}】失败,密码错误!", fileName); - return ReturnResponse.failure("删除文件失败,密码错误!"); - } - return ReturnResponse.success(fileName); - } - @GetMapping("/directory") public Object directory(String urls) { String fileUrl; @@ -219,9 +84,4 @@ public class FileController { } return RarUtils.getTree(fileUrl); } - - private boolean existsFile(String fileName) { - File file = new File(fileDir + demoPath + fileName); - return file.exists(); - } } diff --git a/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java b/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java index c0a40d3e..26566828 100644 --- a/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java +++ b/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java @@ -12,6 +12,9 @@ import cn.keking.utils.WebUtils; import com.fasterxml.jackson.databind.ObjectMapper; import fr.opensagres.xdocreport.core.io.IOUtils; import org.apache.commons.codec.binary.Base64; +import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.DefaultRedirectStrategy; +import org.apache.http.impl.client.HttpClientBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; @@ -120,7 +123,8 @@ public class OnlinePreviewController { * @param response response */ @GetMapping("/getCorsFile") - public void getCorsFile(String urlPath, HttpServletResponse response, FileAttribute fileAttribute) throws IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { + public void getCorsFile(String urlPath, HttpServletResponse response, FileAttribute fileAttribute) + throws IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { URL url; try { urlPath = WebUtils.decodeUrl(urlPath); @@ -146,7 +150,7 @@ public class OnlinePreviewController { request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL)); String proxyAuthorization = fileAttribute.getKkProxyAuthorization(); if (StringUtils.hasText(proxyAuthorization)) { - Map proxyAuthorizationMap = mapper.readValue(proxyAuthorization, Map.class); + Map proxyAuthorizationMap = mapper.readValue(proxyAuthorization, Map.class); proxyAuthorizationMap.forEach((key, value) -> request.getHeaders().set(key, value)); } }; diff --git a/server/src/main/java/cn/keking/web/filter/AttributeSetFilter.java b/server/src/main/java/cn/keking/web/filter/AttributeSetFilter.java index 5f208877..5eb47b95 100644 --- a/server/src/main/java/cn/keking/web/filter/AttributeSetFilter.java +++ b/server/src/main/java/cn/keking/web/filter/AttributeSetFilter.java @@ -43,6 +43,7 @@ public class AttributeSetFilter implements Filter { request.setAttribute("beian", ConfigConstants.getBeian()); request.setAttribute("size", ConfigConstants.maxSize()); request.setAttribute("deleteCaptcha", ConfigConstants.getDeleteCaptcha()); + request.setAttribute("homeEnabled", ConfigConstants.getHomeEnabled()); request.setAttribute("homePageNumber", ConfigConstants.getHomePageNumber()); request.setAttribute("homePagination", ConfigConstants.getHomePagination()); request.setAttribute("homePageSize", ConfigConstants.getHomePageSize()); diff --git a/server/src/main/resources/banner.txt b/server/src/main/resources/banner.txt index 043fb516..2e97e0bb 100644 --- a/server/src/main/resources/banner.txt +++ b/server/src/main/resources/banner.txt @@ -7,7 +7,7 @@ |_|\_\ |_|\_\ |_| |_| |_| \___| \/ |_| \___| \_/\_/ => Spring Boot :: ${spring-boot.version} - => kkFileView :: 4.4.0-beta + => kkFileView :: 4.5.0 => Home site :: https://kkview.cn => Github :: https://github.com/kekingcn/kkFileView => Gitee :: https://gitee.com/kekingcn/file-online-preview diff --git a/server/src/main/resources/web/commonHeader.ftl b/server/src/main/resources/web/commonHeader.ftl index 3932776b..473aa288 100644 --- a/server/src/main/resources/web/commonHeader.ftl +++ b/server/src/main/resources/web/commonHeader.ftl @@ -8,7 +8,19 @@ */ function initWaterMark() { let watermarkTxt = '${watermarkTxt}'; - if (watermarkTxt !== '') { + if (watermarkTxt === '') { + return; + } + let lastWidth = 0; + let lastHeight = 0; + const checkResize = () => { + const currentWidth = document.documentElement.scrollWidth; + const currentHeight = document.documentElement.scrollHeight; + // 检测尺寸是否变化 + if (currentWidth === lastWidth && currentHeight === lastHeight) { + return; + } + // 如果变化了, 重新初始化水印 watermark.init({ watermark_txt: '${watermarkTxt}', watermark_x: 0, @@ -25,7 +37,11 @@ watermark_height: ${watermarkHeight}, watermark_angle: ${watermarkAngle}, }); - } + // 更新存储的宽口大小 + lastWidth = currentWidth; + lastHeight = currentHeight; + }; + setInterval(checkResize, 1000); } diff --git a/server/src/main/resources/web/main/index.ftl b/server/src/main/resources/web/main/index.ftl index c4ebb5f7..237b0fb5 100644 --- a/server/src/main/resources/web/main/index.ftl +++ b/server/src/main/resources/web/main/index.ftl @@ -4,73 +4,80 @@ - kkFileView演示首页 + 首页 - - - - - - + <#if homeEnabled > + + + + + + + -<#-- 删除文件验证码弹窗 --> -<#if deleteCaptcha > -