forked from kk-open/kkFileView
Compare commits
37 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
3e08deb50e | |
|
|
42cf6b2955 | |
|
|
31c7b2dfb8 | |
|
|
d2a8ca2cdd | |
|
|
16fca7ec61 | |
|
|
39d8d38ee9 | |
|
|
99982f07f7 | |
|
|
6382453327 | |
|
|
4ebee74f70 | |
|
|
54750f4e06 | |
|
|
1225362c90 | |
|
|
fe5ad49008 | |
|
|
586561ec5a | |
|
|
cad2d58f44 | |
|
|
4f86a56f59 | |
|
|
c86ca0d8cf | |
|
|
ec633cc0e1 | |
|
|
fa2a2b4342 | |
|
|
2b456c9934 | |
|
|
7965d52f29 | |
|
|
ad322c2e10 | |
|
|
da5dac73ab | |
|
|
b5e711bf87 | |
|
|
535d2baf0b | |
|
|
294dcb1994 | |
|
|
c54a5e9f1a | |
|
|
3571f2b502 | |
|
|
6f8416365f | |
|
|
778a381b5b | |
|
|
770769ead5 | |
|
|
8d49abb797 | |
|
|
d0568c53dc | |
|
|
276ef9d704 | |
|
|
dd75f718aa | |
|
|
0ece6ccb51 | |
|
|
bbd7530f77 | |
|
|
2cfd692171 |
131
README.cn.md
131
README.cn.md
|
|
@ -42,9 +42,7 @@
|
|||
地址:[https://file.kkview.cn](https://file.kkview.cn)
|
||||
|
||||
### 项目文档(Project documentation)
|
||||
1. 详细wiki文档:https://gitee.com/kekingcn/file-online-preview/wikis/pages
|
||||
1. 中文文档:https://gitee.com/kekingcn/file-online-preview/blob/master/README.md
|
||||
1. English document:https://gitee.com/kekingcn/file-online-preview/blob/master/README.en.md
|
||||
1. 详细使用文档:https://kkview.cn/zh-cn/docs/home.html
|
||||
|
||||
### 联系我们,加入组织
|
||||
> 我们会用心回答解决大家在项目使用中的问题,也请大家在提问前至少 Google 或 baidu 过,珍爱生命远离无效的交流沟通
|
||||
|
|
@ -110,12 +108,33 @@ pdf预览模式预览效果如下
|
|||

|
||||
考虑说明篇幅原因,就不贴其他格式文件的预览效果了,感兴趣的可以参考下面的实例搭建下
|
||||
|
||||
#### 10. Excel文件纯前端渲染效果
|
||||
|
||||

|
||||
|
||||
#### 11. 流程图bpmn文件预览效果
|
||||
|
||||

|
||||
|
||||
#### 12. 3D模型文件预览效果:
|
||||
|
||||

|
||||
|
||||
#### 13. dcm医疗数位影像文件预览效果:
|
||||
|
||||

|
||||
|
||||
#### 14. drawio流程图预览效果:
|
||||
|
||||

|
||||
|
||||
### 快速开始
|
||||
> 项目使用技术
|
||||
- spring boot: [spring boot开发参考指南](http://www.kailing.pub/PdfReader/web/viewer.html?file=springboot)
|
||||
- freemarker
|
||||
- redisson
|
||||
- jodconverter
|
||||
|
||||
> 依赖外部环境
|
||||
- redis (可选,默认不用)
|
||||
- OpenOffice 或者 LibreOffice( Windows 下已内置,Linux 脚本启动模式会自动安装,Mac OS 下需要手动安装)
|
||||
|
|
@ -129,7 +148,82 @@ pdf预览模式预览效果如下
|
|||
|
||||
### 历史更新记录
|
||||
|
||||
> 2022年12月14日,v4.1.0 版本发布 :
|
||||
#### > 2023年07月05日,v4.3 版本发布 :
|
||||
|
||||
#### 新增功能:
|
||||
1. 新增dcm等医疗数位影像预
|
||||
2. 新增drawio绘图预览
|
||||
3. 新增开启缓存的情况下重新生成的命令 &forceUpdatedCache=true
|
||||
4. 新增dwg CAD文件预览
|
||||
5. 新增PDF文件支持密码功能
|
||||
6. 新增PDF文件生成图片的dpi自定义配置
|
||||
7. 新增删除转换后OFFICE、CAD、TIFF、压缩包源文件配置 默认开启 节约磁盘空间
|
||||
8. 新增前端解析xlsx方法
|
||||
9. 新增pages,eps, iges , igs, dwt, dng, ifc, dwfx, stl, cf2, plt等格式支持
|
||||
|
||||
#### 优化:
|
||||
1. 调整生成的PDF文件 文件名称添加文件后缀 防止生成同名文件
|
||||
2. 调整SQL文件预览方式
|
||||
3. 优化OFD预览兼容性
|
||||
4. 美化TXT文本 分页框的显示
|
||||
5. 升级Linux、Docker版内置office为LibreOffice-7.5.3版本
|
||||
6. 升级Windows版内置office为LibreOffice-7.5.3 Portable版本
|
||||
7. 其他功能优化
|
||||
|
||||
#### 修复:
|
||||
1. 修复反代情况下压缩包获取路径错误
|
||||
2. 修复预览图片的url中如果包含&会导致.click报错
|
||||
3. 修复OFD预览部分已知问题
|
||||
4. 修复预览压缩包时,如果点击的是文件目录(树节点),页面会报错
|
||||
5. 其他已知问题修复
|
||||
|
||||
#### > 2023年04月18日,v4.2.1 版本发布 :
|
||||
|
||||
#### 更新日志:
|
||||
|
||||
1. 修复 dwg 文件预览报空指针的 bug
|
||||
|
||||
#### > 2023年04月13日,v4.2.0 版本发布 :
|
||||
|
||||
#### 新增功能:
|
||||
1. 新增 SVG 格式文件预览支持
|
||||
2. 新增加密的 Office 文件预览支持
|
||||
3. 新增加密的 zip、rar 等压缩包文件预览支持
|
||||
4. 新增 xmind 软件模型文件预览支持
|
||||
5. 新增 bpmn 工作流模型文件预览支持
|
||||
6. 新增 eml 邮件文件预览支持
|
||||
7. 新增 epub 电子书文件预览支持
|
||||
8. 新增 dotm,ett,xlt,xltm,wpt,dot,xlam,xla,dotx 等格式的办公文档预览支持
|
||||
9. 新增 obj, 3ds, stl, ply, gltf, glb, off, 3dm, fbx, dae, wrl, 3mf, ifc, brep, step, iges, fcstd, bim 等 3D 模型文件预览支持
|
||||
10. 新增可配置限制高风险文件上传的功能,比如 exe 文件
|
||||
11. 新增可配置站点的备案信息
|
||||
12. 新增演示站点删除文件需要密码的功能
|
||||
|
||||
#### 优化:
|
||||
1. 文本文档预览加入缓存
|
||||
2. 美化 404、500 报错页
|
||||
3. 优化发票等 ofd 文件预览的印证渲染兼容性
|
||||
4. 移除 office-plugin 模块, 使用新版 jodconverter组件
|
||||
5. 优化 Excel 文件的预览效果
|
||||
6. 优化 CAD 文件的预览效果
|
||||
7. 更新 xstream 、junrar、pdfbox 等依赖的版本
|
||||
8. 更新 TIF 文件转换 PDF 的插件,添加转换缓存
|
||||
9. 优化演示页 UI 部署
|
||||
10. 压缩包文件预览支持目录
|
||||
|
||||
#### 修复:
|
||||
1. 修复部分接口 XSS 问题
|
||||
2. 修复控制台打印的演示地址不跟着 content-path 配置走的问题
|
||||
3. 修复 ofd 文件预览跨域问题
|
||||
4. 修复内部自签证书 https 协议 url 文件无法下载的问题
|
||||
5. 修复特殊符号的文件无法删除的问题
|
||||
6. 修复 PDF 转图片,内存无法回收导致的 OOM
|
||||
7. 修复 xlsx7.4 以上版本文件预览乱码的问题
|
||||
8. 修复 TrustHostFilter 未拦截跨域接口的问题,这是一个安全问题,有使用到 TrustHost 功能的务必升级
|
||||
9. 修复压缩包文件预览在 Linux 系统下文件名乱码的问题
|
||||
10. 修复 ofd 文件预览页码只能显示10页的问题
|
||||
|
||||
#### > 2022年12月14日,v4.1.0 版本发布 :
|
||||
|
||||
1. 全新首页视觉 @wsd7747
|
||||
2. tif图片预览兼容多页tif的pdf转换、jpg转换,以及jpg在线多页预览功能 @zhangzhen1979
|
||||
|
|
@ -142,7 +236,7 @@ pdf预览模式预览效果如下
|
|||
|
||||
感谢 @yl-yue @wsd7747 @zhangzhen1979 @tomhusky @shenghuadun @kischn.sun 的代码贡献
|
||||
|
||||
> 2021年7月6日,v4.0.0 版本发布 :
|
||||
#### > 2021年7月6日,v4.0.0 版本发布 :
|
||||
|
||||
1. 底层集成OpenOffice替换为LibreOffice,Office文件兼容性增强,预览效果提升
|
||||
2. 修复压缩文件目录穿越漏洞
|
||||
|
|
@ -153,7 +247,7 @@ pdf预览模式预览效果如下
|
|||
7. 优化Windows环境下,查找Office组件逻辑(内置的LibreOffice优先)
|
||||
8. 优化启动Office进程改同步执行
|
||||
|
||||
> 2021年6月17日,v3.6.0 版本发布 :
|
||||
#### > 2021年6月17日,v3.6.0 版本发布 :
|
||||
|
||||
ofd 类型文件支持版本,本次版本重要功能均由社区开发贡献,感谢 @gaoxingzaq、@zhangxiaoxiao9527 的代码贡献
|
||||
1. 新增 ofd 类型文件预览支持,ofd 是国产的类似 pdf 格式的文件
|
||||
|
|
@ -161,7 +255,7 @@ ofd 类型文件支持版本,本次版本重要功能均由社区开发贡献
|
|||
3. 美化了 ppt、pptx 类型文件预览效果,比之前版本好看太多
|
||||
4. 更新了 pdfbox、xstream、common-io 等依赖的版本
|
||||
|
||||
> 2021年1月28日 :
|
||||
#### > 2021年1月28日 :
|
||||
|
||||
2020农历年最后一个版本发布,主要包含了部分 UI 改进,和解决了 QQ 群友、 Issue 里反馈的 Bug 修复,最最重要的是发个新版,过个好年
|
||||
|
||||
|
|
@ -184,7 +278,7 @@ ofd 类型文件支持版本,本次版本重要功能均由社区开发贡献
|
|||
17. escaping of dangerous characters to prevent reflected xss
|
||||
18. 修复重复编码导致文档转图片预览失败的问题&编码规范
|
||||
|
||||
> 2020年12月27日 :
|
||||
#### > 2020年12月27日 :
|
||||
|
||||
2020年年终大版本更新,架构全面设计,代码全面重构,代码质量全面提升,二次开发更便捷,欢迎拉源码品鉴,提issue、pr共同建设
|
||||
|
||||
|
|
@ -204,7 +298,7 @@ ofd 类型文件支持版本,本次版本重要功能均由社区开发贡献
|
|||
14. 修复压缩包里文件再次预览失败的bug
|
||||
15. 修复图片预览的bug
|
||||
|
||||
> 2020年05月20日 :
|
||||
#### > 2020年05月20日 :
|
||||
1. 新增支持全局水印,并支持通过参数动态改变水印内容
|
||||
2. 新增支持CAD文件预览
|
||||
3. 新增base.url配置,支持使用nginx反向代理和使用context-path
|
||||
|
|
@ -222,35 +316,35 @@ ofd 类型文件支持版本,本次版本重要功能均由社区开发贡献
|
|||
15. 官网建设:[https://kkview.cn](https://kkview.cn/)
|
||||
16. 官方Docker镜像仓库建设:[https://hub.docker.com/r/keking/kkfileview](https://hub.docker.com/r/keking/kkfileview)
|
||||
|
||||
> 2019年06月18日 :
|
||||
#### > 2019年06月18日 :
|
||||
1. 支持自动清理缓存及预览文件
|
||||
2. 支持http/https下载流url文件预览
|
||||
3. 支持FTP url文件预览
|
||||
4. 加入Docker构建
|
||||
|
||||
> 2019年04月08日 :
|
||||
#### > 2019年04月08日 :
|
||||
1. 缓存及队列实现抽象,提供JDK和REDIS两种实现(REDIS成为可选依赖)
|
||||
2. 打包方式提供zip和tar.gz包,并提供一键启动脚本
|
||||
|
||||
> 2018年01月19日 :
|
||||
#### > 2018年01月19日 :
|
||||
|
||||
1. 大文件入队提前处理
|
||||
1. 新增addTask文件转换入队接口
|
||||
1. 采用redis队列,支持kkFIleView接口和异构系统入队两种方式
|
||||
|
||||
> 2018年01月17日 :
|
||||
#### > 2018年01月17日 :
|
||||
|
||||
1. 优化项目结构,抽象文件预览接口,更方便的加入更多的文件类型预览支持,方便二次开发
|
||||
1. 新增英文文档说明(@幻幻Fate,@汝辉)贡献
|
||||
1. 新增图片预览文件支持类型
|
||||
1. 修复压缩包内轮播图片总是从第一张开始的问题
|
||||
|
||||
> 2018年01月12日 :
|
||||
#### > 2018年01月12日 :
|
||||
|
||||
1. 新增多图片同时预览
|
||||
1. 支持压缩包内图片轮番预览
|
||||
|
||||
> 2018年01月02日 :
|
||||
#### > 2018年01月02日 :
|
||||
|
||||
1. 修复txt等文本编码问题导致预览乱码
|
||||
1. 修复项目模块依赖引入不到的问题
|
||||
|
|
@ -265,14 +359,15 @@ xmind 引用于 [ xmind-embed-viewer](https://github.com/xmindltd/xmind-embed-v
|
|||
epub 引用于 [ epub.js](https://github.com/futurepress/epub.js) 开源协议 BSD许可证
|
||||
压缩包 引用于 [sevenzipjbinding](https://github.com/borisbrodski/sevenzipjbinding )开源协议LGPL
|
||||
3D 引用于 [Online3DViewer](https://github.com/kovacsv/Online3DViewer )开源协议MIT
|
||||
drawio 引用于 [drawio](https://github.com/jgraph/drawio )开源协议 Apache-2.0
|
||||
bpmn流程图 引用于 [bpmn-js](https://github.com/bpmn-io/bpmn-js ) 自定义协议 保留水印 具体自行查看
|
||||
dcm医疗数位影像 引用于 [dcmjs](https://github.com/dcmjs-org/dcmjs )开源协议MIT
|
||||
|
||||
### 使用登记
|
||||
如果这个项目解决了你的实际问题,可在 https://gitee.com/kekingcn/file-online-preview/issues/IGSBV
|
||||
登记下,如果节省了你的三方预览服务费用,也愿意支持下的话,可点击下方【捐助】请作者喝杯咖啡,也是非常感谢
|
||||
|
||||
### Stars 趋势图
|
||||
#### Gitee
|
||||
[](https://whnb.wang/kekingcn/file-online-preview?e=86400)
|
||||
### Stars
|
||||
|
||||
#### GitHub
|
||||
|
||||
|
|
|
|||
3
pom.xml
3
pom.xml
|
|
@ -32,8 +32,9 @@
|
|||
<ffmpeg.version>4.2.1-1.5.2</ffmpeg.version>
|
||||
<itextpdf.version>5.5.13.3</itextpdf.version>
|
||||
<httpclient.version>3.1</httpclient.version>
|
||||
<aspose-cad.version>23.1</aspose-cad.version>
|
||||
<aspose-cad.version>23.3</aspose-cad.version>
|
||||
<bcprov-jdk15on.version>1.70</bcprov-jdk15on.version>
|
||||
<juniversalchardet.version>1.0.3</juniversalchardet.version>
|
||||
|
||||
<commons-cli.version>1.2</commons-cli.version>
|
||||
<commons-net.version>3.6</commons-net.version>
|
||||
|
|
|
|||
|
|
@ -122,6 +122,14 @@
|
|||
<artifactId>redisson</artifactId>
|
||||
<version>${redisson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 编码检测-JUniversalCharDet-->
|
||||
<dependency>
|
||||
<groupId>com.googlecode.juniversalchardet</groupId>
|
||||
<artifactId>juniversalchardet</artifactId>
|
||||
<version>${juniversalchardet.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 解压(rar)-->
|
||||
<dependency>
|
||||
<groupId>com.github.junrar</groupId>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,13 @@
|
|||
server.port = ${KK_SERVER_PORT:8012}
|
||||
server.servlet.context-path= ${KK_CONTEXT_PATH:/}
|
||||
server.servlet.encoding.charset = utf-8
|
||||
#文件上传限制前端
|
||||
#启用GZIP压缩功能
|
||||
server.compression.enable= true
|
||||
#允许压缩的响应缓冲区最小字节数,默认2048
|
||||
server.compression.min-response-size = 2048
|
||||
#压缩格式
|
||||
server.compression.mime-types=application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain,font/woff,application/font-woff,font/eot,image/svg+xml,image/x-icon
|
||||
# 文件上传限制前端
|
||||
spring.servlet.multipart.max-file-size=500MB
|
||||
#文件上传限制
|
||||
spring.servlet.multipart.max-request-size=500MB
|
||||
|
|
@ -124,6 +130,8 @@ tif.preview.type = ${KK_TIF_PREVIEW_TYPE:tif}
|
|||
beian = ${KK_BEIAN:default}
|
||||
#禁止上传类型
|
||||
prohibit = ${KK_PROHIBIT:exe,dll,dat}
|
||||
#启用验证码删除文件 默认关闭
|
||||
delete.captcha= ${KK_DELETE_CAPTCHA:false}
|
||||
#删除密码
|
||||
delete.password = ${KK_DELETE_PASSWORD:123456}
|
||||
#删除 转换后OFFICE、CAD、TIFF、压缩包源文件 默认开启 节约磁盘空间
|
||||
|
|
@ -131,4 +139,26 @@ delete.source.file = ${KK_DELETE_SOURCE_FILE:true}
|
|||
#配置PDF文件生成图片的像素大小,dpi 越高,图片质量越清晰,同时也会消耗更多的计算资源。
|
||||
pdf2jpg.dpi = ${KK_PDF2JPG_DPI:144}
|
||||
#xlsx格式前端解析
|
||||
office.type.web = ${KK_OFFICE_TYPE_WEB:web}
|
||||
office.type.web = ${KK_OFFICE_TYPE_WEB:web}
|
||||
#Cad类型图片浏览模式:tif(利用前端js插件浏览);svg(转换为svg显示);pdf(转换为pdf后显示,便于打印)
|
||||
cad.preview.type = ${KK_CAD_PREVIEW_TYPE:svg}
|
||||
#Cad转换超时设置
|
||||
cad.timeout =${KK_CAD_TIMEOUT:90}
|
||||
#Cad转换线程设置
|
||||
cad.thread =${KK_CAD_THREAD:5}
|
||||
|
||||
#OFFICE转换模块设置
|
||||
#生成限制 默认不限制 使用方法 (1-5)
|
||||
office.pagerange = ${KK_OFFICE_PAGERANGE:false}
|
||||
#生成水印 默认不启用 使用方法 (kkFileView)
|
||||
office.watermark = ${KK_OFFICE_WATERMARK:false}
|
||||
#OFFICE JPEG图片压缩
|
||||
office.quality = ${KK_OFFICE_QUALITY:80}
|
||||
#图像分辨率限制
|
||||
office.maximageresolution = ${KK_OFFICE_MAXIMAGERESOLUTION:150}
|
||||
#导出书签
|
||||
office.exportbookmarks = ${KK_OFFICE_EXPORTBOOKMARKS:true}
|
||||
#批注作为PDF的注释
|
||||
office.exportnotes = ${KK_OFFICE_EXPORTNOTES:true}
|
||||
#加密文档 生成的PDF文档 添加密码(密码为加密文档的密码)
|
||||
office.documentopenpasswords = ${KK_OFFICE_DOCUMENTOPENPASSWORD:true}
|
||||
|
|
|
|||
|
|
@ -48,10 +48,21 @@ public class ConfigConstants {
|
|||
private static String password;
|
||||
private static int pdf2JpgDpi;
|
||||
private static String officeTypeWeb;
|
||||
private static String cadPreviewType;
|
||||
private static Boolean deleteSourceFile;
|
||||
private static Boolean deleteCaptcha;
|
||||
private static String officePageRange;
|
||||
private static String officeWatermark;
|
||||
private static String officeQuality;
|
||||
private static String officeMaxImageResolution;
|
||||
private static Boolean officeExportBookmarks;
|
||||
private static Boolean officeExportNotes;
|
||||
private static Boolean officeDocumentOpenPasswords;
|
||||
private static String cadTimeout;
|
||||
private static int cadThread;
|
||||
|
||||
public static final String DEFAULT_CACHE_ENABLED = "true";
|
||||
public static final String DEFAULT_TXT_TYPE = "txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd";
|
||||
public static final String DEFAULT_TXT_TYPE = "txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd,xbrl";
|
||||
public static final String DEFAULT_MEDIA_TYPE = "mp3,wav,mp4,flv";
|
||||
public static final String DEFAULT_OFFICE_PREVIEW_TYPE = "image";
|
||||
public static final String DEFAULT_OFFICE_PREVIEW_SWITCH_DISABLED = "false";
|
||||
|
|
@ -69,6 +80,7 @@ public class ConfigConstants {
|
|||
public static final String DEFAULT_PDF_BOOKMARK_DISABLE = "true";
|
||||
public static final String DEFAULT_FILE_UPLOAD_DISABLE = "false";
|
||||
public static final String DEFAULT_TIF_PREVIEW_TYPE = "tif";
|
||||
public static final String DEFAULT_CAD_PREVIEW_TYPE = "pdf";
|
||||
public static final String DEFAULT_BEIAN = "无";
|
||||
public static final String DEFAULT_SIZE = "500MB";
|
||||
public static final String DEFAULT_PROHIBIT = "exe,dll";
|
||||
|
|
@ -76,6 +88,19 @@ public class ConfigConstants {
|
|||
public static final String DEFAULT_PDF2_JPG_DPI = "105";
|
||||
public static final String DEFAULT_OFFICE_TYPE_WEB = "web";
|
||||
public static final String DEFAULT_DELETE_SOURCE_FILE = "true";
|
||||
public static final String DEFAULT_DELETE_CAPTCHA = "false";
|
||||
|
||||
|
||||
public static final String DEFAULT_CAD_TIMEOUT = "90";
|
||||
public static final String DEFAULT_CAD_THREAD = "5";
|
||||
|
||||
public static final String DEFAULT_OFFICE_PAQERANQE = "false";
|
||||
public static final String DEFAULT_OFFICE_WATERMARK = "false";
|
||||
public static final String DEFAULT_OFFICE_QUALITY = "80";
|
||||
public static final String DEFAULT_OFFICE_MAXIMAQERESOLUTION = "150";
|
||||
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 Boolean isCacheEnabled() {
|
||||
return cacheEnabled;
|
||||
|
|
@ -94,7 +119,7 @@ public class ConfigConstants {
|
|||
return simTexts;
|
||||
}
|
||||
|
||||
@Value("${simText:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd}")
|
||||
@Value("${simText:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd,xbrl}")
|
||||
public void setSimText(String simText) {
|
||||
String[] simTextArr = simText.split(",");
|
||||
setSimTextValue(simTextArr);
|
||||
|
|
@ -449,4 +474,143 @@ public class ConfigConstants {
|
|||
ConfigConstants.deleteSourceFile = deleteSourceFile;
|
||||
}
|
||||
|
||||
public static Boolean getDeleteCaptcha() {
|
||||
return deleteCaptcha;
|
||||
}
|
||||
|
||||
@Value("${delete.captcha:false}")
|
||||
public void setDeleteCaptcha(Boolean deleteCaptcha) {
|
||||
setDeleteCaptchaValue(deleteCaptcha);
|
||||
}
|
||||
|
||||
public static void setDeleteCaptchaValue(Boolean deleteCaptcha) {
|
||||
ConfigConstants.deleteCaptcha = deleteCaptcha;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以下为cad转换模块设置
|
||||
*/
|
||||
|
||||
public static String getCadPreviewType() {
|
||||
return cadPreviewType;
|
||||
}
|
||||
|
||||
@Value("${cad.preview.type:svg}")
|
||||
public void setCadPreviewType(String cadPreviewType) {
|
||||
setCadPreviewTypeValue(cadPreviewType);
|
||||
}
|
||||
|
||||
public static void setCadPreviewTypeValue(String cadPreviewType) {
|
||||
ConfigConstants.cadPreviewType = cadPreviewType;
|
||||
}
|
||||
|
||||
|
||||
public static String getCadTimeout() {
|
||||
return cadTimeout;
|
||||
}
|
||||
|
||||
@Value("${cad.timeout:90}")
|
||||
public void setCadTimeout(String cadTimeout) {
|
||||
setCadTimeoutValue(cadTimeout);
|
||||
}
|
||||
|
||||
public static void setCadTimeoutValue(String cadTimeout) {
|
||||
ConfigConstants.cadTimeout = cadTimeout;
|
||||
}
|
||||
|
||||
|
||||
public static int getCadThread() {
|
||||
return cadThread;
|
||||
}
|
||||
|
||||
@Value("${cad.thread:5}")
|
||||
public void setCadThread(int cadThread) {
|
||||
setCadThreadValue(cadThread);
|
||||
}
|
||||
|
||||
public static void setCadThreadValue(int cadThread) {
|
||||
ConfigConstants.cadThread = cadThread;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以下为OFFICE转换模块设置
|
||||
*/
|
||||
|
||||
public static String getOfficePageRange() {
|
||||
return officePageRange;
|
||||
}
|
||||
@Value("${office.pagerange:false}")
|
||||
public void setOfficePageRange(String officePageRange) {
|
||||
setOfficePageRangeValue(officePageRange);
|
||||
}
|
||||
public static void setOfficePageRangeValue(String officePageRange) {
|
||||
ConfigConstants.officePageRange = officePageRange;
|
||||
}
|
||||
|
||||
public static String getOfficeWatermark() {
|
||||
return officeWatermark;
|
||||
}
|
||||
@Value("${office.watermark:false}")
|
||||
public void setOfficeWatermark(String officeWatermark) {
|
||||
setOfficeWatermarkValue(officeWatermark);
|
||||
}
|
||||
public static void setOfficeWatermarkValue(String officeWatermark) {
|
||||
ConfigConstants.officeWatermark = officeWatermark;
|
||||
}
|
||||
|
||||
public static String getOfficeQuality() {
|
||||
return officeQuality;
|
||||
}
|
||||
@Value("${office.quality:80}")
|
||||
public void setOfficeQuality(String officeQuality) {
|
||||
setOfficeQualityValue(officeQuality);
|
||||
}
|
||||
public static void setOfficeQualityValue(String officeQuality) {
|
||||
ConfigConstants.officeQuality = officeQuality;
|
||||
}
|
||||
|
||||
public static String getOfficeMaxImageResolution() {
|
||||
return officeMaxImageResolution;
|
||||
}
|
||||
@Value("${office.maximageresolution:150}")
|
||||
public void setOfficeMaxImageResolution(String officeMaxImageResolution) {
|
||||
setOfficeMaxImageResolutionValue(officeMaxImageResolution);
|
||||
}
|
||||
public static void setOfficeMaxImageResolutionValue(String officeMaxImageResolution) {
|
||||
ConfigConstants.officeMaxImageResolution = officeMaxImageResolution;
|
||||
}
|
||||
|
||||
public static Boolean getOfficeExportBookmarks() {
|
||||
return officeExportBookmarks;
|
||||
}
|
||||
@Value("${office.exportbookmarks:true}")
|
||||
public void setOfficeExportBookmarks(Boolean officeExportBookmarks) {
|
||||
setOfficeExportBookmarksValue(officeExportBookmarks);
|
||||
}
|
||||
public static void setOfficeExportBookmarksValue(Boolean officeExportBookmarks) {
|
||||
ConfigConstants.officeExportBookmarks = officeExportBookmarks;
|
||||
}
|
||||
|
||||
public static Boolean getOfficeExportNotes() {
|
||||
return officeExportNotes;
|
||||
}
|
||||
@Value("${office.exportnotes:true}")
|
||||
public void setExportNotes(Boolean officeExportNotes) {
|
||||
setOfficeExportNotesValue(officeExportNotes);
|
||||
}
|
||||
public static void setOfficeExportNotesValue(Boolean officeExportNotes) {
|
||||
ConfigConstants.officeExportNotes = officeExportNotes;
|
||||
}
|
||||
|
||||
public static Boolean getOfficeDocumentOpenPasswords() {
|
||||
return officeDocumentOpenPasswords;
|
||||
}
|
||||
@Value("${office.documentopenpasswords:true}")
|
||||
public void setDocumentOpenPasswords(Boolean officeDocumentOpenPasswords) {
|
||||
setOfficeDocumentOpenPasswordsValue(officeDocumentOpenPasswords);
|
||||
}
|
||||
public static void setOfficeDocumentOpenPasswordsValue(Boolean officeDocumentOpenPasswords) {
|
||||
ConfigConstants.officeDocumentOpenPasswords = officeDocumentOpenPasswords;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,18 @@ public class ConfigRefreshComponent {
|
|||
String password;
|
||||
int pdf2JpgDpi;
|
||||
String officeTypeWeb;
|
||||
String cadPreviewType;
|
||||
boolean deleteSourceFile;
|
||||
boolean deleteCaptcha;
|
||||
String officPageRange;
|
||||
String officWatermark;
|
||||
String officQuality;
|
||||
String officMaxImageResolution;
|
||||
boolean officExportBookmarks;
|
||||
boolean officeExportNotes;
|
||||
boolean officeDocumentOpenPasswords;
|
||||
String cadTimeout;
|
||||
int cadThread;
|
||||
while (true) {
|
||||
FileReader fileReader = new FileReader(configFilePath);
|
||||
BufferedReader bufferedReader = new BufferedReader(fileReader);
|
||||
|
|
@ -85,6 +96,7 @@ public class ConfigRefreshComponent {
|
|||
pdfBookmarkDisable = properties.getProperty("pdf.bookmark.disable", ConfigConstants.DEFAULT_PDF_BOOKMARK_DISABLE);
|
||||
fileUploadDisable = Boolean.parseBoolean(properties.getProperty("file.upload.disable", ConfigConstants.DEFAULT_FILE_UPLOAD_DISABLE));
|
||||
tifPreviewType = properties.getProperty("tif.preview.type", ConfigConstants.DEFAULT_TIF_PREVIEW_TYPE);
|
||||
cadPreviewType = properties.getProperty("cad.preview.type", ConfigConstants.DEFAULT_CAD_PREVIEW_TYPE);
|
||||
size = properties.getProperty("spring.servlet.multipart.max-file-size", ConfigConstants.DEFAULT_SIZE);
|
||||
beian = properties.getProperty("beian", ConfigConstants.DEFAULT_BEIAN);
|
||||
prohibit = properties.getProperty("prohibit", ConfigConstants.DEFAULT_PROHIBIT);
|
||||
|
|
@ -92,6 +104,16 @@ public class ConfigRefreshComponent {
|
|||
pdf2JpgDpi = Integer.parseInt(properties.getProperty("pdf2jpg.dpi", ConfigConstants.DEFAULT_PDF2_JPG_DPI));
|
||||
officeTypeWeb = properties.getProperty("office.type.web", ConfigConstants.DEFAULT_OFFICE_TYPE_WEB);
|
||||
deleteSourceFile = Boolean.parseBoolean(properties.getProperty("delete.source.file", ConfigConstants.DEFAULT_DELETE_SOURCE_FILE));
|
||||
deleteCaptcha = Boolean.parseBoolean(properties.getProperty("delete.captcha", ConfigConstants.DEFAULT_DELETE_CAPTCHA));
|
||||
officPageRange = properties.getProperty("office.pagerange", ConfigConstants.DEFAULT_OFFICE_PAQERANQE);
|
||||
officWatermark = properties.getProperty("office.watermark", ConfigConstants.DEFAULT_OFFICE_WATERMARK);
|
||||
officQuality = properties.getProperty("office.quality", ConfigConstants.DEFAULT_OFFICE_QUALITY);
|
||||
officMaxImageResolution = properties.getProperty("office.maximageresolution", ConfigConstants.DEFAULT_OFFICE_MAXIMAQERESOLUTION);
|
||||
officExportBookmarks = Boolean.parseBoolean(properties.getProperty("office.exportbookmarks", ConfigConstants.DEFAULT_OFFICE_EXPORTBOOKMARKS));
|
||||
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);
|
||||
cadThread = Integer.parseInt(properties.getProperty("cad.thread", ConfigConstants.DEFAULT_CAD_THREAD));
|
||||
prohibitArray = prohibit.split(",");
|
||||
|
||||
ConfigConstants.setCacheEnabledValueValue(cacheEnabled);
|
||||
|
|
@ -111,13 +133,24 @@ public class ConfigRefreshComponent {
|
|||
ConfigConstants.setPdfBookmarkDisableValue(pdfBookmarkDisable);
|
||||
ConfigConstants.setFileUploadDisableValue(fileUploadDisable);
|
||||
ConfigConstants.setTifPreviewTypeValue(tifPreviewType);
|
||||
ConfigConstants.setCadPreviewTypeValue(cadPreviewType);
|
||||
ConfigConstants.setBeianValue(beian);
|
||||
ConfigConstants.setSizeValue(size);
|
||||
ConfigConstants.setProhibitValue(prohibitArray);
|
||||
ConfigConstants.setPasswordValue(password);
|
||||
ConfigConstants.setPdf2JpgDpiValue(pdf2JpgDpi);
|
||||
ConfigConstants.setOfficeTypeWebValue(officeTypeWeb);
|
||||
ConfigConstants.setOfficePageRangeValue(officPageRange);
|
||||
ConfigConstants.setOfficeWatermarkValue(officWatermark);
|
||||
ConfigConstants.setOfficeQualityValue(officQuality);
|
||||
ConfigConstants.setOfficeMaxImageResolutionValue(officMaxImageResolution);
|
||||
ConfigConstants.setOfficeExportBookmarksValue(officExportBookmarks);
|
||||
ConfigConstants.setOfficeExportNotesValue(officeExportNotes);
|
||||
ConfigConstants.setOfficeDocumentOpenPasswordsValue(officeDocumentOpenPasswords);
|
||||
ConfigConstants.setDeleteSourceFileValue(deleteSourceFile);
|
||||
ConfigConstants.setDeleteCaptchaValue(deleteCaptcha);
|
||||
ConfigConstants.setCadTimeoutValue(cadTimeout);
|
||||
ConfigConstants.setCadThreadValue(cadThread);
|
||||
setWatermarkConfig(properties);
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ public enum FileType {
|
|||
private static final String[] EPUB_TYPES = {"epub"};
|
||||
private static final String[] DCM_TYPES = {"dcm"};
|
||||
private static final String[] DRAWIO_TYPES = {"drawio"};
|
||||
private static final String[] XML_TYPES = {"xml","xbrl"};
|
||||
private static final String[] TIFF_TYPES = {"tif", "tiff"};
|
||||
private static final String[] OFD_TYPES = {"ofd"};
|
||||
private static final String[] SVG_TYPES = {"svg"};
|
||||
|
|
@ -105,8 +106,10 @@ public enum FileType {
|
|||
for (String drawio : DRAWIO_TYPES) {
|
||||
FILE_TYPE_MAPPER.put(drawio, FileType.DRAWIO);
|
||||
}
|
||||
for (String xml : XML_TYPES) {
|
||||
FILE_TYPE_MAPPER.put(xml, FileType.XML);
|
||||
}
|
||||
FILE_TYPE_MAPPER.put("md", FileType.MARKDOWN);
|
||||
FILE_TYPE_MAPPER.put("xml", FileType.XML);
|
||||
FILE_TYPE_MAPPER.put("pdf", FileType.PDF);
|
||||
FILE_TYPE_MAPPER.put("flv", FileType.FLV);
|
||||
FILE_TYPE_MAPPER.put("bpmn", FileType.BPMN);
|
||||
|
|
|
|||
|
|
@ -9,12 +9,13 @@ import cn.keking.utils.EncodingDetects;
|
|||
import cn.keking.utils.KkFileUtils;
|
||||
import cn.keking.utils.WebUtils;
|
||||
import cn.keking.web.filter.BaseUrlFilter;
|
||||
import com.aspose.cad.CodePages;
|
||||
import com.aspose.cad.Color;
|
||||
import com.aspose.cad.Image;
|
||||
import com.aspose.cad.LoadOptions;
|
||||
import com.aspose.cad.*;
|
||||
import com.aspose.cad.fileformats.tiff.enums.TiffExpectedFormat;
|
||||
import com.aspose.cad.imageoptions.CadRasterizationOptions;
|
||||
import com.aspose.cad.imageoptions.PdfOptions;
|
||||
import com.aspose.cad.imageoptions.SvgOptions;
|
||||
import com.aspose.cad.imageoptions.TiffOptions;
|
||||
import com.aspose.cad.internal.Exceptions.TimeoutException;
|
||||
import com.itextpdf.text.pdf.PdfReader;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
|
|
@ -38,6 +39,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
|
|
@ -52,7 +54,6 @@ public class FileHandlerService {
|
|||
private final Logger logger = LoggerFactory.getLogger(FileHandlerService.class);
|
||||
private final String fileDir = ConfigConstants.getFileDir();
|
||||
private final CacheService cacheService;
|
||||
|
||||
@Value("${server.tomcat.uri-encoding:UTF-8}")
|
||||
private String uriEncoding;
|
||||
|
||||
|
|
@ -143,6 +144,10 @@ public class FileHandlerService {
|
|||
cacheService.putImgCache(fileKey, imgs);
|
||||
}
|
||||
|
||||
/**
|
||||
cad定义线程池
|
||||
*/
|
||||
private static final ExecutorService pool = Executors.newFixedThreadPool(ConfigConstants.getCadThread());
|
||||
/**
|
||||
* 对转换后的文件进行操作(改变编码方式)
|
||||
*
|
||||
|
|
@ -259,7 +264,7 @@ public class FileHandlerService {
|
|||
imageUrls.add(imageUrl);
|
||||
}
|
||||
try {
|
||||
pdfReader = new PdfReader(pdfFilePath); //读取PDF文件
|
||||
pdfReader = new PdfReader(pdfFilePath,filePassword.getBytes()); //读取PDF文件
|
||||
} catch (Exception e) { //获取异常方法 判断是否有加密字符串
|
||||
Throwable[] throwableArray = ExceptionUtils.getThrowables(e);
|
||||
for (Throwable throwable : throwableArray) {
|
||||
|
|
@ -297,34 +302,79 @@ public class FileHandlerService {
|
|||
* @param outputFilePath pdf输出文件路径
|
||||
* @return 转换是否成功
|
||||
*/
|
||||
public String cadToPdf(String inputFilePath, String outputFilePath) throws Exception {
|
||||
File outputFile = new File(outputFilePath);
|
||||
LoadOptions opts = new LoadOptions();
|
||||
opts.setSpecifiedEncoding(CodePages.SimpChinese);
|
||||
com.aspose.cad.Image cadImage = Image.load(inputFilePath, opts);
|
||||
CadRasterizationOptions cadRasterizationOptions = new CadRasterizationOptions();
|
||||
cadRasterizationOptions.setBackgroundColor(Color.getWhite());
|
||||
cadRasterizationOptions.setPageWidth(1400);
|
||||
cadRasterizationOptions.setPageHeight(650);
|
||||
cadRasterizationOptions.setAutomaticLayoutsScaling(true);
|
||||
cadRasterizationOptions.setNoScaling(false);
|
||||
cadRasterizationOptions.setDrawType(1);
|
||||
PdfOptions pdfOptions = new PdfOptions();
|
||||
pdfOptions.setVectorRasterizationOptions(cadRasterizationOptions);
|
||||
OutputStream stream = null;
|
||||
public String cadToPdf(String inputFilePath, String outputFilePath ,String cadPreviewType) throws Exception {
|
||||
final InterruptionTokenSource source = new InterruptionTokenSource();//CAD延时
|
||||
Callable<String> call = () -> {
|
||||
File outputFile = new File(outputFilePath);
|
||||
LoadOptions opts = new LoadOptions();
|
||||
opts.setSpecifiedEncoding(CodePages.SimpChinese);
|
||||
Image cadImage = Image.load(inputFilePath, opts);
|
||||
CadRasterizationOptions cadRasterizationOptions = new CadRasterizationOptions();
|
||||
cadRasterizationOptions.setBackgroundColor(Color.getWhite());
|
||||
cadRasterizationOptions.setPageWidth(1400);
|
||||
cadRasterizationOptions.setPageHeight(650);
|
||||
cadRasterizationOptions.setAutomaticLayoutsScaling(true);
|
||||
cadRasterizationOptions.setNoScaling(false);
|
||||
cadRasterizationOptions.setDrawType(1);
|
||||
SvgOptions SvgOptions = null;
|
||||
PdfOptions pdfOptions = null;
|
||||
TiffOptions TiffOptions = null;
|
||||
switch (cadPreviewType) { //新增格式方法
|
||||
case "svg":
|
||||
SvgOptions = new SvgOptions();
|
||||
SvgOptions.setVectorRasterizationOptions(cadRasterizationOptions);
|
||||
SvgOptions.setInterruptionToken(source.getToken());
|
||||
break;
|
||||
case "pdf":
|
||||
pdfOptions = new PdfOptions();
|
||||
pdfOptions.setVectorRasterizationOptions(cadRasterizationOptions);
|
||||
pdfOptions.setInterruptionToken(source.getToken());
|
||||
break;
|
||||
case "tif":
|
||||
TiffOptions = new TiffOptions(TiffExpectedFormat.TiffJpegRgb);
|
||||
TiffOptions.setVectorRasterizationOptions(cadRasterizationOptions);
|
||||
TiffOptions.setInterruptionToken(source.getToken());
|
||||
break;
|
||||
}
|
||||
try (OutputStream stream = new FileOutputStream(outputFile)) {
|
||||
switch (cadPreviewType) {
|
||||
case "svg":
|
||||
cadImage.save(stream, SvgOptions);
|
||||
break;
|
||||
case "pdf":
|
||||
cadImage.save(stream, pdfOptions);
|
||||
break;
|
||||
case "tif":
|
||||
cadImage.save(stream, TiffOptions);
|
||||
break;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("PDFFileNotFoundException,inputFilePath:{}", inputFilePath, e);
|
||||
return null;
|
||||
} finally {
|
||||
//关闭
|
||||
if (cadImage != null) { //关闭
|
||||
cadImage.dispose();
|
||||
}
|
||||
source.interrupt(); //结束任务
|
||||
}
|
||||
return "true";
|
||||
};
|
||||
Future<String> result = pool.submit(call);
|
||||
try {
|
||||
stream = new FileOutputStream(outputFile);
|
||||
cadImage.save(stream, pdfOptions);
|
||||
} catch (IOException e) {
|
||||
logger.error("PDFFileNotFoundException,inputFilePath:{}", inputFilePath, e);
|
||||
return "null";
|
||||
// 如果在超时时间内,没有数据返回:则抛出TimeoutException异常
|
||||
result.get(Long.parseLong(ConfigConstants.getCadTimeout()), TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println("InterruptedException发生");
|
||||
return null;
|
||||
} catch (ExecutionException e) {
|
||||
System.out.println("ExecutionException发生");
|
||||
return null;
|
||||
} catch (TimeoutException e) {
|
||||
System.out.println("TimeoutException发生,意味着线程超时报错");
|
||||
return null;
|
||||
} finally {
|
||||
if (stream != null) { //关闭
|
||||
stream.close();
|
||||
}
|
||||
if (cadImage != null) { //关闭
|
||||
cadImage.close();
|
||||
}
|
||||
source.dispose();
|
||||
}
|
||||
return "true";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package cn.keking.service;
|
||||
|
||||
import cn.keking.config.ConfigConstants;
|
||||
import cn.keking.model.FileAttribute;
|
||||
import com.sun.star.document.UpdateDocMode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
|
@ -33,15 +34,36 @@ public class OfficeToPdfService {
|
|||
logger.error("创建目录【{}】失败,请检查目录权限!",outputFilePath_end);
|
||||
}
|
||||
LocalConverter.Builder builder;
|
||||
Map<String, Object> filterData = new HashMap<>();
|
||||
filterData.put("EncryptFile", true);
|
||||
if(!ConfigConstants.getOfficePageRange().equals("false")){
|
||||
filterData.put("PageRange", ConfigConstants.getOfficePageRange()); //限制页面
|
||||
}
|
||||
if(!ConfigConstants.getOfficeWatermark().equals("false")){
|
||||
filterData.put("Watermark", ConfigConstants.getOfficeWatermark()); //水印
|
||||
}
|
||||
filterData.put("Quality", ConfigConstants.getOfficeQuality()); //图片压缩
|
||||
filterData.put("MaxImageResolution", ConfigConstants.getOfficeMaxImageResolution()); //DPI
|
||||
if(ConfigConstants.getOfficeExportBookmarks()){
|
||||
filterData.put("ExportBookmarks", true); //导出书签
|
||||
}
|
||||
if(ConfigConstants.getOfficeExportNotes()){
|
||||
filterData.put("ExportNotes", true); //批注作为PDF的注释
|
||||
}
|
||||
if(ConfigConstants.getOfficeDocumentOpenPasswords()){
|
||||
filterData.put("DocumentOpenPassword", fileAttribute.getFilePassword()); //给PDF添加密码
|
||||
}
|
||||
Map<String, Object> customProperties = new HashMap<>();
|
||||
customProperties.put("FilterData", filterData);
|
||||
if (StringUtils.isNotBlank(fileAttribute.getFilePassword())) {
|
||||
Map<String, Object> loadProperties = new HashMap<>();
|
||||
loadProperties.put("Hidden", true);
|
||||
loadProperties.put("ReadOnly", true);
|
||||
loadProperties.put("UpdateDocMode", UpdateDocMode.NO_UPDATE);
|
||||
loadProperties.put("Password", fileAttribute.getFilePassword());
|
||||
builder = LocalConverter.builder().loadProperties(loadProperties);
|
||||
builder = LocalConverter.builder().loadProperties(loadProperties).storeProperties(customProperties);
|
||||
} else {
|
||||
builder = LocalConverter.builder();
|
||||
builder = LocalConverter.builder().storeProperties(customProperties);
|
||||
}
|
||||
builder.build().convert(inputFile).to(outputFile).execute();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,17 +3,15 @@ package cn.keking.service.impl;
|
|||
import cn.keking.config.ConfigConstants;
|
||||
import cn.keking.model.FileAttribute;
|
||||
import cn.keking.model.ReturnResponse;
|
||||
import cn.keking.service.FileHandlerService;
|
||||
import cn.keking.service.FilePreview;
|
||||
import cn.keking.utils.DownloadUtils;
|
||||
import cn.keking.service.FileHandlerService;
|
||||
import cn.keking.utils.KkFileUtils;
|
||||
import cn.keking.web.filter.BaseUrlFilter;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.keking.service.impl.OfficeFilePreviewImpl.getPreviewType;
|
||||
|
||||
/**
|
||||
|
|
@ -43,7 +41,8 @@ public class CadFilePreviewImpl implements FilePreview {
|
|||
boolean forceUpdatedCache=fileAttribute.forceUpdatedCache();
|
||||
String fileName = fileAttribute.getName();
|
||||
String suffix = fileAttribute.getSuffix();
|
||||
String pdfName = fileName.substring(0, fileName.lastIndexOf(".")) + suffix +"." + "pdf" ; //生成文件添加类型后缀 防止同名文件
|
||||
String cadPreviewType = ConfigConstants.getCadPreviewType();
|
||||
String pdfName = fileName.substring(0, fileName.lastIndexOf(".")) + suffix +"." + cadPreviewType ; //生成文件添加类型后缀 防止同名文件
|
||||
String outFilePath = FILE_DIR + pdfName;
|
||||
// 判断之前是否已转换过,如果转换过,直接返回,否则执行转换
|
||||
if (forceUpdatedCache || !fileHandlerService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
|
||||
|
|
@ -56,7 +55,7 @@ public class CadFilePreviewImpl implements FilePreview {
|
|||
String imageUrls = null;
|
||||
if (StringUtils.hasText(outFilePath)) {
|
||||
try {
|
||||
imageUrls = fileHandlerService.cadToPdf(filePath, outFilePath);
|
||||
imageUrls = fileHandlerService.cadToPdf(filePath, outFilePath,cadPreviewType);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
@ -73,12 +72,17 @@ public class CadFilePreviewImpl implements FilePreview {
|
|||
}
|
||||
}
|
||||
}
|
||||
if("tif".equalsIgnoreCase(cadPreviewType)){
|
||||
model.addAttribute("currentUrl", pdfName);
|
||||
return TIFF_FILE_PREVIEW_PAGE;
|
||||
}else if("svg".equalsIgnoreCase(cadPreviewType)){
|
||||
model.addAttribute("currentUrl", pdfName);
|
||||
return SVG_FILE_PREVIEW_PAGE;
|
||||
}
|
||||
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);
|
||||
}
|
||||
model.addAttribute("pdfUrl", pdfName);
|
||||
return PDF_FILE_PREVIEW_PAGE;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,11 +124,15 @@ public class DownloadUtils {
|
|||
if (!dirFile.exists() && !dirFile.mkdirs()) {
|
||||
logger.error("创建目录【{}】失败,可能是权限不够,请检查", fileDir);
|
||||
}
|
||||
// 文件已在本地存在,跳过文件下载
|
||||
File realFile = new File(realPath);
|
||||
if (realFile.exists()) {
|
||||
fileAttribute.setSkipDownLoad(true);
|
||||
return "cunzhai";
|
||||
Boolean forceUpdatedCache = fileAttribute.forceUpdatedCache();
|
||||
//判断是否启用强制更新功能如果启用 文件必须重新下载
|
||||
if (null == forceUpdatedCache || !forceUpdatedCache) {
|
||||
// 文件已在本地存在,跳过文件下载
|
||||
File realFile = new File(realPath);
|
||||
if (realFile.exists()) {
|
||||
fileAttribute.setSkipDownLoad(true);
|
||||
return "cunzhai"; //这里给的值是不能修改的 对应的是下载方法里面有个强制输出地址的
|
||||
}
|
||||
}
|
||||
return realPath;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,90 @@
|
|||
package cn.keking.utils;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
public class RandomValidateCodeUtil {
|
||||
|
||||
private static final int width = 100;// 定义图片的width
|
||||
private static final int height = 30;// 定义图片的height
|
||||
private static final int codeCount = 4;// 定义图片上显示验证码的个数
|
||||
private static final int xx = 18;
|
||||
private static final int fontHeight = 28;
|
||||
private static final int codeY = 27;
|
||||
private static final char[] codeSequence = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R','T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
'a','b','c','d','e','f','g','h','j','k','m','n','p','q','r','s','t','u','v','w','x','y', '2', '3', '4','5', '6', '7', '8', '9' };
|
||||
|
||||
/**
|
||||
* 生成一个map集合
|
||||
* code为生成的验证码
|
||||
* codePic为生成的验证码BufferedImage对象
|
||||
*/
|
||||
public static Map<String,Object> generateCodeAndPic(String ip, String sessionCode, int lx) {
|
||||
// 定义图像buffer
|
||||
BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
// Graphics2D gd = buffImg.createGraphics();
|
||||
// Graphics2D gd = (Graphics2D) buffImg.getGraphics();
|
||||
Graphics gd = buffImg.getGraphics();
|
||||
// 创建一个随机数生成器类
|
||||
Random random = new Random();
|
||||
// 将图像填充为白色
|
||||
gd.setColor(Color.WHITE);
|
||||
gd.fillRect(0, 0, width, height);
|
||||
|
||||
// 创建字体,字体的大小应该根据图片的高度来定。
|
||||
Font font = new Font("Times New Roman", Font.BOLD, fontHeight);
|
||||
// 设置字体。
|
||||
gd.setFont(font);
|
||||
|
||||
// 画边框。
|
||||
gd.setColor(Color.BLACK);
|
||||
gd.drawRect(0, 0, width - 1, height - 1);
|
||||
|
||||
// 随机产生40条干扰线,使图象中的认证码不易被其它程序探测到。
|
||||
gd.setColor(Color.BLACK);
|
||||
for (int i = 0; i < 30; i++) {
|
||||
int x = random.nextInt(width);
|
||||
int y = random.nextInt(height);
|
||||
int xl = random.nextInt(12);
|
||||
int yl = random.nextInt(12);
|
||||
gd.drawLine(x, y, x + xl, y + yl);
|
||||
}
|
||||
StringBuffer randomCode = new StringBuffer();
|
||||
Map<String,Object> map = new HashMap<>();
|
||||
// randomCode用于保存随机产生的验证码,以便用户登录后进行验证。
|
||||
int red, green, blue;
|
||||
if (lx ==1){
|
||||
// 产生随机的颜色分量来构造颜色值,这样输出的每位数字的颜色值都将不同。
|
||||
red = random.nextInt(255);
|
||||
green = random.nextInt(255);
|
||||
blue = random.nextInt(255);
|
||||
// 用随机产生的颜色将验证码绘制到图像中。
|
||||
gd.setColor(new Color(red, green, blue));
|
||||
gd.drawString(sessionCode, 18, codeY);
|
||||
randomCode.append(sessionCode);
|
||||
}else {
|
||||
// 随机产生codeCount数字的验证码。
|
||||
for (int i = 0; i < codeCount; i++) {
|
||||
// 得到随机产生的验证码数字。
|
||||
String code = String.valueOf(codeSequence[random.nextInt(52)]);
|
||||
// 产生随机的颜色分量来构造颜色值,这样输出的每位数字的颜色值都将不同。
|
||||
red = random.nextInt(255);
|
||||
green = random.nextInt(255);
|
||||
blue = random.nextInt(255);
|
||||
// 用随机产生的颜色将验证码绘制到图像中。
|
||||
gd.setColor(new Color(red, green, blue));
|
||||
gd.drawString(code, (i + 1) * xx, codeY);
|
||||
// 将产生的四个随机数组合在一起。
|
||||
randomCode.append(code);
|
||||
}
|
||||
}
|
||||
//存放验证码
|
||||
map.put("code", randomCode);
|
||||
//存放生成的验证码BufferedImage对象
|
||||
map.put("codePic", buffImg);
|
||||
return map;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
|
@ -59,15 +60,28 @@ public class FileController {
|
|||
}
|
||||
|
||||
@GetMapping("/deleteFile")
|
||||
public ReturnResponse<Object> deleteFile(String fileName,String password) {
|
||||
public ReturnResponse<Object> deleteFile(HttpServletRequest request, String fileName, String password) {
|
||||
ReturnResponse<Object> checkResult = this.deleteFileCheck(fileName);
|
||||
if (checkResult.isFailure()) {
|
||||
return checkResult;
|
||||
}
|
||||
fileName = checkResult.getContent().toString();
|
||||
if(!ConfigConstants.getPassword().equalsIgnoreCase(password)) {
|
||||
logger.error("删除文件【{}】失败,密码错误!",fileName);
|
||||
return ReturnResponse.failure("删除文件失败,密码错误!");
|
||||
if(ConfigConstants.getDeleteCaptcha()){
|
||||
String sessionCode;
|
||||
try {
|
||||
sessionCode = request.getSession().getAttribute("code").toString(); //获取已经保存的验证码
|
||||
} catch (Exception e) {
|
||||
sessionCode = "null";
|
||||
}
|
||||
if (password==null || !sessionCode.equalsIgnoreCase(password)){
|
||||
logger.error("删除文件【{}】失败,密码错误!",fileName);
|
||||
return ReturnResponse.failure("删除文件失败,密码错误!");
|
||||
}
|
||||
}else {
|
||||
if(password==null || !ConfigConstants.getPassword().equalsIgnoreCase(password)) {
|
||||
logger.error("删除文件【{}】失败,密码错误!",fileName);
|
||||
return ReturnResponse.failure("删除文件失败,密码错误!");
|
||||
}
|
||||
}
|
||||
File file = new File(fileDir + demoPath + fileName);
|
||||
logger.info("删除文件:{}", file.getAbsolutePath());
|
||||
|
|
@ -76,6 +90,7 @@ public class FileController {
|
|||
logger.error(msg);
|
||||
return ReturnResponse.failure(msg);
|
||||
}
|
||||
request.getSession().removeAttribute("code"); //删除缓存验证码
|
||||
return ReturnResponse.success();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package cn.keking.web.controller;
|
||||
|
||||
import cn.keking.config.ConfigConstants;
|
||||
import cn.keking.model.FileAttribute;
|
||||
import cn.keking.service.FileHandlerService;
|
||||
import cn.keking.service.FilePreview;
|
||||
|
|
@ -7,6 +8,7 @@ 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.RandomValidateCodeUtil;
|
||||
import cn.keking.utils.WebUtils;
|
||||
import fr.opensagres.xdocreport.core.io.IOUtils;
|
||||
import io.mola.galimatias.GalimatiasParseException;
|
||||
|
|
@ -15,18 +17,26 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.keking.service.FilePreview.PICTURE_FILE_PREVIEW_PAGE;
|
||||
|
||||
|
|
@ -160,6 +170,82 @@ public class OnlinePreviewController {
|
|||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 验证码方法
|
||||
*/
|
||||
@RequestMapping("/captcha")
|
||||
public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
if(!ConfigConstants.getDeleteCaptcha()){
|
||||
return;
|
||||
}
|
||||
response.setContentType("image/gif");
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setDateHeader("Expires", 0);
|
||||
Date date = new Date(); // 当前时间
|
||||
SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 设置时间格式
|
||||
String sessionCode;
|
||||
try {
|
||||
sessionCode = request.getSession().getAttribute("code").toString(); //获取已经保存的验证码
|
||||
} catch (Exception e) {
|
||||
sessionCode= null;
|
||||
}
|
||||
Object time = request.getSession().getAttribute("time"); //获取已经保存的时间
|
||||
if (ObjectUtils.isEmpty(time)){ //判断时间是否为空
|
||||
request.getSession().setAttribute("time", formater.format(date)); //为空重新添加缓存时间
|
||||
time = request.getSession().getAttribute("time");
|
||||
}
|
||||
Date joinTime = formater.parse(String.valueOf(time));
|
||||
String dateStart = formater.format(joinTime);
|
||||
Date d1=formater.parse(dateStart);
|
||||
// 时间差:
|
||||
long diff = date.getTime() - d1.getTime();
|
||||
long diffSeconds = diff / 1000 % 60;
|
||||
String ip=request.getRemoteAddr();
|
||||
ServletOutputStream sos = null;
|
||||
if (ObjectUtils.isEmpty(sessionCode) || diffSeconds > 50){ //判断验证码是否为空 为空重新生成 判断是否在有效时间内 默认50秒
|
||||
Map<String, Object> codeMap = RandomValidateCodeUtil.generateCodeAndPic(ip,sessionCode,0);
|
||||
// 验证码存入session
|
||||
request.getSession().setAttribute("code", codeMap.get("code").toString());
|
||||
// 时间存入session
|
||||
request.getSession().setAttribute("time", formater.format(date));
|
||||
// 禁止图像缓存。
|
||||
response.setHeader("Pragma", "no-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setDateHeader("Expires", -1);
|
||||
response.setContentType("image/jpeg");
|
||||
// 将图像输出到Servlet输出流中。
|
||||
try {
|
||||
sos = response.getOutputStream();
|
||||
ImageIO.write((RenderedImage) codeMap.get("codePic"), "jpeg", sos);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
assert sos != null;
|
||||
sos.close();
|
||||
}
|
||||
}else {
|
||||
// System.out.println("请输入你的姓名:");
|
||||
Map<String, Object> codeMap = RandomValidateCodeUtil.generateCodeAndPic(ip,sessionCode,1);
|
||||
// 禁止图像缓存。
|
||||
response.setHeader("Pragma", "no-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setDateHeader("Expires", -1);
|
||||
response.setContentType("image/jpeg");
|
||||
// 将图像输出到Servlet输出流中。
|
||||
try {
|
||||
sos = response.getOutputStream();
|
||||
ImageIO.write((RenderedImage) codeMap.get("codePic"), "jpeg", sos);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
assert sos != null;
|
||||
sos.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过api接口入队
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ public class AttributeSetFilter implements Filter {
|
|||
request.setAttribute("fileUploadDisable", ConfigConstants.getFileUploadDisable());
|
||||
request.setAttribute("beian", ConfigConstants.getBeian());
|
||||
request.setAttribute("size", ConfigConstants.maxSize());
|
||||
request.setAttribute("deleteCaptcha", ConfigConstants.getDeleteCaptcha());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -22,6 +22,39 @@
|
|||
width: 50%;
|
||||
}
|
||||
</style>
|
||||
<#-- 删除吗CSS样式 -->
|
||||
<#if deleteCaptcha >
|
||||
<style>
|
||||
.code{
|
||||
position: fixed;
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
background-color: #F0FFF0;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
z-index: 100002;
|
||||
}
|
||||
.close{
|
||||
margin-top: 20px;
|
||||
}
|
||||
.code-input{
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
width: 110px;
|
||||
height: 40px;
|
||||
}
|
||||
.code-input:focus{
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)
|
||||
}
|
||||
|
||||
</style>
|
||||
</#if>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -111,6 +144,10 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<#-- 删除吗弹窗 -->
|
||||
<#if deleteCaptcha >
|
||||
<div id="codeContent"> </div>
|
||||
</#if>
|
||||
<#-- 预览测试 -->
|
||||
<div class="panel panel-success">
|
||||
<div class="panel-heading">
|
||||
|
|
@ -162,13 +199,82 @@
|
|||
</div>
|
||||
</#if>
|
||||
<script>
|
||||
<#if deleteCaptcha >
|
||||
function deleteFile(fileName) {
|
||||
var codename =`<div class="code"><h4>请输入下面删除码!</h4><div><img id="verImg" width="130px" height="48px" src="/captcha"></div><form><input type="type" oninput="if(value.length>5)value=value.slice(0,5);" class="code-input" id="_code" placeholder="请输入验证码"><button id="deleteFile1" type="button" class="btn btn-success">提交</button></form><button id="close" type="button"class="btn btn-danger">关闭</button></div>`;
|
||||
$('#codeContent').html(codename);
|
||||
var code = document.querySelector('.code');
|
||||
var closeBtn = document.getElementById("close");
|
||||
closeBtn.addEventListener('click', hidePopup);
|
||||
function hidePopup(){
|
||||
code.style.display = 'none';
|
||||
}
|
||||
var closedelete = document.getElementById("deleteFile1");
|
||||
closedelete.addEventListener('click', deleteFile1);
|
||||
function deleteFile1(){
|
||||
var password = $("#_code").val();
|
||||
//console.log(password);
|
||||
$.ajax({
|
||||
url: '${baseUrl}deleteFile?fileName=' + fileName +'&password='+password,
|
||||
success: function (data) {
|
||||
// console.log(data);
|
||||
// 删除完成,刷新table
|
||||
if ("删除文件失败,密码错误!" === data.msg) {
|
||||
alert(data.msg);
|
||||
} else {
|
||||
$('#table').bootstrapTable('refresh', {});
|
||||
code.style.display = 'none';
|
||||
}
|
||||
},
|
||||
error: function (data) {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
}
|
||||
var windowUrl = window.URL || window.webkitURL; //处理浏览器兼容性
|
||||
document.getElementById('verImg').onclick = function(e){
|
||||
//1、创建ajax对象
|
||||
var xhr = null;
|
||||
try{
|
||||
xhr = new XMLHttpRequest();
|
||||
}catch(error){
|
||||
xhr = new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
//2、调用open
|
||||
xhr.open("get", "/captcha", true);
|
||||
xhr.responseType = "blob";
|
||||
//3、调用send
|
||||
xhr.send();
|
||||
//4、等待数据响应
|
||||
xhr.onreadystatechange = function(){
|
||||
if(xhr.readyState == 4){
|
||||
//判断本次下载的状态码都是多少
|
||||
if(xhr.status == 200){
|
||||
var blob = this.response;
|
||||
$("#verImg").attr("src",windowUrl.createObjectURL(blob));
|
||||
//$('#verImg').attr('src', xhr.responseText);
|
||||
|
||||
// alert(windowUrl.createObjectURL(blob));
|
||||
}else{
|
||||
alert("Error:" + xhr.status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
<#else>
|
||||
function deleteFile(fileName,password) {
|
||||
if(window.confirm('你确定要删除文件吗?')){
|
||||
<#if deleteCaptcha >
|
||||
password = prompt("请输入获取的验证码:");
|
||||
<#else>
|
||||
password = prompt("请输入默认密码:123456");
|
||||
</#if>
|
||||
$.ajax({
|
||||
url: '${baseUrl}deleteFile?fileName=' + fileName +'&password='+password,
|
||||
success: function (data) {
|
||||
// console.log(data);
|
||||
// console.log(data);
|
||||
// 删除完成,刷新table
|
||||
if ("删除文件失败,密码错误!" === data.msg) {
|
||||
alert(data.msg);
|
||||
|
|
@ -185,6 +291,7 @@
|
|||
}
|
||||
|
||||
}
|
||||
</#if>
|
||||
|
||||
function showLoadingDiv() {
|
||||
var height = window.document.documentElement.clientHeight - 1;
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>${file.name}预览</title>
|
||||
<link rel='stylesheet' href='xlsx/plugins/css/pluginsCss.css' />
|
||||
<link rel='stylesheet' href='xlsx/plugins/plugins.css' />
|
||||
<link rel='stylesheet' href='xlsx/css/luckysheet.css' />
|
||||
<link rel='stylesheet' href='xlsx/assets/iconfont/iconfont.css' />
|
||||
<script src="xlsx/plugins/js/plugin.js"></script>
|
||||
<script src="xlsx/luckysheet.umd.js"></script>
|
||||
<script src="js/watermark.js" type="text/javascript"></script>
|
||||
<script src="js/base64.min.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>${file.name}预览</title>
|
||||
<link rel='stylesheet' href='xlsx/plugins/css/pluginsCss.css' />
|
||||
<link rel='stylesheet' href='xlsx/plugins/plugins.css' />
|
||||
<link rel='stylesheet' href='xlsx/css/luckysheet.css' />
|
||||
<link rel='stylesheet' href='xlsx/assets/iconfont/iconfont.css' />
|
||||
<script src="xlsx/plugins/js/plugin.js"></script>
|
||||
<script src="xlsx/luckysheet.umd.js"></script>
|
||||
<script src="js/watermark.js" type="text/javascript"></script>
|
||||
<script src="js/base64.min.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<#if pdfUrl?contains("http://") || pdfUrl?contains("https://") || pdfUrl?contains("ftp://")>
|
||||
<#assign finalUrl="${pdfUrl}">
|
||||
<#assign finalUrl="${pdfUrl}">
|
||||
<#else>
|
||||
<#assign finalUrl="${baseUrl}${pdfUrl}">
|
||||
<#assign finalUrl="${baseUrl}${pdfUrl}">
|
||||
</#if>
|
||||
<script>
|
||||
<script>
|
||||
/**
|
||||
* 初始化水印
|
||||
*/
|
||||
function initWaterMark() {
|
||||
|
||||
|
||||
let watermarkTxt = '${watermarkTxt}';
|
||||
if (watermarkTxt !== '') {
|
||||
watermark.init({
|
||||
|
|
@ -57,89 +57,89 @@
|
|||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
<div id="lucky-mask-demo" style="position: absolute;z-index: 1000000;left: 0px;top: 0px;bottom: 0px;right: 0px; background: rgba(255, 255, 255, 0.8); text-align: center;font-size: 40px;align-items:center;justify-content: center;display: none;">加载中</div>
|
||||
<p style="text-align:center;">
|
||||
<div id="button-area">
|
||||
<label><button onclick="tiaozhuan()">跳转HTML预览</button></label>
|
||||
<button id="confirm-button" onclick="print()">打印</button>
|
||||
</div>
|
||||
<div id="luckysheet" style="margin:0px;padding:0px;position:absolute;width:100%;left: 0px;top: 20px;bottom: 0px;outline: none;"></div>
|
||||
|
||||
<script src="xlsx/luckyexcel.umd.js"></script>
|
||||
<script>
|
||||
function tiaozhuan(){
|
||||
var test = window.location.href;
|
||||
test = test.replace(new RegExp("&officePreviewType=xlsx",("gm")),"");
|
||||
test = test+'&officePreviewType=html';
|
||||
window.location.href=test;
|
||||
}
|
||||
var url = '${finalUrl}';
|
||||
<body>
|
||||
<div id="lucky-mask-demo" style="position: absolute;z-index: 1000000;left: 0px;top: 0px;bottom: 0px;right: 0px; background: rgba(255, 255, 255, 0.8); text-align: center;font-size: 40px;align-items:center;justify-content: center;display: none;">加载中</div>
|
||||
<p style="text-align:center;">
|
||||
<div id="button-area">
|
||||
<label><button onclick="tiaozhuan()">跳转HTML预览</button></label>
|
||||
<button id="confirm-button" onclick="print()">打印</button>
|
||||
</div>
|
||||
<div id="luckysheet" style="margin:0px;padding:0px;position:absolute;width:100%;left: 0px;top: 20px;bottom: 0px;outline: none;"></div>
|
||||
|
||||
<script src="xlsx/luckyexcel.umd.js"></script>
|
||||
<script>
|
||||
function tiaozhuan(){
|
||||
var test = window.location.href;
|
||||
test = test.replace(new RegExp("&officePreviewType=xlsx",("gm")),"");
|
||||
test = test+'&officePreviewType=html';
|
||||
window.location.href=test;
|
||||
}
|
||||
var url = '${finalUrl}';
|
||||
var baseUrl = '${baseUrl}'.endsWith('/') ? '${baseUrl}' : '${baseUrl}' + '/';
|
||||
if (!url.startsWith(baseUrl)) {
|
||||
url = baseUrl + 'getCorsFile?urlPath=' + encodeURIComponent(Base64.encode(url));
|
||||
}
|
||||
let mask = document.getElementById("lucky-mask-demo");
|
||||
|
||||
function loadText() {
|
||||
initWaterMark(); // 是否显示水印
|
||||
var value = url;
|
||||
var name = '${file.name}';
|
||||
if(value==""){
|
||||
return;
|
||||
}
|
||||
// mask.style.display = "flex";
|
||||
LuckyExcel.transformExcelToLuckyByUrl(value, name, function(exportJson, luckysheetfile){
|
||||
if(exportJson.sheets==null || exportJson.sheets.length==0){
|
||||
alert("读取excel文件内容失败!");
|
||||
return;
|
||||
}
|
||||
// console.log(exportJson.sheets);
|
||||
mask.style.display = "none";
|
||||
window.luckysheet.destroy();
|
||||
window.luckysheet.create({
|
||||
container: 'luckysheet', //luckysheet is the container id
|
||||
lang: "zh",
|
||||
showtoolbarConfig:{
|
||||
image: true,
|
||||
print: false,
|
||||
},
|
||||
hook:{
|
||||
onTogglePager:function(range){
|
||||
// range.data = exportJson.sheets
|
||||
},
|
||||
},
|
||||
|
||||
allowCopy: true, // 是否允许拷贝
|
||||
showtoolbar: true, // 是否显示工具栏
|
||||
showinfobar: false, // 是否显示顶部信息栏
|
||||
// myFolderUrl: "/",//作用:左上角<返回按钮的链接
|
||||
showsheetbar: true, // 是否显示底部sheet页按钮
|
||||
showstatisticBar: true, // 是否显示底部计数栏
|
||||
sheetBottomConfig: true, // sheet页下方的添加行按钮和回到顶部按钮配置
|
||||
allowEdit: true, // 是否允许前台编辑
|
||||
enableAddRow: false, // 允许增加行
|
||||
enableAddCol: false, // 允许增加列
|
||||
userInfo: false, // 右上角的用户信息展示样式
|
||||
showRowBar: true, // 是否显示行号区域
|
||||
showColumnBar: false, // 是否显示列号区域
|
||||
sheetFormulaBar: false, // 是否显示公式栏
|
||||
enableAddBackTop: true,//返回头部按钮
|
||||
data:exportJson.sheets,
|
||||
title:exportJson.info.name,
|
||||
plugins: ['chart'],
|
||||
userInfo:exportJson.info.name.creator
|
||||
});
|
||||
});
|
||||
}
|
||||
loadText();
|
||||
// 打印时,获取luckysheet指定区域html内容,拼接至div,隐藏luckysheet容器并显示打印区域html
|
||||
function to_print() {
|
||||
const html = luckysheet.getRangeHtml();
|
||||
document.querySelector('#print-html').innerHTML = html;
|
||||
document.querySelector('#print-area').style.display = 'block';
|
||||
document.querySelector('#button-area').style.display = 'none';
|
||||
let mask = document.getElementById("lucky-mask-demo");
|
||||
function loadText() {
|
||||
initWaterMark(); // 是否显示水印
|
||||
var value = url;
|
||||
var name = '${file.name}';
|
||||
if(value==""){
|
||||
return;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
// mask.style.display = "flex";
|
||||
LuckyExcel.transformExcelToLuckyByUrl(value, name, function(exportJson, luckysheetfile){
|
||||
if(exportJson.sheets==null || exportJson.sheets.length==0){
|
||||
alert("读取excel文件内容失败!");
|
||||
return;
|
||||
}
|
||||
mask.style.display = "none";
|
||||
window.luckysheet.destroy();
|
||||
window.luckysheet.create({
|
||||
container: 'luckysheet', //luckysheet is the container id
|
||||
lang: "zh",
|
||||
showtoolbarConfig:{
|
||||
image: true,
|
||||
print: false, //关闭打印按钮 启用也不能用 等以后看情况而定
|
||||
exportXlsx: false, //关闭导出按钮 启用也不能用 等以后看情况而定
|
||||
},
|
||||
|
||||
allowCopy: true, // 是否允许拷贝
|
||||
showtoolbar: true, // 是否显示工具栏
|
||||
showinfobar: false, // 是否显示顶部信息栏
|
||||
// myFolderUrl: "/",//作用:左上角<返回按钮的链接
|
||||
showsheetbar: true, // 是否显示底部sheet页按钮
|
||||
showstatisticBar: true, // 是否显示底部计数栏
|
||||
sheetBottomConfig: true, // sheet页下方的添加行按钮和回到顶部按钮配置
|
||||
allowEdit: true, // 是否允许前台编辑
|
||||
enableAddRow: false, // 允许增加行
|
||||
enableAddCol: false, // 允许增加列
|
||||
userInfo: false, // 右上角的用户信息展示样式
|
||||
showRowBar: true, // 是否显示行号区域
|
||||
showColumnBar: false, // 是否显示列号区域
|
||||
sheetFormulaBar: false, // 是否显示公式栏
|
||||
enableAddBackTop: true,//返回头部按钮
|
||||
forceCalculation: false, //下面是导出插件 默认关闭
|
||||
enableAddRow: false, // 允许增加行
|
||||
plugins: [{ name: 'chart' }, { name: 'exportXlsx', config: { url: 'luckyToXlsx' } }, {
|
||||
name: 'print', config: {
|
||||
license: ''
|
||||
}
|
||||
}],
|
||||
data:exportJson.sheets,
|
||||
title:exportJson.info.name,
|
||||
userInfo:exportJson.info.name.creator
|
||||
});
|
||||
});
|
||||
}
|
||||
loadText();
|
||||
// 打印时,获取luckysheet指定区域html内容,拼接至div,隐藏luckysheet容器并显示打印区域html
|
||||
function to_print() {
|
||||
const html = luckysheet.getRangeHtml();
|
||||
document.querySelector('#print-html').innerHTML = html;
|
||||
document.querySelector('#print-area').style.display = 'block';
|
||||
document.querySelector('#button-area').style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package cn.keking;
|
||||
|
||||
import cn.keking.utils.EncodingDetects;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
/**
|
||||
* 编码检测测试类
|
||||
*
|
||||
* @author asiawu3
|
||||
* @create 2023-07-24 16:53
|
||||
**/
|
||||
@SpringBootTest
|
||||
public class EncodingTests {
|
||||
@Test
|
||||
void testCharDet() throws URISyntaxException {
|
||||
for (int i = 0; i < 29; i++) {
|
||||
File dir = new File(getClass().getClassLoader().getResource("testData\\" + i).toURI());
|
||||
String dirPath = dir.getPath();
|
||||
String textFileName = dir.list()[0];
|
||||
String textFilePath = dirPath + "/" + textFileName;
|
||||
System.out.printf("%-15s -->\t %-10s\n", textFileName, EncodingDetects.getJavaEncode(textFilePath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,10 +4,8 @@ import org.junit.jupiter.api.Test;
|
|||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class ServerMainTests {
|
||||
|
||||
class ServerMainTests {
|
||||
@Test
|
||||
void contextLoads() {
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
|
||||
|
|
@ -0,0 +1 @@
|
|||
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
|
||||
|
|
@ -0,0 +1 @@
|
|||
ポリシーパターンは、一連のアルゴリズムを定義し、それぞれのアルゴリズムをパッケージ化して相互に置換できるようにし、アルゴリズムの変化は、アルゴリズムを使用する顧客に影響を与えません。ポリシーパターンは、アルゴリズムをカプセル化することで、アルゴリズムを使用する責任とアルゴリズムを実装する責任を分離し、アルゴリズムの管理を異なるオブジェクトに委ねます。
|
||||
|
|
@ -0,0 +1 @@
|
|||
정책 모델은 알고리즘의 집합을 정의하며, 각각의 알고리즘은 변경되지 않고 변경될 수 있도록 패키지된다.정책 모델은 객체 행동 모델에 속하며, 알고리즘의 사용과 구현에 대한 책임을 분리하여 알고리즘을 관리하기 위해 다른 객체들에게 위임한다.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Стратегическая модель определяет ряд алгоритмов и запечатывает каждый из них, чтобы они могли быть заменены друг на друга, и изменения алгоритма не влияют на клиентов, которые используют алгоритм. Стратегическая модель относится к модели поведения объекта, которая разделяет ответственность за использование алгоритма и реализацию алгоритма путем инжинизации алгоритма и поручает управление этим алгоритмам различным объектам.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Le schéma de politique définit une série d?algorithmes et encapsule chacun de manière à ce qu?ils puissent être remplacés les uns par les autres et que les changements d?algorithmes n?affectent pas les clients qui utilisent l?algorithme. Le mode de politique appartient au mode de comportement des objets. Il scinde la responsabilité de l?utilisation de l?algorithme et de sa mise en ?uvre en encapsulant l?algorithme et en déléguant la gestion de ces algorithmes à différents objets.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Wzorzec strategii definiuje serię algorytmów i hermetyzuje każdy algorytm, dzięki czemu można je zastąpić sobą, a zmiana algorytmu nie wpłynie na klientów korzystających z algorytmu. Wzorzec strategii należy do wzorca zachowania obiektu, zawiera w sobie algorytm, oddziela odpowiedzialność za użycie algorytmu od implementacji algorytmu i deleguje różne obiekty do zarządzania tymi algorytmami.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Strateji modeli, bir dizi algoritmay¹ tan¹mlar ve her bir algoritmay¹ kendi aralar¹nda de»iºtirilebilecek ºekilde kapsüller ve algoritma de»iºikli»i, algoritmay¹ kullanan müºterileri etkilemez. Strateji kal¹b¹, nesne davran¹º kal¹b¹na aittir, algoritmay¹ kapsar, algoritmay¹ kullanma sorumlulu»unu algoritman¹n gerçekleºtirilmesinden ay¹r¹r ve bu algoritmalar¹ yönetmek için farkl¹ nesnelere yetki verir.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Strateegiamuster määratleb algoritmide seeria ja kapseldab iga algoritmi nii, et neid saaks üksteisega asendada ning algoritmi muutmine ei mõjuta algoritmi kasutavaid kliente. Strateegiamuster kuulub objekti käitumismustrisse, mis kapseldab algoritmi, eraldab algoritmi kasutamise vastutuse algoritmi realiseerimisest ja delegeerib erinevatele objektidele nende algoritmide haldamise.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Шаблон стратегии определяет ряд алгоритмов и инкапсулирует каждый алгоритм, чтобы их можно было заменить друг другом, а изменение алгоритма не повлияет на клиентов, которые его используют. Паттерн стратегии относится к паттерну поведения объекта, инкапсулирует алгоритм, отделяет ответственность за использование алгоритма от реализации алгоритма и делегирует управление этими алгоритмами разным объектам.
|
||||
|
|
@ -0,0 +1 @@
|
|||
يحدد نمط الإستراتيجية سلسلة من الخوارزميات ويغلف كل خوارزمية بحيث يمكن استبدالها ببعضها البعض ، ولن يؤثر تغيير الخوارزمية على العملاء الذين يستخدمون الخوارزمية. ينتمي نمط الإستراتيجية إلى نمط سلوك الكائن ، حيث يقوم بتغليف الخوارزمية ، ويفصل مسؤولية استخدام الخوارزمية عن تحقيق الخوارزمية ، ويفوض إلى كائنات مختلفة لإدارة هذه الخوارزميات.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Το μοτίβο στρατηγικής ορίζει μια σειρά από αλγόριθμους και ενσωματώνει κάθε αλγόριθμο έτσι ώστε να μπορούν να αντικατασταθούν μεταξύ τους και η αλλαγή του αλγορίθμου δεν θα επηρεάσει τους πελάτες που χρησιμοποιούν τον αλγόριθμο. Το μοτίβο στρατηγικής ανήκει στο μοτίβο συμπεριφοράς αντικειμένου.Ενθυλακώνει τον αλγόριθμο, διαχωρίζει την ευθύνη χρήσης του αλγορίθμου από την υλοποίηση του αλγορίθμου και αναθέτει σε διαφορετικά αντικείμενα τη διαχείριση αυτών των αλγορίθμων.
|
||||
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
דפוס האסטרטגיה מגדיר סדרה של אלגוריתמים ומעטף כל אלגוריתם כך שניתן להחליפם זה בזה, ושינוי האלגוריתם לא ישפיע על הלקוחות המשתמשים באלגוריתם. דפוס האסטרטגיה שייך לדפוס התנהגות האובייקט, הוא מקפל את האלגוריתם, מפריד בין אחריות השימוש באלגוריתם לבין יישום האלגוריתם ומאציל לאובייקטים שונים לנהל את האלגוריתמים הללו.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Strateji modeli, bir dizi algoritmayı tanımlar ve her bir algoritmayı kendi aralarında değiştirilebilecek şekilde kapsüller ve algoritma değişikliği, algoritmayı kullanan müşterileri etkilemez. Strateji kalıbı, nesne davranış kalıbına aittir, algoritmayı kapsar, algoritmayı kullanma sorumluluğunu algoritmanın gerçekleştirilmesinden ayırır ve bu algoritmaları yönetmek için farklı nesnelere yetki verir.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Strategimønsteret definerer en række algoritmer og indkapsler hver algoritme, så de kan erstattes med hinanden, og ændringen af ??algoritmen vil ikke påvirke de kunder, der bruger algoritmen. Strategimønsteret hører til objektadfærdsmønsteret. Det indkapsler algoritmen, adskiller ansvaret for at bruge algoritmen fra realiseringen af ??algoritmen og uddelegerer til forskellige objekter for at styre disse algoritmer.
|
||||
|
|
@ -0,0 +1 @@
|
|||
觅会汉∨仑犯臁铀勾<EFBFBD>储艇脱拧兔苑至崤惺秘会佃判脱拧兔苑至嗑阻豌碎室烈枚岱狗砧⊙逛撮 崤小颐嗷耪杪贯慌Б艇脱拧兔苑至ㄐ淞枋瑙寂∶蟹旱柰刨·橐氛桡<E6B09B>脱拧兔苑至 觅会汉∨仑犯爨荤耿艇觅会汉灸翟∶昧⑼<E698A7>秃啜〉<E5959C> <20>瑙ㄐ拭鼗脱拧兔苑至 崧·且撩押荚椽秃⑼А颐悛橥雅⊥迷分镣汀ㄒ 颐酚闼猷荤龚迷Б艇脱拧兔苑至 崤辛秃肆衣闼橥缤亨ㄧ〉斓枰ф ㄑ础颐脱拧兔苑至嗨盆夜臻
|
||||
|
|
@ -0,0 +1 @@
|
|||
Strateegiamuster määratleb algoritmide seeria ja kapseldab iga algoritmi nii, et neid saaks üksteisega asendada ning algoritmi muutmine ei mõjuta algoritmi kasutavaid kliente. Strateegiamuster kuulub objekti käitumismustrisse, mis kapseldab algoritmi, eraldab algoritmi kasutamise vastutuse algoritmi realiseerimisest ja delegeerib erinevatele objektidele nende algoritmide haldamise.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Mae patrwm y strategaeth yn diffinio cyfres o algorithmau ac yn crynhoi pob algorithm fel y gellir eu disodli â'i gilydd, ac ni fydd newid yr algorithm yn effeithio ar y cwsmeriaid sy'n defnyddio'r algorithm. Mae'r patrwm strategaeth yn perthyn i'r patrwm ymddygiad gwrthrych, mae'n crynhoi'r algorithm, yn gwahanu'r cyfrifoldeb o ddefnyddio'r algorithm oddi wrth wireddu'r algorithm, ac yn dirprwyo i wahanol wrthrychau i reoli'r algorithmau hyn.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Strategiamalli määrittelee sarjan algoritmeja ja kapseloi jokaisen algoritmin siten, että ne voidaan korvata toisillaan, eikä algoritmin muutos vaikuta algoritmia käyttäviin asiakkaisiin. Kohteen käyttäytymismalliin kuuluu strategiamalli, joka kapseloi algoritmin, erottaa algoritmin käytön vastuun algoritmin toteuttamisesta ja delegoi eri objekteille näiden algoritmien hallinnan.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Strate¨ki obrazac definira niz algoritama i enkapsulira svaki algoritam tako da se mogu meðusobno zamijeniti, a promjena algoritma neåe utjecati na kupce koji koriste algoritam. Strate¨ki obrazac pripada obrascu pona¨anja objekta. On enkapsulira algoritam, odvaja odgovornost za kori¨tenje algoritma od realizacije algoritma i delegira razli¹itim objektima da upravljaju tim algoritmima.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
发斯蒂芬斯蒂芬
|
||||
|
||||
顶顶顶顶~
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
|
||||
|
|
@ -0,0 +1 @@
|
|||
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
|
||||
|
|
@ -0,0 +1 @@
|
|||
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
|
||||
|
|
@ -0,0 +1 @@
|
|||
策略模式定義了一系列算法,並將每個算法封裝起來,使它們可以相互替換,且算法的變化不會影響使用算法的客戶。策略模式屬於對象行為模式,它通過對算法進行封裝,把使用算法的責任和算法的實現分割開來,並委派給不同的對象對這些算法進行管理。
|
||||
Loading…
Reference in New Issue