5.0版本 发布 新增 cadviewer转换方法

This commit is contained in:
高雄
2026-01-22 11:28:25 +08:00
parent 7dc0469b30
commit e43f10138f
1022 changed files with 293112 additions and 3187 deletions

View File

@@ -9,18 +9,18 @@
7. 支持 pdf ,ofd, rtf 等文档 7. 支持 pdf ,ofd, rtf 等文档
8. 支持 xmind 软件模型文件 8. 支持 xmind 软件模型文件
9. 支持 bpmn 工作流文件 9. 支持 bpmn 工作流文件
10. 支持 eml 邮件文件 10. 支持 eml, msg 邮件文件
11. 支持 epub 图书文档 11. 支持 epub 图书文档
12. 支持 obj, 3ds, stl, ply, gltf, glb, off, 3dm, fbx, dae, wrl, 3mf, ifc, brep, step, iges, fcstd, bim 3D 模型文件 12. 支持 obj, 3ds, stl, ply, gltf, glb, off, 3dm, fbx, dae, wrl, 3mf, ifc, brep, step, iges, fcstd, bim 3D 模型文件
13. 支持 dwg, dxf, dwf, iges , igs, dwt, dng, ifc, dwfx, stl, cf2, plt CAD 模型文件 13. 支持 dwg, dxf, dwf, iges , igs, dwt, dng, ifc, dwfx, stl, cf2, plt CAD 模型文件
14. 支持 txt, xml(渲染), xbrl(渲染), md(渲染), java, php, py, js, css 等所有纯文本 14. 支持 txt, xml(渲染), xbrl(渲染), md(渲染), java, php, py, js, css 等所有纯文本
15. 支持 zip, rar, jar, tar, gzip, 7z 等压缩包 15. 支持 zip, rar, jar, tar, gzip, 7z 等压缩包
16. 支持 jpg, jpeg, png, gif, bmp, ico, jfif, webp 等图片预览翻转缩放镜像 16. 支持 jpg, jpeg, png, gif, bmp, ico, jfif, webp ,heic等图片预览翻转缩放镜像
17. 支持 tif, tiff 图信息模型文件 17. 支持 tif, tiff 图信息模型文件
18. 支持 tga 图像格式文件 18. 支持 tga 图像格式文件
19. 支持 svg 矢量图像格式文件 19. 支持 svg 矢量图像格式文件
20. 支持 mp3,wav,mp4,flv 等音视频格式文件 20. 支持 mp3,wav,mp4,flv 等音视频格式文件
21. 支持 avi,mov,rm,webm,ts,rm,mkv,mpeg,ogg,mpg,rmvb,wmv,3gp,ts,swf 等视频格式转码预览 21. 支持 avi,mov,rm,webm,ts,rm,mkv,mpeg,ogg,mpg,rmvb,wmv,3gp,ts 等视频格式转码预览
22. 支持 dcm 等医疗数位影像预览 22. 支持 dcm 等医疗数位影像预览
23. 支持 drawio 绘图预览 23. 支持 drawio 绘图预览
@@ -149,6 +149,47 @@ pdf预览模式预览效果如下
### 历史更新记录 ### 历史更新记录
#### > 2026年01月20日v5.0 版本发布
#### 优化内容
1. xlsx 前端解析优化 - 提升Excel文件前端渲染性能
2. 图片解析优化 - 改进图片处理机制
3. tif 解析优化 - 增强TIF格式支持
4. svg 解析优化 - 优化SVG矢量图渲染
5. json 解析优化 - 改进JSON文件处理
6. ftp多客户端接入优化 - 提升FTP服务兼容性
7. 首页目录访问优化 - 采用post服务端分页机制
8. marked 解析优化 - 改进Markdown渲染
#### 新增功能
1. msg邮件解析 - 新增msg格式邮件文件预览支持
2. heic图片解析 - 新增HEIC格式图片预览支持
3. 跨域方法 - 新增跨域处理机制
4. 高亮方法 - 新增文本高亮功能
5. 页码方法 - 新增文档页码控制
6. AES加密方法 - 新增AES加密支持
7. Basic鉴权方法 - 新增Basic认证机制
8. 秘钥方法 - 新增密钥管理功能
9. 防重复转换 - 新增重复文件转换防护
10. 异步等待 - 新增异步处理机制
11. 上传限制 - 新增不支持文件上传限制
12. cadviewer转换方法 - 新增CAD查看器转换功能
#### 修复问题
1. 压缩包路径问题 - 修复压缩包内部路径处理
2. 安全问题 - 修复安全漏洞
3. 图片水印不全问题 - 修复水印显示不完整
4. SSL自签证书接入问题 - 修复自签名证书兼容性
#### 更新内容
1. JDK版本要求 - 强制要求JDK 21及以上版本
2. pdf前端解析更新 - 升级PDF前端渲染组件
3. odf前端解析更新 - 升级ODF文档前端渲染
4. 3D模型前端解析更新 - 升级3D模型查看器
5. pdf后端异步转换优化 - 实现多线程异步转换
6. tif后端异步转换优化 - 实现多线程异步转换
7. 视频后端异步转换优化 - 实现多线程异步转换
8. CAD后端异步转换优化 - 实现多线程异步转换
#### > 2025年01月16日v4.4.0 版本发布 #### > 2025年01月16日v4.4.0 版本发布
### 新增功能 ### 新增功能

172
README.md
View File

@@ -13,13 +13,13 @@ Document online preview project solution, built using the popular Spring Boot fr
7. Supports document formats like `pdf`, `ofd`, and `rtf`. 7. Supports document formats like `pdf`, `ofd`, and `rtf`.
8. Supports software model files like `xmind`. 8. Supports software model files like `xmind`.
9. Support for `bpmn` workflow files. 9. Support for `bpmn` workflow files.
10. Support for `eml` mail files 10. Support for `eml` , `msg` mail files
11. Support for `epub` book documents 11. Support for `epub` book documents
12. Supports 3D model files like `obj`, `3ds`, `stl`, `ply`, `gltf`, `glb`, `off`, `3dm`, `fbx`, `dae`, `wrl`, `3mf`, `ifc`, `brep`, `step`, `iges`, `fcstd`, `bim`, etc. 12. Supports 3D model files like `obj`, `3ds`, `stl`, `ply`, `gltf`, `glb`, `off`, `3dm`, `fbx`, `dae`, `wrl`, `3mf`, `ifc`, `brep`, `step`, `iges`, `fcstd`, `bim`, etc.
13. Supports CAD model files such as `dwg`, `dxf`, `dwf` `iges` ,` igs`, `dwt` , `dng` , `ifc` , `dwfx` , `stl` , `cf2` , `plt`, etc. 13. Supports CAD model files such as `dwg`, `dxf`, `dwf` `iges` ,` igs`, `dwt` , `dng` , `ifc` , `dwfx` , `stl` , `cf2` , `plt`, etc.
14. Supports all plain text files such as `txt`, `xml` (rendering), `md` (rendering), `java`, `php`, `py`, `js`, `css`, etc. 14. Supports all plain text files such as `txt`, `xml` (rendering), `md` (rendering), `java`, `php`, `py`, `js`, `css`, etc.
15. Supports compressed packages such as `zip`, `rar`, `jar`, `tar`, `gzip`, `7z`, etc. 15. Supports compressed packages such as `zip`, `rar`, `jar`, `tar`, `gzip`, `7z`, etc.
16. Supports image previewing (flip, zoom, mirror) of `jpg`, `jpeg`, `png`, `gif`, `bmp`, `ico`, `jfif`, `webp`, etc. 16. Supports image previewing (flip, zoom, mirror) of `jpg`, `jpeg`, `png`, `gif`, `bmp`, `ico`, `jfif`, `webp`, `heic`, etc.
17. Supports image information model files such as `tif` and `tiff`. 17. Supports image information model files such as `tif` and `tiff`.
18. Supports image format files such as `tga`. 18. Supports image format files such as `tga`.
19. Supports vector image format files such as `svg`. 19. Supports vector image format files such as `svg`.
@@ -63,6 +63,174 @@ URL[https://file.kkview.cn](https://file.kkview.cn)
2. second stepRun the main method of `/server/src/main/java/cn/keking/ServerMain.java`. After starting,visit `http://localhost:8012/`. 2. second stepRun the main method of `/server/src/main/java/cn/keking/ServerMain.java`. After starting,visit `http://localhost:8012/`.
## Change History
### Version 5.0 (January 20, 2026)
#### Optimizations
1. Enhanced xlsx front-end parsing - Improved Excel file front-end rendering performance
2. Optimized image parsing - Enhanced image processing mechanism
3. Improved tif parsing - Enhanced TIF format support
4. Enhanced svg parsing - Optimized SVG vector image rendering
5. Improved json parsing - Enhanced JSON file processing
6. Optimized ftp multi-client access - Improved FTP service compatibility
7. Enhanced home page directory access - Implemented post server-side pagination mechanism
8. Improved marked parsing - Enhanced Markdown rendering
#### New Features
1. msg email parsing - Added support for msg format email file preview
2. heic image parsing - Added support for HEIC format image preview
3. Cross-domain methods - Added cross-domain processing mechanism
4. Highlighting methods - Added text highlighting functionality
5. Pagination methods - Added document page control
6. AES encryption methods - Added AES encryption support
7. Basic authentication methods - Added Basic authentication mechanism
8. Key management methods - Added key management functionality
9. Anti-duplicate conversion - Added duplicate file conversion protection
10. Async waiting - Added asynchronous processing mechanism
11. Upload restrictions - Added restrictions for unsupported file uploads
12. cadviewer conversion methods - Added CAD viewer conversion functionality
#### Fixed Issues
1. Compressed file path issues - Fixed internal path handling in compressed files
2. Security issues - Fixed security vulnerabilities
3. Incomplete image watermark issues - Fixed incomplete watermark display
4. SSL self-signed certificate access issues - Fixed compatibility with self-signed certificates
#### Updates
1. JDK version requirement - Mandatory requirement for JDK 21 or higher
2. pdf front-end parsing update - Upgraded PDF front-end rendering component
3. odf front-end parsing update - Upgraded ODF document front-end rendering
4. 3D model front-end parsing update - Upgraded 3D model viewer
5. pdf backend async conversion optimization - Implemented multi-threaded asynchronous conversion
6. tif backend async conversion optimization - Implemented multi-threaded asynchronous conversion
7. Video backend async conversion optimization - Implemented multi-threaded asynchronous conversion
8. CAD backend async conversion optimization - Implemented multi-threaded asynchronous conversion
### Version 4.4.0 (January 16, 2025)
#### New Features
1. xlsx printing support
2. Added GZIP compression enablement in configuration
3. CAD format now supports conversion to SVG and TIF formats, added timeout termination and thread management
4. Added captcha verification for file deletion
5. Added xbrl format preview support
6. PDF preview added control over signatures, drawings, illustration control, search positioning pagination, and display content definition
7. Added CSV format front-end parsing support
8. Added Docker image support for ARM64
9. Added Office preview conversion timeout property setting
10. Added preview file host blacklist mechanism
#### Optimizations
1. Optimized OFD mobile preview page adaptability
2. Updated xlsx front-end parsing component to accelerate parsing speed
3. Upgraded CAD component
4. Office function adjustments, supporting comments, conversion page limit, watermark generation, etc.
5. Upgraded markdown component
6. Upgraded dcm parsing component
7. Upgraded PDF.JS parsing component
8. Changed video player plugin to ckplayer
9. Smarter tif parsing, supporting modified image formats
10. Improved character encoding detection accuracy for large and small text files, handling concurrency vulnerabilities
11. Refactored file download code, added general file server authentication access design
12. Updated bootstrap component and streamlined unnecessary files
13. Updated epub version, optimized epub display effect
14. Fixed issue where scheduled cache cleanup only deleted disk cache files for multimedia file types
15. Auto-detection of installed Office components, added default paths for LibreOffice 7.5 & 7.6 versions
16. Changed drawio default to preview mode
17. Added PDF thread management, timeout management, memory cache management, updated PDF parsing component version
18. Optimized Dockerfile for true cross-platform image building
#### Fixes
1. Fixed forceUpdatedCache property setting issue where local cache files weren't updated
2. Fixed PDF decryption error after successful encrypted file conversion
3. Fixed BPMN cross-domain support issue
4. Fixed special character error in compressed package secondary reverse proxy
5. Fixed video cross-domain configuration causing video preview failure
6. Fixed TXT text pagination secondary loading issue
7. Fixed Drawio missing Base64 component issue
8. Fixed Markdown escaping issue
9. Fixed EPUB cross-domain error
10. Fixed URL special character issues
11. Fixed compressed package traversal vulnerability
12. Fixed compressed file path errors, image collection path errors, watermark issues, etc.
13. Fixed front-end parsing XLSX containing EMF format file errors
### Version 4.3.0 (July 5, 2023)
#### New Features
1. Added DCM medical digital imaging preview
2. Added drawio drawing preview
3. Added command to regenerate with cache enabled: &forceUpdatedCache=true
4. Added dwg CAD file preview
5. Added PDF file password support
6. Added DPI customization for PDF file image generation
7. Added configuration to delete converted OFFICE, CAD, TIFF, compressed package source files (enabled by default to save disk space)
8. Added front-end xlsx parsing method
9. Added support for pages, eps, iges, igs, dwt, dng, ifc, dwfx, stl, cf2, plt and other formats
#### Optimizations
1. Modified generated PDF file names to include file extensions to prevent duplicate names
2. Adjusted SQL file preview method
3. Optimized OFD preview compatibility
4. Beautified TXT text pagination box display
5. Upgraded Linux/Docker built-in office to LibreOffice-7.5.3
6. Upgraded Windows built-in office to LibreOffice-7.5.3 Portable
7. Other functional optimizations
#### Fixes
1. Fixed compressed package path errors in reverse proxy scenarios
2. Fixed .click error when image preview URLs contain &
3. Fixed known OFD preview issues
4. Fixed page error when clicking on file directories (tree nodes) in compressed package preview
5. Other known issue fixes
### Version 4.2.1 (April 18, 2023)
#### Change Log
1. Fixed null pointer bug in dwg file preview
### Version 4.2.0 (April 13, 2023)
#### New Features
1. Added SVG format file preview support
2. Added encrypted Office file preview support
3. Added encrypted zip, rar, and other compressed package file preview support
4. Added xmind software model file preview support
5. Added BPMN workflow model file preview support
6. Added eml email file preview support
7. Added EPUB e-book file preview support
8. Added office document format support: dotm, ett, xlt, xltm, wpt, dot, xlam, xla, dotx, etc.
9. Added 3D model file support: obj, 3ds, stl, ply, gltf, glb, off, 3dm, fbx, dae, wrl, 3mf, ifc, brep, step, iges, fcstd, bim, etc.
10. Added configurable high-risk file upload restrictions (e.g., exe files)
11. Added configurable site filing information
12. Added password requirement for demo site file deletion
#### Optimizations
1. Added caching for text document preview
2. Beautified 404, 500 error pages
3. Optimized invoice and other OFD file preview seal rendering compatibility
4. Removed office-plugin module, using new jodconverter component
5. Optimized Excel file preview effect
6. Optimized CAD file preview effect
7. Updated xstream, junrar, pdfbox, and other dependency versions
8. Updated TIF to PDF conversion plugin, added conversion cache
9. Optimized demo page UI deployment
10. Compressed package file preview supports directories
#### Fixes
1. Fixed XSS issues in some interfaces
2. Fixed console printed demo address not following content-path configuration
3. Fixed OFD file preview cross-domain issues
4. Fixed internal self-signed certificate HTTPS URL file download issues
5. Fixed special character file deletion issues
6. Fixed OOM caused by unreclaimed memory in PDF to image conversion
7. Fixed garbled preview for xlsx 7.4+ version files
8. Fixed TrustHostFilter not intercepting cross-domain interfaces (security issue - upgrade required if using TrustHost)
9. Fixed compressed package file preview filename garbled issue on Linux systems
10. Fixed OFD file preview only displaying 10 pages
### Changelog ### Changelog
> December 14, 2022, version 4.1.0 released: > December 14, 2022, version 4.1.0 released:

View File

@@ -6,7 +6,7 @@
<groupId>cn.keking</groupId> <groupId>cn.keking</groupId>
<artifactId>kkFileView-parent</artifactId> <artifactId>kkFileView-parent</artifactId>
<version>4.4.0</version> <version>5.0</version>
<properties> <properties>
<!-- ========== Java 和编译配置 ========== --> <!-- ========== Java 和编译配置 ========== -->

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<artifactId>kkFileView-parent</artifactId> <artifactId>kkFileView-parent</artifactId>
<groupId>cn.keking</groupId> <groupId>cn.keking</groupId>
<version>4.4.0</version> <version>5.0</version>
</parent> </parent>
<artifactId>kkFileView</artifactId> <artifactId>kkFileView</artifactId>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,478 @@
###############################################################################
# 服务器基础配置需要重启生效
###############################################################################
# 服务器端口号默认8012
# 可以通过环境变量 KK_SERVER_PORT 覆盖
server.port = ${KK_SERVER_PORT:8012}
# 应用上下文路径默认为根路径 /
# 可以通过环境变量 KK_CONTEXT_PATH 覆盖
server.servlet.context-path = ${KK_CONTEXT_PATH:/}
# 字符编码设置统一使用UTF-8
server.servlet.encoding.charset = utf-8
# 启用响应压缩减少网络传输
server.compression.enabled = true
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
# 文件上传大小限制默认500MB
# 注意需要同时设置spring.servlet.multipart.max-file-size和max-request-size
spring.servlet.multipart.max-file-size = 500MB
spring.servlet.multipart.max-request-size = 500MB
# FreeMarker模板引擎配置
spring.freemarker.template-loader-path = classpath:/web/
spring.freemarker.cache = false
spring.freemarker.charset = UTF-8
spring.freemarker.check-template-location = true
spring.freemarker.content-type = text/html
spring.freemarker.expose-request-attributes = true
spring.freemarker.expose-session-attributes = true
spring.freemarker.request-context-attribute = request
spring.freemarker.suffix = .ftl
# Spring Boot Actuator监控端点配置
management.endpoints.web.exposure.include = health,info,metrics
management.endpoint.health.show-details = always
management.health.defaults.enabled = true
###############################################################################
# Office文档处理配置部分支持动态配置
###############################################################################
# Office组件安装路径默认为自动查找
# Windows示例注意双反斜杠C:\\Program Files (x86)\\OpenOffice 4
# Linux示例/opt/libreoffice
# MacOS示例/Applications/LibreOffice.app/Contents
office.home = ${KK_OFFICE_HOME:default}
# Office组件服务端口支持多个端口实现负载均衡
office.plugin.server.ports = 2001,2002
# Office组件任务超时时间默认5分钟
office.plugin.task.timeout = 5m
# 每个进程最大任务数防止内存溢出
office.plugin.task.maxtasksperprocess = 200
# 任务执行超时时间默认5分钟
office.plugin.task.taskexecutiontimeout = 5m
# Office文档分页范围支持动态配置
# 默认false开启后可以指定转换的页面范围
office.pagerange = ${KK_OFFICE_PAGERANGE:false}
# Office文档水印功能支持动态配置
# 默认false开启后会在Office文档上添加水印
office.watermark = ${KK_OFFICE_WATERMARK:false}
# Office图片质量1-100默认80
# 值越高图片质量越好但文件越大
office.quality = ${KK_OFFICE_QUALITY:80}
# Office图片最大分辨率默认150
# 控制生成图片的最大分辨率
office.maximageresolution = ${KK_OFFICE_MAXIMAGERESOLUTION:150}
# 导出Office书签支持动态配置
# 默认true转换PDF时保留书签
office.exportbookmarks = ${KK_OFFICE_EXPORTBOOKMARKS:true}
# 是否将Office文档中的批注作为PDF注释导出默认为true导出
# 保留批注便于文档审阅
office.exportnotes = ${KK_OFFICE_EXPORTNOTES:true}
# 加密文档生成的PDF是否添加密码默认为true添加
# 密码为原始加密文档的密码增强文档安全性
office.documentopenpasswords = ${KK_OFFICE_DOCUMENTOPENPASSWORD:true}
# Excel文档(xlsx)的前端解析方式默认为webWeb端解析
# web: 使用前端SheetJS库解析减轻服务器压力
# image: 服务器转换为图片兼容性更好
office.type.web = ${KK_OFFICE_TYPE_WEB:web}
# Office文档预览类型
# 支持动态配置可选值image/pdf
office.preview.type = ${KK_OFFICE_PREVIEW_TYPE:image}
# 是否关闭Office预览模式切换开关默认为false允许切换
# 设置为true时用户无法在图片和PDF模式间切换
office.preview.switch.disabled = ${KK_OFFICE_PREVIEW_SWITCH_DISABLED:false}
###############################################################################
# CAD文件处理配置支持动态配置
###############################################################################
# CAD文件预览类型
# svg: 转换为SVG矢量格式缩放不失真
# pdf: 转换为PDF格式便于打印和标注
cad.preview.type = ${KK_CAD_PREVIEW_TYPE:svg}
# Cad转换模块设置(aspose-cad=1 ,cadviewer=3)
# aspose-cad 默认集成到系统,但是特别吃服务器性能 (支持转换格式为 svgpdf)
# cadviewer 下载地址 https://cadviewer.com/alldownloads/autoxchange/ 更具自己系统下载转换包(支持转换格式为 svg、svgz、pdf)
# 1=aspose-cad 转换格式为 pdf,svg,tif 支持类型最多
# 2=cadviewer 转换格式为 pdf,svg 支持的类型 dwg dxf dwf
cad.conversionmodule = 2
# Cad 后端转换包路径 linux 严格注意大小写
# cadviewer windows 修改名称为 cadviewer.exe linux修改名称为 cadviewer 需要安装字体
# cadviewer 字体下载 https://cadviewer.com/downloads/fonts/fonts.tar.gz 放在 cad.file.path 目录里面的fonts.
cad.cadconverterpath = D:/github/AutoXChange/
# CAD文件处理线程数
cad.thread = ${KK_CAD_THREAD:5}
# CAD文件处理超时时间
cad.timeout = ${KK_CAD_TIMEOUT:90}
###############################################################################
# PDF文件处理配置支持动态配置
###############################################################################
# 是否禁止PDF演示模式默认为true禁止
pdf.presentationMode.disable = ${KK_PDF_PRESENTATION_MODE_DISABLE:true}
# 是否禁止PDF文件菜单中的"打开文件"选项默认为true禁止
pdf.openFile.disable = ${KK_PDF_OPEN_FILE_DISABLE:true}
# 是否禁止PDF打印功能默认为true禁止
pdf.print.disable = ${KK_PDF_PRINT_DISABLE:true}
# 是否禁止PDF下载功能默认为true禁止
pdf.download.disable = ${KK_PDF_DOWNLOAD_DISABLE:true}
# 是否禁止PDF书签/大纲功能默认为true禁止
pdf.bookmark.disable = ${KK_PDF_BOOKMARK_DISABLE:true}
# 是否禁止PDF编辑功能注释表单等默认为false允许编辑
pdf.disable.editing = ${KK_PDF_DISABLE_EDITING:false}
# PDF处理最大线程数控制并发处理能力
pdf.max.threads = 10
# PDF处理超时配置
pdf.timeout.small = 90
pdf.timeout.medium = 180
pdf.timeout.large = 300
pdf.timeout.xlarge = 600
# PDF智能DPI优化
# 是否启用PDF DPI智能调整默认为true启用
# 根据PDF页数自动调整DPI平衡清晰度和性能
pdf.dpi.enabled = true
# PDF转图片的基准DPI默认为144
# 当DPI优化禁用时使用此值
pdf2jpg.dpi = ${KK_PDF2JPG_DPI:144}
# 智能DPI分级配置
# 小文件0-50页150 DPI高质量
pdf.dpi.small = 150
# 中等文件50-100页120 DPI平衡质量与性能
pdf.dpi.medium = 120
# 大文件100-200页96 DPI优化性能
pdf.dpi.large = 96
# 超大文件200-500页72 DPI快速转换
pdf.dpi.xlarge = 72
# 巨量文件>500页72 DPI最小资源消耗
pdf.dpi.xxlarge = 72
###############################################################################
# TIF文件处理配置支持动态配置
###############################################################################
# TIF文件预览类型
# tif: 使用前端Tiff.js插件直接浏览需要浏览器支持
# jpg: 服务器转换为JPG格式后显示
# pdf: 服务器转换为PDF格式显示
tif.preview.type = ${KK_TIF_PREVIEW_TYPE:tif}
# TIF文件处理线程数
tif.thread = 5
# TIF文件处理超时时间
tif.timeout = 90
###############################################################################
# 媒体文件处理配置支持动态配置
###############################################################################
# 媒体文件类型音频视频
media = ${KK_MEDIA:mp3,wav,mp4,flv,mpd,m3u8,ts,mpeg,m4a}
# 需要转换的媒体文件类型
convertMedias = ${KK_CONVERTMEDIAS:avi,mov,wmv,mkv,3gp,rm,mpeg}
# 媒体文件超时控制
media.timeout.enabled = true
media.small.file.timeout = 30
media.medium.file.timeout = 60
media.large.file.timeout = 180
media.xl.file.timeout = 300
media.xxl.file.timeout = 600
media.xxxl.file.timeout = 1200
# 媒体文件转换最大大小MB
media.convert.max.size = 300
# 是否禁用视频格式转换功能默认为false禁用
# 重要视频转换非常消耗CPU和内存资源
media.convert.disable = ${KK_MEDIA_CONVERT_DISABLE:true}
###############################################################################
# 文件存储与缓存配置支持动态配置
###############################################################################
# 预览生成资源的存储路径默认为应用根路径下的file目录
# Windows示例D:\\kkFileview\\注意双反斜杠
# Linux示例/opt/kkfileview/file/
# 重要确保应用有该目录的读写权限
file.dir = ${KK_FILE_DIR:default}
# 允许预览的本地文件夹路径默认为default禁止所有本地文件预览
# 安全警告配置此路径可能允许访问系统文件请谨慎配置
# Windows示例注意前面加反斜杠\D:\\kkFileview\\1\\1.txt
# Linux示例注意前面加正斜杠/opt/1.txt
# 使用file协议访问file://d:/1/1.txtWindows或 file:/opt/1.txtLinux
local.preview.dir = \D:\\
# 是否启用缓存支持动态配置
# 默认true开启缓存提高性能
cache.enabled = ${KK_CACHE_ENABLED:true}
# 缓存实现类型默认为jdk使用JDK内置对象实现
# 可选值
# jdk: JDK内置ConcurrentHashMap单机部署推荐
# redis: Redis分布式缓存集群部署推荐
# default: 内嵌RocksDB支持持久化
cache.type = ${KK_CACHE_TYPE:jdk}
# Redis部署模式默认为single单机模式
# 可选值
# single: 单机模式默认
# cluster: 集群模式
# sentinel: 哨兵模式高可用
# master-slave: 主从模式
spring.redisson.mode = single
# Redis连接地址支持多种格式
# 单机模式redis://127.0.0.1:6379
# 集群模式redis://node1:6379,redis://node2:6379,redis://node3:6379
# 哨兵模式redis://sentinel1:26379,redis://sentinel2:26379
spring.redisson.address = ${KK_SPRING_REDISSON_ADDRESS:redis://127.0.0.1:6379}
# Redis连接密码无密码时留空
spring.redisson.password = ${KK_SPRING_REDISSON_PASSWORD:}
# Redis数据库索引默认为00-15
# 不同业务可使用不同数据库隔离
spring.redisson.database = ${KK_SPRING_REDISSON_DATABASE:0}
# 缓存清理配置
# 是否启用缓存自动清理默认为true启用
# 定期清理过期缓存避免磁盘空间无限增长
cache.clean.enabled = ${KK_CACHE_CLEAN_ENABLED:true}
# 缓存自动清理时间使用Quartz cron表达式默认为每天凌晨3点执行
# 表达式格式 可选
# 0 0 3 * * ? 表示每天3:00:00执行清理
cache.clean.cron = ${KK_CACHE_CLEAN_CRON:0 0 3 * * ?}
###############################################################################
# 安全与访问控制配置支持动态配置
###############################################################################
# 提供预览服务的地址默认从请求url读如果使用nginx等反向代理需要手动设置
# base.url = https://file.keking.cn
base.url = ${KK_BASE_URL:default}
# 信任站点白名单配置多个用','隔开
# 安全提示为防止SSRF攻击强烈建议配置信任主机白名单
# 如果不配置系统将默认拒绝所有外部文件预览请求
# 配置示例
# trust.host = kkview.cn,yourdomain.com,cdn.example.com
# 如果需要允许所有域名不推荐仅用于测试环境请设置为
# trust.host = *
# 当前配置默认本机测试 正式启用请修改
trust.host = *
# 不信任站点黑名单配置多个用逗号隔开
# 黑名单优先级高于白名单设置后将禁止预览来自这些站点的文件
# 建议配置禁止访问内网地址和本地地址防止内部信息泄露
# 配置示例
# not.trust.host = localhost,127.0.0.1,0.0.0.0,192.168.*,10.*,172.16.*,172.17.*,172.18.*,172.19.*,172.20.*,172.21.*,172.22.*,172.23.*,172.24.*,172.25.*,172.26.*,172.27.*,172.28.*,172.29.*,172.30.*,172.31.*
not.trust.host = ${KK_NOT_TRUST_HOST:default}
# 禁止访问的文件类型安全限制
# 支持动态配置格式exe,dll,dat
prohibit = ${KK_PROHIBIT:exe,dll,dat}
# 是否忽略SSL证书验证默认为true忽略
# 用于开发环境或自签名证书场景
# 生产环境建议设置为false启用完整的证书验证
kk.ignore.ssl = true
# 是否启用URL重定向功能默认为true启用
# 用于处理文件下载外部资源引用等场景
kk.enable.redirect = true
###############################################################################
# 水印配置支持动态配置
###############################################################################
# 水印文本内容
# 可以通过环境变量 WATERMARK_TXT 覆盖
watermark.txt = ${WATERMARK_TXT:}
# 水印X轴间距
# 可以通过环境变量 WATERMARK_X_SPACE 覆盖
watermark.x.space = ${WATERMARK_X_SPACE:10}
# 水印Y轴间距
# 可以通过环境变量 WATERMARK_Y_SPACE 覆盖
watermark.y.space = ${WATERMARK_Y_SPACE:10}
# 水印字体
# 可以通过环境变量 WATERMARK_FONT 覆盖
watermark.font = ${WATERMARK_FONT:微软雅黑}
# 水印字体大小
# 可以通过环境变量 WATERMARK_FONTSIZE 覆盖
watermark.fontsize = ${WATERMARK_FONTSIZE:18px}
# 水印颜色
# 可以通过环境变量 WATERMARK_COLOR 覆盖
watermark.color = ${WATERMARK_COLOR:black}
# 水印透明度0.0-1.0
# 可以通过环境变量 WATERMARK_ALPHA 覆盖
watermark.alpha = ${WATERMARK_ALPHA:0.2}
# 水印宽度
# 可以通过环境变量 WATERMARK_WIDTH 覆盖
watermark.width = ${WATERMARK_WIDTH:180}
# 水印高度
# 可以通过环境变量 WATERMARK_HEIGHT 覆盖
watermark.height = ${WATERMARK_HEIGHT:80}
# 水印旋转角度
# 可以通过环境变量 WATERMARK_ANGLE 覆盖
watermark.angle = ${WATERMARK_ANGLE:10}
###############################################################################
# FTP文件访问配置支持动态配置
###############################################################################
# FTP模块设置
# 预览源为FTP时可在ftp url后面加参数?ftp.username=ftpuser&ftp.password=123456&ftp.control.encoding=GBK,指定,不指定默认用配置的 (为了安全我们强烈建议在配置中设置相关信息)
# ftp.control.encodin (根据FTP服务器操作系统选择Linux一般为UTF-8Windows一般为GBK)
# 使用方法,支持,分割第一个是域名或者IP地址后面是用户名在后面是密码,在后面是编码(用户名密码和编码用:分割, 域名用,分割),切记url不需要任何协议头
# ftp.username = 地址:端口:用户名:密码:编码,192.168.0.2:21:name:123456:UTF-8,www.xxx.com:21:admin:pass:UTF-8 多客户端,分割
# ftp.username =10.99.1.2:21:666:88888:GBK
ftp.username = false
###############################################################################
# 十一首页与文件管理配置支持动态配置
###############################################################################
# 是否禁用首页文件上传功能默认为true禁用
# 设置为true可关闭上传功能仅用于预览
file.upload.disable = false
# 网站备案信息显示在首页底部默认为空
beian = ${KK_BEIAN:default}
# 首页初始化加载的页码默认为1第一页
home.pagenumber = ${DEFAULT_HOME_PAGENUMBER:1}
# 首页每页显示的文件数量默认为20
home.pagesize = ${DEFAULT_HOME_PAGSIZE:20}
# 文件删除验证配置
# 是否启用验证码验证删除文件默认为false不启用
# 启用后删除文件需要输入验证码防止误删
delete.captcha = ${KK_DELETE_CAPTCHA:false}
# 删除文件密码默认为123456
delete.password = ${KK_DELETE_PASSWORD:123456}
# 是否删除转换后的源文件默认为true删除
# 启用可节约磁盘空间但会丢失原始文件
delete.source.file = ${KK_DELETE_SOURCE_FILE:true}
###############################################################################
# 十二权限与认证配置支持动态配置
###############################################################################
# 是否启用图片预览权限默认为true启用
# 设置为false可禁用所有图片预览功能
kk.Picturespreview = true
# 是否启用跨域文件获取权限默认为true启用
kk.Getcorsfile = true
# 是否启用添加异步任务权限默认为true启用
# 大文件转换通常使用异步任务处理
kk.addTask = true
# API密钥功能默认为false禁用
# 启用后需要提供密钥才能调用API
kk.key = false
# AES加密密钥必须为16位字符
# 启用AES加密时接入方需使用相同的密钥
# 用于敏感数据传输加密
aes.key = 1234567890123456
# Basic认证配置格式域名:用户名:密码多个用逗号分隔
# 用于保护特定域名的访问
# 示例192.168.0.1:admin:pass123,example.com:user:pass456
basic.name = 10.99.1.2:aaa:bbb
# User-Agent验证字符串默认不启用
# 可用于简单的客户端验证
useragent = false
###############################################################################
# 十三高级功能与兼容性配置支持动态配置
###############################################################################
# 异步配置刷新定时时间
kk.refreshschedule = 2
# 首页是否显示AES密钥 默认为false禁用
kk.isshowaeskey = false
# 是否允许XLSX编辑
kk.xlsxallowedit = true
# 是否显示XLSX工具栏
kk.xlsxshowtoolbar = true
# 首页是否显示key密钥 默认为false禁用
kk.isshowkey = true
# 预览html文件 是否启用JavaScript 默认为true启用
kk.scriptjs = true
###############################################################################
# 十四文件类型分类配置支持动态配置
###############################################################################
# 纯文本文件类型直接显示
simText = ${KK_SIMTEXT:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd}

File diff suppressed because it is too large Load Diff

View File

@@ -33,13 +33,15 @@ public enum FileType {
EPUB("epubFilePreviewImpl"), EPUB("epubFilePreviewImpl"),
BPMN("bpmnFilePreviewImpl"), BPMN("bpmnFilePreviewImpl"),
DCM("dcmFilePreviewImpl"), DCM("dcmFilePreviewImpl"),
MSG("msgFilePreviewImpl"),
DRAWIO("drawioFilePreviewImpl"); DRAWIO("drawioFilePreviewImpl");
private static final String[] OFFICE_TYPES = {"docx", "wps", "doc", "docm", "xls", "xlsx", "csv" ,"xlsm", "ppt", "pptx", "vsd", "rtf", "odt", "wmf", "emf", "dps", "et", "ods", "ots", "tsv", "odp", "otp", "sxi", "ott", "vsdx", "fodt", "fods", "xltx","tga","psd","dotm","ett","xlt","xltm","wpt","dot","xlam","dotx","xla","pages", "eps"}; private static final String[] OFFICE_TYPES = {"docx", "wps", "doc", "docm", "xls", "xlsx", "csv" ,"xlsm", "ppt", "pptx", "vsd", "rtf", "odt", "wmf", "emf", "dps", "et", "ods", "ots", "tsv", "odp", "otp", "sxi", "ott", "vsdx", "fodt", "fods", "xltx","tga","psd","dotm","ett","xlt","xltm","wpt","dot","xlam","dotx","xla","pages", "eps"};
private static final String[] PICTURE_TYPES = {"jpg", "jpeg", "png", "gif", "bmp", "ico", "jfif", "webp"}; private static final String[] PICTURE_TYPES = {"jpg", "jpeg", "png", "gif", "bmp", "ico", "jfif", "webp", "heic", "avif"};
private static final String[] ARCHIVE_TYPES = {"rar", "zip", "jar", "7-zip", "tar", "gzip", "7z"}; private static final String[] ARCHIVE_TYPES = {"rar", "zip", "jar", "7-zip", "tar", "gzip", "7z"};
private static final String[] ONLINE3D_TYPES = {"obj", "3ds", "stl", "ply", "off", "3dm", "fbx", "dae", "wrl", "3mf", "ifc","glb","o3dv","gltf","stp","bim","fcstd","step","iges","brep"}; private static final String[] ONLINE3D_TYPES = {"obj", "3ds", "stl", "ply", "off", "3dm", "fbx", "dae", "wrl", "3mf", "ifc","glb","o3dv","gltf","stp","bim","fcstd","step","iges","brep"};
private static final String[] EML_TYPES = {"eml"}; private static final String[] EML_TYPES = {"eml"};
private static final String[] MSG_TYPES = {"msg"};
private static final String[] XMIND_TYPES = {"xmind"}; private static final String[] XMIND_TYPES = {"xmind"};
private static final String[] EPUB_TYPES = {"epub"}; private static final String[] EPUB_TYPES = {"epub"};
private static final String[] DCM_TYPES = {"dcm"}; private static final String[] DCM_TYPES = {"dcm"};
@@ -96,6 +98,9 @@ public enum FileType {
for (String eml : EML_TYPES) { for (String eml : EML_TYPES) {
FILE_TYPE_MAPPER.put(eml, FileType.EML); FILE_TYPE_MAPPER.put(eml, FileType.EML);
} }
for (String msg : MSG_TYPES) {
FILE_TYPE_MAPPER.put(msg, FileType.MSG);
}
for (String xmind : XMIND_TYPES) { for (String xmind : XMIND_TYPES) {
FILE_TYPE_MAPPER.put(xmind, FileType.XMIND); FILE_TYPE_MAPPER.put(xmind, FileType.XMIND);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -78,7 +78,7 @@ public class CompressFileReader {
FileType type = FileType.typeFromUrl(filePathInsideArchive.toString()); FileType type = FileType.typeFromUrl(filePathInsideArchive.toString());
if (type.equals(FileType.PICTURE)) { //图片缓存到集合,为了特殊符号需要进行编码 if (type.equals(FileType.PICTURE)) { //图片缓存到集合,为了特殊符号需要进行编码
imgUrls.add(baseUrl + URLEncoder.encode(fileName + packagePath+"/"+ folderPath.relativize(filePathInsideArchive).toString().replace("\\", "/"), "UTF-8")); imgUrls.add(baseUrl + URLEncoder.encode(fileName + packagePath+"/"+ folderPath.relativize(filePathInsideArchive).toString().replace("\\", "/"), StandardCharsets.UTF_8).replaceAll("%2F", "/"));
} }
} }
} }

View File

@@ -264,8 +264,13 @@ public class FileHandlerService {
.replaceAll("%3F", "?") .replaceAll("%3F", "?")
.replaceAll("%26", "&") .replaceAll("%26", "&")
.replaceAll("%3D", "="); .replaceAll("%3D", "=");
} }
originFileName = KkFileUtils.htmlEscape(originFileName); //文件名处理 originFileName = KkFileUtils.htmlEscape(originFileName); //文件名处理
if (!KkFileUtils.validateFileNameLength(originFileName)) {
// 处理逻辑:抛出异常、记录日志、返回错误等
throw new IllegalArgumentException("文件名超过系统限制");
}
boolean isHtmlView = suffix.equalsIgnoreCase("xls") || suffix.equalsIgnoreCase("xlsx") || suffix.equalsIgnoreCase("csv") || suffix.equalsIgnoreCase("xlsm") || suffix.equalsIgnoreCase("xlt") || suffix.equalsIgnoreCase("xltm") || suffix.equalsIgnoreCase("et") || suffix.equalsIgnoreCase("ett") || suffix.equalsIgnoreCase("xlam"); boolean isHtmlView = suffix.equalsIgnoreCase("xls") || suffix.equalsIgnoreCase("xlsx") || suffix.equalsIgnoreCase("csv") || suffix.equalsIgnoreCase("xlsm") || suffix.equalsIgnoreCase("xlt") || suffix.equalsIgnoreCase("xltm") || suffix.equalsIgnoreCase("et") || suffix.equalsIgnoreCase("ett") || suffix.equalsIgnoreCase("xlam");
String cacheFilePrefixName = null; String cacheFilePrefixName = null;
try { try {

View File

@@ -34,6 +34,9 @@ public interface FilePreview {
String NOT_SUPPORTED_FILE_PAGE = "fileNotSupported"; String NOT_SUPPORTED_FILE_PAGE = "fileNotSupported";
String XLSX_FILE_PREVIEW_PAGE = "officeweb"; String XLSX_FILE_PREVIEW_PAGE = "officeweb";
String CSV_FILE_PREVIEW_PAGE = "csv"; String CSV_FILE_PREVIEW_PAGE = "csv";
String MSG_FILE_PREVIEW_PAGE = "msg";
String HEIC_FILE_PREVIEW_PAGE = "heic";
String CADVIEWER_FILE_PREVIEW_PAGE = "cadviewer";
String WAITING_FILE_PREVIEW_PAGE = "waiting"; String WAITING_FILE_PREVIEW_PAGE = "waiting";
String filePreviewHandle(String url, Model model, FileAttribute fileAttribute); String filePreviewHandle(String url, Model model, FileAttribute fileAttribute);

View File

@@ -28,7 +28,6 @@ import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/** /**
* PDF转JPG服务 - JDK 21 高性能优化版本(使用虚拟线程和结构化并发) * PDF转JPG服务 - JDK 21 高性能优化版本(使用虚拟线程和结构化并发)
@@ -59,8 +58,7 @@ public class PdfToJpgService {
// JDK 21: 使用虚拟线程调度器 // JDK 21: 使用虚拟线程调度器
private final ScheduledExecutorService virtualCacheCleanupScheduler; private final ScheduledExecutorService virtualCacheCleanupScheduler;
// 使用读写锁保护缓存操作
private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock();
// 加密PDF缓存记录类 // 加密PDF缓存记录类
private static class EncryptedPdfCache { private static class EncryptedPdfCache {
@@ -286,49 +284,6 @@ public class PdfToJpgService {
logger.info("缓存清理任务已启动每5分钟执行一次"); logger.info("缓存清理任务已启动每5分钟执行一次");
} }
/**
* 启动性能监控
*/
private void startPerformanceMonitoring() {
// 每10分钟记录一次性能统计
virtualCacheCleanupScheduler.scheduleAtFixedRate(() -> {
try {
logPerformanceStatistics();
} catch (Exception e) {
logger.error("性能监控任务执行失败", e);
}
}, 5, 10, TimeUnit.MINUTES);
}
/**
* 记录性能统计信息
*/
private void logPerformanceStatistics() {
try {
// 收集性能指标
int runningTasksCount = runningTasks.size();
int activeTasks = activeTaskCount.get();
int totalCompleted = totalCompletedTasks.get();
int cacheSize = encryptedPdfCacheMap.size();
// 计算内存使用
long totalMemory = Runtime.getRuntime().totalMemory();
long freeMemory = Runtime.getRuntime().freeMemory();
long usedMemory = totalMemory - freeMemory;
logger.info(
"PDF转换服务性能统计 - 运行中任务数: {}, 活跃任务数: {}, 累计完成任务数: {}, " +
"内存缓存数量: {}, 内存使用: {} MB / {} MB ({}%), 虚拟线程: {}",
runningTasksCount, activeTasks, totalCompleted, cacheSize,
usedMemory / 1024 / 1024, totalMemory / 1024 / 1024,
(usedMemory * 100 / totalMemory),
Thread.currentThread().isVirtual() ? "" : ""
);
} catch (Exception e) {
logger.error("记录性能统计时发生异常", e);
}
}
/** /**
* 监控缓存统计信息 * 监控缓存统计信息
@@ -527,84 +482,11 @@ public class PdfToJpgService {
return imageUrls; return imageUrls;
} }
/**
* PDF转JPG - 异步版本(虚拟线程优化)
public CompletableFuture<List<String>> pdf2jpgAsync(String fileNameFilePath, String pdfFilePath,
String pdfName, FileAttribute fileAttribute) {
CompletableFuture<List<String>> future = new CompletableFuture<>();
// 尝试获取信号量,如果获取不到,则立即拒绝任务
if (!concurrentTaskSemaphore.tryAcquire()) {
future.completeExceptionally(new RejectedExecutionException("系统繁忙,请稍后再试"));
return future;
}
// JDK 21: 使用虚拟线程提交任务
Future<?> taskFuture = virtualThreadExecutor.submit(() -> {
try {
List<String> result = pdf2jpg(fileNameFilePath, pdfFilePath, pdfName, fileAttribute);
future.complete(result);
} catch (Exception e) {
future.completeExceptionally(e);
} finally {
// 必须在finally中释放信号量
concurrentTaskSemaphore.release();
runningTasks.remove(pdfName);
}
});
// 记录正在运行的任务
runningTasks.put(pdfName, taskFuture);
// 设置超时检查(使用虚拟线程)
scheduleVirtualTimeoutCheck(pdfName, future, taskFuture);
return future;
}
*/
/**
* 虚拟线程超时检查
*/
private void scheduleVirtualTimeoutCheck(String fileName, CompletableFuture<List<String>> taskFuture,
Future<?> future) {
CompletableFuture.runAsync(() -> {
try {
int timeout = calculateTimeoutByFileName();
taskFuture.get(timeout, TimeUnit.SECONDS);
} catch (TimeoutException e) {
handleConversionTimeout(fileName, taskFuture, future);
} catch (Exception e) {
// 正常完成或异常
}
}, virtualThreadExecutor);
}
/**
* 处理转换超时
*/
private void handleConversionTimeout(String fileName, CompletableFuture<List<String>> taskFuture,
Future<?> future) {
logger.error("PDF转换超时: {}", fileName);
// 取消正在运行的任务
if (future != null && !future.isDone()) {
boolean cancelled = future.cancel(true);
logger.info("尝试取消PDF转换任务 {}: {}", fileName, cancelled ? "成功" : "失败");
}
// 从运行任务列表中移除
runningTasks.remove(fileName);
// 完成Future
taskFuture.completeExceptionally(new TimeoutException("PDF转换超时: " + fileName));
}
/** /**
* PDF转JPG - 高性能主方法 * PDF转JPG - 高性能主方法
*/ */
public List<String> pdf2jpg(String fileNameFilePath, String pdfFilePath, public List<String> pdf2jpg(String fileNameFilePath, String pdfFilePath,
String pdfName, FileAttribute fileAttribute) throws Exception { FileAttribute fileAttribute) throws Exception {
boolean forceUpdatedCache = fileAttribute.forceUpdatedCache(); boolean forceUpdatedCache = fileAttribute.forceUpdatedCache();
boolean usePasswordCache = fileAttribute.getUsePasswordCache(); boolean usePasswordCache = fileAttribute.getUsePasswordCache();
String filePassword = fileAttribute.getFilePassword(); String filePassword = fileAttribute.getFilePassword();
@@ -951,12 +833,6 @@ public class PdfToJpgService {
} }
} }
/**
* 根据文件名计算超时时间
*/
private int calculateTimeoutByFileName() {
return ConfigConstants.getPdfTimeoutMedium();
}
/** /**
* 按页码排序 * 按页码排序
@@ -975,107 +851,5 @@ public class PdfToJpgService {
return sortedImageUrls; return sortedImageUrls;
} }
/**
* 强制取消指定文件的转换任务
*/
public boolean cancelConversion(String fileName) {
Future<?> future = runningTasks.get(fileName);
if (future != null) {
boolean cancelled = future.cancel(true);
if (cancelled) {
logger.info("成功取消PDF转换任务: {}", fileName);
runningTasks.remove(fileName);
}
return cancelled;
}
return false;
}
/**
* 获取正在运行的任务数量
*/
public int getRunningTaskCount() {
return runningTasks.size();
}
/**
* 获取所有正在运行的文件名
*/
public Set<String> getRunningTasks() {
return new HashSet<>(runningTasks.keySet());
}
/**
* 获取缓存统计信息(用于监控)
*/
public Map<String, Object> getCacheStatistics() {
Map<String, Object> stats = new HashMap<>();
cacheLock.readLock().lock();
try {
stats.put("cacheSize", encryptedPdfCacheMap.size());
stats.put("runningTasks", runningTasks.size());
stats.put("activeTasks", activeTaskCount.get());
stats.put("totalCompleted", totalCompletedTasks.get());
// 计算缓存过期情况
long expireTime = 10 * 60 * 1000L;
int expiredCount = 0;
for (EncryptedPdfCache cache : encryptedPdfCacheMap.values()) {
if (cache.isExpired(expireTime)) {
expiredCount++;
}
}
stats.put("expiredCaches", expiredCount);
} finally {
cacheLock.readLock().unlock();
}
return stats;
}
/**
* 手动清理所有过期缓存(供管理接口调用)
*/
public int cleanupAllExpiredCaches() {
try {
// 使用虚拟线程执行清理
Future<Integer> future = virtualThreadExecutor.submit(() -> {
long expireTime = 10 * 60 * 1000L;
List<String> expiredKeys = new ArrayList<>();
cacheLock.readLock().lock();
try {
for (Map.Entry<String, EncryptedPdfCache> entry : encryptedPdfCacheMap.entrySet()) {
if (entry.getValue().isExpired(expireTime)) {
expiredKeys.add(entry.getKey());
}
}
} finally {
cacheLock.readLock().unlock();
}
// 清理
int cleaned = 0;
for (String key : expiredKeys) {
EncryptedPdfCache cache = encryptedPdfCacheMap.remove(key);
if (cache != null) {
try {
deleteCacheFolder(cache.outputFolder());
cleaned++;
} catch (Exception e) {
logger.warn("清理缓存文件失败: {}", cache.outputFolder(), e);
}
}
}
return cleaned;
});
return future.get(30, TimeUnit.SECONDS);
} catch (Exception e) {
logger.error("手动清理缓存失败", e);
return 0;
}
}
} }

View File

@@ -6,10 +6,7 @@ import cn.keking.model.ReturnResponse;
import cn.keking.service.CadToPdfService; import cn.keking.service.CadToPdfService;
import cn.keking.service.FileHandlerService; import cn.keking.service.FileHandlerService;
import cn.keking.service.FilePreview; import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils; import cn.keking.utils.*;
import cn.keking.utils.FileConvertStatusManager;
import cn.keking.utils.KkFileUtils;
import cn.keking.utils.WebUtils;
import cn.keking.web.filter.BaseUrlFilter; import cn.keking.web.filter.BaseUrlFilter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -78,12 +75,14 @@ public class CadFilePreviewImpl implements FilePreview {
} }
String filePath = response.getContent(); String filePath = response.getContent();
int refreshSchedule = ConfigConstants.getTime();
if (StringUtils.hasText(outFilePath)) { if (StringUtils.hasText(outFilePath)) {
try { try {
// 启动异步转换,并添加回调处理 // 启动异步转换,并添加回调处理
startAsyncConversion(filePath, outFilePath, cacheName, fileAttribute); startAsyncConversion(filePath, outFilePath, cacheName, fileAttribute);
// 返回等待页面 // 返回等待页面
model.addAttribute("fileName", fileName); model.addAttribute("fileName", fileName);
model.addAttribute("time", refreshSchedule);
model.addAttribute("message", "文件正在转换中,请稍候..."); model.addAttribute("message", "文件正在转换中,请稍候...");
return WAITING_FILE_PREVIEW_PAGE; return WAITING_FILE_PREVIEW_PAGE;
} catch (Exception e) { } catch (Exception e) {
@@ -101,14 +100,33 @@ public class CadFilePreviewImpl implements FilePreview {
*/ */
private void startAsyncConversion(String filePath, String outFilePath, private void startAsyncConversion(String filePath, String outFilePath,
String cacheName, FileAttribute fileAttribute) { String cacheName, FileAttribute fileAttribute) {
//获取cad转换路径
String cadConverterPath = ConfigConstants.getCadConverterPath();
int conversionModule= ConfigConstants.getConversionModule();
if(cadConverterPath.equals("false")){
//默认aspose-cad
conversionModule = 1;
}
CompletableFuture<Boolean> conversionFuture;
// 启动异步转换 // 启动异步转换
CompletableFuture<Boolean> conversionFuture = cadtopdfservice.cadToPdfAsync( if(conversionModule==2){
conversionFuture = cadtopdfservice.cadViewerConvert(
filePath,
outFilePath,
cadConverterPath,
ConfigConstants.getCadPreviewType(),
cacheName
);
}else {
conversionFuture = cadtopdfservice.cadToPdfAsync(
filePath, filePath,
outFilePath, outFilePath,
cacheName, cacheName,
ConfigConstants.getCadPreviewType(), ConfigConstants.getCadPreviewType(),
fileAttribute fileAttribute
); );
}
// 添加转换完成后的回调 // 添加转换完成后的回调
conversionFuture.whenCompleteAsync((success, throwable) -> { conversionFuture.whenCompleteAsync((success, throwable) -> {
@@ -144,15 +162,20 @@ public class CadFilePreviewImpl implements FilePreview {
FileAttribute fileAttribute) { FileAttribute fileAttribute) {
cacheName = WebUtils.encodeFileName(cacheName); cacheName = WebUtils.encodeFileName(cacheName);
String baseUrl = BaseUrlFilter.getBaseUrl(); String baseUrl = BaseUrlFilter.getBaseUrl();
int conversionModule= ConfigConstants.getConversionModule();
if ("tif".equalsIgnoreCase(cadPreviewType)) { if ("tif".equalsIgnoreCase(cadPreviewType)) {
model.addAttribute("currentUrl", cacheName); model.addAttribute("currentUrl", cacheName);
return TIFF_FILE_PREVIEW_PAGE; return TIFF_FILE_PREVIEW_PAGE;
} else if ("svg".equalsIgnoreCase(cadPreviewType)) { } else if ("svg".equalsIgnoreCase(cadPreviewType)) {
model.addAttribute("currentUrl", cacheName); model.addAttribute("currentUrl", cacheName);
if(conversionModule==2){
return CADVIEWER_FILE_PREVIEW_PAGE;
}else {
return SVG_FILE_PREVIEW_PAGE; return SVG_FILE_PREVIEW_PAGE;
} }
}
if (baseUrl != null && (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || if (baseUrl != null && (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) ||
OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType))) { OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType))) {
return officefilepreviewimpl.getPreviewType(model, fileAttribute, officePreviewType, cacheName, outFilePath); return officefilepreviewimpl.getPreviewType(model, fileAttribute, officePreviewType, cacheName, outFilePath);

View File

@@ -22,10 +22,8 @@ public class CodeFilePreviewImpl implements FilePreview {
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) { public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
filePreviewHandle.filePreviewHandle(url, model, fileAttribute); filePreviewHandle.filePreviewHandle(url, model, fileAttribute);
String suffix = fileAttribute.getSuffix(); String suffix = fileAttribute.getSuffix();
if(suffix.equalsIgnoreCase("htm") || suffix.equalsIgnoreCase("html") || suffix.equalsIgnoreCase("shtml") ){ boolean isHtmlFile = suffix.equalsIgnoreCase("htm") || suffix.equalsIgnoreCase("html") || suffix.equalsIgnoreCase("shtml");
model.addAttribute("pdfUrl", url); model.addAttribute("isHtmlFile", isHtmlFile);
return TXT_FILE_PREVIEW_PAGE; //直接输出html
}
return CODE_FILE_PREVIEW_PAGE; return CODE_FILE_PREVIEW_PAGE;
} }
} }

View File

@@ -1,5 +1,6 @@
package cn.keking.service.impl; package cn.keking.service.impl;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute; import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse; import cn.keking.model.ReturnResponse;
import cn.keking.service.FileHandlerService; import cn.keking.service.FileHandlerService;
@@ -10,6 +11,7 @@ import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -32,17 +34,26 @@ public class CommonPreviewImpl implements FilePreview {
@Override @Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) { public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
// 不是http开头浏览器不能直接访问需下载到本地 // 不是http开头浏览器不能直接访问需下载到本地
boolean forceUpdatedCache = fileAttribute.forceUpdatedCache(); //是否启用强制更新命令
String fileName = fileAttribute.getName(); //获取原始文件名
if (forceUpdatedCache || !fileHandlerService.listConvertedFiles().containsKey(fileName) || !ConfigConstants.isCacheEnabled()) {
if (url != null && !url.toLowerCase().startsWith("http")) { if (url != null && !url.toLowerCase().startsWith("http")) {
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null); ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (response.isFailure()) { if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg()); return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
} else { } else {
String file = fileHandlerService.getRelativePath(response.getContent()); String file = fileHandlerService.getRelativePath(response.getContent());
model.addAttribute("currentUrl", file); model.addAttribute("currentUrl", file);
if (ConfigConstants.isCacheEnabled()) {
fileHandlerService.addConvertedFile(fileName, file);
}
} }
} else { } else {
model.addAttribute("currentUrl", url); model.addAttribute("currentUrl", url);
} }
return null; return null;
} }
model.addAttribute("currentUrl", fileHandlerService.getConvertedFile(fileName));
return null;
}
} }

View File

@@ -97,8 +97,10 @@ public class MediaFilePreviewImpl implements FilePreview {
try { try {
// 启动异步转换,并添加回调处理 // 启动异步转换,并添加回调处理
startAsyncConversion(filePath, outFilePath, cacheName, fileAttribute); startAsyncConversion(filePath, outFilePath, cacheName, fileAttribute);
int refreshSchedule = ConfigConstants.getTime();
// 返回等待页面 // 返回等待页面
model.addAttribute("fileName", fileName); model.addAttribute("fileName", fileName);
model.addAttribute("time", refreshSchedule);
model.addAttribute("message", "视频文件正在转换中,请稍候..."); model.addAttribute("message", "视频文件正在转换中,请稍候...");
return WAITING_FILE_PREVIEW_PAGE; return WAITING_FILE_PREVIEW_PAGE;
} catch (Exception e) { } catch (Exception e) {

View File

@@ -0,0 +1,25 @@
package cn.keking.service.impl;
import cn.keking.model.FileAttribute;
import cn.keking.service.FilePreview;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
/**
* Dcm 文件处理
*/
@Service
public class MsgFilePreviewImpl implements FilePreview {
private final CommonPreviewImpl commonPreview;
public MsgFilePreviewImpl(CommonPreviewImpl commonPreview) {
this.commonPreview = commonPreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
commonPreview.filePreviewHandle(url,model,fileAttribute);
return MSG_FILE_PREVIEW_PAGE;
}
}

View File

@@ -112,8 +112,10 @@ public class OfficeFilePreviewImpl implements FilePreview {
try { try {
// 启动异步转换 // 启动异步转换
startAsyncOfficeConversion(filePath, outFilePath, cacheName, fileAttribute, officePreviewType); startAsyncOfficeConversion(filePath, outFilePath, cacheName, fileAttribute, officePreviewType);
int refreshSchedule = ConfigConstants.getTime();
// 返回等待页面 // 返回等待页面
model.addAttribute("fileName", fileName); model.addAttribute("fileName", fileName);
model.addAttribute("time", refreshSchedule);
model.addAttribute("message", "文件正在转换中,请稍候..."); model.addAttribute("message", "文件正在转换中,请稍候...");
return WAITING_FILE_PREVIEW_PAGE; return WAITING_FILE_PREVIEW_PAGE;
} catch (Exception e) { } catch (Exception e) {
@@ -160,7 +162,7 @@ public class OfficeFilePreviewImpl implements FilePreview {
if (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || if (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) ||
OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType)) { OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType)) {
FileConvertStatusManager.updateProgress(cacheName, "正在转换PDF为图片", 90); FileConvertStatusManager.updateProgress(cacheName, "正在转换PDF为图片", 90);
imageUrls = pdftojpgservice.pdf2jpg(outFilePath, outFilePath, cacheName, fileAttribute); imageUrls = pdftojpgservice.pdf2jpg(outFilePath, outFilePath, fileAttribute);
} }
// 缓存处理 // 缓存处理

View File

@@ -8,7 +8,6 @@ import cn.keking.service.FilePreview;
import cn.keking.service.PdfToJpgService; import cn.keking.service.PdfToJpgService;
import cn.keking.utils.DownloadUtils; import cn.keking.utils.DownloadUtils;
import cn.keking.utils.FileConvertStatusManager; import cn.keking.utils.FileConvertStatusManager;
import cn.keking.utils.KkFileUtils;
import cn.keking.utils.WebUtils; import cn.keking.utils.WebUtils;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.poi.EncryptedDocumentException; import org.apache.poi.EncryptedDocumentException;
@@ -37,16 +36,19 @@ public class PdfFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService; private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview; private final OtherFilePreviewImpl otherFilePreview;
private final PdfToJpgService pdftojpgservice; private final PdfToJpgService pdftojpgservice;
private final OfficeFilePreviewImpl officefilepreviewimpl;
// 用于处理回调的线程池 // 用于处理回调的线程池
private static final ExecutorService callbackExecutor = Executors.newFixedThreadPool(3); private static final ExecutorService callbackExecutor = Executors.newFixedThreadPool(3);
public PdfFilePreviewImpl(FileHandlerService fileHandlerService, public PdfFilePreviewImpl(FileHandlerService fileHandlerService,
OtherFilePreviewImpl otherFilePreview, OtherFilePreviewImpl otherFilePreview,
OfficeFilePreviewImpl officefilepreviewimpl,
PdfToJpgService pdftojpgservice) { PdfToJpgService pdftojpgservice) {
this.fileHandlerService = fileHandlerService; this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview; this.otherFilePreview = otherFilePreview;
this.pdftojpgservice = pdftojpgservice; this.pdftojpgservice = pdftojpgservice;
this.officefilepreviewimpl = officefilepreviewimpl;
} }
@Override @Override
@@ -56,35 +58,25 @@ public class PdfFilePreviewImpl implements FilePreview {
boolean forceUpdatedCache = fileAttribute.forceUpdatedCache(); //是否启用强制更新命令 boolean forceUpdatedCache = fileAttribute.forceUpdatedCache(); //是否启用强制更新命令
String outFilePath = fileAttribute.getOutFilePath(); //生成的文件路径 String outFilePath = fileAttribute.getOutFilePath(); //生成的文件路径
String originFilePath; //原始文件路径 String originFilePath; //原始文件路径
String cacheName = fileAttribute.getCacheName(); String cacheName = pdfName+officePreviewType;
String filePassword = fileAttribute.getFilePassword(); // 获取密码 String filePassword = fileAttribute.getFilePassword(); // 获取密码
int refreshSchedule = ConfigConstants.getTime();
// 查询转换状态 // 查询转换状态
FileConvertStatusManager.ConvertStatus status = FileConvertStatusManager.getConvertStatus(cacheName); String statusResult = officefilepreviewimpl.checkAndHandleConvertStatus(model, pdfName, cacheName, fileAttribute);
if (status != null) { if (statusResult != null) {
if (status.getStatus() == FileConvertStatusManager.Status.CONVERTING) { return statusResult;
// 正在转换中,返回等待页面
model.addAttribute("fileName", pdfName);
model.addAttribute("time", refreshSchedule);
model.addAttribute("message", status.getRealTimeMessage());
return WAITING_FILE_PREVIEW_PAGE;
} else if (status.getStatus() == FileConvertStatusManager.Status.TIMEOUT) {
// 超时状态,不允许重新转换
return otherFilePreview.notSupportedFile(model, fileAttribute, "文件转换已超时,无法继续转换");
}
} }
boolean jiami=false; boolean jiami=false;
if(!ObjectUtils.isEmpty(filePassword)){ if(!ObjectUtils.isEmpty(filePassword)){
jiami=pdftojpgservice.hasEncryptedPdfCacheSimple(outFilePath); jiami=pdftojpgservice.hasEncryptedPdfCacheSimple(outFilePath);
} }
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) ||
OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType)) { OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType)) {
// 判断之前是否已转换过,如果转换过,直接返回,否则执行转换 // 判断之前是否已转换过,如果转换过,直接返回,否则执行转换
if (forceUpdatedCache || !fileHandlerService.listConvertedFiles().containsKey(cacheName) || !ConfigConstants.isCacheEnabled()) { if (forceUpdatedCache || !fileHandlerService.listConvertedFiles().containsKey(cacheName) || !ConfigConstants.isCacheEnabled()) {
if(jiami){ if(jiami){
return renderPreview(model, cacheName, outFilePath, return renderPreview(model, cacheName, outFilePath,
officePreviewType, pdfName, fileAttribute); officePreviewType, fileAttribute);
} }
// 当文件不存在时,就去下载 // 当文件不存在时,就去下载
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, pdfName); ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, pdfName);
@@ -98,15 +90,17 @@ public class PdfFilePreviewImpl implements FilePreview {
if (checkIfPdfNeedsPassword(originFilePath, cacheName, pdfName)) { if (checkIfPdfNeedsPassword(originFilePath, cacheName, pdfName)) {
model.addAttribute("needFilePassword", true); model.addAttribute("needFilePassword", true);
model.addAttribute("fileName", pdfName); model.addAttribute("fileName", pdfName);
model.addAttribute("cacheName", cacheName); model.addAttribute("cacheName", pdfName);
return EXEL_FILE_PREVIEW_PAGE; return EXEL_FILE_PREVIEW_PAGE;
} }
} }
try { try {
// 启动异步转换 // 启动异步转换
startAsyncPdfConversion(originFilePath, outFilePath, cacheName, pdfName, fileAttribute); startAsyncPdfConversion(originFilePath, outFilePath, cacheName, pdfName, fileAttribute);
int refreshSchedule = ConfigConstants.getTime();
// 返回等待页面 // 返回等待页面
model.addAttribute("fileName", pdfName); model.addAttribute("fileName", pdfName);
model.addAttribute("time", refreshSchedule);
model.addAttribute("message", "文件正在转换中,请稍候..."); model.addAttribute("message", "文件正在转换中,请稍候...");
return WAITING_FILE_PREVIEW_PAGE; return WAITING_FILE_PREVIEW_PAGE;
} catch (Exception e) { } catch (Exception e) {
@@ -116,7 +110,7 @@ public class PdfFilePreviewImpl implements FilePreview {
} else { } else {
// 如果已有缓存,直接渲染预览 // 如果已有缓存,直接渲染预览
return renderPreview(model, cacheName, outFilePath, return renderPreview(model, cacheName, outFilePath,
officePreviewType, pdfName, fileAttribute); officePreviewType, fileAttribute);
} }
} else { } else {
// 处理普通PDF预览非图片转换 // 处理普通PDF预览非图片转换
@@ -175,13 +169,13 @@ public class PdfFilePreviewImpl implements FilePreview {
FileConvertStatusManager.updateProgress(cacheName, "正在启动PDF转换", 10); FileConvertStatusManager.updateProgress(cacheName, "正在启动PDF转换", 10);
List<String> imageUrls = pdftojpgservice.pdf2jpg(originFilePath, outFilePath, List<String> imageUrls = pdftojpgservice.pdf2jpg(originFilePath, outFilePath,
pdfName, fileAttribute); fileAttribute);
if (imageUrls != null && !imageUrls.isEmpty()) { if (imageUrls != null && !imageUrls.isEmpty()) {
boolean usePasswordCache = fileAttribute.getUsePasswordCache(); boolean usePasswordCache = fileAttribute.getUsePasswordCache();
String filePassword = fileAttribute.getFilePassword(); String filePassword = fileAttribute.getFilePassword();
if (ConfigConstants.isCacheEnabled() && (ObjectUtils.isEmpty(filePassword) || usePasswordCache)) { if (ConfigConstants.isCacheEnabled() && (ObjectUtils.isEmpty(filePassword) || usePasswordCache)) {
fileHandlerService.addConvertedFile(pdfName, fileHandlerService.getRelativePath(outFilePath)); fileHandlerService.addConvertedFile(cacheName, fileHandlerService.getRelativePath(outFilePath));
} }
FileConvertStatusManager.updateProgress(cacheName, "转换完成", 100); FileConvertStatusManager.updateProgress(cacheName, "转换完成", 100);
// 短暂延迟后清理状态 // 短暂延迟后清理状态
@@ -214,17 +208,7 @@ public class PdfFilePreviewImpl implements FilePreview {
// 添加转换完成后的回调 // 添加转换完成后的回调
conversionFuture.whenCompleteAsync((imageUrls, throwable) -> { conversionFuture.whenCompleteAsync((imageUrls, throwable) -> {
if (imageUrls != null && !imageUrls.isEmpty()) { if (imageUrls == null || imageUrls.isEmpty()) {
try {
// 是否保留PDF源文件只在转换成功后才删除
if (!fileAttribute.isCompressFile() && ConfigConstants.getDeleteSourceFile()) {
KkFileUtils.deleteFileByPath(originFilePath);
}
} catch (Exception e) {
logger.error("PDF转换后续处理失败: {}", originFilePath, e);
}
} else {
// 转换失败,保留源文件供排查问题
logger.error("PDF转换失败保留源文件: {}", originFilePath); logger.error("PDF转换失败保留源文件: {}", originFilePath);
if (throwable != null) { if (throwable != null) {
logger.error("转换失败原因: ", throwable); logger.error("转换失败原因: ", throwable);
@@ -238,7 +222,7 @@ public class PdfFilePreviewImpl implements FilePreview {
*/ */
private String renderPreview(Model model, String cacheName, private String renderPreview(Model model, String cacheName,
String outFilePath, String officePreviewType, String outFilePath, String officePreviewType,
String pdfName, FileAttribute fileAttribute) { FileAttribute fileAttribute) {
try { try {
List<String> imageUrls; List<String> imageUrls;
if(pdftojpgservice.hasEncryptedPdfCacheSimple(outFilePath)){ if(pdftojpgservice.hasEncryptedPdfCacheSimple(outFilePath)){
@@ -251,7 +235,7 @@ public class PdfFilePreviewImpl implements FilePreview {
} }
model.addAttribute("imgUrls", imageUrls); model.addAttribute("imgUrls", imageUrls);
model.addAttribute("currentUrl", imageUrls.get(0)); model.addAttribute("currentUrl", imageUrls.getFirst());
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType)) { if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType)) {
return OFFICE_PICTURE_FILE_PREVIEW_PAGE; return OFFICE_PICTURE_FILE_PREVIEW_PAGE;

View File

@@ -27,16 +27,28 @@ public class PictureFilePreviewImpl extends CommonPreviewImpl {
@Override @Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) { public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
url= KkFileUtils.htmlEscape(url); url= KkFileUtils.htmlEscape(url);
String suffix = fileAttribute.getSuffix();
List<String> imgUrls = new ArrayList<>(); List<String> imgUrls = new ArrayList<>();
imgUrls.add(url); imgUrls.add(url);
String compressFileKey = fileAttribute.getCompressFileKey(); String compressFileKey = fileAttribute.getCompressFileKey();
List<String> zipImgUrls = fileHandlerService.getImgCache(compressFileKey); List<String> zipImgUrls = fileHandlerService.getImgCache(compressFileKey);
if (!CollectionUtils.isEmpty(zipImgUrls)) { if (!CollectionUtils.isEmpty(zipImgUrls)) {
imgUrls.addAll(zipImgUrls); imgUrls.addAll(zipImgUrls);
} model.addAttribute("imgUrls", imgUrls);
}else {
// 不是http开头浏览器不能直接访问需下载到本地 // 不是http开头浏览器不能直接访问需下载到本地
super.filePreviewHandle(url, model, fileAttribute); super.filePreviewHandle(url, model, fileAttribute);
model.addAttribute("imgUrls", imgUrls); if ( url.toLowerCase().startsWith("file") || url.toLowerCase().startsWith("ftp")) {
model.addAttribute("imgUrls", fileAttribute.getName());
}else {
model.addAttribute("imgUrls", url);
}
}
if(suffix.equalsIgnoreCase("heic")){
return HEIC_FILE_PREVIEW_PAGE;
}else {
return PICTURE_FILE_PREVIEW_PAGE; return PICTURE_FILE_PREVIEW_PAGE;
} }
} }
}

View File

@@ -70,8 +70,10 @@ public class TiffFilePreviewImpl implements FilePreview {
try { try {
// 启动异步转换 // 启动异步转换
startAsyncTiffConversion(filePath, outFilePath, cacheName, fileName, fileAttribute, tifPreviewType, forceUpdatedCache); startAsyncTiffConversion(filePath, outFilePath, cacheName, fileName, fileAttribute, tifPreviewType, forceUpdatedCache);
int refreshSchedule = ConfigConstants.getTime();
// 返回等待页面 // 返回等待页面
model.addAttribute("fileName", fileName); model.addAttribute("fileName", fileName);
model.addAttribute("time", refreshSchedule);
model.addAttribute("message", "文件正在转换中,请稍候..."); model.addAttribute("message", "文件正在转换中,请稍候...");
return WAITING_FILE_PREVIEW_PAGE; return WAITING_FILE_PREVIEW_PAGE;
} catch (Exception e) { } catch (Exception e) {

View File

@@ -97,9 +97,10 @@ public class DownloadUtils {
factory.setHttpClient(httpClient); factory.setHttpClient(httpClient);
restTemplate.setRequestFactory(factory); restTemplate.setRequestFactory(factory);
String finalUrlStr = urlStr;
RequestCallback requestCallback = request -> { RequestCallback requestCallback = request -> {
request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL)); request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
WebUtils.applyBasicAuthHeaders(request.getHeaders(), fileAttribute); WebUtils.applyBasicAuthHeaders(request.getHeaders(), finalUrlStr);
String proxyAuthorization = fileAttribute.getKkProxyAuthorization(); String proxyAuthorization = fileAttribute.getKkProxyAuthorization();
if(StringUtils.hasText(proxyAuthorization)){ if(StringUtils.hasText(proxyAuthorization)){
Map<String, String> proxyAuthorizationMap = mapper.readValue( Map<String, String> proxyAuthorizationMap = mapper.readValue(

View File

@@ -60,6 +60,18 @@ public class KkFileUtils {
} }
return false; return false;
} }
public static boolean validateFileNameLength(String fileName) {
if (fileName == null) {
return false;
}
// 文件名长度限制255个字符不包含路径
int windowsMaxLength = 255;
if (fileName.length() > windowsMaxLength) {
System.err.println("文件名长度超过限制255个字符");
return false;
}
return true;
}
/** /**
* 检查是否是数字 * 检查是否是数字

View File

@@ -1,7 +1,6 @@
package cn.keking.utils; package cn.keking.utils;
import cn.keking.config.ConfigConstants; import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import io.mola.galimatias.GalimatiasParseException; import io.mola.galimatias.GalimatiasParseException;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -465,10 +464,8 @@ public class WebUtils {
/** /**
* 支持basic 下载方法 * 支持basic 下载方法
*/ */
public static void applyBasicAuthHeaders(HttpHeaders headers, FileAttribute fileAttribute) { public static void applyBasicAuthHeaders(HttpHeaders headers, String url) {
String url = fileAttribute.getUrl();
System.out.println(" T555.");
System.out.println(url);
// 从配置文件读取User-Agent如果没有配置则使用默认值 // 从配置文件读取User-Agent如果没有配置则使用默认值
String customUserAgent=ConfigConstants.getUserAgent(); String customUserAgent=ConfigConstants.getUserAgent();
String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"; String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";

View File

@@ -197,9 +197,10 @@ public class OnlinePreviewController {
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(factory); restTemplate.setRequestFactory(factory);
String finalUrlPath = urlPath;
RequestCallback requestCallback = request -> { RequestCallback requestCallback = request -> {
request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL)); request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
WebUtils.applyBasicAuthHeaders(request.getHeaders(), fileAttribute); WebUtils.applyBasicAuthHeaders(request.getHeaders(), finalUrlPath);
String proxyAuthorization = fileAttribute.getKkProxyAuthorization(); String proxyAuthorization = fileAttribute.getKkProxyAuthorization();
if(StringUtils.hasText(proxyAuthorization)){ if(StringUtils.hasText(proxyAuthorization)){
Map<String, String> proxyAuthorizationMap = mapper.readValue( Map<String, String> proxyAuthorizationMap = mapper.readValue(

View File

@@ -8,7 +8,7 @@
=> Java Version :: ${java.version} => Java Version :: ${java.version}
=> Spring Boot :: ${spring-boot.version} => Spring Boot :: ${spring-boot.version}
=> kkFileView :: 4.4.0 => kkFileView :: 5.0
=> Home site :: https://kkview.cn => Home site :: https://kkview.cn
=> Github :: https://github.com/kekingcn/kkFileView => Github :: https://github.com/kekingcn/kkFileView
=> Gitee :: https://gitee.com/kekingcn/file-online-preview => Gitee :: https://gitee.com/kekingcn/file-online-preview

View File

@@ -0,0 +1,479 @@
/*!
* Generated using the Bootstrap Customizer (https://getbootstrap.com/docs/3.4/customize/)
*/
/*!
* Bootstrap v3.4.1 (https://getbootstrap.com/)
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
html {
font-family: sans-serif;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
display: block;
}
audio,
canvas,
progress,
video {
display: inline-block;
vertical-align: baseline;
}
audio:not([controls]) {
display: none;
height: 0;
}
[hidden],
template {
display: none;
}
a {
background-color: transparent;
}
a:active,
a:hover {
outline: 0;
}
abbr[title] {
border-bottom: none;
text-decoration: underline;
text-decoration: underline dotted;
}
b,
strong {
font-weight: bold;
}
dfn {
font-style: italic;
}
h1 {
font-size: 2em;
margin: 0.67em 0;
}
mark {
background: #ff0;
color: #000;
}
small {
font-size: 80%;
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
img {
border: 0;
}
svg:not(:root) {
overflow: hidden;
}
figure {
margin: 1em 40px;
}
hr {
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
pre {
overflow: auto;
}
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
button,
input,
optgroup,
select,
textarea {
color: inherit;
font: inherit;
margin: 0;
}
button {
overflow: visible;
}
button,
select {
text-transform: none;
}
button,
html input[type="button"],
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button;
cursor: pointer;
}
button[disabled],
html input[disabled] {
cursor: default;
}
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
input {
line-height: normal;
}
input[type="checkbox"],
input[type="radio"] {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 0;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
input[type="search"] {
-webkit-appearance: textfield;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
legend {
border: 0;
padding: 0;
}
textarea {
overflow: auto;
}
optgroup {
font-weight: bold;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
*:before,
*:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
html {
font-size: 10px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.42857143;
color: #333333;
background-color: #ffffff;
}
input,
button,
select,
textarea {
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
a {
color: #337ab7;
text-decoration: none;
}
a:hover,
a:focus {
color: #23527c;
text-decoration: underline;
}
a:focus {
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
figure {
margin: 0;
}
img {
vertical-align: middle;
}
.img-responsive {
display: block;
max-width: 100%;
height: auto;
}
.img-rounded {
border-radius: 6px;
}
.img-thumbnail {
padding: 4px;
line-height: 1.42857143;
background-color: #ffffff;
border: 1px solid #dddddd;
border-radius: 4px;
-webkit-transition: all 0.2s ease-in-out;
-o-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
display: inline-block;
max-width: 100%;
height: auto;
}
.img-circle {
border-radius: 50%;
}
hr {
margin-top: 20px;
margin-bottom: 20px;
border: 0;
border-top: 1px solid #eeeeee;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
.sr-only-focusable:active,
.sr-only-focusable:focus {
position: static;
width: auto;
height: auto;
margin: 0;
overflow: visible;
clip: auto;
}
[role="button"] {
cursor: pointer;
}
.caret {
display: inline-block;
width: 0;
height: 0;
margin-left: 2px;
vertical-align: middle;
border-top: 4px dashed;
border-top: 4px solid \9;
border-right: 4px solid transparent;
border-left: 4px solid transparent;
}
.dropup,
.dropdown {
position: relative;
}
.dropdown-toggle:focus {
outline: 0;
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
display: none;
float: left;
min-width: 160px;
padding: 5px 0;
margin: 2px 0 0;
font-size: 14px;
text-align: left;
list-style: none;
background-color: #ffffff;
-webkit-background-clip: padding-box;
background-clip: padding-box;
border: 1px solid #cccccc;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 4px;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
}
.dropdown-menu.pull-right {
right: 0;
left: auto;
}
.dropdown-menu .divider {
height: 1px;
margin: 9px 0;
overflow: hidden;
background-color: #e5e5e5;
}
.dropdown-menu > li > a {
display: block;
padding: 3px 20px;
clear: both;
font-weight: 400;
line-height: 1.42857143;
color: #333333;
white-space: nowrap;
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
color: #262626;
text-decoration: none;
background-color: #f5f5f5;
}
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
color: #ffffff;
text-decoration: none;
background-color: #337ab7;
outline: 0;
}
.dropdown-menu > .disabled > a,
.dropdown-menu > .disabled > a:hover,
.dropdown-menu > .disabled > a:focus {
color: #777777;
}
.dropdown-menu > .disabled > a:hover,
.dropdown-menu > .disabled > a:focus {
text-decoration: none;
cursor: not-allowed;
background-color: transparent;
background-image: none;
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
}
.open > .dropdown-menu {
display: block;
}
.open > a {
outline: 0;
}
.dropdown-menu-right {
right: 0;
left: auto;
}
.dropdown-menu-left {
right: auto;
left: 0;
}
.dropdown-header {
display: block;
padding: 3px 20px;
font-size: 12px;
line-height: 1.42857143;
color: #777777;
white-space: nowrap;
}
.dropdown-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 990;
}
.pull-right > .dropdown-menu {
right: 0;
left: auto;
}
.dropup .caret,
.navbar-fixed-bottom .dropdown .caret {
content: "";
border-top: 0;
border-bottom: 4px dashed;
border-bottom: 4px solid \9;
}
.dropup .dropdown-menu,
.navbar-fixed-bottom .dropdown .dropdown-menu {
top: auto;
bottom: 100%;
margin-bottom: 2px;
}
@media (min-width: 768px) {
.navbar-right .dropdown-menu {
right: 0;
left: auto;
}
.navbar-right .dropdown-menu-left {
right: auto;
left: 0;
}
}
.clearfix:before,
.clearfix:after {
display: table;
content: " ";
}
.clearfix:after {
clear: both;
}
.center-block {
display: block;
margin-right: auto;
margin-left: auto;
}
.pull-right {
float: right !important;
}
.pull-left {
float: left !important;
}
.hide {
display: none !important;
}
.show {
display: block !important;
}
.invisible {
visibility: hidden;
}
.text-hide {
font: 0/0 a;
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}
.hidden {
display: none !important;
}
.affix {
position: fixed;
}

View File

@@ -0,0 +1 @@
span.multiselect-native-select{position:relative}span.multiselect-native-select select{border:0!important;clip:rect(0 0 0 0)!important;height:1px!important;margin:-1px -1px -1px -3px!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;left:50%;top:30px}.multiselect-container{position:absolute;list-style-type:none;margin:0;padding:0}.multiselect-container .input-group{margin:5px}.multiselect-container .multiselect-reset .input-group{width:93%}.multiselect-container>li{padding:0}.multiselect-container>li>a.multiselect-all label{font-weight:700}.multiselect-container>li.multiselect-group label{margin:0;padding:3px 20px;height:100%;font-weight:700}.multiselect-container>li.multiselect-group-clickable label{cursor:pointer}.multiselect-container>li>a{padding:0}.multiselect-container>li>a>label{margin:0;height:100%;cursor:pointer;font-weight:400;padding:3px 20px 3px 40px}.multiselect-container>li>a>label.checkbox,.multiselect-container>li>a>label.radio{margin:0}.multiselect-container>li>a>label>input[type=checkbox]{margin-bottom:5px}.btn-group>.btn-group:nth-child(2)>.multiselect.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.form-inline .multiselect-container label.checkbox,.form-inline .multiselect-container label.radio{padding:3px 20px 3px 40px}.form-inline .multiselect-container li a label.checkbox input[type=checkbox],.form-inline .multiselect-container li a label.radio input[type=radio]{margin-left:-20px;margin-right:0}

View File

@@ -0,0 +1,444 @@
.cadviewer-bootstrap {
/*!
* Generated using the Bootstrap Customizer (https://getbootstrap.com/docs/3.4/customize/)
*/
/*!
* Bootstrap v3.4.1 (https://getbootstrap.com/)
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
}
.cadviewer-bootstrap html {
font-family: sans-serif;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
.cadviewer-bootstrap body {
margin: 0;
}
.cadviewer-bootstrap article, .cadviewer-bootstrap aside, .cadviewer-bootstrap details, .cadviewer-bootstrap figcaption, .cadviewer-bootstrap figure, .cadviewer-bootstrap footer, .cadviewer-bootstrap header, .cadviewer-bootstrap hgroup, .cadviewer-bootstrap main, .cadviewer-bootstrap menu, .cadviewer-bootstrap nav, .cadviewer-bootstrap section, .cadviewer-bootstrap summary {
display: block;
}
.cadviewer-bootstrap audio, .cadviewer-bootstrap canvas, .cadviewer-bootstrap progress, .cadviewer-bootstrap video {
display: inline-block;
vertical-align: baseline;
}
.cadviewer-bootstrap audio:not([controls]) {
display: none;
height: 0;
}
.cadviewer-bootstrap [hidden], .cadviewer-bootstrap template {
display: none;
}
.cadviewer-bootstrap a {
background-color: transparent;
}
.cadviewer-bootstrap a:active, .cadviewer-bootstrap a:hover {
outline: 0;
}
.cadviewer-bootstrap abbr[title] {
border-bottom: none;
text-decoration: underline;
text-decoration: underline dotted;
}
.cadviewer-bootstrap b, .cadviewer-bootstrap strong {
font-weight: bold;
}
.cadviewer-bootstrap dfn {
font-style: italic;
}
.cadviewer-bootstrap h1 {
font-size: 2em;
margin: 0.67em 0;
}
.cadviewer-bootstrap mark {
background: #ff0;
color: #000;
}
.cadviewer-bootstrap small {
font-size: 80%;
}
.cadviewer-bootstrap sub, .cadviewer-bootstrap sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
.cadviewer-bootstrap sup {
top: -0.5em;
}
.cadviewer-bootstrap sub {
bottom: -0.25em;
}
.cadviewer-bootstrap img {
border: 0;
}
.cadviewer-bootstrap svg:not(:root) {
overflow: hidden;
}
.cadviewer-bootstrap figure {
margin: 1em 40px;
}
.cadviewer-bootstrap hr {
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
.cadviewer-bootstrap pre {
overflow: auto;
}
.cadviewer-bootstrap code, .cadviewer-bootstrap kbd, .cadviewer-bootstrap pre, .cadviewer-bootstrap samp {
font-family: monospace, monospace;
font-size: 1em;
}
.cadviewer-bootstrap button, .cadviewer-bootstrap input, .cadviewer-bootstrap optgroup, .cadviewer-bootstrap select, .cadviewer-bootstrap textarea {
color: inherit;
font: inherit;
margin: 0;
/* 8.45.2 */
/*8.45.2*/
border-radius: 0px !important;
border-width : 1px !important;
border-color: #000 !important;
}
.cadviewer-bootstrap button {
overflow: visible;
}
.cadviewer-bootstrap button, .cadviewer-bootstrap select {
text-transform: none;
}
.cadviewer-bootstrap button, .cadviewer-bootstrap html input[type="button"], .cadviewer-bootstrap input[type="reset"], .cadviewer-bootstrap input[type="submit"] {
-webkit-appearance: button;
cursor: pointer;
}
.cadviewer-bootstrap button[disabled], .cadviewer-bootstrap html input[disabled] {
cursor: default;
}
.cadviewer-bootstrap button::-moz-focus-inner, .cadviewer-bootstrap input::-moz-focus-inner {
border: 0;
padding: 0;
}
.cadviewer-bootstrap input {
line-height: 22px;
/*8.45.2*/
border-radius: 0px !important;
border-width : 1px !important;
border-color: #000 !important;
}
.cadviewer-bootstrap input[type="checkbox"], .cadviewer-bootstrap input[type="radio"] {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 0;
border-radius: 0px;
height: 20px !important;
}
.cadviewer-bootstrap input[type="number"]::-webkit-inner-spin-button, .cadviewer-bootstrap input[type="number"]::-webkit-outer-spin-button {
height: auto;
border-radius: 3px;
}
.cadviewer-bootstrap input[type="search"] {
-webkit-appearance: textfield;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.cadviewer-bootstrap input[type="search"]::-webkit-search-cancel-button, .cadviewer-bootstrap input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
.cadviewer-bootstrap fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
.cadviewer-bootstrap legend {
border: 0;
padding: 0;
}
.cadviewer-bootstrap textarea {
overflow: auto;
}
.cadviewer-bootstrap optgroup {
font-weight: bold;
}
.cadviewer-bootstrap table {
border-collapse: collapse;
border-spacing: 0;
}
.cadviewer-bootstrap td, .cadviewer-bootstrap th {
padding: 0;
}
.cadviewer-bootstrap * {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.cadviewer-bootstrap *:before, .cadviewer-bootstrap *:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.cadviewer-bootstrap html {
font-size: 10px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.cadviewer-bootstrap input, .cadviewer-bootstrap button, .cadviewer-bootstrap select, .cadviewer-bootstrap textarea {
font-family: inherit;
font-size: inherit;
line-height: inherit;
/*8.45.2*/
border-radius: 0px !important;
border-width : 1px !important;
border-color: #000 !important;
}
.cadviewer-bootstrap a {
color: #337ab7;
text-decoration: none;
}
.cadviewer-bootstrap #floorPlan_svg a:hover, .cadviewer-bootstrap #floorPlan_svg a:focus {
color: #23527c;
text-decoration: underline;
}
.cadviewer-bootstrap a:focus {
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
.cadviewer-bootstrap figure {
margin: 0;
}
.cadviewer-bootstrap img {
vertical-align: middle;
}
.cadviewer-bootstrap .img-responsive {
display: block;
max-width: 100%;
height: auto;
}
.cadviewer-bootstrap .img-rounded {
border-radius: 6px;
}
.cadviewer-bootstrap .img-thumbnail {
padding: 4px;
line-height: 1.42857143;
background-color: #ffffff;
border: 1px solid #dddddd;
border-radius: 4px;
-webkit-transition: all 0.2s ease-in-out;
-o-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
display: inline-block;
max-width: 100%;
height: auto;
}
.cadviewer-bootstrap .img-circle {
border-radius: 50%;
}
.cadviewer-bootstrap hr {
margin-top: 20px;
margin-bottom: 20px;
border: 0;
border-top: 1px solid #eeeeee;
}
.cadviewer-bootstrap .sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
.cadviewer-bootstrap .sr-only-focusable:active, .cadviewer-bootstrap .sr-only-focusable:focus {
position: static;
width: auto;
height: auto;
margin: 0;
overflow: visible;
clip: auto;
}
.cadviewer-bootstrap [role="button"] {
cursor: pointer;
}
.cadviewer-bootstrap .caret {
display: inline-block;
width: 0;
height: 0;
margin-left: 2px;
vertical-align: middle;
border-top: 4px dashed;
border-top: 4px solid \9;
border-right: 4px solid transparent;
border-left: 4px solid transparent;
}
.cadviewer-bootstrap .dropup, .cadviewer-bootstrap .dropdown {
position: relative;
}
.cadviewer-bootstrap .dropdown-toggle:focus {
outline: 0;
}
.cadviewer-bootstrap .dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
display: none;
float: left;
min-width: 160px;
padding: 5px 0;
margin: 2px 0 0;
font-size: 14px;
text-align: left;
list-style: none;
background-color: #ffffff;
-webkit-background-clip: padding-box;
background-clip: padding-box;
border: 1px solid #cccccc;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 4px;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
}
.cadviewer-bootstrap .dropdown-menu.pull-right {
right: 0;
left: auto;
}
.cadviewer-bootstrap .dropdown-menu .divider {
height: 1px;
margin: 9px 0;
overflow: hidden;
background-color: #e5e5e5;
}
.cadviewer-bootstrap .dropdown-menu > li > a {
display: block;
padding: 3px 20px;
clear: both;
font-weight: 400;
line-height: 1.42857143;
color: #333333;
white-space: nowrap;
}
.cadviewer-bootstrap .dropdown-menu > li > a:hover, .cadviewer-bootstrap .dropdown-menu > li > a:focus {
color: #262626;
text-decoration: none;
background-color: #f5f5f5;
}
.cadviewer-bootstrap .dropdown-menu > .active > a, .cadviewer-bootstrap .dropdown-menu > .active > a:hover, .cadviewer-bootstrap .dropdown-menu > .active > a:focus {
color: #ffffff;
text-decoration: none;
background-color: #337ab7;
outline: 0;
}
.cadviewer-bootstrap .dropdown-menu > .disabled > a, .cadviewer-bootstrap .dropdown-menu > .disabled > a:hover, .cadviewer-bootstrap .dropdown-menu > .disabled > a:focus {
color: #777777;
}
.cadviewer-bootstrap .dropdown-menu > .disabled > a:hover, .cadviewer-bootstrap .dropdown-menu > .disabled > a:focus {
text-decoration: none;
cursor: not-allowed;
background-color: transparent;
background-image: none;
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
}
.cadviewer-bootstrap .open > .dropdown-menu {
display: block;
}
.cadviewer-bootstrap .open > a {
outline: 0;
}
.cadviewer-bootstrap .dropdown-menu-right {
right: 0;
left: auto;
}
.cadviewer-bootstrap .dropdown-menu-left {
right: auto;
left: 0;
}
.cadviewer-bootstrap .dropdown-header {
display: block;
padding: 3px 20px;
font-size: 12px;
line-height: 1.42857143;
color: #777777;
white-space: nowrap;
}
.cadviewer-bootstrap .dropdown-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 990;
}
.cadviewer-bootstrap .pull-right > .dropdown-menu {
right: 0;
left: auto;
}
.cadviewer-bootstrap .dropup .caret, .cadviewer-bootstrap .navbar-fixed-bottom .dropdown .caret {
content: "";
border-top: 0;
border-bottom: 4px dashed;
border-bottom: 4px solid \9;
}
.cadviewer-bootstrap .dropup .dropdown-menu, .cadviewer-bootstrap .navbar-fixed-bottom .dropdown .dropdown-menu {
top: auto;
bottom: 100%;
margin-bottom: 2px;
}
@media (min-width: 768px) {
.cadviewer-bootstrap .navbar-right .dropdown-menu {
right: 0;
left: auto;
}
.cadviewer-bootstrap .navbar-right .dropdown-menu-left {
right: auto;
left: 0;
}
}
.cadviewer-bootstrap .clearfix:before, .cadviewer-bootstrap .clearfix:after {
display: table;
content: " ";
}
.cadviewer-bootstrap .clearfix:after {
clear: both;
}
.cadviewer-bootstrap .center-block {
display: block;
margin-right: auto;
margin-left: auto;
}
.cadviewer-bootstrap .pull-right {
float: right !important;
}
.cadviewer-bootstrap .pull-left {
float: left !important;
}
.cadviewer-bootstrap .hide {
display: none !important;
}
.cadviewer-bootstrap .show {
display: block !important;
}
.cadviewer-bootstrap .invisible {
visibility: hidden;
}
.cadviewer-bootstrap .text-hide {
font: 0/0 a;
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}
.cadviewer-bootstrap .hidden {
display: none !important;
}
.cadviewer-bootstrap .affix {
position: fixed;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,391 @@
{
"vars": {
"@gray-base": "#000",
"@gray-darker": "lighten(@gray-base, 13.5%)",
"@gray-dark": "lighten(@gray-base, 20%)",
"@gray": "lighten(@gray-base, 33.5%)",
"@gray-light": "lighten(@gray-base, 46.7%)",
"@gray-lighter": "lighten(@gray-base, 93.5%)",
"@brand-primary": "darken(#428bca, 6.5%)",
"@brand-success": "#5cb85c",
"@brand-info": "#5bc0de",
"@brand-warning": "#f0ad4e",
"@brand-danger": "#d9534f",
"@body-bg": "#fff",
"@text-color": "@gray-dark",
"@link-color": "@brand-primary",
"@link-hover-color": "darken(@link-color, 15%)",
"@link-hover-decoration": "underline",
"@font-family-sans-serif": "\"Helvetica Neue\", Helvetica, Arial, sans-serif",
"@font-family-serif": "Georgia, \"Times New Roman\", Times, serif",
"@font-family-monospace": "Menlo, Monaco, Consolas, \"Courier New\", monospace",
"@font-family-base": "@font-family-sans-serif",
"@font-size-base": "14px",
"@font-size-large": "ceil((@font-size-base * 1.25))",
"@font-size-small": "ceil((@font-size-base * .85))",
"@font-size-h1": "floor((@font-size-base * 2.6))",
"@font-size-h2": "floor((@font-size-base * 2.15))",
"@font-size-h3": "ceil((@font-size-base * 1.7))",
"@font-size-h4": "ceil((@font-size-base * 1.25))",
"@font-size-h5": "@font-size-base",
"@font-size-h6": "ceil((@font-size-base * .85))",
"@line-height-base": "1.428571429",
"@line-height-computed": "floor((@font-size-base * @line-height-base))",
"@headings-font-family": "inherit",
"@headings-font-weight": "500",
"@headings-line-height": "1.1",
"@headings-color": "inherit",
"@icon-font-path": "\"../fonts/\"",
"@icon-font-name": "\"glyphicons-halflings-regular\"",
"@icon-font-svg-id": "\"glyphicons_halflingsregular\"",
"@padding-base-vertical": "6px",
"@padding-base-horizontal": "12px",
"@padding-large-vertical": "10px",
"@padding-large-horizontal": "16px",
"@padding-small-vertical": "5px",
"@padding-small-horizontal": "10px",
"@padding-xs-vertical": "1px",
"@padding-xs-horizontal": "5px",
"@line-height-large": "1.3333333",
"@line-height-small": "1.5",
"@border-radius-base": "4px",
"@border-radius-large": "6px",
"@border-radius-small": "3px",
"@component-active-color": "#fff",
"@component-active-bg": "@brand-primary",
"@caret-width-base": "4px",
"@caret-width-large": "5px",
"@table-cell-padding": "8px",
"@table-condensed-cell-padding": "5px",
"@table-bg": "transparent",
"@table-bg-accent": "#f9f9f9",
"@table-bg-hover": "#f5f5f5",
"@table-bg-active": "@table-bg-hover",
"@table-border-color": "#ddd",
"@btn-font-weight": "normal",
"@btn-default-color": "#333",
"@btn-default-bg": "#fff",
"@btn-default-border": "#ccc",
"@btn-primary-color": "#fff",
"@btn-primary-bg": "@brand-primary",
"@btn-primary-border": "darken(@btn-primary-bg, 5%)",
"@btn-success-color": "#fff",
"@btn-success-bg": "@brand-success",
"@btn-success-border": "darken(@btn-success-bg, 5%)",
"@btn-info-color": "#fff",
"@btn-info-bg": "@brand-info",
"@btn-info-border": "darken(@btn-info-bg, 5%)",
"@btn-warning-color": "#fff",
"@btn-warning-bg": "@brand-warning",
"@btn-warning-border": "darken(@btn-warning-bg, 5%)",
"@btn-danger-color": "#fff",
"@btn-danger-bg": "@brand-danger",
"@btn-danger-border": "darken(@btn-danger-bg, 5%)",
"@btn-link-disabled-color": "@gray-light",
"@btn-border-radius-base": "@border-radius-base",
"@btn-border-radius-large": "@border-radius-large",
"@btn-border-radius-small": "@border-radius-small",
"@input-bg": "#fff",
"@input-bg-disabled": "@gray-lighter",
"@input-color": "@gray",
"@input-border": "#ccc",
"@input-border-radius": "@border-radius-base",
"@input-border-radius-large": "@border-radius-large",
"@input-border-radius-small": "@border-radius-small",
"@input-border-focus": "#66afe9",
"@input-color-placeholder": "#999",
"@input-height-base": "(@line-height-computed + (@padding-base-vertical * 2) + 2)",
"@input-height-large": "(ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2)",
"@input-height-small": "(floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2)",
"@form-group-margin-bottom": "15px",
"@legend-color": "@gray-dark",
"@legend-border-color": "#e5e5e5",
"@input-group-addon-bg": "@gray-lighter",
"@input-group-addon-border-color": "@input-border",
"@cursor-disabled": "not-allowed",
"@dropdown-bg": "#fff",
"@dropdown-border": "rgba(0, 0, 0, .15)",
"@dropdown-fallback-border": "#ccc",
"@dropdown-divider-bg": "#e5e5e5",
"@dropdown-link-color": "@gray-dark",
"@dropdown-link-hover-color": "darken(@gray-dark, 5%)",
"@dropdown-link-hover-bg": "#f5f5f5",
"@dropdown-link-active-color": "@component-active-color",
"@dropdown-link-active-bg": "@component-active-bg",
"@dropdown-link-disabled-color": "@gray-light",
"@dropdown-header-color": "@gray-light",
"@dropdown-caret-color": "#000",
"@screen-xs": "480px",
"@screen-xs-min": "@screen-xs",
"@screen-phone": "@screen-xs-min",
"@screen-sm": "768px",
"@screen-sm-min": "@screen-sm",
"@screen-tablet": "@screen-sm-min",
"@screen-md": "992px",
"@screen-md-min": "@screen-md",
"@screen-desktop": "@screen-md-min",
"@screen-lg": "1200px",
"@screen-lg-min": "@screen-lg",
"@screen-lg-desktop": "@screen-lg-min",
"@screen-xs-max": "(@screen-sm-min - 1)",
"@screen-sm-max": "(@screen-md-min - 1)",
"@screen-md-max": "(@screen-lg-min - 1)",
"@grid-columns": "12",
"@grid-gutter-width": "30px",
"@grid-float-breakpoint": "@screen-sm-min",
"@grid-float-breakpoint-max": "(@grid-float-breakpoint - 1)",
"@container-tablet": "(720px + @grid-gutter-width)",
"@container-sm": "@container-tablet",
"@container-desktop": "(940px + @grid-gutter-width)",
"@container-md": "@container-desktop",
"@container-large-desktop": "(1140px + @grid-gutter-width)",
"@container-lg": "@container-large-desktop",
"@navbar-height": "50px",
"@navbar-margin-bottom": "@line-height-computed",
"@navbar-border-radius": "@border-radius-base",
"@navbar-padding-horizontal": "floor((@grid-gutter-width / 2))",
"@navbar-padding-vertical": "((@navbar-height - @line-height-computed) / 2)",
"@navbar-collapse-max-height": "340px",
"@navbar-default-color": "#777",
"@navbar-default-bg": "#f8f8f8",
"@navbar-default-border": "darken(@navbar-default-bg, 6.5%)",
"@navbar-default-link-color": "#777",
"@navbar-default-link-hover-color": "#333",
"@navbar-default-link-hover-bg": "transparent",
"@navbar-default-link-active-color": "#555",
"@navbar-default-link-active-bg": "darken(@navbar-default-bg, 6.5%)",
"@navbar-default-link-disabled-color": "#ccc",
"@navbar-default-link-disabled-bg": "transparent",
"@navbar-default-brand-color": "@navbar-default-link-color",
"@navbar-default-brand-hover-color": "darken(@navbar-default-brand-color, 10%)",
"@navbar-default-brand-hover-bg": "transparent",
"@navbar-default-toggle-hover-bg": "#ddd",
"@navbar-default-toggle-icon-bar-bg": "#888",
"@navbar-default-toggle-border-color": "#ddd",
"@navbar-inverse-color": "lighten(@gray-light, 15%)",
"@navbar-inverse-bg": "#222",
"@navbar-inverse-border": "darken(@navbar-inverse-bg, 10%)",
"@navbar-inverse-link-color": "lighten(@gray-light, 15%)",
"@navbar-inverse-link-hover-color": "#fff",
"@navbar-inverse-link-hover-bg": "transparent",
"@navbar-inverse-link-active-color": "@navbar-inverse-link-hover-color",
"@navbar-inverse-link-active-bg": "darken(@navbar-inverse-bg, 10%)",
"@navbar-inverse-link-disabled-color": "#444",
"@navbar-inverse-link-disabled-bg": "transparent",
"@navbar-inverse-brand-color": "@navbar-inverse-link-color",
"@navbar-inverse-brand-hover-color": "#fff",
"@navbar-inverse-brand-hover-bg": "transparent",
"@navbar-inverse-toggle-hover-bg": "#333",
"@navbar-inverse-toggle-icon-bar-bg": "#fff",
"@navbar-inverse-toggle-border-color": "#333",
"@nav-link-padding": "10px 15px",
"@nav-link-hover-bg": "@gray-lighter",
"@nav-disabled-link-color": "@gray-light",
"@nav-disabled-link-hover-color": "@gray-light",
"@nav-tabs-border-color": "#ddd",
"@nav-tabs-link-hover-border-color": "@gray-lighter",
"@nav-tabs-active-link-hover-bg": "@body-bg",
"@nav-tabs-active-link-hover-color": "@gray",
"@nav-tabs-active-link-hover-border-color": "#ddd",
"@nav-tabs-justified-link-border-color": "#ddd",
"@nav-tabs-justified-active-link-border-color": "@body-bg",
"@nav-pills-border-radius": "@border-radius-base",
"@nav-pills-active-link-hover-bg": "@component-active-bg",
"@nav-pills-active-link-hover-color": "@component-active-color",
"@pagination-color": "@link-color",
"@pagination-bg": "#fff",
"@pagination-border": "#ddd",
"@pagination-hover-color": "@link-hover-color",
"@pagination-hover-bg": "@gray-lighter",
"@pagination-hover-border": "#ddd",
"@pagination-active-color": "#fff",
"@pagination-active-bg": "@brand-primary",
"@pagination-active-border": "@brand-primary",
"@pagination-disabled-color": "@gray-light",
"@pagination-disabled-bg": "#fff",
"@pagination-disabled-border": "#ddd",
"@pager-bg": "@pagination-bg",
"@pager-border": "@pagination-border",
"@pager-border-radius": "15px",
"@pager-hover-bg": "@pagination-hover-bg",
"@pager-active-bg": "@pagination-active-bg",
"@pager-active-color": "@pagination-active-color",
"@pager-disabled-color": "@pagination-disabled-color",
"@jumbotron-padding": "30px",
"@jumbotron-color": "inherit",
"@jumbotron-bg": "@gray-lighter",
"@jumbotron-heading-color": "inherit",
"@jumbotron-font-size": "ceil((@font-size-base * 1.5))",
"@jumbotron-heading-font-size": "ceil((@font-size-base * 4.5))",
"@state-success-text": "#3c763d",
"@state-success-bg": "#dff0d8",
"@state-success-border": "darken(spin(@state-success-bg, -10), 5%)",
"@state-info-text": "#31708f",
"@state-info-bg": "#d9edf7",
"@state-info-border": "darken(spin(@state-info-bg, -10), 7%)",
"@state-warning-text": "#8a6d3b",
"@state-warning-bg": "#fcf8e3",
"@state-warning-border": "darken(spin(@state-warning-bg, -10), 5%)",
"@state-danger-text": "#a94442",
"@state-danger-bg": "#f2dede",
"@state-danger-border": "darken(spin(@state-danger-bg, -10), 5%)",
"@tooltip-max-width": "200px",
"@tooltip-color": "#fff",
"@tooltip-bg": "#000",
"@tooltip-opacity": ".9",
"@tooltip-arrow-width": "5px",
"@tooltip-arrow-color": "@tooltip-bg",
"@popover-bg": "#fff",
"@popover-max-width": "276px",
"@popover-border-color": "rgba(0, 0, 0, .2)",
"@popover-fallback-border-color": "#ccc",
"@popover-title-bg": "darken(@popover-bg, 3%)",
"@popover-arrow-width": "10px",
"@popover-arrow-color": "@popover-bg",
"@popover-arrow-outer-width": "(@popover-arrow-width + 1)",
"@popover-arrow-outer-color": "fadein(@popover-border-color, 5%)",
"@popover-arrow-outer-fallback-color": "darken(@popover-fallback-border-color, 20%)",
"@label-default-bg": "@gray-light",
"@label-primary-bg": "@brand-primary",
"@label-success-bg": "@brand-success",
"@label-info-bg": "@brand-info",
"@label-warning-bg": "@brand-warning",
"@label-danger-bg": "@brand-danger",
"@label-color": "#fff",
"@label-link-hover-color": "#fff",
"@modal-inner-padding": "15px",
"@modal-title-padding": "15px",
"@modal-title-line-height": "@line-height-base",
"@modal-content-bg": "#fff",
"@modal-content-border-color": "rgba(0, 0, 0, .2)",
"@modal-content-fallback-border-color": "#999",
"@modal-backdrop-bg": "#000",
"@modal-backdrop-opacity": ".5",
"@modal-header-border-color": "#e5e5e5",
"@modal-footer-border-color": "@modal-header-border-color",
"@modal-lg": "900px",
"@modal-md": "600px",
"@modal-sm": "300px",
"@alert-padding": "15px",
"@alert-border-radius": "@border-radius-base",
"@alert-link-font-weight": "bold",
"@alert-success-bg": "@state-success-bg",
"@alert-success-text": "@state-success-text",
"@alert-success-border": "@state-success-border",
"@alert-info-bg": "@state-info-bg",
"@alert-info-text": "@state-info-text",
"@alert-info-border": "@state-info-border",
"@alert-warning-bg": "@state-warning-bg",
"@alert-warning-text": "@state-warning-text",
"@alert-warning-border": "@state-warning-border",
"@alert-danger-bg": "@state-danger-bg",
"@alert-danger-text": "@state-danger-text",
"@alert-danger-border": "@state-danger-border",
"@progress-bg": "#f5f5f5",
"@progress-bar-color": "#fff",
"@progress-border-radius": "@border-radius-base",
"@progress-bar-bg": "@brand-primary",
"@progress-bar-success-bg": "@brand-success",
"@progress-bar-warning-bg": "@brand-warning",
"@progress-bar-danger-bg": "@brand-danger",
"@progress-bar-info-bg": "@brand-info",
"@list-group-bg": "#fff",
"@list-group-border": "#ddd",
"@list-group-border-radius": "@border-radius-base",
"@list-group-hover-bg": "#f5f5f5",
"@list-group-active-color": "@component-active-color",
"@list-group-active-bg": "@component-active-bg",
"@list-group-active-border": "@list-group-active-bg",
"@list-group-active-text-color": "lighten(@list-group-active-bg, 40%)",
"@list-group-disabled-color": "@gray-light",
"@list-group-disabled-bg": "@gray-lighter",
"@list-group-disabled-text-color": "@list-group-disabled-color",
"@list-group-link-color": "#555",
"@list-group-link-hover-color": "@list-group-link-color",
"@list-group-link-heading-color": "#333",
"@panel-bg": "#fff",
"@panel-body-padding": "15px",
"@panel-heading-padding": "10px 15px",
"@panel-footer-padding": "@panel-heading-padding",
"@panel-border-radius": "@border-radius-base",
"@panel-inner-border": "#ddd",
"@panel-footer-bg": "#f5f5f5",
"@panel-default-text": "@gray-dark",
"@panel-default-border": "#ddd",
"@panel-default-heading-bg": "#f5f5f5",
"@panel-primary-text": "#fff",
"@panel-primary-border": "@brand-primary",
"@panel-primary-heading-bg": "@brand-primary",
"@panel-success-text": "@state-success-text",
"@panel-success-border": "@state-success-border",
"@panel-success-heading-bg": "@state-success-bg",
"@panel-info-text": "@state-info-text",
"@panel-info-border": "@state-info-border",
"@panel-info-heading-bg": "@state-info-bg",
"@panel-warning-text": "@state-warning-text",
"@panel-warning-border": "@state-warning-border",
"@panel-warning-heading-bg": "@state-warning-bg",
"@panel-danger-text": "@state-danger-text",
"@panel-danger-border": "@state-danger-border",
"@panel-danger-heading-bg": "@state-danger-bg",
"@thumbnail-padding": "4px",
"@thumbnail-bg": "@body-bg",
"@thumbnail-border": "#ddd",
"@thumbnail-border-radius": "@border-radius-base",
"@thumbnail-caption-color": "@text-color",
"@thumbnail-caption-padding": "9px",
"@well-bg": "#f5f5f5",
"@well-border": "darken(@well-bg, 7%)",
"@badge-color": "#fff",
"@badge-link-hover-color": "#fff",
"@badge-bg": "@gray-light",
"@badge-active-color": "@link-color",
"@badge-active-bg": "#fff",
"@badge-font-weight": "bold",
"@badge-line-height": "1",
"@badge-border-radius": "10px",
"@breadcrumb-padding-vertical": "8px",
"@breadcrumb-padding-horizontal": "15px",
"@breadcrumb-bg": "#f5f5f5",
"@breadcrumb-color": "#ccc",
"@breadcrumb-active-color": "@gray-light",
"@breadcrumb-separator": "\"/\"",
"@carousel-text-shadow": "0 1px 2px rgba(0, 0, 0, .6)",
"@carousel-control-color": "#fff",
"@carousel-control-width": "15%",
"@carousel-control-opacity": ".5",
"@carousel-control-font-size": "20px",
"@carousel-indicator-active-bg": "#fff",
"@carousel-indicator-border-color": "#fff",
"@carousel-caption-color": "#fff",
"@close-font-weight": "bold",
"@close-color": "#000",
"@close-text-shadow": "0 1px 0 #fff",
"@code-color": "#c7254e",
"@code-bg": "#f9f2f4",
"@kbd-color": "#fff",
"@kbd-bg": "#333",
"@pre-bg": "#f5f5f5",
"@pre-color": "@gray-dark",
"@pre-border-color": "#ccc",
"@pre-scrollable-max-height": "340px",
"@component-offset-horizontal": "180px",
"@text-muted": "@gray-light",
"@abbr-border-color": "@gray-light",
"@headings-small-color": "@gray-light",
"@blockquote-small-color": "@gray-light",
"@blockquote-font-size": "(@font-size-base * 1.25)",
"@blockquote-border-color": "@gray-lighter",
"@page-header-border-color": "@gray-lighter",
"@dl-horizontal-offset": "@component-offset-horizontal",
"@dl-horizontal-breakpoint": "@grid-float-breakpoint",
"@hr-border": "@gray-lighter"
},
"css": [
"dropdowns.less"
],
"js": [
"button.js",
"dropdown.js"
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Some files were not shown because too many files have changed in this diff Show More