Compare commits

...

96 Commits

Author SHA1 Message Date
dependabot[bot]
96a96b02b9 Bump xstream from 1.4.6 to 1.4.13-java7 in /jodconverter-web
Bumps [xstream](https://github.com/x-stream/xstream) from 1.4.6 to 1.4.13-java7.
- [Release notes](https://github.com/x-stream/xstream/releases)
- [Commits](https://github.com/x-stream/xstream/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2020-11-16 20:29:10 +00:00
kailing
e1035510df !16 修复部分乱码问题
Merge pull request !16 from 隔壁邻居王师傅/N/A
2020-08-31 13:49:53 +08:00
隔壁邻居王师傅
c466d0399c 修复部分乱码问题
由于复制winodws字库会导致run异常,可以直接安装相关中文字库来解决
2020-08-17 11:58:46 +08:00
陈精华
8b33a233dd !15 2.2.1版
Merge pull request !15 from 陈精华/master
2020-08-11 17:49:37 +08:00
陈精华
9e2962bb62 2.2.1版 2020-08-11 10:42:25 +08:00
陈精华
fc73deb3fd 修复上传到demo中的压缩文件及pdf预览异常 2020-08-11 10:42:01 +08:00
陈精华
40ac4b1eb9 !14 2.2.1迭代
Merge pull request !14 from 陈精华/master
2020-06-09 16:42:56 +08:00
陈精华
3d6da5f5a0 预览接口同时支持get和post请求,重新提交gitignore影响的目录文件 2020-06-09 16:35:11 +08:00
陈精华
7c4fc42247 2.2.1迭代,docker镜像底层使用ubuntu 2020-06-09 16:22:00 +08:00
陈精华
0c2a92080e !13 【开源贡献活动】修复电子发票预览图片方式正常pdf预览出现文字缺失异常 #I1I5SZ
Merge pull request !13 from 点蚊子熏烟/master
2020-06-08 14:07:36 +08:00
chenheng
b1fd13bcbb 【开源贡献活动】解决maven打包导致pdf.js bcmap异常问题,升级pdf.js到最新版本2.4.567 2020-06-05 16:34:59 +08:00
lidan
cd37ff4b41 支持 SimText 原样格式输出 2020-06-01 10:22:42 +08:00
陈精华
215e9f0f4a 2.2.0版 2020-05-20 08:44:30 +08:00
陈精华
26e147b426 更新文档&细节调整 2020-05-20 08:44:30 +08:00
陈精华
c7318c2b17 优化:PDF文件下载缓存 2020-05-20 08:44:30 +08:00
陈精华
c16116c7cd 优化:视频文件预览支持FTP 2020-05-20 08:44:30 +08:00
陈精华
1a4748bbec 优化:url特殊字符相关处理 2020-05-20 08:44:30 +08:00
陈精华
c0f7d60213 新增:更新日志 2020-05-18 15:55:26 +08:00
陈精华
bf83a0847d 优化:构建镜像使用阿里云yum源,加速构建 2020-05-18 15:55:26 +08:00
陈精华
7601d49795 修复:允许URL中出现|{}等字符,解决The valid characters are defined in RFC 7230 and RFC 3986问题 2020-05-18 15:55:26 +08:00
陈精华
1edf4d83f1 优化:重构代码 2020-05-18 15:55:26 +08:00
陈精华
f620c00785 优化:首页评论切换到Gitalk 2020-05-18 15:55:26 +08:00
陈精华
180e7bcb8a 优化:重构大量代码,修复异常 2020-05-18 15:55:26 +08:00
陈精华
8a52450629 优化:去除一种文件只允许上传一个提示 2020-05-18 15:55:26 +08:00
陈精华
a535ebfe1d 新功能点:新增配置是否可以下载转换完成的pdf文件 2020-05-18 15:55:26 +08:00
陈精华
3e80590a82 优化:默认启动水印 2020-05-18 15:55:26 +08:00
陈精华
5196536bb4 修复:修复预览ftp时,图片图片、pdf预览异常 2020-05-18 15:55:26 +08:00
陈精华
59ac8effc1 优化:添加日志 2020-05-18 15:55:26 +08:00
陈精华
9cc0267619 优化:允许上传多个同一类型文件 2020-05-18 15:55:26 +08:00
陈精华
a21f35c2b0 修复:删除文件时,也删除文件夹 2020-05-18 15:55:26 +08:00
陈精华
fde31cb327 新功能点:支持全局水印 2020-05-18 15:55:26 +08:00
陈精华
9c096605bb 修复:修复TrustHost静态注入问题 2020-05-18 15:55:26 +08:00
陈精华
b90e326eec 优化:移除localBaseUrl 2020-05-18 15:55:26 +08:00
陈精华
66e2acd063 优化:图片预览背景颜色和office相关预览背景颜色统一 2020-05-12 11:08:02 +08:00
陈精华
44165d655d 优化:修改历史遗留容易造成误解的方法名 2020-05-12 11:08:02 +08:00
陈精华
c9a6956b0d 优化:将前端所有CDN依赖放到本地,方便没有外网连接的用户使用 2020-05-12 11:08:02 +08:00
陈精华
9288564195 优化:去除pom文件多余properties 2020-05-12 11:08:02 +08:00
telami
9b8e7f812c 修复:预览图片时,点击周围会出现白屏 2020-05-07 09:03:40 +08:00
telami
3908f1be7e 修复:需求https://gitee.com/kekingcn/file-online-preview/issues/I14D4X,当使用nginx做代理时,配置了content-path和baseUrl时,访问view.html和getCorsFile会出现404 2020-04-29 09:04:37 +08:00
telami
15bd035304 修复:需求https://gitee.com/kekingcn/file-online-preview/issues/I14D4X,当使用nginx做代理时,配置了content-path和baseUrl时,访问view.html和getCorsFile会出现404 2020-04-29 09:04:37 +08:00
陈精华
e0b1cd76ca 修复:addTask异常 2020-04-14 11:22:07 +08:00
陈精华
2144b776b1 【新特性】支持限制预览源站点,保护预览服务不被滥用 2020-02-19 10:40:15 +08:00
陈精华
a8022df1d9 部分页面移动端样式 2020-02-19 10:40:15 +08:00
kl
a5d92bf905 Update .gitattributes 2020-02-10 15:51:43 +08:00
kl
81a18d07c3 Create .gitattributes 2020-02-10 15:47:24 +08:00
dependabot[bot]
86e5dcb23b Bump commons-compress from 1.18 to 1.19 in /jodconverter-web
Bumps commons-compress from 1.18 to 1.19.

Signed-off-by: dependabot[bot] <support@github.com>
2019-11-29 16:57:25 +08:00
陈精华
1339c09382 优化:调整异常页面提示文案 2019-11-29 16:52:58 +08:00
陈精华
01e310a6e7 优化:预览URL特殊字体使用Hutool解决方案 2019-11-29 16:52:58 +08:00
陈精华
a07c962867 优化:Office组件运行状态检测 2019-11-29 16:52:58 +08:00
陈精华
09b6964c0e 脚本:使用kkFileView站点下载OpenOffice安装文件,加速国内用户构建Docker镜像 2019-11-29 16:52:58 +08:00
陈精华
461030d34f 构建脚本:编译时包含本地jar依赖 2019-11-21 18:19:52 +08:00
陈精华
c1a7cd8c46 新功能点,加入CAD图纸.dwg文件预览 2019-11-21 17:11:36 +08:00
陈精华
5888c56f1d 优化:异常处理、异常提示 2019-11-21 17:11:36 +08:00
陈精华
0798b8d8a0 优化:删除无用代码、页面加header 2019-11-21 17:11:36 +08:00
陈精华
ae93d48b44 配置:默认指定缓存实现为JDK实现 2019-11-21 17:11:36 +08:00
陈精华
506a3ba2b3 优化:服务器无法通过外网访问本地下载时,使用内网IP下载 2019-11-21 17:11:36 +08:00
陈精华
12f197b623 构建脚本:使用kkFileView站点下载OpenOffice安装文件,加速国内用户构建Docker镜像 2019-11-21 17:11:36 +08:00
陈精华
2001b241ee 运行环境:CentOS预先安装OpenOffice相关依赖 2019-11-21 17:11:36 +08:00
陈精华
7911edec4b 修复:文本文档首次预览后缓存文件未清理问题 2019-11-06 16:53:39 +08:00
陈精华
7ea70bf422 优化:图片模式下图片url使用baseUrl 2019-11-06 16:53:39 +08:00
陈精华
b1fdbd26a3 新功能点:全部能识别的纯文本直接预览,不用再转跳下载(.md、.java、.py等浏览器不认识的后缀名) 2019-10-25 15:16:25 +08:00
陈精华
fa7241bd4e 新功能点:所有配置项支持从环境变量里读取,方便Docker镜像部署 2019-10-25 15:16:25 +08:00
陈精华
8fdf462c6c 新功能点:缓存清理时间cron表达式支持自定义 link #I1147X 2019-10-25 15:16:25 +08:00
陈精华
f7c7411bcf fixup! 优化:目录调整,符合maven规范;maven编译指定.sh脚本换行符为unix换行 2019-10-25 15:16:25 +08:00
陈精华
845cb2e657 修复:参数包含特殊字符时url解码失败 2019-10-25 15:16:25 +08:00
陈精华
a4bfde68bd 优化:目录调整,符合maven规范;maven编译指定.sh脚本换行符为unix换行 2019-10-25 15:16:25 +08:00
陈精华
19d1ba6cf9 优化:(内部)移除为pdf文档提供base64缩略图 2019-10-25 15:16:25 +08:00
陈精华
1060bdd00f 新功能点:支持base url配置(主要用于nginx反向代理等) 2019-10-17 11:36:43 +08:00
陈精华
8c2fb2bdee 优化:默认启用缓存 2019-10-17 11:36:43 +08:00
陈精华
0fe75387eb 优化:启动脚本加入官网链接,点star推广等文案 close #I1148F 2019-09-16 11:49:49 +08:00
陈精华
fbea49e54f 优化:加入查看日志脚本 2019-09-16 11:49:49 +08:00
陈精华
41a72798d9 优化:去除可能导致文件不更新的缓存 2019-09-16 11:49:49 +08:00
陈精华
03cc185085 修复:压缩包中文件名有空格异常 2019-09-16 11:49:49 +08:00
陈精华
bfbd8ee25e 新功能点:(内部)为pdf文档提供base64缩略图 2019-09-10 18:24:06 +08:00
陈精华
f3f36169ff 优化:压缩文件名支持有特殊字符 2019-09-10 18:24:06 +08:00
陈精华
2df88544d3 修复:macOS下office组件默认路径错误 2019-09-10 18:24:06 +08:00
陈精华
6b744d77c7 修复:RocksDB缓存实现压缩包图片url缓存失效 2019-09-10 18:24:06 +08:00
doras
ba57dedebb "加入是否启用缓存配置项"后,excel转成的html文件不再转换编码,修复因此出现的乱码问题 2019-08-26 11:17:37 +08:00
陈精华
affd5b3057 图片和pdf预览模式切换按钮大小调整 2019-08-23 18:18:37 +08:00
陈精华
30c3128995 修复压缩文件中文fileKey未编码 link #I111PD 2019-08-23 18:18:37 +08:00
陈精华
b003a05775 加入是否启用缓存配置项 2019-08-23 18:18:37 +08:00
陈精华
98ec3d7dab 首页预览打开新页面 2019-08-23 18:18:37 +08:00
陈精华
69e23dbb99 shutdown脚本更新 2019-08-23 18:18:37 +08:00
陈精华
47bda1023a 修复Chrome76+删除弹出新窗口 2019-08-23 18:18:37 +08:00
陈精华
fd538a74af 中文语言环境 2019-08-23 18:18:37 +08:00
陈精华
300d213a7a cdn资源不指定http/https 2019-08-23 18:18:37 +08:00
陈精华
4c0a70f300 2.2.0迭代 2019-08-23 18:18:37 +08:00
陈精华
8798d344b6 2.1.2版 2019-07-30 16:31:53 +08:00
陈精华
63e62ab57b pdf.js使用里面一个bug 2019-07-30 16:31:53 +08:00
陈精华
9a027674ac 演示首页兼容IE 2019-07-30 16:31:53 +08:00
1045485954@qq.com
e4407467dd IE兼容性问题,目前已兼容到IE9 2019-07-17 09:03:32 +08:00
陈精华
551eeb0390 2.1.1版 2019-07-09 10:46:21 +08:00
陈精华
11d6ad1ed3 修复文件下载流URL参数中包含中文URL编码不正确导致HTTP-400异常 2019-07-09 10:46:21 +08:00
陈精华
e57db6925c 修复config.js 404问题 2019-07-09 10:46:21 +08:00
陈精华
87096364d8 首页示例修改、首页更新记录 2019-07-09 10:46:21 +08:00
陈精华
ad8027a7d0 2.1.1迭代 2019-07-09 10:46:21 +08:00
440 changed files with 232102 additions and 59210 deletions

5
.gitattributes vendored Normal file
View File

@@ -0,0 +1,5 @@
*.css linguist-language=java
*.less linguist-language=java
*.js linguist-language=java
*.html linguist-language=java
*.* linguist-language=java

19
.gitignore vendored
View File

@@ -24,19 +24,8 @@ nbdist/
.nb-gradle/
/*.iml
**/target/
.classpath
.project
**/.settings
**/build/
**/.externalToolBuilders/
*.iml
**/.idea/
**/disconf
**/rpc.properties
/producer/tmp
/.temfile
.temfile
convertedFile/
### VS Code ###
.vscode/
jodconverter-web/src/main/cache/
jodconverter-web/src/main/file/
jodconverter-web/src/main/file/

View File

@@ -1,31 +1,32 @@
FROM centos:centos7.6.1810
FROM ubuntu:20.04
MAINTAINER chenjh "842761733@qq.com"
ADD jodconverter-web/target/kkFileView-*.tar.gz /opt/
COPY fonts/* /usr/share/fonts/
RUN yum install -y kde-l10n-Chinese &&\
yum install -y glibc-common &&\
yum install -y fontconfig &&\
yum install -y mkfontscale &&\
localedef -c -f UTF-8 -i zh_CN zh_CN.utf8 &&\
export LANG=zh_CN.UTF-8 &&\
echo "export LANG=zh_CN.UTF-8" >> /etc/locale.conf &&\
LANG="zh_CN.UTF-8" &&\
yum install -y java-1.8.0-openjdk.x86_64 &&\
yum install -y wget &&\
yum install -y libXext.x86_64 &&\
yum groupinstall -y "X Window System" &&\
cd /tmp &&\
wget https://iweb.dl.sourceforge.net/project/openofficeorg.mirror/4.1.6/binaries/zh-CN/Apache_OpenOffice_4.1.6_Linux_x86-64_install-rpm_zh-CN.tar.gz -cO openoffice_rpm.tar.gz &&\
tar zxf /tmp/openoffice_rpm.tar.gz &&\
cd /tmp/zh-CN/RPMS &&\
rpm -Uvih *.rpm &&\
rpm -Uvih desktop-integration/openoffice4.1.6-redhat-menus-4.1.6-9790.noarch.rpm &&\
rm -f /tmp/openoffice_rpm.tar.gz &&\
rm -rf /tmp/zh-CN &&\
cd /usr/share/fonts &&\
COPY fonts/* /usr/share/fonts/chienes/
RUN echo "deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse" > /etc/apt/sources.list &&\
apt-get clean && apt-get update &&\
apt-get install -y locales && apt-get install -y language-pack-zh-hans &&\
localedef -i zh_CN -c -f UTF-8 -A /usr/share/locale/locale.alias zh_CN.UTF-8 && locale-gen zh_CN.UTF-8 &&\
apt-get install -y tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\
apt-get install -y libxrender1 && apt-get install -y libxt6 && apt-get install -y libxext-dev && apt-get install -y libfreetype6-dev &&\
apt-get install -y wget && apt-get install -y ttf-mscorefonts-installer && apt-get install -y fontconfig &&\
apt-get install ttf-wqy-microhei &&\
apt-get install ttf-wqy-zenhei &&\
apt-get install xfonts-wqy &&\
cd /tmp &&\
wget https://kkfileview.keking.cn/server-jre-8u251-linux-x64.tar.gz &&\
wget https://kkfileview.keking.cn/Apache_OpenOffice_4.1.6_Linux_x86-64_install-deb_zh-CN.tar.gz -cO openoffice_deb.tar.gz &&\
tar -zxf /tmp/server-jre-8u251-linux-x64.tar.gz && mv /tmp/jdk1.8.0_251 /usr/local/ &&\
tar -zxf /tmp/openoffice_deb.tar.gz && cd /tmp/zh-CN/DEBS &&\
dpkg -i *.deb && dpkg -i desktop-integration/openoffice4.1-debian-menus_4.1.6-9790_all.deb &&\
rm -rf /tmp/* && rm -rf /var/lib/apt/lists/* &&\
cd /usr/share/fonts/chienes &&\
mkfontscale &&\
mkfontdir &&\
fc-cache -fv
mkfontdir &&\
fc-cache -fv
ENV JAVA_HOME /usr/local/jdk1.8.0_251
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH $PATH:$JAVA_HOME/bin
ENV LANG zh_CN.UTF-8
ENV LC_ALL zh_CN.UTF-8
ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-2.1.0/bin
ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider","-Dspring.config.location=/opt/kkFileView-2.1.0/conf/application.properties","-jar","/opt/kkFileView-2.1.0/bin/kkFileView-2.1.0.jar"]
ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-2.2.1/bin
ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider","-Dspring.config.location=/opt/kkFileView-2.2.1/config/application.properties","-jar","/opt/kkFileView-2.2.1/bin/kkFileView-2.2.1.jar"]

View File

@@ -16,14 +16,19 @@ special thanks to the supports of @唐老大 and the contributions of @端木详
- REST API
- Abstract file preview interface so that it is easy to extend more file extensions and develop this project on your own
### Official website and DOCS
URL[https://kkfileview.keking.cn](https://kkfileview.keking.cn)
### Live demo
> Please treat public service kindly, or this would stop at any time.
URLhttp://file.keking.cn/
URL[https://file.keking.cn](https://file.keking.cn)
### Documentation
1. Full wiki documenthttps://gitee.com/kekingcn/file-online-preview/wikis/pages
1. 中文文档https://gitee.com/kekingcn/file-online-preview/blob/master/README.md
1. English documenthttps://github.com/kekingcn/kkFileView/blob/master/README.en.md
1. English documenthttps://gitee.com/kekingcn/file-online-preview/blob/master/README.en.md
### Contact us && Join us
> We will answer everyone's questions in use of this project.
@@ -34,20 +39,62 @@ Cherish life away from ineffective communication.
QQ group613025121
### Pictures for some samples
> Excel
#### 1. Text Preview
It supports preview of all types of text documents. Because there are too many types of text documents, it is impossible to enumerate them. The default open types are as follows: txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd
The text preview effect is as follows
![text](https://kkfileview.keking.cn/img/preview/preview-text.png)
![输入图片说明](https://gitee.com/uploads/images/2017/1213/093051_cd55b3ec_492218.png "屏幕截图.png")
> doc
#### 2. Picture preview
Support jpg, jpeg, png, gif and other picture previews (flip, zoom, mirror). The preview effect is as follows
![image](https://kkfileview.keking.cn/img/preview/preview-image.png)
![输入图片说明](https://gitee.com/uploads/images/2017/1213/092350_5b2ecbe5_492218.png "屏幕截图.png")
#### 3. Word document preview
Doc and docx document previews are supported. There are two modes of word previews: one is that each page of word is converted to picture previews, the other is that the whole word document is converted to PDF, and then previews PDF. The applicable scenarios of the two modes are as follows
* Picture preview modes: the word file is large, and the whole PDF loaded in the foreground is too slow
* Pdf Preview modes: intranet access, loading PDF fast
The preview effect of picture preview mode is as follows
![word1](https://kkfileview.keking.cn/img/preview/preview-doc-image.png)
The preview effect of PDF preview mode is as follows
![word2](https://kkfileview.keking.cn/img/preview/preview-doc-pdf.png)
> zip,rar
#### 4. PPT document preview
ppt and pptx document preview are supported. Like word documents, there are two preview modes
The preview effect of picture preview mode is as follows
![ppt1](https://kkfileview.keking.cn/img/preview/preview-ppt-image.png)
The preview effect of PDF preview mode is as follows
![ppt2](https://kkfileview.keking.cn/img/preview/preview-ppt-pdf.png)
![输入图片说明](https://gitee.com/uploads/images/2017/1213/093806_46cede06_492218.png "屏幕截图.png")
#### 5. PDF document preview
Pdf document preview is supported. Like word document, there are two preview modes
The preview effect of picture preview mode is as follows
![pdf1](https://kkfileview.keking.cn/img/preview/preview-pdf-image.png)
The preview effect of PDF preview mode is as follows
![pdf2](https://kkfileview.keking.cn/img/preview/preview-pdf-pdf.png)
> png,jpeg,jpg,etc., support for zooming with mouse scroll, rotation, inversion,etc.
#### 6. EXCEL document preview
Support XLS, xlsx document preview, the preview effect is as follows
![excel](https://kkfileview.keking.cn/img/preview/preview-xls.png)
![输入图片说明](https://gitee.com/uploads/images/2017/1213/094335_657a6f60_492218.png "屏幕截图.png")
#### 7. Compressed file Preview
Support zip, rar, jar, tar, gzip and other compressed packages. The preview effect is as follows
![compress1](https://kkfileview.keking.cn/img/preview/preview-zip.png)
Click the file name in the compressed package to preview the file directly. The preview effect is as follows
![compress2](https://kkfileview.keking.cn/img/preview/preview-zip-inner.png)
#### 8. Multimedia file Preview
Theoretically, all video and audio files are supported. Since all file formats cannot be enumerated, the default open type is as follows
mp3,wav,mp4,flv
The video preview effect is as follows
![media1](https://kkfileview.keking.cn/img/preview/preview-video.png)
The audio preview effect is as follows
![media2](https://kkfileview.keking.cn/img/preview/preview-audio.png)
#### 9. CAD document preview
CAD DWG document preview is supported. Like word document, there are two preview modes
The preview effect of Picture preview mode is as follows
![cad1](https://kkfileview.keking.cn/img/preview/preview-cad-image.png)
The preview effect of PDF preview mode is as follows
![cad2](https://kkfileview.keking.cn/img/preview/preview-cad-pdf.png)
Considering space issues, the pictures of other types of documents will not be shown here.You can deploy it by yourself if you are interested in our project.There is a way to deploy it as below.
### Quick Start
@@ -67,6 +114,23 @@ If everything is ok,you will see the picture below.
![输入图片说明](https://gitee.com/uploads/images/2017/1213/100221_ea15202e_492218.png "屏幕截图.png")
### Changelog
> May 20th 2020
1. Support for global watermark and dynamic change of watermark content through parameters
2. Support for CAD file Preview
3. Add configuration item base.url, support using nginx reverse proxy and set context-path
4. All configuration items can be read from environment variables, which is convenient for docker image deployment and large-scale use in cluster
5. Support the configuration of TrustHost (only the file source from the trust site can be previewed), and protect the preview service from abuse
6. Support configuration of customize cache cleanup time (cron expression)
7. All recognizable plain text can be previewed directly without downloading, such as .md .java .py, etc
8. Support configuration to limit PDF file download after conversion
9. Optimize Maven packaging configuration to solve the problem of line break in .sh script
10. Place all CDN dependencies on the front end locally for users without external network connection
11. Comment Service on home page switched from Sohu ChangYan to gitalk
12. Fixed preview exceptions that may be caused by special characters in the URL
13. Fixed the addtask exception of the transformation file queue
14. Fixed other known issues
15. Official website build: [https://kkfileview.keking.cn](https://kkfileview.keking.cn)
16. Official docker image repository build: [https://hub.docker.com/r/keking/kkfileview](https://hub.docker.com/r/keking/kkfileview)
> June 18th 2019
1. Support automatic cleaning of cache and preview files

View File

@@ -2,7 +2,7 @@
此项目为文件文档在线预览项目解决方案对标业内付费产品有[永中office](http://dcs.yozosoft.com/)】【[office365](http://www.officeweb365.com/)】【[idocv](https://www.idocv.com/)】等在取得公司高层同意后以Apache协议开源出来反哺社区在此特别感谢@唐老大的支持以及@端木详笑的贡献。该项目使用流行的spring boot搭建易上手和部署基本支持主流办公文档的在线预览如doc,docx,Excel,pdf,txt,zip,rar,图片等等
### 项目特性
1. 支持officepdf等办公文档
1. 支持officepdf, cad等办公文档
1. 支持txt,java,php,py,md,js,css等所有纯文本
1. 支持zip,rar,jar,tar,gzip等压缩包
1. 支持jpgjpegpnggif等图片预览翻转缩放镜像
@@ -11,15 +11,19 @@
1. 抽象预览服务接口方便二次开发非常方便添加其他类型文件预览支持
1. 最最重要Apache协议开源代码pull下来想干嘛就干嘛
### 官网及文档
地址[https://kkfileview.keking.cn](https://kkfileview.keking.cn)
### 在线体验
> 请善待公共服务会不定时停用
地址http://file.keking.cn/
地址[https://file.keking.cn](https://file.keking.cn)
### 项目文档Project documentation
1. 详细wiki文档https://gitee.com/kekingcn/file-online-preview/wikis/pages
1. 中文文档https://gitee.com/kekingcn/file-online-preview/blob/master/README.md
1. English documenthttps://github.com/kekingcn/kkFileView/blob/master/README.en.md
1. English documenthttps://gitee.com/kekingcn/file-online-preview/blob/master/README.en.md
### 联系我们加入组织
> 我们会用心回答解决大家在项目使用中的问题也请大家在提问前至少Google或baidu过珍爱生命远离无效的交流沟通
@@ -28,20 +32,62 @@
QQ群号613025121
### 文档预览效果
> Excel预览效果
#### 1. 文本预览
支持所有类型的文本文档预览 由于文本文档类型过多无法全部枚举默认开启的类型如下 txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd
文本预览效果如下
![文本预览效果如下](https://kkfileview.keking.cn/img/preview/preview-text.png)
![输入图片说明](https://gitee.com/uploads/images/2017/1213/093051_cd55b3ec_492218.png "屏幕截图.png")
> doc预览效果
#### 2. 图片预览
支持jpgjpegpnggif等图片预览翻转缩放镜像预览效果如下
![图片预览](https://kkfileview.keking.cn/img/preview/preview-image.png)
![输入图片说明](https://gitee.com/uploads/images/2017/1213/092350_5b2ecbe5_492218.png "屏幕截图.png")
#### 3. word文档预览
支持docdocx文档预览word预览有两种模式一种是每页word转为图片预览另一种是整个word文档转成pdf再预览pdf两种模式的适用场景如下
* 图片预览word文件大前台加载整个pdf过慢
* pdf预览内网访问加载pdf快
图片预览模式预览效果如下
![word文档预览1](https://kkfileview.keking.cn/img/preview/preview-doc-image.png)
pdf预览模式预览效果如下
![word文档预览2](https://kkfileview.keking.cn/img/preview/preview-doc-pdf.png)
> zip,rar压缩预览效果
#### 4. ppt文档预览
支持pptpptx文档预览和word文档一样有两种预览模式
图片预览模式预览效果如下
![ppt文档预览1](https://kkfileview.keking.cn/img/preview/preview-ppt-image.png)
pdf预览模式预览效果如下
![ppt文档预览2](https://kkfileview.keking.cn/img/preview/preview-ppt-pdf.png)
![输入图片说明](https://gitee.com/uploads/images/2017/1213/093806_46cede06_492218.png "屏幕截图.png")
#### 5. pdf文档预览
支持pdf文档预览和word文档一样有两种预览模式
图片预览模式预览效果如下
![pdf文档预览1](https://kkfileview.keking.cn/img/preview/preview-pdf-image.png)
pdf预览模式预览效果如下
![pdf文档预览2](https://kkfileview.keking.cn/img/preview/preview-pdf-pdf.png)
> png,jpeg,jpg等图片预览效果支持滚轮缩放旋转倒置等
#### 6. excel文档预览
支持xlsxlsx文档预览预览效果如下
![excel文档预览](https://kkfileview.keking.cn/img/preview/preview-xls.png)
![输入图片说明](https://gitee.com/uploads/images/2017/1213/094335_657a6f60_492218.png "屏幕截图.png")
#### 7. 压缩文件预览
支持zip,rar,jar,tar,gzip等压缩包预览效果如下
![压缩文件预览1](https://kkfileview.keking.cn/img/preview/preview-zip.png)
可点击压缩包中的文件名直接预览文件预览效果如下
![压缩文件预览2](https://kkfileview.keking.cn/img/preview/preview-zip-inner.png)
#### 8. 多媒体文件预览
理论上支持所有的视频音频文件由于无法枚举所有文件格式默认开启的类型如下
mp3,wav,mp4,flv
视频预览效果如下
![多媒体文件预览1](https://kkfileview.keking.cn/img/preview/preview-video.png)
音频预览效果如下
![多媒体文件预览2](https://kkfileview.keking.cn/img/preview/preview-audio.png)
#### 9. CAD文档预览
支持CAD dwg文档预览和word文档一样有两种预览模式
图片预览模式预览效果如下
![cad文档预览1](https://kkfileview.keking.cn/img/preview/preview-cad-image.png)
pdf预览模式预览效果如下
![cad文档预览2](https://kkfileview.keking.cn/img/preview/preview-cad-pdf.png)
考虑说明篇幅原因就不贴其他格式文件的预览效果了感兴趣的可以参考下面的实例搭建下
### 快速开始
@@ -62,6 +108,24 @@ QQ群号613025121
### 历史更新记录
> 2020年05月20日
1. 新增支持全局水印并支持通过参数动态改变水印内容
2. 新增支持CAD文件预览
3. 新增base.url配置支持使用nginx反向代理和使用context-path
4. 支持所有配置项支持从环境变量里读取方便Docker镜像部署和集群中大规模使用
5. 支持配置限信任站点只能预览来自信任点的文件源保护预览服务不被滥用
6. 支持配置自定义缓存清理时间cron表达式
7. 全部能识别的纯文本直接预览不用再转跳下载.md .java .py等
8. 支持配置限制转换后的PDF文件下载
9. 优化maven打包配置解决 .sh 脚本可能出现换行符问题
10. 将前端所有CDN依赖放到本地方便没有外网连接的用户使用
11. 首页评论服务由搜狐畅言切换到Gitalk
12. 修复url中包含特殊字符可能会引起的预览异常
13. 修复转换文件队列addTask异常
14. 修复其他已经问题
15. 官网建设[https://kkfileview.keking.cn](https://kkfileview.keking.cn)
16. 官方Docker镜像仓库建设[https://hub.docker.com/r/keking/kkfileview](https://hub.docker.com/r/keking/kkfileview)
> 2019年06月18日
1. 支持自动清理缓存及预览文件
2. 支持http/https下载流url文件预览

View File

@@ -15,6 +15,7 @@ package org.artofsolving.jodconverter.office;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
@@ -26,6 +27,8 @@ import com.sun.star.uno.UnoRuntime;
public class OfficeUtils {
public static final String SERVICE_DESKTOP = "com.sun.star.frame.Desktop";
public static final String OFFICE_HOME_KEY = "office.home";
public static final String DEFAULT_OFFICE_HOME_VALUE = "default";
private OfficeUtils() {
throw new AssertionError("utility class must not be instantiated");
@@ -69,9 +72,11 @@ public class OfficeUtils {
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader(customizedConfigPath));
properties.load(bufferedReader);
restorePropertiesFromEnvFormat(properties);
} catch (Exception e) {}
if (properties.getProperty("office.home") != null) {
return new File(properties.getProperty("office.home"));
String officeHome = properties.getProperty(OFFICE_HOME_KEY);
if (officeHome != null && !DEFAULT_OFFICE_HOME_VALUE.equals(officeHome)) {
return new File(officeHome);
}
if (PlatformUtils.isWindows()) {
// %ProgramFiles(x86)% on 64-bit machines; %ProgramFiles% on 32-bit ones
@@ -116,7 +121,7 @@ public class OfficeUtils {
public static File getOfficeExecutable(File officeHome) {
if (PlatformUtils.isMac()) {
return new File(officeHome, "MacOS/soffice.bin");
return new File(officeHome, "MacOS/soffice");
} else {
return new File(officeHome, "program/soffice.bin");
}
@@ -143,8 +148,36 @@ public class OfficeUtils {
public static String getCustomizedConfigPath() {
String homePath = OfficeUtils.getHomePath();
String separator = java.io.File.separator;
String configFilePath = homePath + separator + "conf" + separator + "application.properties";
String configFilePath = homePath + separator + "config" + separator + "application.properties";
return configFilePath;
}
/**
* SpringBoot application.properties 支持从环境变量获取值
* @param properties
*/
public synchronized static void restorePropertiesFromEnvFormat(Properties properties) {
Iterator<Map.Entry<Object, Object>> iterator = properties.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Object, Object> entry = iterator.next();
String key = entry.getKey().toString();
String value = entry.getValue().toString();
if (value.trim().startsWith("${") && value.trim().endsWith("}")) {
int beginIndex = value.indexOf(":");
if (beginIndex < 0) {
beginIndex = value.length() - 1;
}
int endIndex = value.length() - 1;
String envKey = value.substring(2, beginIndex);
String envValue = System.getenv(envKey);
if (envValue == null || "".equals(envValue.trim())) {
value = value.substring(beginIndex + 1, endIndex);
} else {
value = envValue;
}
properties.setProperty(key, value);
}
}
}
}

Binary file not shown.

View File

@@ -12,16 +12,13 @@
<groupId>cn.keking</groupId>
<artifactId>kkFileView</artifactId>
<version>2.1.0</version>
<version>2.2.1</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<ufile.sdk.verison>1.0-SNAPSHOT</ufile.sdk.verison>
<logging.path>${basedir}/target/classes/logs</logging.path>
<appName>file-preview</appName>
</properties>
<repositories>
<repository>
@@ -60,6 +57,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<!-- REDISSON -->
<dependency>
<groupId>org.redisson</groupId>
@@ -106,7 +108,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.18</version>
<version>1.19</version>
</dependency>
<!-- 解压(rar)-->
<dependency>
@@ -156,7 +158,7 @@
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.6</version>
<version>1.4.13-java7</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
@@ -183,6 +185,13 @@
<artifactId>pdfbox-tools</artifactId>
<version>2.0.15</version>
</dependency>
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-cad</artifactId>
<version>19.9</version>
<scope>system</scope>
<systemPath>${basedir}/lib/aspose-cad-19.9.jar</systemPath>
</dependency>
</dependencies>
<build>
<resources>
@@ -191,26 +200,30 @@
<includes>
<include>**/*</include>
</includes>
<filtering>true</filtering>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/conf</directory>
<directory>src/main/config</directory>
<excludes>
<exclude>${build.exclude.resource}</exclude>
</excludes>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/main/resources/assembly.xml</descriptor>
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>

View File

@@ -11,18 +11,24 @@
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>src/main/conf</directory>
<outputDirectory>${file.separator}conf</outputDirectory>
<directory>src/main/bin</directory>
<outputDirectory>${file.separator}bin</outputDirectory>
<includes>
<include>*.sh</include>
</includes>
<fileMode>755</fileMode>
<lineEnding>unix</lineEnding>
</fileSet>
<fileSet>
<directory>src/main/bin</directory>
<outputDirectory>${file.separator}bin</outputDirectory>
<fileMode>755</fileMode>
<includes>
<include>*.bat</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/script</directory>
<outputDirectory>${file.separator}script</outputDirectory>
<fileMode>755</fileMode>
<directory>src/main/config</directory>
<outputDirectory>${file.separator}config</outputDirectory>
</fileSet>
<fileSet>
<directory>src/main/log</directory>

View File

@@ -2,8 +2,10 @@
cd /tmp
install_redhat() {
wget https://iweb.dl.sourceforge.net/project/openofficeorg.mirror/4.1.6/binaries/zh-CN/Apache_OpenOffice_4.1.6_Linux_x86-64_install-rpm_zh-CN.tar.gz -cO openoffice_rpm.tar.gz && tar zxf /tmp/openoffice_rpm.tar.gz && cd /tmp/zh-CN/RPMS
wget https://kkfileview.keking.cn/Apache_OpenOffice_4.1.6_Linux_x86-64_install-rpm_zh-CN.tar.gz -cO openoffice_rpm.tar.gz && tar zxf /tmp/openoffice_rpm.tar.gz && cd /tmp/zh-CN/RPMS
if [ $? -eq 0 ];then
yum install -y libXext.x86_64
yum groupinstall -y "X Window System"
rpm -Uvih *.rpm
echo 'install desktop service ...'
rpm -Uvih desktop-integration/openoffice4.1.6-redhat-menus-4.1.6-9790.noarch.rpm
@@ -14,9 +16,13 @@ install_redhat() {
}
install_ubuntu() {
wget https://iweb.dl.sourceforge.net/project/openofficeorg.mirror/4.1.6/binaries/zh-CN/Apache_OpenOffice_4.1.6_Linux_x86-64_install-deb_zh-CN.tar.gz -cO openoffice_deb.tar.gz && tar zxf /tmp/openoffice_deb.tar.gz && cd /tmp/zh-CN/DEBS
wget https://kkfileview.keking.cn/Apache_OpenOffice_4.1.6_Linux_x86-64_install-deb_zh-CN.tar.gz -cO openoffice_deb.tar.gz && tar zxf /tmp/openoffice_deb.tar.gz && cd /tmp/zh-CN/DEBS
echo $?
if [ $? -eq 0 ];then
apt-get install -y libxrender1
apt-get install -y libxt6
apt-get install -y libxext-dev
apt-get install -y libfreetype6-dev
dpkg -i *.deb
echo 'install desktop service ...'
dpkg -i desktop-integration/openoffice4.1-debian-menus_4.1.6-9790_all.deb

View File

@@ -0,0 +1,2 @@
#!/bin/bash
tail -fn 300 ../log/kkFileView.log

View File

@@ -1,2 +1,2 @@
#!/bin/bash
kill 15 `ps -ef|grep kkFileView|awk '{print $2}'`
kill -15 `ps -ef|grep kkFileView|awk 'NR==1{print $2}'`

View File

@@ -3,5 +3,7 @@ set "KKFILEVIEW_BIN_FOLDER=%cd%"
cd "%KKFILEVIEW_BIN_FOLDER%"
echo Using KKFILEVIEW_BIN_FOLDER %KKFILEVIEW_BIN_FOLDER%
echo Starting kkFileView...
echo Please check log file for more information
java -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=..\conf\application.properties -jar kkFileView-2.1.0.jar -> ..\log\kkFileView.log
echo Please check log file in ../log/kkFileView.log for more information
echo You can get help in our official homesite: https://kkFileView.keking.cn
echo If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers
java -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=..\config\application.properties -jar kkFileView-2.2.1.jar -> ..\log\kkFileView.log

View File

@@ -6,7 +6,7 @@ KKFILEVIEW_BIN_FOLDER=$(cd "$(dirname "$0")";pwd)
export KKFILEVIEW_BIN_FOLDER=$KKFILEVIEW_BIN_FOLDER
cd $KKFILEVIEW_BIN_FOLDER
echo "Using KKFILEVIEW_BIN_FOLDER $KKFILEVIEW_BIN_FOLDER"
grep 'office\.home' ../conf/application.properties | grep '!^#'
grep 'office\.home' ../config/application.properties | grep '!^#'
if [ $? -eq 0 ]; then
echo "Using customized office.home"
else
@@ -20,11 +20,13 @@ else
done
if [ ! -n "${FLAG}" ]; then
echo "Installing OpenOffice"
sh ../script/install.sh
sh ./install.sh
else
echo "Detected office component has been installed in $OFFICE_HOME"
fi
fi
echo "Starting kkFileView..."
echo "Please check log file for more information"
nohup java -Dfile.encoding=UTF-8 -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=../conf/application.properties -jar kkFileView-2.1.0.jar > ../log/kkFileView.log 2>&1 &
echo "Please execute ./showlog.sh to check log for more information"
echo "You can get help in our official homesite: https://kkFileView.keking.cn"
echo "If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers"
nohup java -Dfile.encoding=UTF-8 -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=../config/application.properties -jar kkFileView-2.2.1.jar > ../log/kkFileView.log 2>&1 &

View File

@@ -1,48 +0,0 @@
#######################################不可动态配置需要重启生效#######################################
server.port = 8012
spring.http.encoding.charset = utf8
## 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
server.tomcat.uri-encoding = UTF-8
#文件上传限制
spring.http.multipart.max-request-size=100MB
spring.http.multipart.max-file-size=100MB
#文件资源路径默认为打包根路径下的file目录下
#file.dir = D:\\kkFileview\\
#openoffice home路径
#office.home = C:\\Program Files (x86)\\OpenOffice 4
#缓存实现类型不配默认为内嵌RocksDB实现可配置为redis(type = redis)实现需要配置spring.redisson.address等参数 JDK 内置对象实现type = jdk,
#cache.type = redis
#redis连接
#spring.redisson.address = 192.168.1.204:6379
#spring.redisson.password = xxx
#缓存自动清理(每晚3点自动清理) true 为开启注释掉或其他值都为关闭
cache.clean = true
#######################################可在运行时动态配置#######################################
#文本类型默认如下可自定义添加
#simText = txt,html,xml,properties,md,java,py,c,cpp,sql
#多媒体类型默认如下可自定义添加
#media = mp3,wav,mp4,flv
#文件转换编码默认根据操作系统获取
#converted.file.charset = GBK
#office类型文档(word ppt)样式默认为图片(image)可配置为pdf预览时也有按钮切换
#office.preview.type = pdf
#预览源为FTP时 FTP用户名可在ftp url后面加参数ftp.username=ftpuser指定不指定默认用配置的
ftp.username = ftpuser
#预览源为FTP时 FTP密码可在ftp url后面加参数ftp.password=123456指定不指定默认用配置的
ftp.password = 123456
#预览源为FTP时, FTP连接默认ControlEncoding(根据FTP服务器操作系统选择Linux一般为UTF-8Windows一般为GBK)可在ftp url后面加参数ftp.control.encoding=UTF-8指定不指定默认用配置的
ftp.control.encoding = UTF-8

View File

@@ -0,0 +1,88 @@
#######################################不可动态配置需要重启生效#######################################
server.port = ${KK_SERVER_PORT:8012}
server.context-path = ${KK_CONTEXT_PATH:/}
spring.http.encoding.charset = utf8
## 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
server.tomcat.uri-encoding = UTF-8
#文件上传限制
spring.http.multipart.max-request-size=500MB
spring.http.multipart.max-file-size=500MB
#文件资源路径默认为打包根路径下的file目录下
#file.dir = D:\\kkFileview\\
file.dir = ${KK_FILE_DIR:default}
#openoffice home路径
#office.home = C:\\Program Files (x86)\\OpenOffice 4
office.home = ${KK_OFFICE_HOME:default}
#缓存实现类型不配默认为内嵌RocksDB(type = default)实现可配置为redis(type = redis)实现需要配置spring.redisson.address等参数 JDK 内置对象实现type = jdk,
cache.type = ${KK_CACHE_TYPE:jdk}
#redis连接只有当cache.type = redis时才有用
spring.redisson.address = ${KK_SPRING_REDISSON_ADDRESS:127.0.0.1:6379}
spring.redisson.password = ${KK_SPRING_REDISSON_PASSWORD:123456}
#缓存是否自动清理 true 为开启注释掉或其他值都为关闭
cache.clean.enabled = ${KK_CACHE_CLEAN_ENABLED:true}
#缓存自动清理时间cache.clean.enabled = true时才有用cron表达式基于Quartz cron
cache.clean.cron = ${KK_CACHE_CLEAN_CRON:0 0 3 * * ?}
#######################################可在运行时动态配置#######################################
#提供预览服务的地址默认从请求url读如果使用nginx等反向代理需要手动设置
#base.url = https://file.keking.cn
base.url = ${KK_BASE_URL:default}
#信任站点多个用','隔开设置了之后会限制只能预览来自信任站点列表的文件默认不限制
#trust.host = file.keking.cn,kkfileview.keking.cn
trust.host = ${KK_TRUST_HOST:default}
#是否启用缓存
cache.enabled = ${KK_CACHE_ENABLED: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}
#多媒体类型默认如下可自定义添加
media = ${KK_MEDIA:mp3,wav,mp4,flv}
#office类型文档(word ppt)样式默认为图片(image)可配置为pdf预览时也有按钮切换
office.preview.type = ${KK_OFFICE_PREVIEW_TYPE:image}
#是否禁止下载转换生成的pdf文件
pdf.download.disable = ${KK_PDF_DOWNLOAD_DISABLE:true}
#预览源为FTP时 FTP用户名可在ftp url后面加参数ftp.username=ftpuser指定不指定默认用配置的
ftp.username = ${KK_FTP_USERNAME:ftpuser}
#预览源为FTP时 FTP密码可在ftp url后面加参数ftp.password=123456指定不指定默认用配置的
ftp.password = ${KK_FTP_PASSWORD:123456}
#预览源为FTP时, FTP连接默认ControlEncoding(根据FTP服务器操作系统选择Linux一般为UTF-8Windows一般为GBK)可在ftp url后面加参数ftp.control.encoding=UTF-8指定不指定默认用配置的
ftp.control.encoding = ${KK_FTP_CONTROL_ENCODING:UTF-8}
#水印内容
#watermark.txt = ${WATERMARK_TXT:凯京科技内部文件严禁外泄}
#如需取消水印内容设置为空即可watermark.txt = ${WATERMARK_TXT:}
watermark.txt = ${WATERMARK_TXT:}
#水印x轴间隔
watermark.x.space = ${WATERMARK_X_SPACE:10}
#水印y轴间隔
watermark.y.space = ${WATERMARK_Y_SPACE:10}
#水印字体
watermark.font = ${WATERMARK_FONT:微软雅黑}
#水印字体大小
watermark.fontsize = ${WATERMARK_FONTSIZE:18px}
#水印字体颜色
watermark.color = ${WATERMARK_COLOR:black}
#水印透明度要求设置在大于等于0.005小于1
watermark.alpha = ${WATERMARK_ALPHA:0.2}
#水印宽度
watermark.width = ${WATERMARK_WIDTH:180}
#水印高度
watermark.height = ${WATERMARK_HEIGHT:80}
#水印倾斜度数要求设置在大于等于0小于90
watermark.angle = ${WATERMARK_ANGLE:10}

View File

@@ -4,15 +4,12 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.util.Properties;
@SpringBootApplication
@EnableScheduling
@ComponentScan(value = "cn.keking.*")
public class FilePreviewApplication {
public static void main(String[] args) {
Properties properties = System.getProperties();
System.out.println(properties.get("user.dir"));
SpringApplication.run(FilePreviewApplication.class, args);
}
}

View File

@@ -5,92 +5,201 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.Arrays;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.Set;
/**
* @auther: chenjh
* @time: 2019/4/10 17:22
* @description
* @author: chenjh
* @since: 2019/4/10 17:22
*/
@Component
public class ConfigConstants {
private static String[] simText = {};
private static String[] media = {};
private static String convertedFileCharset;
private static String officePreviewType;
private static String ftpUsername;
private static String ftpPassword;
private static String ftpControlEncoding;
private static String fileDir = OfficeUtils.getHomePath() + File.separator + "file" + File.separator;
private static Boolean CACHE_ENABLED;
private static String[] SIM_TEXT = {};
private static String[] MEDIA = {};
private static String OFFICE_PREVIEW_TYPE;
private static String FTP_USERNAME;
private static String FTP_PASSWORD;
private static String FTP_CONTROL_ENCODING;
private static String BASE_URL;
private static String FILE_DIR = OfficeUtils.getHomePath() + File.separator + "file" + File.separator;
private static CopyOnWriteArraySet<String> TRUST_HOST_SET;
private static String PDF_DOWNLOAD_DISABLE;
public static String[] getSimText() {
return simText;
public static final String DEFAULT_CACHE_ENABLED = "true";
public static final String DEFAULT_TXT_TYPE = "txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd";
public static final String DEFAULT_MEDIA_TYPE = "mp3,wav,mp4,flv";
public static final String DEFAULT_OFFICE_PREVIEW_TYPE = "image";
public static final String DEFAULT_FTP_USERNAME = null;
public static final String DEFAULT_FTP_PASSWORD = null;
public static final String DEFAULT_FTP_CONTROL_ENCODING = "UTF-8";
public static final String DEFAULT_BASE_URL = "default";
public static final String DEFAULT_FILE_DIR_VALUE = "default";
public static final String DEFAULT_TRUST_HOST = "default";
public static final String DEFAULT_PDF_DOWNLOAD_DISABLE = "true";
public static Boolean isCacheEnabled() {
return CACHE_ENABLED;
}
public static void setSimText(String[] simText) {
ConfigConstants.simText = simText;
@Value("${cache.enabled:true}")
public void setCacheEnabled(String cacheEnabled) {
setCacheEnabledValueValue(Boolean.parseBoolean(cacheEnabled));
}
public static void setCacheEnabledValueValue(Boolean cacheEnabled) {
CACHE_ENABLED = cacheEnabled;
}
public static String[] getSimText() {
return SIM_TEXT;
}
@Value("${simText:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd}")
public void setSimText(String simText) {
String[] simTextArr = simText.split(",");
setSimTextValue(simTextArr);
}
public static void setSimTextValue(String[] simText) {
SIM_TEXT = simText;
}
public static String[] getMedia() {
return media;
return MEDIA;
}
public static void setMedia(String[] media) {
ConfigConstants.media = media;
@Value("${media:mp3,wav,mp4,flv}")
public void setMedia(String media) {
String[] mediaArr = media.split(",");
setMediaValue(mediaArr);
}
public static String getConvertedFileCharset() {
return convertedFileCharset;
}
public static void setConvertedFileCharset(String convertedFileCharset) {
ConfigConstants.convertedFileCharset = convertedFileCharset;
public static void setMediaValue(String[] Media) {
MEDIA = Media;
}
public static String getOfficePreviewType() {
return officePreviewType;
return OFFICE_PREVIEW_TYPE;
}
public static void setOfficePreviewType(String officePreviewType) {
ConfigConstants.officePreviewType = officePreviewType;
@Value("${office.preview.type:image}")
public void setOfficePreviewType(String officePreviewType) {
setOfficePreviewTypeValue(officePreviewType);
}
public static void setOfficePreviewTypeValue(String officePreviewType) {
OFFICE_PREVIEW_TYPE = officePreviewType;
}
public static String getFtpUsername() {
return ftpUsername;
return FTP_USERNAME;
}
public static void setFtpUsername(String ftpUsername) {
ConfigConstants.ftpUsername = ftpUsername;
@Value("${ftp.username:}")
public void setFtpUsername(String ftpUsername) {
setFtpUsernameValue(ftpUsername);
}
public static void setFtpUsernameValue(String ftpUsername) {
FTP_USERNAME = ftpUsername;
}
public static String getFtpPassword() {
return ftpPassword;
return FTP_PASSWORD;
}
@Value("${ftp.password:}")
public void setFtpPassword(String ftpPassword) {
setFtpPasswordValue(ftpPassword);
}
public static void setFtpPasswordValue(String ftpPassword) {
FTP_PASSWORD = ftpPassword;
}
public static String getFtpControlEncoding() {
return ftpControlEncoding;
return FTP_CONTROL_ENCODING;
}
public static void setFtpControlEncoding(String ftpControlEncoding) {
ConfigConstants.ftpControlEncoding = ftpControlEncoding;
@Value("${ftp.control.encoding:UTF-8}")
public void setFtpControlEncoding(String ftpControlEncoding) {
setFtpControlEncodingValue(ftpControlEncoding);
}
public static void setFtpPassword(String ftpPassword) {
ConfigConstants.ftpPassword = ftpPassword;
public static void setFtpControlEncodingValue(String ftpControlEncoding) {
FTP_CONTROL_ENCODING = ftpControlEncoding;
}
public static String getBaseUrl() {
return BASE_URL;
}
@Value("${base.url:default}")
public void setBaseUrl(String baseUrl) {
setBaseUrlValue(baseUrl);
}
public static void setBaseUrlValue(String baseUrl) {
BASE_URL = baseUrl;
}
public static String getFileDir() {
return fileDir;
return FILE_DIR;
}
@Value("${file.dir:default}")
public void setFileDir(String fileDir) {
if (!"default".equals(fileDir)) {
setFileDirValue(fileDir);
}
public static void setFileDirValue(String fileDir) {
if (!DEFAULT_FILE_DIR_VALUE.equals(fileDir.toLowerCase())) {
if (!fileDir.endsWith(File.separator)) {
fileDir = fileDir + File.separator;
}
ConfigConstants.fileDir = fileDir;
FILE_DIR = fileDir;
}
}
@Value("${trust.host:default}")
public void setTrustHost(String trustHost) {
setTrustHostValue(trustHost);
}
public static void setTrustHostValue(String trustHost) {
CopyOnWriteArraySet<String> trustHostSet;
if (DEFAULT_TRUST_HOST.equals(trustHost.toLowerCase())) {
trustHostSet = new CopyOnWriteArraySet<>();
} else {
String[] trustHostArray = trustHost.toLowerCase().split(",");
trustHostSet = new CopyOnWriteArraySet<>(Arrays.asList(trustHostArray));
setTrustHostSet(trustHostSet);
}
setTrustHostSet(trustHostSet);
}
public static Set<String> getTrustHostSet() {
return TRUST_HOST_SET;
}
private static void setTrustHostSet(CopyOnWriteArraySet<String> trustHostSet) {
ConfigConstants.TRUST_HOST_SET = trustHostSet;
}
public static String getPdfDownloadDisable() {
return PDF_DOWNLOAD_DISABLE;
}
@Value("${pdf.download.disable:true}")
public void setPdfDownloadDisable(String pdfDownloadDisable) {
setPdfDownloadDisableValue(pdfDownloadDisable);
}
public static void setPdfDownloadDisableValue(String pdfDownloadDisable) {
PDF_DOWNLOAD_DISABLE = pdfDownloadDisable;
}
}

View File

@@ -1,6 +1,5 @@
package cn.keking.config;
import cn.keking.service.impl.OfficeFilePreviewImpl;
import org.artofsolving.jodconverter.office.OfficeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -22,55 +21,58 @@ public class ConfigRefreshComponent {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigRefreshComponent.class);
public static final String DEFAULT_TXT_TYPE = "txt,html,xml,properties,md,java,py,c,cpp,sql";
public static final String DEFAULT_MEDIA_TYPE = "mp3,wav,mp4,flv";
public static final String DEFAULT_CONVERTER_CHARSET = System.getProperty("sun.jnu.encoding");
public static final String DEFAULT_FTP_USERNAME = null;
public static final String DEFAULT_FTP_PASSWORD = null;
public static final String DEFAULT_FTP_CONTROL_ENCODING = "UTF-8";
@PostConstruct
void refresh() {
Thread configRefreshThread = new Thread(new ConfigRefreshThread());
configRefreshThread.start();
}
class ConfigRefreshThread implements Runnable {
static class ConfigRefreshThread implements Runnable {
@Override
public void run() {
try {
Properties properties = new Properties();
String text;
String media;
boolean cacheEnabled;
String[] textArray;
String[] mediaArray;
String convertedFileCharset;
String officePreviewType;
String ftpUsername;
String ftpPassword;
String ftpControlEncoding;
String configFilePath = OfficeUtils.getCustomizedConfigPath();
String baseUrl;
String trustHost;
String pdfDownloadDisable;
while (true) {
FileReader fileReader = new FileReader(configFilePath);
BufferedReader bufferedReader = new BufferedReader(fileReader);
properties.load(bufferedReader);
text = properties.getProperty("simText", DEFAULT_TXT_TYPE);
media = properties.getProperty("media", DEFAULT_MEDIA_TYPE);
convertedFileCharset = properties.getProperty("converted.file.charset", DEFAULT_CONVERTER_CHARSET);
officePreviewType = properties.getProperty("office.preview.type", OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE);
ftpUsername = properties.getProperty("ftp.username", DEFAULT_FTP_USERNAME);
ftpPassword = properties.getProperty("ftp.password", DEFAULT_FTP_PASSWORD);
ftpControlEncoding = properties.getProperty("ftp.control.encoding", DEFAULT_FTP_CONTROL_ENCODING);
OfficeUtils.restorePropertiesFromEnvFormat(properties);
cacheEnabled = Boolean.parseBoolean(properties.getProperty("cache.enabled", ConfigConstants.DEFAULT_CACHE_ENABLED));
text = properties.getProperty("simText", ConfigConstants.DEFAULT_TXT_TYPE);
media = properties.getProperty("media", ConfigConstants.DEFAULT_MEDIA_TYPE);
officePreviewType = properties.getProperty("office.preview.type", ConfigConstants.DEFAULT_OFFICE_PREVIEW_TYPE);
ftpUsername = properties.getProperty("ftp.username", ConfigConstants.DEFAULT_FTP_USERNAME);
ftpPassword = properties.getProperty("ftp.password", ConfigConstants.DEFAULT_FTP_PASSWORD);
ftpControlEncoding = properties.getProperty("ftp.control.encoding", ConfigConstants.DEFAULT_FTP_CONTROL_ENCODING);
textArray = text.split(",");
mediaArray = media.split(",");
ConfigConstants.setSimText(textArray);
ConfigConstants.setMedia(mediaArray);
ConfigConstants.setConvertedFileCharset(convertedFileCharset);
ConfigConstants.setOfficePreviewType(officePreviewType);
ConfigConstants.setFtpUsername(ftpUsername);
ConfigConstants.setFtpPassword(ftpPassword);
ConfigConstants.setFtpControlEncoding(ftpControlEncoding);
baseUrl = properties.getProperty("base.url", ConfigConstants.DEFAULT_BASE_URL);
trustHost = properties.getProperty("trust.host", ConfigConstants.DEFAULT_TRUST_HOST);
pdfDownloadDisable = properties.getProperty("pdf.download.disable", ConfigConstants.DEFAULT_PDF_DOWNLOAD_DISABLE);
ConfigConstants.setCacheEnabledValueValue(cacheEnabled);
ConfigConstants.setSimTextValue(textArray);
ConfigConstants.setMediaValue(mediaArray);
ConfigConstants.setOfficePreviewTypeValue(officePreviewType);
ConfigConstants.setFtpUsernameValue(ftpUsername);
ConfigConstants.setFtpPasswordValue(ftpPassword);
ConfigConstants.setFtpControlEncodingValue(ftpControlEncoding);
ConfigConstants.setBaseUrlValue(baseUrl);
ConfigConstants.setTrustHostValue(trustHost);
ConfigConstants.setPdfDownloadDisableValue(pdfDownloadDisable);
setWatermarkConfig(properties);
bufferedReader.close();
fileReader.close();
Thread.sleep(1000L);
@@ -79,5 +81,29 @@ public class ConfigRefreshComponent {
LOGGER.error("读取配置文件异常", e);
}
}
private void setWatermarkConfig(Properties properties) {
String watermarkTxt = properties.getProperty("watermark.txt", WatermarkConfigConstants.DEFAULT_WATERMARK_TXT);
String watermarkXSpace = properties.getProperty("watermark.x.space", WatermarkConfigConstants.DEFAULT_WATERMARK_X_SPACE);
String watermarkYSpace = properties.getProperty("watermark.y.space", WatermarkConfigConstants.DEFAULT_WATERMARK_Y_SPACE);
String watermarkFont = properties.getProperty("watermark.font", WatermarkConfigConstants.DEFAULT_WATERMARK_FONT);
String watermarkFontsize = properties.getProperty("watermark.fontsize", WatermarkConfigConstants.DEFAULT_WATERMARK_FONTSIZE);
String watermarkColor = properties.getProperty("watermark.color", WatermarkConfigConstants.DEFAULT_WATERMARK_COLOR);
String watermarkAlpha = properties.getProperty("watermark.alpha", WatermarkConfigConstants.DEFAULT_WATERMARK_ALPHA);
String watermarkWidth = properties.getProperty("watermark.width", WatermarkConfigConstants.DEFAULT_WATERMARK_WIDTH);
String watermarkHeight = properties.getProperty("watermark.height", WatermarkConfigConstants.DEFAULT_WATERMARK_HEIGHT);
String watermarkAngle = properties.getProperty("watermark.angle", WatermarkConfigConstants.DEFAULT_WATERMARK_ANGLE);
WatermarkConfigConstants.setWatermarkTxtValue(watermarkTxt);
WatermarkConfigConstants.setWatermarkXSpaceValue(watermarkXSpace);
WatermarkConfigConstants.setWatermarkYSpaceValue(watermarkYSpace);
WatermarkConfigConstants.setWatermarkFontValue(watermarkFont);
WatermarkConfigConstants.setWatermarkFontsizeValue(watermarkFontsize);
WatermarkConfigConstants.setWatermarkColorValue(watermarkColor);
WatermarkConfigConstants.setWatermarkAlphaValue(watermarkAlpha);
WatermarkConfigConstants.setWatermarkWidthValue(watermarkWidth);
WatermarkConfigConstants.setWatermarkHeightValue(watermarkHeight);
WatermarkConfigConstants.setWatermarkAngleValue(watermarkAngle);
}
}
}

View File

@@ -0,0 +1,19 @@
package cn.keking.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author chenjh
* @since 2020/5/18 13:41
*/
@Configuration
public class RFCConfig {
@Bean
public Boolean setRequestTargetAllow() {
// RFC 7230RFC 3986规范不允许url相关特殊字符手动指定Tomcat url允许特殊符号 如{}做入参其他符号按需添加。见tomcat的HttpParser源码。
System.setProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow", "|{}");
return true;
}
}

View File

@@ -0,0 +1,164 @@
package cn.keking.config;
import org.springframework.beans.factory.annotation.Value;
/**
* @author chenjh
* @since 2020/5/13 17:44
*/
public class WatermarkConfigConstants {
private static String WATERMARK_TXT;
private static String WATERMARK_X_SPACE;
private static String WATERMARK_Y_SPACE;
private static String WATERMARK_FONT;
private static String WATERMARK_FONTSIZE;
private static String WATERMARK_COLOR;
private static String WATERMARK_ALPHA;
private static String WATERMARK_WIDTH;
private static String WATERMARK_HEIGHT;
private static String WATERMARK_ANGLE;
public static String DEFAULT_WATERMARK_TXT = "";
public static String DEFAULT_WATERMARK_X_SPACE = "10";
public static String DEFAULT_WATERMARK_Y_SPACE = "10";
public static String DEFAULT_WATERMARK_FONT = "微软雅黑";
public static String DEFAULT_WATERMARK_FONTSIZE = "18px";
public static String DEFAULT_WATERMARK_COLOR = "black";
public static String DEFAULT_WATERMARK_ALPHA = "0.2";
public static String DEFAULT_WATERMARK_WIDTH = "240";
public static String DEFAULT_WATERMARK_HEIGHT = "80";
public static String DEFAULT_WATERMARK_ANGLE = "10";
public static String getWatermarkTxt() {
return WATERMARK_TXT;
}
public static void setWatermarkTxtValue(String watermarkTxt) {
WATERMARK_TXT = watermarkTxt;
}
@Value("${watermark.txt:}")
public void setWatermarkTxt(String watermarkTxt) {
setWatermarkTxtValue(watermarkTxt);
}
public static String getWatermarkXSpace() {
return WATERMARK_X_SPACE;
}
public static void setWatermarkXSpaceValue(String watermarkXSpace) {
WATERMARK_X_SPACE = watermarkXSpace;
}
@Value("${watermark.x.space:10}")
public void setWatermarkXSpace(String watermarkXSpace) {
setWatermarkXSpaceValue(watermarkXSpace);
}
public static String getWatermarkYSpace() {
return WATERMARK_Y_SPACE;
}
public static void setWatermarkYSpaceValue(String watermarkYSpace) {
WATERMARK_Y_SPACE = watermarkYSpace;
}
@Value("${watermark.y.space:10}")
public void setWatermarkYSpace(String watermarkYSpace) {
setWatermarkYSpaceValue(watermarkYSpace);
}
public static String getWatermarkFont() {
return WATERMARK_FONT;
}
public static void setWatermarkFontValue(String watermarkFont) {
WATERMARK_FONT = watermarkFont;
}
@Value("${watermark.font:微软雅黑}")
public void setWatermarkFont(String watermarkFont) {
setWatermarkFontValue(watermarkFont);
}
public static String getWatermarkFontsize() {
return WATERMARK_FONTSIZE;
}
public static void setWatermarkFontsizeValue(String watermarkFontsize) {
WATERMARK_FONTSIZE = watermarkFontsize;
}
@Value("${watermark.fontsize:18px}")
public void setWatermarkFontsize(String watermarkFontsize) {
setWatermarkFontsizeValue(watermarkFontsize);
}
public static String getWatermarkColor() {
return WATERMARK_COLOR;
}
public static void setWatermarkColorValue(String watermarkColor) {
WATERMARK_COLOR = watermarkColor;
}
@Value("${watermark.color:black}")
public void setWatermarkColor(String watermarkColor) {
setWatermarkColorValue(watermarkColor);
}
public static String getWatermarkAlpha() {
return WATERMARK_ALPHA;
}
public static void setWatermarkAlphaValue(String watermarkAlpha) {
WATERMARK_ALPHA = watermarkAlpha;
}
@Value("${watermark.alpha:0.2}")
public void setWatermarkAlpha(String watermarkAlpha) {
setWatermarkAlphaValue(watermarkAlpha);
}
public static String getWatermarkWidth() {
return WATERMARK_WIDTH;
}
public static void setWatermarkWidthValue(String watermarkWidth) {
WATERMARK_WIDTH = watermarkWidth;
}
@Value("${watermark.width:240}")
public void setWatermarkWidth(String watermarkWidth) {
WATERMARK_WIDTH = watermarkWidth;
}
public static String getWatermarkHeight() {
return WATERMARK_HEIGHT;
}
public static void setWatermarkHeightValue(String watermarkHeight) {
WATERMARK_HEIGHT = watermarkHeight;
}
@Value("${watermark.height:80}")
public void setWatermarkHeight(String watermarkHeight) {
WATERMARK_HEIGHT = watermarkHeight;
}
public static String getWatermarkAngle() {
return WATERMARK_ANGLE;
}
public static void setWatermarkAngleValue(String watermarkAngle) {
WATERMARK_ANGLE = watermarkAngle;
}
@Value("${watermark.angle:10}")
public void setWatermarkAngle(String watermarkAngle) {
WATERMARK_ANGLE = watermarkAngle;
}
}

View File

@@ -7,9 +7,8 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @auther: chenjh
* @time: 2019/4/16 20:04
* @description
* @author: chenjh
* @since: 2019/4/16 20:04
*/
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

View File

@@ -1,7 +1,5 @@
package cn.keking.extend;
import com.google.common.collect.Maps;
import com.sun.star.beans.PropertyValue;
import org.artofsolving.jodconverter.document.DocumentFamily;
import org.artofsolving.jodconverter.document.DocumentFormat;
import org.artofsolving.jodconverter.document.SimpleDocumentFormatRegistry;
@@ -42,7 +40,7 @@ public class ControlDocumentFormatRegistry extends SimpleDocumentFormatRegistry
// available for exporting Spreadsheet and Presentation formats
html.setInputFamily(DocumentFamily.TEXT);
html.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "HTML (StarWriter)"));
Map<String,Object> htmlLoadAndStoreProperties = new LinkedHashMap<String,Object>();
Map<String,Object> htmlLoadAndStoreProperties = new LinkedHashMap<>();
htmlLoadAndStoreProperties.put("FilterName", "HTML (StarCalc)");
htmlLoadAndStoreProperties.put("FilterOptions", "utf8");
html.setStoreProperties(DocumentFamily.SPREADSHEET, htmlLoadAndStoreProperties);
@@ -79,7 +77,7 @@ public class ControlDocumentFormatRegistry extends SimpleDocumentFormatRegistry
DocumentFormat txt = new DocumentFormat("Plain Text", "txt", "text/plain");
txt.setInputFamily(DocumentFamily.TEXT);
Map<String,Object> txtLoadAndStoreProperties = new LinkedHashMap<String,Object>();
Map<String,Object> txtLoadAndStoreProperties = new LinkedHashMap<>();
txtLoadAndStoreProperties.put("FilterName", "Text (encoded)");
txtLoadAndStoreProperties.put("FilterOptions", "utf8");
txt.setLoadProperties(txtLoadAndStoreProperties);
@@ -111,7 +109,7 @@ public class ControlDocumentFormatRegistry extends SimpleDocumentFormatRegistry
DocumentFormat csv = new DocumentFormat("Comma Separated Values", "csv", "text/csv");
csv.setInputFamily(DocumentFamily.SPREADSHEET);
Map<String,Object> csvLoadAndStoreProperties = new LinkedHashMap<String,Object>();
Map<String,Object> csvLoadAndStoreProperties = new LinkedHashMap<>();
csvLoadAndStoreProperties.put("FilterName", "Text - txt - csv (StarCalc)");
csvLoadAndStoreProperties.put("FilterOptions", "44,34,0"); // Field Separator: ','; Text Delimiter: '"'
csv.setLoadProperties(csvLoadAndStoreProperties);
@@ -120,7 +118,7 @@ public class ControlDocumentFormatRegistry extends SimpleDocumentFormatRegistry
DocumentFormat tsv = new DocumentFormat("Tab Separated Values", "tsv", "text/tab-separated-values");
tsv.setInputFamily(DocumentFamily.SPREADSHEET);
Map<String,Object> tsvLoadAndStoreProperties = new LinkedHashMap<String,Object>();
Map<String,Object> tsvLoadAndStoreProperties = new LinkedHashMap<>();
tsvLoadAndStoreProperties.put("FilterName", "Text - txt - csv (StarCalc)");
tsvLoadAndStoreProperties.put("FilterOptions", "9,34,0"); // Field Separator: '\t'; Text Delimiter: '"'
tsv.setLoadProperties(tsvLoadAndStoreProperties);
@@ -156,56 +154,4 @@ public class ControlDocumentFormatRegistry extends SimpleDocumentFormatRegistry
addFormat(svg);
}
/**
* 创建默认的导出属性
* @return
*/
private PropertyValue[] getCommonPropertyValue() {
PropertyValue[] aFilterData = new PropertyValue[11];
// 不显示文档标题
aFilterData[0] = new PropertyValue();
aFilterData[0].Name = "DisplayPDFDocumentTitle";
aFilterData[0].Value= true;
// 导出文件编码方式
aFilterData[1] = new PropertyValue();
aFilterData[1].Name = "Encoding";
aFilterData[1].Value= "UTF-8";
// 隐藏工具条
aFilterData[2] = new PropertyValue();
aFilterData[2].Name = "HideViewerToolbar";
aFilterData[2].Value= false;
// 隐藏窗口控制条
aFilterData[3] = new PropertyValue();
aFilterData[3].Name = "HideViewerWindowControls";
aFilterData[3].Value= true;
// 全屏展示
aFilterData[4] = new PropertyValue();
aFilterData[4].Name = "OpenInFullScreenMode";
aFilterData[4].Value= false;
// 第一页左边展示
aFilterData[5] = new PropertyValue();
aFilterData[5].Name = "MathToMathType";
aFilterData[5].Value= true;
// 文档标题内容
aFilterData[6] = new PropertyValue();
aFilterData[6].Name = "Watermark";
aFilterData[6].Value= "KEKING.CN";
// 导出文件编码方式
aFilterData[7] = new PropertyValue();
aFilterData[7].Name = "CharacterSet";
aFilterData[7].Value= "UTF-8";
// 导出文件编码方式
aFilterData[8] = new PropertyValue();
aFilterData[8].Name = "Encoding";
aFilterData[8].Value= "UTF-8";
// 导出文件编码方式
aFilterData[9] = new PropertyValue();
aFilterData[9].Name = "CharSet";
aFilterData[9].Value= "UTF-8";
// 导出文件编码方式
aFilterData[10] = new PropertyValue();
aFilterData[10].Name = "charset";
aFilterData[10].Value= "UTF-8";
return aFilterData;
}
}

View File

@@ -1,23 +0,0 @@
package cn.keking.filters;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
*
* @author yudian-it
* @date 2017/11/30
*/
@Configuration
public class FilterConfiguration {
@Bean
public FilterRegistrationBean getChinesePathFilter(){
ChinesePathFilter filter = new ChinesePathFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
return registrationBean;
}
}

View File

@@ -0,0 +1,388 @@
package cn.keking.hutool;
import java.awt.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* 十六进制简写为hex或下标16在数学中是一种逢16进1的进位制一般用数字0到9和字母A到F表示其中:A~F即10~15。<br>
* 例如十进制数57在二进制写作111001在16进制写作39。<br>
* 像java,c这样的语言为了区分十六进制和十进制数值,会在十六进制数的前面加上 0x,比如0x20是十进制的32,而不是十进制的20<br>
* <p>
* 参考https://my.oschina.net/xinxingegeya/blog/287476
*
* @author Looly
*/
public class HexUtil {
/**
* 用于建立十六进制字符的输出的小写字符数组
*/
private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* 用于建立十六进制字符的输出的大写字符数组
*/
private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
/**
* 判断给定字符串是否为16进制数<br>
* 如果是,需要使用对应数字类型对象的<code>decode</code>方法解码<br>
* 例如:{@code Integer.decode}方法解码int类型的16进制数字
*
* @param value 值
* @return 是否为16进制
*/
public static boolean isHexNumber(String value) {
final int index = (value.startsWith("-") ? 1 : 0);
if (value.startsWith("0x", index) || value.startsWith("0X", index) || value.startsWith("#", index)) {
try {
Long.decode(value);
} catch (NumberFormatException e) {
return false;
}
return true;
} else {
return false;
}
}
// ---------------------------------------------------------------------------------------------------- encode
/**
* 将字节数组转换为十六进制字符数组
*
* @param data byte[]
* @return 十六进制char[]
*/
public static char[] encodeHex(byte[] data) {
return encodeHex(data, true);
}
/**
* 将字节数组转换为十六进制字符数组
*
* @param str 字符串
* @param charset 编码
* @return 十六进制char[]
*/
public static char[] encodeHex(String str, Charset charset) {
return encodeHex(StrUtil.bytes(str, charset), true);
}
/**
* 将字节数组转换为十六进制字符数组
*
* @param data byte[]
* @param toLowerCase <code>true</code> 传换成小写格式 <code>false</code> 传换成大写格式
* @return 十六进制char[]
*/
public static char[] encodeHex(byte[] data, boolean toLowerCase) {
return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
/**
* 将字节数组转换为十六进制字符串
*
* @param data byte[]
* @return 十六进制String
*/
public static String encodeHexStr(byte[] data) {
return encodeHexStr(data, true);
}
/**
* 将字节数组转换为十六进制字符串,结果为小写
*
* @param data 被编码的字符串
* @param charset 编码
* @return 十六进制String
*/
public static String encodeHexStr(String data, Charset charset) {
return encodeHexStr(StrUtil.bytes(data, charset), true);
}
/**
* 将字节数组转换为十六进制字符串结果为小写默认编码是UTF-8
*
* @param data 被编码的字符串
* @return 十六进制String
*/
public static String encodeHexStr(String data) {
return encodeHexStr(data, StandardCharsets.UTF_8);
}
/**
* 将字节数组转换为十六进制字符串
*
* @param data byte[]
* @param toLowerCase <code>true</code> 传换成小写格式 <code>false</code> 传换成大写格式
* @return 十六进制String
*/
public static String encodeHexStr(byte[] data, boolean toLowerCase) {
return encodeHexStr(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
// ---------------------------------------------------------------------------------------------------- decode
/**
* 将十六进制字符数组转换为字符串默认编码UTF-8
*
* @param hexStr 十六进制String
* @return 字符串
*/
public static String decodeHexStr(String hexStr) {
return decodeHexStr(hexStr, StandardCharsets.UTF_8);
}
/**
* 将十六进制字符数组转换为字符串
*
* @param hexStr 十六进制String
* @param charset 编码
* @return 字符串
*/
public static String decodeHexStr(String hexStr, Charset charset) {
if (StrUtil.isEmpty(hexStr)) {
return hexStr;
}
return decodeHexStr(hexStr.toCharArray(), charset);
}
/**
* 将十六进制字符数组转换为字符串
*
* @param hexData 十六进制char[]
* @param charset 编码
* @return 字符串
*/
public static String decodeHexStr(char[] hexData, Charset charset) {
return StrUtil.str(decodeHex(hexData), charset);
}
/**
* 将十六进制字符数组转换为字节数组
*
* @param hexData 十六进制char[]
* @return byte[]
* @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常
*/
public static byte[] decodeHex(char[] hexData) {
int len = hexData.length;
if ((len & 0x01) != 0) {
throw new RuntimeException("Odd number of characters.");
}
byte[] out = new byte[len >> 1];
// two characters form the hex value.
for (int i = 0, j = 0; j < len; i++) {
int f = toDigit(hexData[j], j) << 4;
j++;
f = f | toDigit(hexData[j], j);
j++;
out[i] = (byte) (f & 0xFF);
}
return out;
}
/**
* 将十六进制字符串解码为byte[]
*
* @param hexStr 十六进制String
* @return byte[]
*/
public static byte[] decodeHex(String hexStr) {
if (StrUtil.isEmpty(hexStr)) {
return null;
}
return decodeHex(hexStr.toCharArray());
}
// ---------------------------------------------------------------------------------------- Color
/**
* 将{@link Color}编码为Hex形式
*
* @param color {@link Color}
* @return Hex字符串
* @since 3.0.8
*/
public static String encodeColor(Color color) {
return encodeColor(color, "#");
}
/**
* 将{@link Color}编码为Hex形式
*
* @param color {@link Color}
* @param prefix 前缀字符串,可以是#、0x等
* @return Hex字符串
* @since 3.0.8
*/
public static String encodeColor(Color color, String prefix) {
final StringBuilder builder = new StringBuilder(prefix);
String colorHex;
colorHex = Integer.toHexString(color.getRed());
if (1 == colorHex.length()) {
builder.append('0');
}
builder.append(colorHex);
colorHex = Integer.toHexString(color.getGreen());
if (1 == colorHex.length()) {
builder.append('0');
}
builder.append(colorHex);
colorHex = Integer.toHexString(color.getBlue());
if (1 == colorHex.length()) {
builder.append('0');
}
builder.append(colorHex);
return builder.toString();
}
/**
* 将Hex颜色值转为
*
* @param hexColor 16进制颜色值可以以#开头也可以用0x开头
* @return {@link Color}
* @since 3.0.8
*/
public static Color decodeColor(String hexColor) {
return Color.decode(hexColor);
}
/**
* 将指定int值转换为Unicode字符串形式常用于特殊字符例如汉字转Unicode形式<br>
* 转换的字符串如果u后不足4位则前面用0填充例如
*
* <pre>
* '我' =》\u4f60
* </pre>
*
* @param value int值也可以是char
* @return Unicode表现形式
*/
public static String toUnicodeHex(int value) {
final StringBuilder builder = new StringBuilder(6);
builder.append("\\u");
String hex = toHex(value);
int len = hex.length();
if (len < 4) {
builder.append("0000", 0, 4 - len);// 不足4位补0
}
builder.append(hex);
return builder.toString();
}
/**
* 将指定char值转换为Unicode字符串形式常用于特殊字符例如汉字转Unicode形式<br>
* 转换的字符串如果u后不足4位则前面用0填充例如
*
* <pre>
* '我' =》\u4f60
* </pre>
*
* @param ch char值
* @return Unicode表现形式
* @since 4.0.1
*/
public static String toUnicodeHex(char ch) {
return "\\u" +//
DIGITS_LOWER[(ch >> 12) & 15] +//
DIGITS_LOWER[(ch >> 8) & 15] +//
DIGITS_LOWER[(ch >> 4) & 15] +//
DIGITS_LOWER[(ch) & 15];
}
/**
* 转为16进制字符串
*
* @param value int值
* @return 16进制字符串
* @since 4.4.1
*/
public static String toHex(int value) {
return Integer.toHexString(value);
}
/**
* 转为16进制字符串
*
* @param value int值
* @return 16进制字符串
* @since 4.4.1
*/
public static String toHex(long value) {
return Long.toHexString(value);
}
/**
* 将byte值转为16进制并添加到{@link StringBuilder}中
*
* @param builder {@link StringBuilder}
* @param b byte
* @param toLowerCase 是否使用小写
* @since 4.4.1
*/
public static void appendHex(StringBuilder builder, byte b, boolean toLowerCase) {
final char[] toDigits = toLowerCase ? DIGITS_LOWER : DIGITS_UPPER;
int high = (b & 0xf0) >>> 4;//高位
int low = b & 0x0f;//低位
builder.append(toDigits[high]);
builder.append(toDigits[low]);
}
// ---------------------------------------------------------------------------------------- Private method start
/**
* 将字节数组转换为十六进制字符串
*
* @param data byte[]
* @param toDigits 用于控制输出的char[]
* @return 十六进制String
*/
private static String encodeHexStr(byte[] data, char[] toDigits) {
return new String(encodeHex(data, toDigits));
}
/**
* 将字节数组转换为十六进制字符数组
*
* @param data byte[]
* @param toDigits 用于控制输出的char[]
* @return 十六进制char[]
*/
private static char[] encodeHex(byte[] data, char[] toDigits) {
final int len = data.length;
final char[] out = new char[len << 1];//len*2
// two characters from the hex value.
for (int i = 0, j = 0; i < len; i++) {
out[j++] = toDigits[(0xF0 & data[i]) >>> 4];// 高位
out[j++] = toDigits[0x0F & data[i]];// 低位
}
return out;
}
/**
* 将十六进制字符转换成一个整数
*
* @param ch 十六进制char
* @param index 十六进制字符在字符数组中的位置
* @return 一个整数
* @throws RuntimeException 当ch不是一个合法的十六进制字符时抛出运行时异常
*/
private static int toDigit(char ch, int index) {
int digit = Character.digit(ch, 16);
if (digit == -1) {
throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index);
}
return digit;
}
// ---------------------------------------------------------------------------------------- Private method end
}

View File

@@ -0,0 +1,283 @@
package cn.keking.hutool;
import java.nio.charset.Charset;
/**
* 字符串工具类
*
* @author xiaoleilu
*
*/
public class StrUtil {
public static final String EMPTY = "";
/**
* 是否空白符<br>
* 空白符包括空格、制表符、全角空格和不间断空格<br>
*
* @see Character#isWhitespace(int)
* @see Character#isSpaceChar(int)
* @param c 字符
* @return 是否空白符
* @since 4.0.10
*/
public static boolean isBlankChar(int c) {
return Character.isWhitespace(c) || Character.isSpaceChar(c) || c == '\ufeff' || c == '\u202a';
}
/**
* 是否空白符<br>
* 空白符包括空格、制表符、全角空格和不间断空格<br>
*
* @param c 字符
* @return 是否空白符
* @see Character#isWhitespace(int)
* @see Character#isSpaceChar(int)
* @since 4.0.10
*/
public static boolean isBlankChar(char c) {
return isBlankChar((int) c);
}
/**
* 字符串是否为空白 空白的定义如下: <br>
* 1、为null <br>
* 2、为不可见字符如空格<br>
* 3、""<br>
*
* @param str 被检测的字符串
* @return 是否为空
*/
public static boolean isBlank(CharSequence str) {
int length;
if ((str == null) || ((length = str.length()) == 0)) {
return true;
}
for (int i = 0; i < length; i++) {
// 只要有一个非空字符即为非空字符串
if (false == isBlankChar(str.charAt(i))) {
return false;
}
}
return true;
}
/**
* 字符串是否为空,空的定义如下:<br>
* 1、为null <br>
* 2、为""<br>
*
* @param str 被检测的字符串
* @return 是否为空
*/
public static boolean isEmpty(CharSequence str) {
return str == null || str.length() == 0;
}
/**
* 编码字符串
*
* @param str 字符串
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
* @return 编码后的字节码
*/
public static byte[] bytes(CharSequence str, Charset charset) {
if (str == null) {
return null;
}
if (null == charset) {
return str.toString().getBytes();
}
return str.toString().getBytes(charset);
}
/**
* {@link CharSequence} 转为字符串null安全
*
* @param cs {@link CharSequence}
* @return 字符串
*/
public static String str(CharSequence cs) {
return null == cs ? null : cs.toString();
}
/**
* 解码字节码
*
* @param data 字符串
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
* @return 解码后的字符串
*/
public static String str(byte[] data, Charset charset) {
if (data == null) {
return null;
}
if (null == charset) {
return new String(data);
}
return new String(data, charset);
}
/**
* 改进JDK subString<br>
* index从0开始计算最后一个字符为-1<br>
* 如果from和to位置一样返回 "" <br>
* 如果from或to为负数则按照length从后向前数位置如果绝对值大于字符串长度则from归到0to归到length<br>
* 如果经过修正的index中from大于to则互换from和to example: <br>
* abcdefgh 2 3 =》 c <br>
* abcdefgh 2 -3 =》 cde <br>
*
* @param str String
* @param fromIndex 开始的index包括
* @param toIndex 结束的index不包括
* @return 字串
*/
public static String sub(CharSequence str, int fromIndex, int toIndex) {
if (isEmpty(str)) {
return str(str);
}
int len = str.length();
if (fromIndex < 0) {
fromIndex = len + fromIndex;
if (fromIndex < 0) {
fromIndex = 0;
}
} else if (fromIndex > len) {
fromIndex = len;
}
if (toIndex < 0) {
toIndex = len + toIndex;
if (toIndex < 0) {
toIndex = len;
}
} else if (toIndex > len) {
toIndex = len;
}
if (toIndex < fromIndex) {
int tmp = fromIndex;
fromIndex = toIndex;
toIndex = tmp;
}
if (fromIndex == toIndex) {
return EMPTY;
}
return str.toString().substring(fromIndex, toIndex);
}
/**
* 切割指定位置之前部分的字符串
*
* @param string 字符串
* @param toIndex 切割到的位置(不包括)
* @return 切割后的剩余的前半部分字符串
*/
public static String subPre(CharSequence string, int toIndex) {
return sub(string, 0, toIndex);
}
/**
* 切割指定位置之后部分的字符串
*
* @param string 字符串
* @param fromIndex 切割开始的位置(包括)
* @return 切割后后剩余的后半部分字符串
*/
public static String subSuf(CharSequence string, int fromIndex) {
if (isEmpty(string)) {
return null;
}
return sub(string, fromIndex, string.length());
}
/**
* 指定范围内查找指定字符
*
* @param str 字符串
* @param searchChar 被查找的字符
* @param start 起始位置如果小于0从0开始查找
* @param end 终止位置如果超过str.length()则默认查找到字符串末尾
* @return 位置
*/
public static int indexOf(final CharSequence str, char searchChar, int start, int end) {
final int len = str.length();
if (start < 0 || start > len) {
start = 0;
}
if (end > len || end < 0) {
end = len;
}
for (int i = start; i < end; i++) {
if (str.charAt(i) == searchChar) {
return i;
}
}
return -1;
}
/**
* 指定范围内查找指定字符
*
* @param str 字符串
* @param searchChar 被查找的字符
* @param start 起始位置如果小于0从0开始查找
* @return 位置
*/
public static int indexOf(final CharSequence str, char searchChar, int start) {
if (str instanceof String) {
return ((String) str).indexOf(searchChar, start);
} else {
return indexOf(str, searchChar, start, -1);
}
}
/**
* 指定范围内查找指定字符
*
* @param str 字符串
* @param searchChar 被查找的字符
* @return 位置
*/
public static int indexOf(final CharSequence str, char searchChar) {
return indexOf(str, searchChar, 0);
}
/**
* 如果字符串是<code>null</code>,则返回指定默认字符串,否则返回字符串本身。
*
* <pre>
* nullToDefault(null, &quot;default&quot;) = &quot;default&quot;
* nullToDefault(&quot;&quot;, &quot;default&quot;) = &quot;&quot;
* nullToDefault(&quot; &quot;, &quot;default&quot;) = &quot; &quot;
* nullToDefault(&quot;bat&quot;, &quot;default&quot;) = &quot;bat&quot;
* </pre>
*
* @param str 要转换的字符串
* @param defaultStr 默认字符串
*
* @return 字符串本身或指定的默认字符串
*/
public static String nullToDefault(CharSequence str, String defaultStr) {
return (str == null) ? defaultStr : str.toString();
}
/**
* 当给定字符串为null时转换为Empty
*
* @param str 被转换的字符串
* @return 转换后的字符串
*/
public static String nullToEmpty(CharSequence str) {
return nullToDefault(str, EMPTY);
}
}

View File

@@ -0,0 +1,232 @@
package cn.keking.hutool;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.BitSet;
/**
* URL编码数据内容的类型是 application/x-www-form-urlencoded。
*
* <pre>
* 1.字符"a"-"z""A"-"Z""0"-"9"".""-""*",和"_" 都不会被编码;
* 2.将空格转换为%20 ;
* 3.将非文本内容转换成"%xy"的形式,xy是两位16进制的数值;
* 4.在每个 name=value 对之间放置 &amp; 符号。
* </pre>
*
* @author looly,
*
*/
public class URLEncoder implements Serializable{
private static final long serialVersionUID = 1L;
// --------------------------------------------------------------------------------------------- Static method start
/**
* 默认{@link URLEncoder}<br>
* 默认的编码器针对URI路径编码定义如下
*
* <pre>
* pchar = unreserved不处理 / pct-encoded / sub-delims子分隔符 / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* sub-delims = "!" / "$" / "&amp;" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
* </pre>
*/
public static final URLEncoder DEFAULT = createDefault();
/**
* 用于查询语句的{@link URLEncoder}<br>
* 编码器针对URI路径编码定义如下
*
* <pre>
* 0x20 ' ' =》 '+'
* 0x2A, 0x2D, 0x2E, 0x30 to 0x39, 0x41 to 0x5A, 0x5F, 0x61 to 0x7A as-is
* '*', '-', '.', '0' to '9', 'A' to 'Z', '_', 'a' to 'z' Also '=' and '&amp;' 不编码
* 其它编码为 %nn 形式
* </pre>
*
* 详细见https://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
*/
public static final URLEncoder QUERY = createQuery();
/**
* 创建默认{@link URLEncoder}<br>
* 默认的编码器针对URI路径编码定义如下
*
* <pre>
* pchar = unreserved不处理 / pct-encoded / sub-delims子分隔符 / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* sub-delims = "!" / "$" / "&amp;" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
* </pre>
*
* @return {@link URLEncoder}
*/
public static URLEncoder createDefault() {
final URLEncoder encoder = new URLEncoder();
encoder.addSafeCharacter('-');
encoder.addSafeCharacter('.');
encoder.addSafeCharacter('_');
encoder.addSafeCharacter('~');
// Add the sub-delims
encoder.addSafeCharacter('!');
encoder.addSafeCharacter('$');
encoder.addSafeCharacter('&');
encoder.addSafeCharacter('\'');
encoder.addSafeCharacter('(');
encoder.addSafeCharacter(')');
encoder.addSafeCharacter('*');
encoder.addSafeCharacter('+');
encoder.addSafeCharacter(',');
encoder.addSafeCharacter(';');
encoder.addSafeCharacter('=');
// Add the remaining literals
encoder.addSafeCharacter(':');
encoder.addSafeCharacter('@');
// Add '/' so it isn't encoded when we encode a path
encoder.addSafeCharacter('/');
return encoder;
}
/**
* 创建用于查询语句的{@link URLEncoder}<br>
* 编码器针对URI路径编码定义如下
*
* <pre>
* 0x20 ' ' =》 '+'
* 0x2A, 0x2D, 0x2E, 0x30 to 0x39, 0x41 to 0x5A, 0x5F, 0x61 to 0x7A as-is
* '*', '-', '.', '0' to '9', 'A' to 'Z', '_', 'a' to 'z' Also '=' and '&amp;' 不编码
* 其它编码为 %nn 形式
* </pre>
*
* 详细见https://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
*
* @return {@link URLEncoder}
*/
public static URLEncoder createQuery() {
final URLEncoder encoder = new URLEncoder();
// Special encoding for space
encoder.setEncodeSpaceAsPlus(true);
// Alpha and digit are safe by default
// Add the other permitted characters
encoder.addSafeCharacter('*');
encoder.addSafeCharacter('-');
encoder.addSafeCharacter('.');
encoder.addSafeCharacter('_');
encoder.addSafeCharacter('=');
encoder.addSafeCharacter('&');
return encoder;
}
// --------------------------------------------------------------------------------------------- Static method end
/** 存放安全编码 */
private final BitSet safeCharacters;
/** 是否编码空格为+ */
private boolean encodeSpaceAsPlus = false;
/**
* 构造<br>
*
* [a-zA-Z0-9]默认不被编码
*/
public URLEncoder() {
this(new BitSet(256));
for (char i = 'a'; i <= 'z'; i++) {
addSafeCharacter(i);
}
for (char i = 'A'; i <= 'Z'; i++) {
addSafeCharacter(i);
}
for (char i = '0'; i <= '9'; i++) {
addSafeCharacter(i);
}
}
/**
* 构造
*
* @param safeCharacters 安全字符,安全字符不被编码
*/
private URLEncoder(BitSet safeCharacters) {
this.safeCharacters = safeCharacters;
}
/**
* 增加安全字符<br>
* 安全字符不被编码
*
* @param c 字符
*/
public void addSafeCharacter(char c) {
safeCharacters.set(c);
}
/**
* 移除安全字符<br>
* 安全字符不被编码
*
* @param c 字符
*/
public void removeSafeCharacter(char c) {
safeCharacters.clear(c);
}
/**
* 是否将空格编码为+
*
* @param encodeSpaceAsPlus 是否将空格编码为+
*/
public void setEncodeSpaceAsPlus(boolean encodeSpaceAsPlus) {
this.encodeSpaceAsPlus = encodeSpaceAsPlus;
}
/**
* 将URL中的字符串编码为%形式
*
* @param path 需要编码的字符串
* @param charset 编码
*
* @return 编码后的字符串
*/
public String encode(String path, Charset charset) {
int maxBytesPerChar = 10;
final StringBuilder rewrittenPath = new StringBuilder(path.length());
ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
OutputStreamWriter writer = new OutputStreamWriter(buf, charset);
int c;
for (int i = 0; i < path.length(); i++) {
c = path.charAt(i);
if (safeCharacters.get(c)) {
rewrittenPath.append((char) c);
} else if (encodeSpaceAsPlus && c == ' ') {
// 对于空格单独处理
rewrittenPath.append('+');
} else {
// convert to external encoding before hex conversion
try {
writer.write((char) c);
writer.flush();
} catch (IOException e) {
buf.reset();
continue;
}
byte[] ba = buf.toByteArray();
for (int j = 0; j < ba.length; j++) {
// Converting each byte in the buffer
byte toEncode = ba[j];
rewrittenPath.append('%');
HexUtil.appendHex(rewrittenPath, toEncode, false);
}
buf.reset();
}
}
return rewrittenPath.toString();
}
}

View File

@@ -0,0 +1,74 @@
package cn.keking.hutool;
import java.nio.charset.StandardCharsets;
/**
* 统一资源定位符相关工具类
*
* @author xiaoleilu
*
*/
public class URLUtil {
/**
* 标准化URL字符串包括
*
* <pre>
* 1. 多个/替换为一个
* </pre>
*
* @param url URL字符串
* @return 标准化后的URL字符串
*/
public static String normalize(String url) {
return normalize(url, false, false);
}
/**
* 标准化URL字符串包括
*
* <pre>
* 1. 多个/替换为一个
* </pre>
*
* @param url URL字符串
* @param isEncodeBody 是否对URL中body部分的中文和特殊字符做转义不包括http:和/
* @param isEncodeParam 是否对URL中参数部分的中文和特殊字符做转义
* @return 标准化后的URL字符串
* @since 4.4.1
*/
public static String normalize(String url, boolean isEncodeBody, boolean isEncodeParam) {
if (StrUtil.isBlank(url)) {
return url;
}
final int sepIndex = url.indexOf("://");
String pre;
String body;
if (sepIndex > 0) {
pre = StrUtil.subPre(url, sepIndex + 3);
body = StrUtil.subSuf(url, sepIndex + 3);
} else {
pre = "http://";
body = url;
}
final int paramsSepIndex = StrUtil.indexOf(body, '?');
String params = null;
if (paramsSepIndex > 0) {
params = StrUtil.subSuf(body, paramsSepIndex + 1);
body = StrUtil.subPre(body, paramsSepIndex);
}
// 去除开头的\或者/
body = body.replaceAll("^[\\\\/]+", StrUtil.EMPTY);
// 替换多个\或/为单个/
body = body.replace("\\", "/").replaceAll("//+", "/");
if (isEncodeBody) {
body = URLEncoder.DEFAULT.encode(body, StandardCharsets.UTF_8);
if (params != null) {
params = "?" + URLEncoder.DEFAULT.encode(params, StandardCharsets.UTF_8);
}
}
return pre + body + StrUtil.nullToEmpty(params);
}
}

View File

@@ -14,17 +14,14 @@ public class FileAttribute {
private String url;
private String decodedUrl;
public FileAttribute() {
}
public FileAttribute(FileType type, String suffix, String name, String url, String decodedUrl) {
public FileAttribute(FileType type, String suffix, String name, String url) {
this.type = type;
this.suffix = suffix;
this.name = name;
this.url = url;
this.decodedUrl = decodedUrl;
}
public FileType getType() {
@@ -58,12 +55,4 @@ public class FileAttribute {
public void setUrl(String url) {
this.url = url;
}
public String getDecodedUrl() {
return decodedUrl;
}
public void setDecodedUrl(String decodedUrl) {
this.decodedUrl = decodedUrl;
}
}

View File

@@ -11,9 +11,11 @@ public enum FileType {
simText("simTextFilePreviewImpl"),
pdf("pdfFilePreviewImpl"),
other("otherFilePreviewImpl"),
media("mediaFilePreviewImpl");
media("mediaFilePreviewImpl"),
cad("cadFilePreviewImpl");
private final String instanceName;
private String instanceName;
FileType(String instanceName){
this.instanceName=instanceName;
}
@@ -22,7 +24,4 @@ public enum FileType {
return instanceName;
}
public void setInstanceName(String instanceName) {
this.instanceName = instanceName;
}
}

View File

@@ -1,82 +0,0 @@
package cn.keking.service;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.service.cache.CacheService;
import cn.keking.utils.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.ExtendedModelMap;
import javax.annotation.PostConstruct;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by kl on 2018/1/19.
* Content :消费队列中的转换文件
*/
@Service
public class FileConverQueueTask {
Logger logger= LoggerFactory.getLogger(getClass());
public static final String queueTaskName="FileConverQueueTask";
@Autowired
FilePreviewFactory previewFactory;
@Autowired
CacheService cacheService;
@Autowired
FileUtils fileUtils;
@PostConstruct
public void startTask(){
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.submit(new ConverTask(previewFactory, cacheService, fileUtils));
logger.info("队列处理文件转换任务启动完成 ");
}
class ConverTask implements Runnable{
FilePreviewFactory previewFactory;
CacheService cacheService;
FileUtils fileUtils;
public ConverTask(FilePreviewFactory previewFactory, CacheService cacheService, FileUtils fileUtils) {
this.previewFactory = previewFactory;
this.cacheService = cacheService;
this.fileUtils=fileUtils;
}
@Override
public void run() {
while (true) {
try {
String url = cacheService.takeQueueTask();
if(url != null){
FileAttribute fileAttribute = fileUtils.getFileAttribute(url);
logger.info("正在处理转换任务,文件名称【{}】",fileAttribute.getName());
FileType fileType=fileAttribute.getType();
if(fileType.equals(FileType.compress) || fileType.equals(FileType.office)){
FilePreview filePreview=previewFactory.get(fileAttribute);
filePreview.filePreviewHandle(url, new ExtendedModelMap(), fileAttribute);
}
}
} catch (Exception e) {
try {
Thread.sleep(1000*10);
}catch (Exception ex){
ex.printStackTrace();
}
e.printStackTrace();
}
}
}
}
}

View File

@@ -0,0 +1,92 @@
package cn.keking.service;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.service.cache.CacheService;
import cn.keking.utils.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.ui.ExtendedModelMap;
import javax.annotation.PostConstruct;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by kl on 2018/1/19.
* Content :消费队列中的转换文件
*/
@Service
public class FileConvertQueueTask {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final FilePreviewFactory previewFactory;
private final CacheService cacheService;
private final FileUtils fileUtils;
public FileConvertQueueTask(FilePreviewFactory previewFactory,
CacheService cacheService,
FileUtils fileUtils) {
this.previewFactory = previewFactory;
this.cacheService = cacheService;
this.fileUtils=fileUtils;
}
@PostConstruct
public void startTask(){
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.submit(new ConvertTask(previewFactory, cacheService, fileUtils));
logger.info("队列处理文件转换任务启动完成 ");
}
static class ConvertTask implements Runnable {
private final Logger logger = LoggerFactory.getLogger(ConvertTask.class);
private final FilePreviewFactory previewFactory;
private final CacheService cacheService;
private final FileUtils fileUtils;
public ConvertTask(FilePreviewFactory previewFactory,
CacheService cacheService,
FileUtils fileUtils) {
this.previewFactory = previewFactory;
this.cacheService = cacheService;
this.fileUtils=fileUtils;
}
@Override
public void run() {
while (true) {
String url = null;
try {
url = cacheService.takeQueueTask();
if(url != null){
FileAttribute fileAttribute = fileUtils.getFileAttribute(url);
FileType fileType = fileAttribute.getType();
logger.info("正在处理预览转换任务url{},预览类型:{}", url, fileType);
if(fileType.equals(FileType.compress) || fileType.equals(FileType.office) || fileType.equals(FileType.cad)) {
FilePreview filePreview = previewFactory.get(fileAttribute);
filePreview.filePreviewHandle(url, new ExtendedModelMap(), fileAttribute);
} else {
logger.info("预览类型无需处理url{},预览类型:{}", url, fileType);
}
}
} catch (Exception e) {
try {
Thread.sleep(1000*10);
} catch (Exception ex){
ex.printStackTrace();
}
logger.info("处理预览转换任务异常url{}", url, e);
}
}
}
}
}

View File

@@ -1,8 +1,6 @@
package cn.keking.service;
import cn.keking.model.FileAttribute;
import cn.keking.utils.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
@@ -15,11 +13,11 @@ import java.util.Map;
@Service
public class FilePreviewFactory {
@Autowired
FileUtils fileUtils;
private final ApplicationContext context;
@Autowired
ApplicationContext context;
public FilePreviewFactory(ApplicationContext context) {
this.context = context;
}
public FilePreview get(FileAttribute fileAttribute) {
Map<String, FilePreview> filePreviewMap = context.getBeansOfType(FilePreview.class);

View File

@@ -4,19 +4,19 @@ import java.util.List;
import java.util.Map;
/**
* @auther: chenjh
* @time: 2019/4/2 16:45
* @description
* @author: chenjh
* @since: 2019/4/2 16:45
*/
public interface CacheService {
final String REDIS_FILE_PREVIEW_PDF_KEY = "converted-preview-pdf-file";
final String REDIS_FILE_PREVIEW_IMGS_KEY = "converted-preview-imgs-file";//压缩包内图片文件集合
final String REDIS_FILE_PREVIEW_PDF_IMGS_KEY = "converted-preview-pdfimgs-file";
String FILE_PREVIEW_PDF_KEY = "converted-preview-pdf-file";
String FILE_PREVIEW_IMGS_KEY = "converted-preview-imgs-file";//压缩包内图片文件集合
String FILE_PREVIEW_PDF_IMGS_KEY = "converted-preview-pdfimgs-file";
String TASK_QUEUE_NAME = "convert-task";
final Integer DEFAULT_PDF_CAPACITY = 500000;
final Integer DEFAULT_IMG_CAPACITY = 500000;
final Integer DEFAULT_PDFIMG_CAPACITY = 500000;
Integer DEFAULT_PDF_CAPACITY = 500000;
Integer DEFAULT_IMG_CAPACITY = 500000;
Integer DEFAULT_PDFIMG_CAPACITY = 500000;
void initPDFCachePool(Integer capacity);
void initIMGCachePool(Integer capacity);
@@ -29,12 +29,8 @@ public interface CacheService {
List<String> getImgCache(String key);
Integer getPdfImageCache(String key);
void putPdfImageCache(String pdfFilePath, int num);
void cleanCache();
void addQueueTask(String url);
String takeQueueTask() throws InterruptedException;
}

View File

@@ -3,7 +3,6 @@ package cn.keking.service.cache.impl;
import cn.keking.service.cache.CacheService;
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import com.googlecode.concurrentlinkedhashmap.Weighers;
import org.rocksdb.RocksDB;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
@@ -29,7 +28,7 @@ public class CacheServiceJDKImpl implements CacheService {
private static final int QUEUE_SIZE = 500000;
private BlockingQueue blockingQueue = new ArrayBlockingQueue(QUEUE_SIZE);
private final BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(QUEUE_SIZE);
@Override
public void initPDFCachePool(Integer capacity) {
@@ -130,6 +129,6 @@ public class CacheServiceJDKImpl implements CacheService {
@Override
public String takeQueueTask() throws InterruptedException {
return String.valueOf(blockingQueue.take());
return blockingQueue.take();
}
}

View File

@@ -1,13 +1,11 @@
package cn.keking.service.cache.impl;
import cn.keking.service.FileConverQueueTask;
import cn.keking.service.cache.CacheService;
import org.redisson.Redisson;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
@@ -23,16 +21,12 @@ import java.util.Map;
@Service
public class CacheServiceRedisImpl implements CacheService {
private Config config;
private final RedissonClient redissonClient;
@Autowired
public CacheServiceRedisImpl(Config config) {
this.config = config;
this.redissonClient = Redisson.create(config);
}
private RedissonClient redissonClient;
@Override
public void initPDFCachePool(Integer capacity) {
@@ -50,47 +44,47 @@ public class CacheServiceRedisImpl implements CacheService {
@Override
public void putPDFCache(String key, String value) {
RMapCache<String, String> convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_KEY);
RMapCache<String, String> convertedList = redissonClient.getMapCache(FILE_PREVIEW_PDF_KEY);
convertedList.fastPut(key, value);
}
@Override
public void putImgCache(String key, List<String> value) {
RMapCache<String, List<String>> convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_IMGS_KEY);
RMapCache<String, List<String>> convertedList = redissonClient.getMapCache(FILE_PREVIEW_IMGS_KEY);
convertedList.fastPut(key, value);
}
@Override
public Map<String, String> getPDFCache() {
return redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_KEY);
return redissonClient.getMapCache(FILE_PREVIEW_PDF_KEY);
}
@Override
public String getPDFCache(String key) {
RMapCache<String, String> convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_KEY);
RMapCache<String, String> convertedList = redissonClient.getMapCache(FILE_PREVIEW_PDF_KEY);
return convertedList.get(key);
}
@Override
public Map<String, List<String>> getImgCache() {
return redissonClient.getMapCache(REDIS_FILE_PREVIEW_IMGS_KEY);
return redissonClient.getMapCache(FILE_PREVIEW_IMGS_KEY);
}
@Override
public List<String> getImgCache(String key) {
RMapCache<String, List<String>> convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_IMGS_KEY);
RMapCache<String, List<String>> convertedList = redissonClient.getMapCache(FILE_PREVIEW_IMGS_KEY);
return convertedList.get(key);
}
@Override
public Integer getPdfImageCache(String key) {
RMapCache<String, Integer> convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_IMGS_KEY);
RMapCache<String, Integer> convertedList = redissonClient.getMapCache(FILE_PREVIEW_PDF_IMGS_KEY);
return convertedList.get(key);
}
@Override
public void putPdfImageCache(String pdfFilePath, int num) {
RMapCache<String, Integer> convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_IMGS_KEY);
RMapCache<String, Integer> convertedList = redissonClient.getMapCache(FILE_PREVIEW_PDF_IMGS_KEY);
convertedList.fastPut(pdfFilePath, num);
}
@@ -103,28 +97,28 @@ public class CacheServiceRedisImpl implements CacheService {
@Override
public void addQueueTask(String url) {
RBlockingQueue<String> queue = redissonClient.getBlockingQueue(FileConverQueueTask.queueTaskName);
RBlockingQueue<String> queue = redissonClient.getBlockingQueue(TASK_QUEUE_NAME);
queue.addAsync(url);
}
@Override
public String takeQueueTask() throws InterruptedException {
RBlockingQueue<String> queue = redissonClient.getBlockingQueue(FileConverQueueTask.queueTaskName);
RBlockingQueue<String> queue = redissonClient.getBlockingQueue(TASK_QUEUE_NAME);
return queue.take();
}
private void cleanPdfCache() {
RMapCache<String, String> pdfCache = redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_KEY);
RMapCache<String, String> pdfCache = redissonClient.getMapCache(FILE_PREVIEW_PDF_KEY);
pdfCache.clear();
}
private void cleanImgCache() {
RMapCache<String, List<String>> imgCache = redissonClient.getMapCache(REDIS_FILE_PREVIEW_IMGS_KEY);
RMapCache<String, List<String>> imgCache = redissonClient.getMapCache(FILE_PREVIEW_IMGS_KEY);
imgCache.clear();
}
private void cleanPdfImgCache() {
RMapCache<String, Integer> pdfImg = redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_IMGS_KEY);
RMapCache<String, Integer> pdfImg = redissonClient.getMapCache(FILE_PREVIEW_PDF_IMGS_KEY);
pdfImg.clear();
}
}

View File

@@ -36,24 +36,24 @@ public class CacheServiceRocksDBImpl implements CacheService {
private static final Logger LOGGER = LoggerFactory.getLogger(CacheServiceRocksDBImpl.class);
private BlockingQueue blockingQueue = new ArrayBlockingQueue(QUEUE_SIZE);
private final BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(QUEUE_SIZE);
private RocksDB db;
{
try {
db = RocksDB.open(DB_PATH);
if (db.get(REDIS_FILE_PREVIEW_PDF_KEY.getBytes()) == null) {
if (db.get(FILE_PREVIEW_PDF_KEY.getBytes()) == null) {
Map<String, String> initPDFCache = new HashMap<>();
db.put(REDIS_FILE_PREVIEW_PDF_KEY.getBytes(), toByteArray(initPDFCache));
db.put(FILE_PREVIEW_PDF_KEY.getBytes(), toByteArray(initPDFCache));
}
if (db.get(REDIS_FILE_PREVIEW_IMGS_KEY.getBytes()) == null) {
if (db.get(FILE_PREVIEW_IMGS_KEY.getBytes()) == null) {
Map<String, List<String>> initIMGCache = new HashMap<>();
db.put(REDIS_FILE_PREVIEW_IMGS_KEY.getBytes(), toByteArray(initIMGCache));
db.put(FILE_PREVIEW_IMGS_KEY.getBytes(), toByteArray(initIMGCache));
}
if (db.get(REDIS_FILE_PREVIEW_PDF_IMGS_KEY.getBytes()) == null) {
if (db.get(FILE_PREVIEW_PDF_IMGS_KEY.getBytes()) == null) {
Map<String, Integer> initPDFIMGCache = new HashMap<>();
db.put(REDIS_FILE_PREVIEW_PDF_IMGS_KEY.getBytes(), toByteArray(initPDFIMGCache));
db.put(FILE_PREVIEW_PDF_IMGS_KEY.getBytes(), toByteArray(initPDFIMGCache));
}
} catch (RocksDBException | IOException e) {
LOGGER.error("Uable to init RocksDB" + e);
@@ -81,7 +81,7 @@ public class CacheServiceRocksDBImpl implements CacheService {
try {
Map<String, String> pdfCacheItem = getPDFCache();
pdfCacheItem.put(key, value);
db.put(REDIS_FILE_PREVIEW_PDF_KEY.getBytes(), toByteArray(pdfCacheItem));
db.put(FILE_PREVIEW_PDF_KEY.getBytes(), toByteArray(pdfCacheItem));
} catch (RocksDBException | IOException e) {
LOGGER.error("Put into RocksDB Exception" + e);
}
@@ -92,17 +92,18 @@ public class CacheServiceRocksDBImpl implements CacheService {
try {
Map<String, List<String>> imgCacheItem = getImgCache();
imgCacheItem.put(key, value);
db.put(REDIS_FILE_PREVIEW_PDF_KEY.getBytes(), toByteArray(imgCacheItem));
db.put(FILE_PREVIEW_IMGS_KEY.getBytes(), toByteArray(imgCacheItem));
} catch (RocksDBException | IOException e) {
LOGGER.error("Put into RocksDB Exception" + e);
}
}
@Override
@SuppressWarnings("unchecked")
public Map<String, String> getPDFCache() {
Map<String, String> result = new HashMap<>();
try{
result = (Map<String, String>) toObject(db.get(REDIS_FILE_PREVIEW_PDF_KEY.getBytes()));
result = (Map<String, String>) toObject(db.get(FILE_PREVIEW_PDF_KEY.getBytes()));
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
@@ -110,10 +111,11 @@ public class CacheServiceRocksDBImpl implements CacheService {
}
@Override
@SuppressWarnings("unchecked")
public String getPDFCache(String key) {
String result = "";
try{
Map<String, String> map = (Map<String, String>) toObject(db.get(REDIS_FILE_PREVIEW_PDF_KEY.getBytes()));
Map<String, String> map = (Map<String, String>) toObject(db.get(FILE_PREVIEW_PDF_KEY.getBytes()));
result = map.get(key);
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
@@ -122,10 +124,11 @@ public class CacheServiceRocksDBImpl implements CacheService {
}
@Override
@SuppressWarnings("unchecked")
public Map<String, List<String>> getImgCache() {
Map<String, List<String>> result = new HashMap<>();
try{
result = (Map<String, List<String>>) toObject(db.get(REDIS_FILE_PREVIEW_IMGS_KEY.getBytes()));
result = (Map<String, List<String>>) toObject(db.get(FILE_PREVIEW_IMGS_KEY.getBytes()));
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
@@ -133,11 +136,12 @@ public class CacheServiceRocksDBImpl implements CacheService {
}
@Override
@SuppressWarnings("unchecked")
public List<String> getImgCache(String key) {
List<String> result = new ArrayList<>();
Map<String, List<String>> map;
try{
map = (Map<String, List<String>>) toObject(db.get(REDIS_FILE_PREVIEW_IMGS_KEY.getBytes()));
map = (Map<String, List<String>>) toObject(db.get(FILE_PREVIEW_IMGS_KEY.getBytes()));
result = map.get(key);
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
@@ -145,22 +149,13 @@ public class CacheServiceRocksDBImpl implements CacheService {
return result;
}
public Map<String, Integer> getPdfImageCaches() {
Map<String, Integer> map = new HashMap<>();
try{
map = (Map<String, Integer>) toObject(db.get(REDIS_FILE_PREVIEW_PDF_IMGS_KEY.getBytes()));
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
return map;
}
@Override
@SuppressWarnings("unchecked")
public Integer getPdfImageCache(String key) {
Integer result = 0;
Map<String, Integer> map;
try{
map = (Map<String, Integer>) toObject(db.get(REDIS_FILE_PREVIEW_PDF_IMGS_KEY.getBytes()));
map = (Map<String, Integer>) toObject(db.get(FILE_PREVIEW_PDF_IMGS_KEY.getBytes()));
result = map.get(key);
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
@@ -173,7 +168,7 @@ public class CacheServiceRocksDBImpl implements CacheService {
try {
Map<String, Integer> pdfImageCacheItem = getPdfImageCaches();
pdfImageCacheItem.put(pdfFilePath, num);
db.put(REDIS_FILE_PREVIEW_PDF_IMGS_KEY.getBytes(), toByteArray(pdfImageCacheItem));
db.put(FILE_PREVIEW_PDF_IMGS_KEY.getBytes(), toByteArray(pdfImageCacheItem));
} catch (RocksDBException | IOException e) {
LOGGER.error("Put into RocksDB Exception" + e);
}
@@ -197,11 +192,23 @@ public class CacheServiceRocksDBImpl implements CacheService {
@Override
public String takeQueueTask() throws InterruptedException {
return String.valueOf(blockingQueue.take());
return blockingQueue.take();
}
@SuppressWarnings("unchecked")
private Map<String, Integer> getPdfImageCaches() {
Map<String, Integer> map = new HashMap<>();
try{
map = (Map<String, Integer>) toObject(db.get(FILE_PREVIEW_PDF_IMGS_KEY.getBytes()));
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
return map;
}
private byte[] toByteArray (Object obj) throws IOException {
byte[] bytes = null;
byte[] bytes;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
@@ -213,7 +220,7 @@ public class CacheServiceRocksDBImpl implements CacheService {
}
private Object toObject (byte[] bytes) throws IOException, ClassNotFoundException {
Object obj = null;
Object obj;
ByteArrayInputStream bis = new ByteArrayInputStream (bytes);
ObjectInputStream ois = new ObjectInputStream (bis);
obj = ois.readObject();
@@ -224,16 +231,16 @@ public class CacheServiceRocksDBImpl implements CacheService {
private void cleanPdfCache() throws IOException, RocksDBException {
Map<String, String> initPDFCache = new HashMap<>();
db.put(REDIS_FILE_PREVIEW_PDF_KEY.getBytes(), toByteArray(initPDFCache));
db.put(FILE_PREVIEW_PDF_KEY.getBytes(), toByteArray(initPDFCache));
}
private void cleanImgCache() throws IOException, RocksDBException {
Map<String, List<String>> initIMGCache = new HashMap<>();
db.put(REDIS_FILE_PREVIEW_IMGS_KEY.getBytes(), toByteArray(initIMGCache));
db.put(FILE_PREVIEW_IMGS_KEY.getBytes(), toByteArray(initIMGCache));
}
private void cleanPdfImgCache() throws IOException, RocksDBException {
Map<String, Integer> initPDFIMGCache = new HashMap<>();
db.put(REDIS_FILE_PREVIEW_PDF_IMGS_KEY.getBytes(), toByteArray(initPDFIMGCache));
db.put(FILE_PREVIEW_PDF_IMGS_KEY.getBytes(), toByteArray(initPDFIMGCache));
}
}

View File

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

View File

@@ -1,12 +1,12 @@
package cn.keking.service.impl;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils;
import cn.keking.utils.FileUtils;
import cn.keking.utils.ZipReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
@@ -16,16 +16,21 @@ import org.springframework.util.StringUtils;
* Content :处理压缩包文件
*/
@Service
public class CompressFilePreviewImpl implements FilePreview{
public class CompressFilePreviewImpl implements FilePreview {
@Autowired
FileUtils fileUtils;
private final FileUtils fileUtils;
@Autowired
DownloadUtils downloadUtils;
private final DownloadUtils downloadUtils;
@Autowired
ZipReader zipReader;
private final ZipReader zipReader;
public CompressFilePreviewImpl(FileUtils fileUtils,
DownloadUtils downloadUtils,
ZipReader zipReader) {
this.fileUtils = fileUtils;
this.downloadUtils = downloadUtils;
this.zipReader = zipReader;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
@@ -33,7 +38,7 @@ public class CompressFilePreviewImpl implements FilePreview{
String suffix=fileAttribute.getSuffix();
String fileTree = null;
// 判断文件名是否存在(redis缓存读取)
if (!StringUtils.hasText(fileUtils.getConvertedFile(fileName))) {
if (!StringUtils.hasText(fileUtils.getConvertedFile(fileName)) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileName);
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
@@ -48,7 +53,7 @@ public class CompressFilePreviewImpl implements FilePreview{
} else if ("7z".equalsIgnoreCase(suffix)) {
fileTree = zipReader.read7zFile(filePath, fileName);
}
if (fileTree != null && !"null".equals(fileTree)) {
if (fileTree != null && !"null".equals(fileTree) && ConfigConstants.isCacheEnabled()) {
fileUtils.addConvertedFile(fileName, fileTree);
}
} else {

View File

@@ -1,9 +1,11 @@
package cn.keking.service.impl;
import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils;
import cn.keking.utils.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
/**
@@ -15,11 +17,31 @@ import org.springframework.ui.Model;
@Service
public class MediaFilePreviewImpl implements FilePreview {
@Autowired
FileUtils fileUtils;
private final DownloadUtils downloadUtils;
private final FileUtils fileUtils;
public MediaFilePreviewImpl(DownloadUtils downloadUtils,
FileUtils fileUtils) {
this.downloadUtils = downloadUtils;
this.fileUtils = fileUtils;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileAttribute.getName());
if (0 != response.getCode()) {
model.addAttribute("fileType", fileAttribute.getSuffix());
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
} else {
model.addAttribute("mediaUrl", BaseUrlFilter.getBaseUrl() + fileUtils.getRelativePath(response.getContent()));
}
} else {
model.addAttribute("mediaUrl", url);
}
model.addAttribute("mediaUrl", url);
String suffix=fileAttribute.getSuffix();
if ("flv".equalsIgnoreCase(suffix)) {

View File

@@ -8,12 +8,11 @@ import cn.keking.utils.DownloadUtils;
import cn.keking.utils.FileUtils;
import cn.keking.utils.OfficeToPdf;
import cn.keking.utils.PdfUtils;
import org.springframework.beans.factory.annotation.Autowired;
import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import java.io.File;
import java.util.List;
/**
@@ -23,76 +22,80 @@ import java.util.List;
@Service
public class OfficeFilePreviewImpl implements FilePreview {
@Autowired
FileUtils fileUtils;
private final FileUtils fileUtils;
@Autowired
PdfUtils pdfUtils;
private final PdfUtils pdfUtils;
@Autowired
DownloadUtils downloadUtils;
private final DownloadUtils downloadUtils;
@Autowired
private OfficeToPdf officeToPdf;
private final OfficeToPdf officeToPdf;
String fileDir = ConfigConstants.getFileDir();
public OfficeFilePreviewImpl(FileUtils fileUtils,
PdfUtils pdfUtils,
DownloadUtils downloadUtils,
OfficeToPdf officeToPdf) {
this.fileUtils = fileUtils;
this.pdfUtils = pdfUtils;
this.downloadUtils = downloadUtils;
this.officeToPdf = officeToPdf;
}
public static final String OFFICE_PREVIEW_TYPE_PDF = "pdf";
public static final String OFFICE_PREVIEW_TYPE_IMAGE = "image";
public static final String OFFICE_PREVIEW_TYPE_ALLIMAGES = "allImages";
public static final String OFFICE_PREVIEW_TYPE_ALL_IMAGES = "allImages";
private static final String FILE_DIR = ConfigConstants.getFileDir();
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
// 预览Type参数传了就取参数的没传取系统默认
String officePreviewType = model.asMap().get("officePreviewType") == null ? ConfigConstants.getOfficePreviewType() : model.asMap().get("officePreviewType").toString();
String originUrl = (String) model.asMap().get("originUrl");
String baseUrl = BaseUrlFilter.getBaseUrl();
String suffix=fileAttribute.getSuffix();
String fileName=fileAttribute.getName();
boolean isHtml = suffix.equalsIgnoreCase("xls") || suffix.equalsIgnoreCase("xlsx");
String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + (isHtml ? "html" : "pdf");
String outFilePath = fileDir + pdfName;
String outFilePath = FILE_DIR + pdfName;
// 判断之前是否已转换过,如果转换过,直接返回,否则执行转换
if (!fileUtils.listConvertedFiles().containsKey(pdfName)) {
String filePath = fileDir + fileName;
if (!new File(filePath).exists()) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, null);
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
}
filePath = response.getContent();
if (!fileUtils.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
String filePath;
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, null);
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
}
filePath = response.getContent();
if (StringUtils.hasText(outFilePath)) {
officeToPdf.openOfficeToPDF(filePath, outFilePath);
File f = new File(filePath);
if (f.exists()) {
f.delete();
}
if (isHtml) {
// 对转换后的文件进行操作(改变编码方式)
fileUtils.doActionConvertedFile(outFilePath);
}
// 加入缓存
fileUtils.addConvertedFile(pdfName, fileUtils.getRelativePath(outFilePath));
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
fileUtils.addConvertedFile(pdfName, fileUtils.getRelativePath(outFilePath));
}
}
}
if (!isHtml && originUrl != null && (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OFFICE_PREVIEW_TYPE_ALLIMAGES.equals(officePreviewType))) {
List<String> imageUrls = pdfUtils.pdf2jpg(outFilePath, pdfName, originUrl);
if (imageUrls == null || imageUrls.size() < 1) {
model.addAttribute("msg", "office转图片异常请联系管理员");
model.addAttribute("fileType",fileAttribute.getSuffix());
return "fileNotSupported";
}
model.addAttribute("imgurls", imageUrls);
model.addAttribute("currentUrl", imageUrls.get(0));
if (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType)) {
return "officePicture";
} else {
return "picture";
}
if (!isHtml && baseUrl != null && (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType))) {
return getPreviewType(model, fileAttribute, officePreviewType, baseUrl, pdfName, outFilePath, pdfUtils, OFFICE_PREVIEW_TYPE_IMAGE);
}
model.addAttribute("pdfUrl", pdfName);
return isHtml ? "html" : "pdf";
}
static String getPreviewType(Model model, FileAttribute fileAttribute, String officePreviewType, String baseUrl, String pdfName, String outFilePath, PdfUtils pdfUtils, String officePreviewTypeImage) {
List<String> imageUrls = pdfUtils.pdf2jpg(outFilePath, pdfName, baseUrl);
if (imageUrls == null || imageUrls.size() < 1) {
model.addAttribute("msg", "office转图片异常请联系管理员");
model.addAttribute("fileType",fileAttribute.getSuffix());
return "fileNotSupported";
}
model.addAttribute("imgurls", imageUrls);
model.addAttribute("currentUrl", imageUrls.get(0));
if (officePreviewTypeImage.equals(officePreviewType)) {
return "officePicture";
} else {
return "picture";
}
}
}

View File

@@ -2,8 +2,6 @@ package cn.keking.service.impl;
import cn.keking.model.FileAttribute;
import cn.keking.service.FilePreview;
import cn.keking.utils.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
@@ -13,14 +11,10 @@ import org.springframework.ui.Model;
*/
@Service
public class OtherFilePreviewImpl implements FilePreview {
@Autowired
FileUtils fileUtils;
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
model.addAttribute("fileType",fileAttribute.getSuffix());
model.addAttribute("msg", "系统还不支持该格式文件的在线预览" +
"如有需要请按下方显示的邮箱地址联系系统维护人员");
model.addAttribute("msg", "系统还不支持该格式文件的在线预览");
return "fileNotSupported";
}
}

View File

@@ -7,11 +7,10 @@ import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils;
import cn.keking.utils.FileUtils;
import cn.keking.utils.PdfUtils;
import org.springframework.beans.factory.annotation.Autowired;
import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import java.io.File;
import java.util.List;
/**
@@ -19,32 +18,35 @@ import java.util.List;
* Content :处理pdf文件
*/
@Service
public class PdfFilePreviewImpl implements FilePreview{
public class PdfFilePreviewImpl implements FilePreview {
private final FileUtils fileUtils;
@Autowired
FileUtils fileUtils;
private final PdfUtils pdfUtils;
@Autowired
PdfUtils pdfUtils;
private final DownloadUtils downloadUtils;
@Autowired
DownloadUtils downloadUtils;
private static final String FILE_DIR = ConfigConstants.getFileDir();
String fileDir = ConfigConstants.getFileDir();
public PdfFilePreviewImpl(FileUtils fileUtils,
PdfUtils pdfUtils,
DownloadUtils downloadUtils) {
this.fileUtils = fileUtils;
this.pdfUtils = pdfUtils;
this.downloadUtils = downloadUtils;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String suffix=fileAttribute.getSuffix();
String fileName=fileAttribute.getName();
String officePreviewType = model.asMap().get("officePreviewType") == null ? ConfigConstants.getOfficePreviewType() : model.asMap().get("officePreviewType").toString();
String originUrl = model.asMap().get("originUrl").toString();
model.addAttribute("pdfUrl", url);
String baseUrl = BaseUrlFilter.getBaseUrl();
String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + "pdf";
String outFilePath = fileDir + pdfName;
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_ALLIMAGES.equals(officePreviewType)) {
String outFilePath = FILE_DIR + pdfName;
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType)) {
//当文件不存在时,就去下载
if (!new File(outFilePath).exists()) {
if (!fileUtils.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileName);
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
@@ -52,8 +54,12 @@ public class PdfFilePreviewImpl implements FilePreview{
return "fileNotSupported";
}
outFilePath = response.getContent();
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
fileUtils.addConvertedFile(pdfName, fileUtils.getRelativePath(outFilePath));
}
}
List<String> imageUrls = pdfUtils.pdf2jpg(outFilePath, pdfName, originUrl);
List<String> imageUrls = pdfUtils.pdf2jpg(outFilePath, pdfName, baseUrl);
if (imageUrls == null || imageUrls.size() < 1) {
model.addAttribute("msg", "pdf转图片异常请联系管理员");
model.addAttribute("fileType",fileAttribute.getSuffix());
@@ -66,6 +72,27 @@ public class PdfFilePreviewImpl implements FilePreview{
} else {
return "picture";
}
} else {
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
if (!fileUtils.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, pdfName);
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
}
model.addAttribute("pdfUrl", fileUtils.getRelativePath(response.getContent()));
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
fileUtils.addConvertedFile(pdfName, fileUtils.getRelativePath(outFilePath));
}
} else {
model.addAttribute("pdfUrl", pdfName);
}
} else {
model.addAttribute("pdfUrl", url);
}
}
return "pdf";
}

View File

@@ -1,10 +1,11 @@
package cn.keking.service.impl;
import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils;
import cn.keking.utils.FileUtils;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.web.context.request.RequestContextHolder;
@@ -18,21 +19,42 @@ import java.util.List;
@Service
public class PictureFilePreviewImpl implements FilePreview {
@Autowired
FileUtils fileUtils;
private final FileUtils fileUtils;
private final DownloadUtils downloadUtils;
public PictureFilePreviewImpl(FileUtils fileUtils,
DownloadUtils downloadUtils) {
this.fileUtils = fileUtils;
this.downloadUtils = downloadUtils;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String fileKey=(String) RequestContextHolder.currentRequestAttributes().getAttribute("fileKey",0);
List imgUrls = Lists.newArrayList(url);
try{
String fileKey = (String) RequestContextHolder.currentRequestAttributes().getAttribute("fileKey",0);
List<String> imgUrls = Lists.newArrayList(url);
try {
imgUrls.clear();
imgUrls.addAll(fileUtils.getRedisImgUrls(fileKey));
}catch (Exception e){
imgUrls.addAll(fileUtils.getImgCache(fileKey));
} catch (Exception e){
imgUrls = Lists.newArrayList(url);
}
model.addAttribute("imgurls", imgUrls);
model.addAttribute("currentUrl",url);
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, null);
if (0 != response.getCode()) {
model.addAttribute("fileType", fileAttribute.getSuffix());
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
} else {
String file = fileUtils.getRelativePath(response.getContent());
model.addAttribute("imgurls", Lists.newArrayList(file));
model.addAttribute("currentUrl", file);
}
} else {
model.addAttribute("imgurls", imgUrls);
model.addAttribute("currentUrl", url);
}
return "picture";
}
}

View File

@@ -3,35 +3,48 @@ package cn.keking.service.impl;
import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
import cn.keking.utils.FileUtils;
import cn.keking.utils.SimTextUtil;
import org.springframework.beans.factory.annotation.Autowired;
import cn.keking.utils.DownloadUtils;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
/**
* Created by kl on 2018/1/17.
* Content :处理文本文件
*/
@Service
public class SimTextFilePreviewImpl implements FilePreview{
public class SimTextFilePreviewImpl implements FilePreview {
@Autowired
SimTextUtil simTextUtil;
private final DownloadUtils downloadUtils;
@Autowired
FileUtils fileUtils;
public SimTextFilePreviewImpl(DownloadUtils downloadUtils) {
this.downloadUtils = downloadUtils;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute){
String decodedUrl=fileAttribute.getDecodedUrl();
String fileName=fileAttribute.getName();
ReturnResponse<String> response = simTextUtil.readSimText(decodedUrl, fileName);
String fileName = fileAttribute.getName();
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileName);
if (0 != response.getCode()) {
model.addAttribute("msg", response.getMsg());
model.addAttribute("fileType",fileAttribute.getSuffix());
return "fileNotSupported";
}
try {
File originFile = new File(response.getContent());
File previewFile = new File(response.getContent() + ".txt");
if (previewFile.exists()) {
previewFile.delete();
}
Files.copy(originFile.toPath(), previewFile.toPath());
} catch (IOException e) {
model.addAttribute("msg", e.getLocalizedMessage());
model.addAttribute("fileType",fileAttribute.getSuffix());
return "fileNotSupported";
}
model.addAttribute("ordinaryUrl", response.getMsg());
return "txt";
}

View File

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

View File

@@ -2,18 +2,25 @@ package cn.keking.utils;
import com.sun.star.document.UpdateDocMode;
import cn.keking.extend.ControlDocumentFormatRegistry;
import org.apache.commons.lang3.StringUtils;
import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.artofsolving.jodconverter.office.DefaultOfficeManagerConfiguration;
import org.artofsolving.jodconverter.office.OfficeManager;
import org.artofsolving.jodconverter.office.OfficeUtils;
import org.springframework.beans.factory.annotation.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.nio.charset.Charset;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* 创建文件转换器
@@ -24,25 +31,35 @@ import java.util.Map;
@Component
public class ConverterUtils {
// @Value("${office.home}")
// String officeHome;
// OpenOfficeConnection connection;
OfficeManager officeManager;
private final Logger logger = LoggerFactory.getLogger(ConverterUtils.class);
private OfficeManager officeManager;
@PostConstruct
public void initOfficeManager() {
//// connection = new SocketOpenOfficeConnection(host,8100);
//// connection.connect();
String officeHome = OfficeUtils.getDefaultOfficeHome().getAbsolutePath();
DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration();
configuration.setOfficeHome(officeHome);
configuration.setPortNumber(8100);
officeManager = configuration.buildOfficeManager();
officeManager.start();
// 设置任务执行超时为5分钟
// configuration.setTaskExecutionTimeout(1000 * 60 * 5L);//
// 设置任务队列超时为24小时
// configuration.setTaskQueueTimeout(1000 * 60 * 60 * 24L);//
File officeHome;
officeHome = OfficeUtils.getDefaultOfficeHome();
if (officeHome == null) {
throw new RuntimeException("找不到office组件请确认'office.home'配置是否有误");
}
boolean killOffice = killProcess();
if (killOffice) {
logger.warn("检测到有正在运行的office进程已自动结束该进程");
}
try {
DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration();
configuration.setOfficeHome(officeHome);
configuration.setPortNumber(8100);
// 设置任务执行超时为5分钟
configuration.setTaskExecutionTimeout(1000 * 60 * 5L);
// 设置任务队列超时为24小时
//configuration.setTaskQueueTimeout(1000 * 60 * 60 * 24L);
officeManager = configuration.buildOfficeManager();
officeManager.start();
} catch (Exception e) {
logger.error("启动office组件失败请检查office组件是否可用");
throw e;
}
}
public OfficeDocumentConverter getDocumentConverter() {
@@ -56,10 +73,48 @@ public class ConverterUtils {
loadProperties.put("Hidden", true);
loadProperties.put("ReadOnly", true);
loadProperties.put("UpdateDocMode", UpdateDocMode.QUIET_UPDATE);
loadProperties.put("CharacterSet", Charset.forName("UTF-8").name());
loadProperties.put("CharacterSet", StandardCharsets.UTF_8.name());
return loadProperties;
}
private boolean killProcess() {
boolean flag = false;
Properties props = System.getProperties();
try {
if (props.getProperty("os.name").toLowerCase().contains("windows")) {
Process p = Runtime.getRuntime().exec("cmd /c tasklist ");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream os = p.getInputStream();
byte[] b = new byte[256];
while (os.read(b) > 0) {
baos.write(b);
}
String s = baos.toString();
if (s.contains("soffice.bin")) {
Runtime.getRuntime().exec("taskkill /im " + "soffice.bin" + " /f");
flag = true;
}
} else {
Process p = Runtime.getRuntime().exec(new String[]{"sh","-c","ps -ef | grep " + "soffice.bin"});
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream os = p.getInputStream();
byte[] b = new byte[256];
while (os.read(b) > 0) {
baos.write(b);
}
String s = baos.toString();
if (StringUtils.ordinalIndexOf(s, "soffice.bin", 3) > 0) {
String[] cmd ={"sh","-c","kill -15 `ps -ef|grep " + "soffice.bin" + "|awk 'NR==1{print $2}'`"};
Runtime.getRuntime().exec(cmd);
flag = true;
}
}
} catch (IOException e) {
logger.error("检测office进程异常", e);
}
return flag;
}
@PreDestroy
public void destroyOfficeManager(){
if (null != officeManager && officeManager.isRunning()) {

View File

@@ -4,6 +4,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.Objects;
public class DeleteFileUtil {
@@ -55,21 +56,22 @@ public class DeleteFileUtil {
boolean flag = true;
// 删除文件夹中的所有文件包括子目录
File[] files = dirFile.listFiles();
for (int i = 0; i < files.length; i++) {
for (int i = 0; i < Objects.requireNonNull(files).length; i++) {
// 删除子文件
if (files[i].isFile()) {
flag = DeleteFileUtil.deleteFile(files[i].getAbsolutePath());
if (!flag)
if (!flag) {
break;
}
// 删除子目录
else if (files[i].isDirectory()) {
flag = DeleteFileUtil.deleteDirectory(files[i]
.getAbsolutePath());
if (!flag)
}
} else if (files[i].isDirectory()) {
// 删除子目录
flag = DeleteFileUtil.deleteDirectory(files[i].getAbsolutePath());
if (!flag) {
break;
}
}
}
dirFile.delete();
if (!flag) {
LOGGER.info("删除目录失败!");
return false;

View File

@@ -1,14 +1,17 @@
package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import cn.keking.hutool.URLUtil;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.model.ReturnResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
/**
@@ -17,40 +20,29 @@ import java.util.UUID;
@Component
public class DownloadUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(DownloadUtils.class);
private final Logger logger = LoggerFactory.getLogger(DownloadUtils.class);
private String fileDir = ConfigConstants.getFileDir();
private final String fileDir = ConfigConstants.getFileDir();
@Autowired
private FileUtils fileUtils;
private final FileUtils fileUtils;
public DownloadUtils(FileUtils fileUtils) {
this.fileUtils = fileUtils;
}
private static final String URL_PARAM_FTP_USERNAME = "ftp.username";
private static final String URL_PARAM_FTP_PASSWORD = "ftp.password";
private static final String URL_PARAM_FTP_CONTROL_ENCODING = "ftp.control.encoding";
/**
* 一开始测试的时候发现有些文件没有下载下来,而有些可以;当时也是郁闷了好一阵,但是最终还是不得解
* 再次测试的时候通过前台对比url发现原来参数中有+号特殊字符存在,但是到后之后却变成了空格,突然恍然大悟
* 应该是转义出了问题url转义中会把+号当成空格来计算所以才会出现这种情况遂想要通过整体替换空格为加号因为url
* 中的参数部分是不会出现空格的但是文件名中就不好确定了所以只对url参数部分做替换
* 注: 针对URLEncoder.encode(s,charset)会将空格转成+的情况需要做下面的替换工作
* @param fileAttribute
* @return
* @param fileAttribute fileAttribute
* @param fileName 文件名
* @return 本地文件绝对路径
*/
public ReturnResponse<String> downLoad(FileAttribute fileAttribute, String fileName) {
String urlAddress = fileAttribute.getDecodedUrl();
String urlStr = fileAttribute.getUrl();
String type = fileAttribute.getSuffix();
ReturnResponse<String> response = new ReturnResponse<>(0, "下载成功!!!", "");
URL url = null;
try {
urlAddress = replacePlusMark(urlAddress);
urlAddress = encodeUrlParam(urlAddress);
// 因为tomcat不能处理'+'号,所以讲'+'号替换成'%20%'
urlAddress = urlAddress.replaceAll("\\+", "%20");
url = new URL(urlAddress);
} catch (MalformedURLException e) {
e.printStackTrace();
}
UUID uuid = UUID.randomUUID();
if (null == fileName) {
fileName = uuid+ "."+type;
@@ -63,35 +55,29 @@ public class DownloadUtils {
dirFile.mkdirs();
}
try {
if ("ftp".equals(url.getProtocol())) {
URL url = new URL(urlStr);
if (url.getProtocol() != null && url.getProtocol().toLowerCase().startsWith("http")) {
byte[] bytes = getBytesFromUrl(urlStr);
OutputStream os = new FileOutputStream(new File(realPath));
saveBytesToOutStream(bytes, os);
} else if (url.getProtocol() != null && "ftp".equals(url.getProtocol().toLowerCase())) {
String ftpUsername = fileUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME);
String ftpPassword = fileUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD);
String ftpControlEncoding = fileUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING);
FtpUtils.download(fileAttribute.getUrl(), realPath, ftpUsername, ftpPassword, ftpControlEncoding);
} else {
URLConnection connection = url.openConnection();
InputStream in = connection.getInputStream();
FileOutputStream os = new FileOutputStream(realPath);
byte[] buffer = new byte[4 * 1024];
int read;
while ((read = in.read(buffer)) > 0) {
os.write(buffer, 0, read);
}
os.close();
in.close();
response.setCode(1);
response.setContent(null);
response.setMsg("url不能识别url" + urlStr);
}
response.setContent(realPath);
// 同样针对类txt文件如果成功msg包含的是转换后的文件名
response.setMsg(fileName);
// txt转换文件编码为utf8
if("txt".equals(type)){
if(FileType.simText.equals(fileAttribute.getType())){
convertTextPlainFileCharsetToUtf8(realPath);
}
return response;
} catch (IOException e) {
LOGGER.error("文件下载失败", e);
logger.error("文件下载失败url{}", urlStr, e);
response.setCode(1);
response.setContent(null);
if (e instanceof FileNotFoundException) {
@@ -103,77 +89,51 @@ public class DownloadUtils {
}
}
/**
* 注:可能是原来因为前端通过encodeURI来编码的因为通过encodeURI编码+会被转成+号(亦即没有转)
* 而通过encodeURIComponent则会转成%2B这样URLDecoder是可以正确处理的所以也就没有必要在这里替换了
* 转换url参数部分的空格为加号(因为在url编解码的过程中出现+转为空格的情况)
* @param urlAddress
* @return
*/
private String replacePlusMark(String urlAddress) {
if (urlAddress.contains("?")) {
String nonParamStr = urlAddress.substring(0,urlAddress.indexOf("?") + 1);
String paramStr = urlAddress.substring(nonParamStr.length());
return nonParamStr + paramStr.replace(" ", "+");
public byte[] getBytesFromUrl(String urlStr) throws IOException {
InputStream is = getInputStreamFromUrl(urlStr);
if (is != null) {
return getBytesFromStream(is);
} else {
urlStr = URLUtil.normalize(urlStr, true, true);
is = getInputStreamFromUrl(urlStr);
if (is == null) {
logger.error("文件下载异常url{}", urlStr);
throw new IOException("文件下载异常url" + urlStr);
}
return getBytesFromStream(is);
}
return urlAddress;
}
/**
* 对最有一个路径进行转码
* @param urlAddress
* http://192.168.2.111:8013/demo/Handle中文.zip
* @return
*/
private String encodeUrlParam(String urlAddress) {
String newUrl = "";
public void saveBytesToOutStream(byte[] b, OutputStream os) throws IOException {
os.write(b);
os.close();
}
private InputStream getInputStreamFromUrl(String urlStr) {
try {
String path = "";
String param = "";
if (urlAddress.contains("?")) {
path = urlAddress.substring(0, urlAddress.indexOf("?"));
param = urlAddress.substring(urlAddress.indexOf("?"));
}else {
path = urlAddress;
URL url = new URL(urlStr);
URLConnection connection = url.openConnection();
if (connection instanceof HttpURLConnection) {
connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
}
String lastPath = path.substring(path.lastIndexOf("/") + 1);
String leftPath = path.substring(0, path.lastIndexOf("/") + 1);
String encodeLastPath = URLEncoder.encode(lastPath, "UTF-8");
newUrl += leftPath + encodeLastPath;
if (urlAddress.contains("?")) {
newUrl += param;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return connection.getInputStream();
} catch (IOException e) {
logger.warn("连接url异常url{}", urlStr);
return null;
}
return newUrl;
}
/**
* 因为jodConvert2.1不支持ms2013版本的office转换这里偷懒尝试看改一下文件类型让jodConvert2.1去
* 处理ms2013看结果如何如果问题很大的话只能采取其他方式如果没有问题暂时使用该版本来转换
* @param type
* @return
*/
private String dealWithMS2013(String type) {
String newType = null;
switch (type){
case "docx":
newType = "doc";
break;
case "xlsx":
newType = "doc";
break;
case "pptx":
newType = "ppt";
break;
default:
newType = type;
break;
private byte[] getBytesFromStream(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
return newType;
byte[] b = baos.toByteArray();
is.close();
baos.close();
return b;
}
/**
@@ -197,7 +157,7 @@ public class DownloadUtils {
if(encoding != null && !"UTF-8".equals(encoding)){
// 不为utf8,进行转码
File tmpUtf8File = new File(filePath+".utf8");
Writer writer = new OutputStreamWriter(new FileOutputStream(tmpUtf8File),"UTF-8");
Writer writer = new OutputStreamWriter(new FileOutputStream(tmpUtf8File), StandardCharsets.UTF_8);
Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile),encoding));
char[] buf = new char[1024];
int read;

View File

@@ -5,15 +5,12 @@ import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.service.cache.CacheService;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -26,33 +23,34 @@ import java.util.Map;
*/
@Component
public class FileUtils {
Logger log= LoggerFactory.getLogger(getClass());
@Autowired
CacheService cacheService;
private static final String DEFAULT_CONVERTER_CHARSET = System.getProperty("sun.jnu.encoding");
String fileDir = ConfigConstants.getFileDir();
private final String fileDir = ConfigConstants.getFileDir();
private final CacheService cacheService;
public FileUtils(CacheService cacheService) {
this.cacheService = cacheService;
}
/**
* 已转换过的文件集合(redis缓存)
* @return
* @return 已转换过的文件集合(缓存)
*/
public Map<String, String> listConvertedFiles() {
return cacheService.getPDFCache();
}
/**
* 已转换过的文件,根据文件名获取
* @return
* @return 已转换过的文件,根据文件名获取
*/
public String getConvertedFile(String key) {
return cacheService.getPDFCache(key);
}
/**
* 已将pdf转换成图片的图片本地路径
* @param key pdf本地路径
* @return
* @return 已将pdf转换成图片的图片本地相对路径
*/
public Integer getConvertedPdfImage(String key) {
return cacheService.getPdfImageCache(key);
@@ -61,11 +59,11 @@ public class FileUtils {
/**
* 查看文件类型(防止参数中存在.点号或者其他特殊字符,所以先抽取文件名,然后再获取文件类型)
*
* @param url
* @return
* @param url url
* @return 文件类型
*/
public FileType typeFromUrl(String url) {
String nonPramStr = url.substring(0, url.indexOf("?") != -1 ? url.indexOf("?") : url.length());
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return typeFromFileName(fileName);
}
@@ -89,40 +87,32 @@ public class FileUtils {
if (Arrays.asList(media).contains(fileType.toLowerCase())) {
return FileType.media;
}
if("pdf".equalsIgnoreCase(fileType)){
if ("pdf".equalsIgnoreCase(fileType)) {
return FileType.pdf;
}
if ("dwg".equalsIgnoreCase(fileType)) {
return FileType.cad;
}
return FileType.other;
}
/**
* 从url中剥离出文件名
* @param url
* 格式如http://keking.ufile.ucloud.com.cn/20171113164107_月度绩效表模板(新).xls?UCloudPublicKey=ucloudtangshd@weifenf.com14355492830001993909323&Expires=&Signature=I D1NOFtAJSPT16E6imv6JWuq0k=
* @return
* @return 文件名
*/
public String getFileNameFromURL(String url) {
// 因为url的参数中可能会存在/的情况所以直接url.lastIndexOf("/")会有问题
// 所以先从处将url截断然后运用url.lastIndexOf("/")获取文件名
String noQueryUrl = url.substring(0, url.indexOf("?") != -1 ? url.indexOf("?"): url.length());
String fileName = noQueryUrl.substring(noQueryUrl.lastIndexOf("/") + 1);
return fileName;
String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?"): url.length());
return noQueryUrl.substring(noQueryUrl.lastIndexOf("/") + 1);
}
/**
* 获取文件后缀
* @param fileName
* @return
*/
public String getSuffixFromFileName(String fileName) {
String suffix = fileName.substring(fileName.lastIndexOf("."));
return suffix;
}
/**
* 从路径中获取
* 从路径中获取文件
* @param path
* 类似这种C:\Users\yudian-it\Downloads
* @return
* @return 文件名
*/
public String getFileNameFromPath(String path) {
return path.substring(path.lastIndexOf(File.separator) + 1);
@@ -165,21 +155,26 @@ public class FileUtils {
/**
* 获取相对路径
* @param absolutePath
* @return
* @param absolutePath 绝对路径
* @return 相对路径
*/
public String getRelativePath(String absolutePath) {
return absolutePath.substring(fileDir.length());
}
/**
* 添加转换后PDF缓存
* @param fileName pdf文件名
* @param value 缓存相对路径
*/
public void addConvertedFile(String fileName, String value){
cacheService.putPDFCache(fileName, value);
}
/**
*
* @param pdfFilePath
* @param num
* 添加转换后图片组缓存
* @param pdfFilePath pdf文件绝对路径
* @param num 图片张数
*/
public void addConvertedPdfImage(String pdfFilePath, int num){
cacheService.putPdfImageCache(pdfFilePath, num);
@@ -187,40 +182,38 @@ public class FileUtils {
/**
* 获取redis中压缩包内图片文件
* @param fileKey
* @return
* @param fileKey fileKey
* @return 图片文件访问url列表
*/
public List getRedisImgUrls(String fileKey){
public List<String> getImgCache(String fileKey){
return cacheService.getImgCache(fileKey);
}
/**
* 设置redis中压缩包内图片文件
* @param fileKey
* @param imgs
* @param fileKey fileKey
* @param imgs 图片文件访问url列表
*/
public void setRedisImgUrls(String fileKey,List imgs){
public void putImgCache(String fileKey,List<String> imgs){
cacheService.putImgCache(fileKey, imgs);
}
/**
* 判断文件编码格式
* @param path
* @return
* @param path 绝对路径
* @return 编码格式
*/
public String getFileEncodeUTFGBK(String path){
String enc = Charset.forName("GBK").name();
File file = new File(path);
InputStream in= null;
InputStream in;
try {
in = new FileInputStream(file);
byte[] b = new byte[3];
in.read(b);
in.close();
if (b[0] == -17 && b[1] == -69 && b[2] == -65) {
enc = Charset.forName("UTF-8").name();
enc = StandardCharsets.UTF_8.name();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
@@ -230,13 +223,12 @@ public class FileUtils {
/**
* 对转换后的文件进行操作(改变编码方式)
* @param outFilePath
* @param outFilePath 文件绝对路径
*/
public void doActionConvertedFile(String outFilePath) {
StringBuffer sb = new StringBuffer();
String charset = ConfigConstants.getConvertedFileCharset();
StringBuilder sb = new StringBuilder();
try (InputStream inputStream = new FileInputStream(outFilePath);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charset))){
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, DEFAULT_CONVERTER_CHARSET))){
String line;
while(null != (line = reader.readLine())){
if (line.contains("charset=gb2312")) {
@@ -247,54 +239,49 @@ public class FileUtils {
// 添加sheet控制头
sb.append("<script src=\"js/jquery-3.0.0.min.js\" type=\"text/javascript\"></script>");
sb.append("<script src=\"js/excel.header.js\" type=\"text/javascript\"></script>");
sb.append("<link rel=\"stylesheet\" href=\"http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css\">");
} catch (FileNotFoundException e) {
e.printStackTrace();
sb.append("<link rel=\"stylesheet\" href=\"bootstrap/css/bootstrap.min.css\">");
} catch (IOException e) {
e.printStackTrace();
}
// 重新写入文件
try(FileOutputStream fos = new FileOutputStream(outFilePath);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"))) {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8))) {
writer.write(sb.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取文件后缀
* @param url
* @return
* @param url url
* @return 文件后缀
*/
private String suffixFromUrl(String url) {
String nonPramStr = url.substring(0, url.indexOf("?") != -1 ? url.indexOf("?") : url.length());
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return suffixFromFileName(fileName);
}
private String suffixFromFileName(String fileName) {
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
return fileType;
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
/**
* 获取url中的参数
* @param url
* @param name
* @return
* @param url url
* @param name 参数名
* @return 参数值
*/
public String getUrlParameterReg(String url, String name) {
Map<String, String> mapRequest = new HashMap();
Map<String, String> mapRequest = new HashMap<>();
String strUrlParam = truncateUrlPage(url);
if (strUrlParam == null) {
return "";
}
//每个键值为一组
String[] arrSplit=strUrlParam.split("[&]");
for(String strSplit:arrSplit) {
String[] arrSplitEqual= strSplit.split("[=]");
String[] arrSplit = strUrlParam.split("[&]");
for(String strSplit : arrSplit) {
String[] arrSplitEqual = strSplit.split("[=]");
//解析出键值
if(arrSplitEqual.length > 1) {
//正确解析
@@ -326,28 +313,25 @@ public class FileUtils {
return strAllParam;
}
/**
* 获取文件属性
* @param url url
* @return 文件属性
*/
public FileAttribute getFileAttribute(String url) {
String decodedUrl = null;
try {
decodedUrl = URLDecoder.decode(url, "utf-8");
} catch (UnsupportedEncodingException e){
log.error("url解码失败");
}
String fileName;
FileType type;
String suffix;
String fullFileName = getUrlParameterReg(decodedUrl, "fullfilename");
String fullFileName = getUrlParameterReg(url, "fullfilename");
if (!StringUtils.isEmpty(fullFileName)) {
fileName = fullFileName;
type = typeFromFileName(fileName);
suffix = suffixFromFileName(fileName);
} else {
fileName = getFileNameFromURL(decodedUrl);
fileName = getFileNameFromURL(url);
type = typeFromUrl(url);
suffix = suffixFromUrl(url);
}
return new FileAttribute(type,suffix,fileName,url,decodedUrl);
return new FileAttribute(type,suffix,fileName,url);
}
}

View File

@@ -11,6 +11,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
/**
* @auther: chenjh
@@ -47,7 +48,7 @@ public class FtpUtils {
FTPClient ftpClient = connect(host, port, username, password, controlEncoding);
OutputStream outputStream = new FileOutputStream(localFilePath);
ftpClient.enterLocalPassiveMode();
boolean downloadResult = ftpClient.retrieveFile(new String(remoteFilePath.getBytes(controlEncoding), "ISO-8859-1"), outputStream);
boolean downloadResult = ftpClient.retrieveFile(new String(remoteFilePath.getBytes(controlEncoding), StandardCharsets.ISO_8859_1), outputStream);
LOGGER.debug("FTP download result {}", downloadResult);
outputStream.flush();
outputStream.close();

View File

@@ -1,7 +1,8 @@
package cn.keking.utils;
import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
/**
@@ -9,38 +10,18 @@ import java.io.File;
*/
@Component
public class OfficeToPdf {
/**
* 获取OpenOffice.org 3的安装目录
*
* @return OpenOffice.org 3的安装目录
*/
@Autowired
ConverterUtils converterUtils;
/**
* 使Office2003-2007全部格式的文档(.doc|.docx|.xls|.xlsx|.ppt|.pptx) 转化为pdf文件<br>
*
* @param inputFilePath
* 源文件路径,如:"e:/test.docx"
* @param outputFilePath
* 目标文件路径,如:"e:/test_docx.pdf"
* @return
*/
public boolean openOfficeToPDF(String inputFilePath, String outputFilePath) {
return office2pdf(inputFilePath, outputFilePath);
private final ConverterUtils converterUtils;
public OfficeToPdf(ConverterUtils converterUtils) {
this.converterUtils = converterUtils;
}
public void openOfficeToPDF(String inputFilePath, String outputFilePath) {
office2pdf(inputFilePath, outputFilePath);
}
/**
* 转换文件
*
* @param inputFile
* @param outputFilePath_end
* @param inputFilePath
* @param outputFilePath
* @param converter
*/
public static void converterFile(File inputFile, String outputFilePath_end,
String inputFilePath, String outputFilePath,
OfficeDocumentConverter converter) {
File outputFile = new File(outputFilePath_end);
// 假如目标路径不存在,则新建该路径
@@ -50,17 +31,8 @@ public class OfficeToPdf {
converter.convert(inputFile, outputFile);
}
/**
* 使Office2003-2007全部格式的文档(.doc|.docx|.xls|.xlsx|.ppt|.pptx) 转化为pdf文件
*
* @param inputFilePath
* 源文件路径,如:"e:/test.docx"
* @param outputFilePath
* 目标文件路径,如:"e:/test_docx.pdf"
* @return
*/
public boolean office2pdf(String inputFilePath, String outputFilePath) {
boolean flag = false;
public void office2pdf(String inputFilePath, String outputFilePath) {
OfficeDocumentConverter converter = converterUtils.getDocumentConverter();
if (null != inputFilePath) {
File inputFile = new File(inputFilePath);
@@ -68,43 +40,23 @@ public class OfficeToPdf {
if (null == outputFilePath) {
// 转换后的文件路径
String outputFilePath_end = getOutputFilePath(inputFilePath);
if (inputFile.exists()) {// 找不到源文件, 则返回
converterFile(inputFile, outputFilePath_end, inputFilePath,
outputFilePath, converter);
flag = true;
if (inputFile.exists()) {
// 找不到源文件, 则返回
converterFile(inputFile, outputFilePath_end,converter);
}
} else {
if (inputFile.exists()) {// 找不到源文件, 则返回
converterFile(inputFile, outputFilePath, inputFilePath,
outputFilePath, converter);
flag = true;
if (inputFile.exists()) {
// 找不到源文件, 则返回
converterFile(inputFile, outputFilePath, converter);
}
}
// officeManager.stop();
} else {
flag = false;
}
return flag;
}
/**
* 获取输出文件
*
* @param inputFilePath
* @return
*/
public static String getOutputFilePath(String inputFilePath) {
String outputFilePath = inputFilePath.replaceAll("."
+ getPostfix(inputFilePath), ".pdf");
return outputFilePath;
return inputFilePath.replaceAll("."+ getPostfix(inputFilePath), ".pdf");
}
/**
* 获取inputFilePath的后缀名"e:/test.pptx"的后缀名为:"pptx"
*
* @param inputFilePath
* @return
*/
public static String getPostfix(String inputFilePath) {
return inputFilePath.substring(inputFilePath.lastIndexOf(".") + 1);
}

View File

@@ -6,32 +6,44 @@ import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.tools.imageio.ImageIOUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
@Component
public class PdfUtils {
private final Logger LOGGER = LoggerFactory.getLogger(PdfUtils.class);
private final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
@Autowired
FileUtils fileUtils;
private final FileUtils fileUtils;
public List<String> pdf2jpg(String pdfFilePath, String pdfName, String url) {
@Value("${server.tomcat.uri-encoding:UTF-8}")
private String uriEncoding;
public PdfUtils(FileUtils fileUtils) {
this.fileUtils = fileUtils;
}
public List<String> pdf2jpg(String pdfFilePath, String pdfName, String baseUrl) {
List<String> imageUrls = new ArrayList<>();
Integer imageCount = fileUtils.getConvertedPdfImage(pdfFilePath);
String imageFileSuffix = ".jpg";
// https://8个字符 http://7个字符 从这后面开始出现的第一个/就是当前file.Dir下的根目录
int index1 = url.indexOf("/", 8);
String pdfFolder = pdfName.substring(0, pdfName.length() - 4);
String urlPrefix = url.substring(0, index1 + 1) + pdfFolder;
if (imageCount != null && imageCount.intValue() > 0) {
String urlPrefix = null;
try {
urlPrefix = baseUrl + URLEncoder.encode(URLEncoder.encode(pdfFolder, uriEncoding).replaceAll("\\+", "%20"), uriEncoding);
} catch (UnsupportedEncodingException e) {
logger.error("UnsupportedEncodingException", e);
urlPrefix = baseUrl + pdfFolder;
}
if (imageCount != null && imageCount > 0) {
for (int i = 0; i < imageCount ; i++)
imageUrls.add(urlPrefix + "/" + i + imageFileSuffix);
return imageUrls;
@@ -59,7 +71,7 @@ public class PdfUtils {
doc.close();
fileUtils.addConvertedPdfImage(pdfFilePath, pageCount);
} catch (IOException e) {
LOGGER.error("Convert pdf to jpg exception", e);
logger.error("Convert pdf to jpg exception, pdfFilePath{}", pdfFilePath, e);
}
return imageUrls;
}

View File

@@ -4,7 +4,6 @@ import cn.keking.config.ConfigConstants;
import cn.keking.service.cache.CacheService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@@ -14,21 +13,25 @@ import org.springframework.stereotype.Component;
* @since: 2019/6/11 7:45
*/
@Component
@ConditionalOnExpression("'${cache.clean:false}'.equals('true')")
@ConditionalOnExpression("'${cache.clean.enabled:false}'.equals('true')")
public class ShedulerClean {
private static final Logger LOGGER = LoggerFactory.getLogger(ShedulerClean.class);
private final Logger logger = LoggerFactory.getLogger(ShedulerClean.class);
@Autowired
private CacheService cacheService;
private final CacheService cacheService;
private String fileDir = ConfigConstants.getFileDir();
public ShedulerClean(CacheService cacheService) {
this.cacheService = cacheService;
}
@Scheduled(cron = "0 0 3 * * ?") //每晚3点执行一次
private final String fileDir = ConfigConstants.getFileDir();
//默认每晚3点执行一次
@Scheduled(cron = "${cache.clean.cron:0 0 3 * * ?}")
public void clean() {
LOGGER.info("Cache clean start");
logger.info("Cache clean start");
cacheService.cleanCache();
DeleteFileUtil.deleteDirectory(fileDir);
LOGGER.info("Cache clean end");
logger.info("Cache clean end");
}
}

View File

@@ -1,26 +0,0 @@
package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 读取类文本文件
* @author yudian-it
* @date 2017/12/13
*/
@Component
public class SimTextUtil {
@Autowired
private FileUtils fileUtils;
@Autowired
private DownloadUtils downloadUtils;
public ReturnResponse<String> readSimText(String url, String fileName){
FileAttribute fileAttribute = fileUtils.getFileAttribute(url);
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileName);
return response;
}
}

View File

@@ -2,6 +2,7 @@ package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileType;
import cn.keking.web.filter.BaseUrlFilter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.junrar.Archive;
@@ -13,9 +14,7 @@ import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import java.io.*;
import java.math.BigDecimal;
@@ -36,33 +35,21 @@ import java.util.regex.Pattern;
public class ZipReader {
static Pattern pattern = Pattern.compile("^\\d+");
@Autowired
FileUtils fileUtils;
String fileDir = ConfigConstants.getFileDir();
private final FileUtils fileUtils;
ExecutorService executors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private final String fileDir = ConfigConstants.getFileDir();
private final ExecutorService executors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public ZipReader(FileUtils fileUtils) {
this.fileUtils = fileUtils;
}
/**
* 读取压缩文件
* 文件压缩到统一目录fileDir下并且命名使用压缩文件名+文件名因为文件名
* 可能会重复(在系统中对于同一种类型的材料压缩文件内的文件是一样的,如果文件名
* 重复,那么这里会被覆盖[同一个压缩文件中的不同目录中的相同文件名暂时不考虑])
* <b>注:</b>
* <p>
* 文件名命名中的参数的说明:
* 1.archiveName为避免解压的文件中有重名的文件会彼此覆盖所以加上了archiveName因为在ufile中archiveName
* 是不会重复的。
* 2.level这里层级结构的列表我是通过一个map来构造的map的key是文件的名字值是对应的文件这样每次向map中
* 加入节点的时候都会获取父节点是否存在存在则会获取父节点的value并将当前节点加入到父节点的childList中(这里利用
* 的是java语言的引用的特性)。
* </p>
* @param filePath
*/
public String readZipFile(String filePath,String fileKey) {
String archiveSeparator = "/";
Map<String, FileNode> appender = Maps.newHashMap();
List imgUrls=Lists.newArrayList();
String baseUrl= (String) RequestContextHolder.currentRequestAttributes().getAttribute("baseUrl",0);
List<String> imgUrls = Lists.newArrayList();
String baseUrl = BaseUrlFilter.getBaseUrl();
String archiveFileName = fileUtils.getFileNameFromPath(filePath);
try {
ZipFile zipFile = new ZipFile(filePath, fileUtils.getFileEncodeUTFGBK(filePath));
@@ -88,13 +75,13 @@ public class ZipReader {
if (type.equals(FileType.picture)){//添加图片文件到图片列表
imgUrls.add(baseUrl+childName);
}
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory,fileKey);
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey);
addNodes(appender, parentName, node);
appender.put(childName, node);
}
// 开启新的线程处理文件解压
executors.submit(new ZipExtractorWorker(entriesToBeExtracted, zipFile, filePath));
fileUtils.setRedisImgUrls(fileKey,imgUrls);
fileUtils.putImgCache(fileKey,imgUrls);
return new ObjectMapper().writeValueAsString(appender.get(""));
} catch (IOException e) {
e.printStackTrace();
@@ -102,25 +89,19 @@ public class ZipReader {
}
}
/**
* 排序zipEntries(对原来列表倒序)
* @param entries
*/
private Enumeration<ZipArchiveEntry> sortZipEntries(Enumeration<ZipArchiveEntry> entries) {
List<ZipArchiveEntry> sortedEntries = Lists.newArrayList();
while(entries.hasMoreElements()){
sortedEntries.add(entries.nextElement());
}
Collections.sort(sortedEntries, Comparator.comparingInt(o -> o.getName().length()));
sortedEntries.sort(Comparator.comparingInt(o -> o.getName().length()));
return Collections.enumeration(sortedEntries);
}
public String unRar(String filePath,String fileKey){
Map<String, FileNode> appender = Maps.newHashMap();
List imgUrls=Lists.newArrayList();
String baseUrl= (String) RequestContextHolder.currentRequestAttributes().getAttribute("baseUrl",0);
List<String> imgUrls = Lists.newArrayList();
String baseUrl = BaseUrlFilter.getBaseUrl();
try {
Archive archive = new Archive(new FileInputStream(new File(filePath)));
List<FileHeader> headers = archive.getFileHeaders();
@@ -143,36 +124,28 @@ public class ZipReader {
headersToBeExtracted.add(Collections.singletonMap(childName, header));
}
String parentName = getLast2FileName(fullName, "\\", archiveFileName);
FileType type=fileUtils.typeFromUrl(childName);
FileType type = fileUtils.typeFromUrl(childName);
if (type.equals(FileType.picture)){//添加图片文件到图片列表
imgUrls.add(baseUrl+childName);
}
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory,fileKey);
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey);
addNodes(appender, parentName, node);
appender.put(childName, node);
}
executors.submit(new RarExtractorWorker(headersToBeExtracted, archive, filePath));
fileUtils.setRedisImgUrls(fileKey,imgUrls);
fileUtils.putImgCache(fileKey,imgUrls);
return new ObjectMapper().writeValueAsString(appender.get(""));
} catch (RarException e) {
e.printStackTrace();
} catch (IOException e) {
} catch (RarException | IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 解压7z文件
* @param filePath
* @param fileKey
* @return
*/
public String read7zFile(String filePath,String fileKey) {
String archiveSeparator = "/";
Map<String, FileNode> appender = Maps.newHashMap();
List imgUrls=Lists.newArrayList();
String baseUrl= (String) RequestContextHolder.currentRequestAttributes().getAttribute("baseUrl",0);
List<String> imgUrls = Lists.newArrayList();
String baseUrl= BaseUrlFilter.getBaseUrl();
String archiveFileName = fileUtils.getFileNameFromPath(filePath);
try {
SevenZFile zipFile = new SevenZFile(new File(filePath));
@@ -198,41 +171,35 @@ public class ZipReader {
if (type.equals(FileType.picture)){//添加图片文件到图片列表
imgUrls.add(baseUrl+childName);
}
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory,fileKey);
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey);
addNodes(appender, parentName, node);
appender.put(childName, node);
}
// 开启新的线程处理文件解压
executors.submit(new SevenZExtractorWorker(entriesToBeExtracted, filePath));
fileUtils.setRedisImgUrls(fileKey,imgUrls);
fileUtils.putImgCache(fileKey,imgUrls);
return new ObjectMapper().writeValueAsString(appender.get(""));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* 排序7ZEntries(对原来列表倒序)
* @param entries
*/
private Enumeration<SevenZArchiveEntry> sortSevenZEntries(Iterable<SevenZArchiveEntry> entries) {
List<SevenZArchiveEntry> sortedEntries = Lists.newArrayList();
Iterator<SevenZArchiveEntry> iterator = entries.iterator();
while(iterator.hasNext()){
sortedEntries.add(iterator.next());
for (SevenZArchiveEntry entry : entries) {
sortedEntries.add(entry);
}
// Collections.sort(sortedEntries, Comparator.comparingInt(o -> o.getName().length()));
return Collections.enumeration(sortedEntries);
}
private void addNodes(Map<String, FileNode> appender, String parentName, FileNode node) {
if (appender.containsKey(parentName)) {
appender.get(parentName).getChildList().add(node);
Collections.sort(appender.get(parentName).getChildList(), sortComparator);
// appender.get(parentName).getChildList().sort((final FileNode h1, final FileNode h2) -> h1.getOriginName().compareTo(h2.getOriginName()));//排序
}else { // 根节点
appender.get(parentName).getChildList().sort(sortComparator);
} else {
// 根节点
FileNode nodeRoot = new FileNode(parentName, parentName, "", new ArrayList<>(), true);
nodeRoot.getChildList().add(node);
appender.put("", nodeRoot);
@@ -254,15 +221,6 @@ public class ZipReader {
return sortedHeaders;
}
/**
* 获取倒数第二个文件(夹)名
* @param fullName
* @param seperator
* 压缩文件解压后不同的压缩格式分隔符不一样zip是/而rar是\
* @param rootName
* 根目录名:如果倒数第二个路径为空那么赋值为rootName
* @return
*/
private static String getLast2FileName(String fullName, String seperator, String rootName) {
if (fullName.endsWith(seperator)) {
fullName = fullName.substring(0, fullName.length()-1);
@@ -270,34 +228,27 @@ public class ZipReader {
// 1.获取剩余部分
int endIndex = fullName.lastIndexOf(seperator);
String leftPath = fullName.substring(0, endIndex == -1 ? 0 : endIndex);
if (null != leftPath && leftPath.length() > 1) {
if (leftPath.length() > 1) {
// 2.获取倒数第二个
return getLastFileName(leftPath, seperator);
}else {
} else {
return rootName;
}
}
/**
* 获取最后一个文件(夹)的名字
* @param fullName
* @param seperator
* 压缩文件解压后不同的压缩格式分隔符不一样zip是/而rar是\
* @return
*/
private static String getLastFileName(String fullName, String seperator) {
if (fullName.endsWith(seperator)) {
fullName = fullName.substring(0, fullName.length()-1);
}
String newName = fullName;
if (null != fullName && fullName.contains(seperator)) {
if (fullName.contains(seperator)) {
newName = fullName.substring(fullName.lastIndexOf(seperator) + 1);
}
return newName;
}
public static Comparator<FileNode> sortComparator = new Comparator<FileNode>() {
Collator cmp = Collator.getInstance(Locale.US);
final Collator cmp = Collator.getInstance(Locale.US);
@Override
public int compare(FileNode o1, FileNode o2) {
// 判断两个对比对象是否是开头包含数字,如果包含数字则获取数字并按数字真正大小进行排序
@@ -320,21 +271,16 @@ public class ZipReader {
return null;
}
/**
* 文件节点(区分文件上下级)
*/
public class FileNode{
public static class FileNode {
private String originName;
private String fileName;
private String parentFileName;
private boolean directory;
private String fileKey;//用于图片预览时寻址
//用于图片预览时寻址
private String fileKey;
private List<FileNode> childList;
public FileNode() {
}
public FileNode(String originName, String fileName, String parentFileName, List<FileNode> childList, boolean directory) {
this.originName = originName;
this.fileName = fileName;
@@ -409,14 +355,11 @@ public class ZipReader {
}
}
/**
* Zip文件抽取线程
*/
class ZipExtractorWorker implements Runnable {
private List<Map<String, ZipArchiveEntry>> entriesToBeExtracted;
private ZipFile zipFile;
private String filePath;
private final List<Map<String, ZipArchiveEntry>> entriesToBeExtracted;
private final ZipFile zipFile;
private final String filePath;
public ZipExtractorWorker(List<Map<String, ZipArchiveEntry>> entriesToBeExtracted, ZipFile zipFile, String filePath) {
this.entriesToBeExtracted = entriesToBeExtracted;
@@ -426,7 +369,6 @@ public class ZipReader {
@Override
public void run() {
System.out.println("解析压缩文件开始《《《《《《《《《《《《《《《《《《《《《《《");
for (Map<String, ZipArchiveEntry> entryMap : entriesToBeExtracted) {
String childName = entryMap.keySet().iterator().next();
ZipArchiveEntry entry = entryMap.values().iterator().next();
@@ -444,15 +386,8 @@ public class ZipReader {
if (new File(filePath).exists()) {
new File(filePath).delete();
}
System.out.println("解析压缩文件结束《《《《《《《《《《《《《《《《《《《《《《《");
}
/**
* 读取压缩文件并写入到fileDir文件夹下
* @param childName
* @param zipFile
*/
private void extractZipFile(String childName, InputStream zipFile) {
String outPath = fileDir + childName;
try (OutputStream ot = new FileOutputStream(outPath)){
@@ -461,21 +396,16 @@ public class ZipReader {
while ((-1 != (len = zipFile.read(inByte)))){
ot.write(inByte, 0, len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 7z文件抽取线程
*/
class SevenZExtractorWorker implements Runnable {
private List<Map<String, SevenZArchiveEntry>> entriesToBeExtracted;
private String filePath;
private final List<Map<String, SevenZArchiveEntry>> entriesToBeExtracted;
private final String filePath;
public SevenZExtractorWorker(List<Map<String, SevenZArchiveEntry>> entriesToBeExtracted, String filePath) {
this.entriesToBeExtracted = entriesToBeExtracted;
@@ -484,7 +414,6 @@ public class ZipReader {
@Override
public void run() {
System.out.println("解析压缩文件开始《《《《《《《《《《《《《《《《《《《《《《《");
try {
SevenZFile sevenZFile = new SevenZFile(new File(filePath));
SevenZArchiveEntry entry = sevenZFile.getNextEntry();
@@ -494,7 +423,7 @@ public class ZipReader {
continue;
}
String childName = "default_file";
SevenZArchiveEntry entry1 = null;
SevenZArchiveEntry entry1;
for (Map<String, SevenZArchiveEntry> entryMap : entriesToBeExtracted) {
childName = entryMap.keySet().iterator().next();
entry1 = entryMap.values().iterator().next();
@@ -510,8 +439,6 @@ public class ZipReader {
entry = sevenZFile.getNextEntry();
}
sevenZFile.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
@@ -519,20 +446,16 @@ public class ZipReader {
if (new File(filePath).exists()) {
new File(filePath).delete();
}
System.out.println("解析压缩文件结束《《《《《《《《《《《《《《《《《《《《《《《");
}
}
/**
* Rar文件抽取
*/
class RarExtractorWorker implements Runnable {
private List<Map<String, FileHeader>> headersToBeExtracted;
private Archive archive;
private final List<Map<String, FileHeader>> headersToBeExtracted;
private final Archive archive;
/**
* 用以删除源文件
*/
private String filePath;
private final String filePath;
public RarExtractorWorker(List<Map<String, FileHeader>> headersToBeExtracted, Archive archive, String filePath) {
this.headersToBeExtracted = headersToBeExtracted;
@@ -542,7 +465,6 @@ public class ZipReader {
@Override
public void run() {
System.out.println("解析压缩文件开始《《《《《《《《《《《《《《《《《《《《《《《");
for (Map<String, FileHeader> entryMap : headersToBeExtracted) {
String childName = entryMap.keySet().iterator().next();
extractRarFile(childName, entryMap.values().iterator().next(), archive);
@@ -555,24 +477,13 @@ public class ZipReader {
if (new File(filePath).exists()) {
new File(filePath).delete();
}
System.out.println("解析压缩文件结束《《《《《《《《《《《《《《《《《《《《《《《");
}
/**
* 抽取rar文件到指定目录下
* @param childName
* @param header
* @param archive
*/
private void extractRarFile(String childName, FileHeader header, Archive archive) {
String outPath = fileDir + childName;
try(OutputStream ot = new FileOutputStream(outPath)) {
archive.extractFile(header, ot);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (RarException e) {
} catch (IOException | RarException e) {
e.printStackTrace();
}
}

View File

@@ -6,20 +6,20 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import cn.keking.model.ReturnResponse;
import cn.keking.utils.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.Objects;
/**
*
@@ -28,34 +28,43 @@ import java.util.UUID;
*/
@RestController
public class FileController {
String fileDir = ConfigConstants.getFileDir();
@Autowired
FileUtils fileUtils;
String demoDir = "demo";
String demoPath = demoDir + File.separator;
private final Logger logger = LoggerFactory.getLogger(FileController.class);
private final String fileDir = ConfigConstants.getFileDir();
private final String demoDir = "demo";
private final String demoPath = demoDir + File.separator;
@RequestMapping(value = "fileUpload", method = RequestMethod.POST)
public String fileUpload(@RequestParam("file") MultipartFile file,
HttpServletRequest request) throws JsonProcessingException {
public String fileUpload(@RequestParam("file") MultipartFile file) throws JsonProcessingException {
// 获取文件名
String fileName = file.getOriginalFilename();
// 判断该文件类型是否有上传过,如果上传过则提示不允许再次上传
if (existsTypeFile(fileName)) {
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "每一种类型只可以上传一个文件,请先删除原有文件再次上传", null));
//判断是否为IE浏览器的文件名IE浏览器下文件名会带有盘符信息
// Check for Unix-style path
int unixSep = fileName.lastIndexOf('/');
// Check for Windows-style path
int winSep = fileName.lastIndexOf('\\');
// Cut off at latest possible point
int pos = (Math.max(winSep, unixSep));
if (pos != -1) {
fileName = fileName.substring(pos + 1);
}
// 判断是否存在同名文件
if (existsFile(fileName)) {
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "存在同名文件,请先删除原有文件再次上传", null));
}
File outFile = new File(fileDir + demoPath);
if (!outFile.exists()) {
outFile.mkdirs();
}
try(InputStream in = file.getInputStream();
OutputStream ot = new FileOutputStream(fileDir + demoPath + fileName)){
byte[] buffer = new byte[1024];
int len;
while ((-1 != (len = in.read(buffer)))) {
ot.write(buffer, 0, len);
}
logger.info("上传文件:{}", fileDir + demoPath + fileName);
try(InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream(fileDir + demoPath + fileName)) {
StreamUtils.copy(in, out);
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(0, "SUCCESS", null));
} catch (IOException e) {
e.printStackTrace();
logger.error("文件上传失败", e);
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "FAILURE", null));
}
}
@@ -66,6 +75,7 @@ public class FileController {
fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
}
File file = new File(fileDir + demoPath + fileName);
logger.info("删除文件:{}", file.getAbsolutePath());
if (file.exists()) {
file.delete();
}
@@ -77,36 +87,13 @@ public class FileController {
List<Map<String, String>> list = Lists.newArrayList();
File file = new File(fileDir + demoPath);
if (file.exists()) {
Arrays.stream(file.listFiles()).forEach(file1 -> list.add(ImmutableMap.of("fileName", demoDir + "/" + file1.getName())));
Arrays.stream(Objects.requireNonNull(file.listFiles())).forEach(file1 -> list.add(ImmutableMap.of("fileName", demoDir + "/" + file1.getName())));
}
return new ObjectMapper().writeValueAsString(list);
}
private String getFileName(String name) {
String suffix = name.substring(name.lastIndexOf("."));
String nameNoSuffix = name.substring(0, name.lastIndexOf("."));
String uuid = UUID.randomUUID().toString();
return uuid + "-" + nameNoSuffix + suffix;
}
/**
* 是否存在该类型的文件
* @return
* @param fileName
*/
private boolean existsTypeFile(String fileName) {
boolean result = false;
String suffix = fileUtils.getSuffixFromFileName(fileName);
File file = new File(fileDir + demoPath);
if (file.exists()) {
for(File file1 : file.listFiles()){
String existsFileSuffix = fileUtils.getSuffixFromFileName(file1.getName());
if (suffix.equals(existsFileSuffix)) {
result = true;
break;
}
}
}
return result;
private boolean existsFile(String fileName) {
File file = new File(fileDir + demoPath + fileName);
return file.exists();
}
}

View File

@@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class IndexController {
@RequestMapping(value = "index", method = RequestMethod.GET)
@RequestMapping(value = "/index", method = RequestMethod.GET)
public String go2Index(){
return "index";
}

View File

@@ -1,18 +1,17 @@
package cn.keking.web.controller;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.service.FilePreview;
import cn.keking.service.FilePreviewFactory;
import cn.keking.service.cache.CacheService;
import cn.keking.utils.DownloadUtils;
import cn.keking.utils.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@@ -20,7 +19,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.*;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.List;
@@ -30,42 +29,43 @@ import java.util.List;
@Controller
public class OnlinePreviewController {
private static final Logger LOGGER = LoggerFactory.getLogger(OnlinePreviewController.class);
private final Logger logger = LoggerFactory.getLogger(OnlinePreviewController.class);
@Autowired
FilePreviewFactory previewFactory;
private final FilePreviewFactory previewFactory;
@Autowired
CacheService cacheService;
private final CacheService cacheService;
@Autowired
private FileUtils fileUtils;
private final FileUtils fileUtils;
/**
* @param url
* @param model
* @return
*/
@RequestMapping(value = "onlinePreview", method = RequestMethod.GET)
private final DownloadUtils downloadUtils;
public OnlinePreviewController(FilePreviewFactory filePreviewFactory,
FileUtils fileUtils,
CacheService cacheService,
DownloadUtils downloadUtils) {
this.previewFactory = filePreviewFactory;
this.fileUtils = fileUtils;
this.cacheService = cacheService;
this.downloadUtils = downloadUtils;
}
@RequestMapping(value = "/onlinePreview")
public String onlinePreview(String url, Model model, HttpServletRequest req) {
FileAttribute fileAttribute = fileUtils.getFileAttribute(url);
req.setAttribute("fileKey", req.getParameter("fileKey"));
model.addAttribute("pdfDownloadDisable", ConfigConstants.getPdfDownloadDisable());
model.addAttribute("officePreviewType", req.getParameter("officePreviewType"));
model.addAttribute("originUrl", req.getRequestURL().toString());
FilePreview filePreview = previewFactory.get(fileAttribute);
logger.info("预览文件url{}previewType{}", url, fileAttribute.getType());
return filePreview.filePreviewHandle(url, model, fileAttribute);
}
/**
* 多图片切换预览
*
* @param model
* @param req
* @return
* @throws UnsupportedEncodingException
*/
@RequestMapping(value = "picturesPreview", method = RequestMethod.GET)
public String picturesPreview(String urls, String currentUrl, Model model, HttpServletRequest req) throws UnsupportedEncodingException {
@RequestMapping(value = "picturesPreview")
public String picturesPreview(Model model, HttpServletRequest req) throws UnsupportedEncodingException {
String urls = req.getParameter("urls");
String currentUrl = req.getParameter("currentUrl");
logger.info("预览文件url{}urls{}", currentUrl, urls);
// 路径转码
String decodedUrl = URLDecoder.decode(urls, "utf-8");
String decodedCurrentUrl = URLDecoder.decode(currentUrl, "utf-8");
@@ -77,49 +77,21 @@ public class OnlinePreviewController {
return "picture";
}
@RequestMapping(value = "picturesPreview", method = RequestMethod.POST)
public String picturesPreview(Model model, HttpServletRequest req) throws UnsupportedEncodingException {
String urls = req.getParameter("urls");
String currentUrl = req.getParameter("currentUrl");
// 路径转码
String decodedUrl = URLDecoder.decode(urls, "utf-8");
String decodedCurrentUrl = URLDecoder.decode(currentUrl, "utf-8");
// 抽取文件并返回文件列表
String[] imgs = decodedUrl.split("\\|");
List imgurls = Arrays.asList(imgs);
model.addAttribute("imgurls", imgurls);
model.addAttribute("currentUrl",decodedCurrentUrl);
return "picture";
}
/**
* 根据url获取文件内容
* 当pdfjs读取存在跨域问题的文件时将通过此接口读取
*
* @param urlPath
* @param resp
* @param urlPath url
* @param response response
*/
@RequestMapping(value = "/getCorsFile", method = RequestMethod.GET)
public void getCorsFile(String urlPath, HttpServletResponse resp) {
InputStream inputStream = null;
public void getCorsFile(String urlPath, HttpServletResponse response) {
logger.info("下载跨域pdf文件url{}", urlPath);
try {
String strUrl = urlPath.trim();
URL url = new URL(new URI(strUrl).toASCIIString());
//打开请求连接
URLConnection connection = url.openConnection();
HttpURLConnection httpURLConnection = (HttpURLConnection) connection;
httpURLConnection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
inputStream = httpURLConnection.getInputStream();
byte[] bs = new byte[1024];
int len;
while (-1 != (len = inputStream.read(bs))) {
resp.getOutputStream().write(bs, 0, len);
}
} catch (IOException | URISyntaxException e) {
LOGGER.error("下载pdf文件失败", e);
} finally {
if (inputStream != null) {
IOUtils.closeQuietly(inputStream);
}
byte[] bytes = downloadUtils.getBytesFromUrl(urlPath);
downloadUtils.saveBytesToOutStream(bytes, response.getOutputStream());
} catch (IOException e) {
logger.error("下载跨域pdf文件异常url{}", urlPath, e);
}
}
@@ -127,9 +99,10 @@ public class OnlinePreviewController {
* 通过api接口入队
* @param url 请编码后在入队
*/
@GetMapping("/addTask")
@RequestMapping("/addTask")
@ResponseBody
public String addQueueTask(String url) {
logger.info("添加转码队列url{}", url);
cacheService.addQueueTask(url);
return "success";
}

View File

@@ -0,0 +1,58 @@
package cn.keking.web.filter;
import cn.keking.config.ConfigConstants;
import org.springframework.web.context.request.RequestContextHolder;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author chenjh
* @since 2020/5/13 18:27
*/
public class BaseUrlFilter implements Filter {
private static String BASE_URL;
public static String getBaseUrl() {
String baseUrl;
try {
baseUrl = (String) RequestContextHolder.currentRequestAttributes().getAttribute("baseUrl",0);
} catch (Exception e) {
baseUrl = BASE_URL;
}
return baseUrl;
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
String baseUrl;
StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(request.getScheme()).append("://").append(request.getServerName()).append(":")
.append(request.getServerPort()).append(((HttpServletRequest) request).getContextPath()).append("/");
String baseUrlTmp = ConfigConstants.getBaseUrl();
if (baseUrlTmp != null && !ConfigConstants.DEFAULT_BASE_URL.equals(baseUrlTmp.toLowerCase())) {
if (!baseUrlTmp.endsWith("/")) {
baseUrlTmp = baseUrlTmp.concat("/");
}
baseUrl = baseUrlTmp;
} else {
baseUrl = pathBuilder.toString();
}
BASE_URL = baseUrl;
request.setAttribute("baseUrl", baseUrl);
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
}
}

View File

@@ -1,7 +1,7 @@
package cn.keking.filters;
package cn.keking.web.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
@@ -12,17 +12,12 @@ import java.io.IOException;
public class ChinesePathFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(request.getScheme()).append("://").append(request.getServerName()).append(":")
.append(request.getServerPort()).append(((HttpServletRequest)request).getContextPath()).append("/");
request.setAttribute("baseUrl", pathBuilder.toString());
chain.doFilter(request, response);
}

View File

@@ -0,0 +1,66 @@
package cn.keking.web.filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author yudian-it
* @date 2017/11/30
*/
@Configuration
public class FilterConfiguration {
@Bean
public FilterRegistrationBean getChinesePathFilter() {
ChinesePathFilter filter = new ChinesePathFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
return registrationBean;
}
@Bean
public FilterRegistrationBean getTrustHostFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
filterUri.add("/getCorsFile");
filterUri.add("/addTask");
TrustHostFilter filter = new TrustHostFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
@Bean
public FilterRegistrationBean getBaseUrlFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/index");
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
BaseUrlFilter filter = new BaseUrlFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
@Bean
public FilterRegistrationBean getWatermarkConfigFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
WatermarkConfigFilter filter = new WatermarkConfigFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
}

View File

@@ -0,0 +1,75 @@
package cn.keking.web.filter;
import cn.keking.config.ConfigConstants;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;
import javax.servlet.*;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
/**
* @author chenjh
* @since 2020/2/18 19:13
*/
public class TrustHostFilter implements Filter {
private String notTrustHost;
@Override
public void init(FilterConfig filterConfig) {
ClassPathResource classPathResource = new ClassPathResource("web/notTrustHost.html");
try {
classPathResource.getInputStream();
byte[] bytes = FileCopyUtils.copyToByteArray(classPathResource.getInputStream());
this.notTrustHost = new String(bytes, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String url = getSourceUrl(request);
String host = getHost(url);
if (host != null &&!ConfigConstants.getTrustHostSet().isEmpty() && !ConfigConstants.getTrustHostSet().contains(host)) {
String html = this.notTrustHost.replace("${current_host}", host);
response.getWriter().write(html);
response.getWriter().close();
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
private String getSourceUrl(ServletRequest request) {
String url = request.getParameter("url");
String currentUrl = request.getParameter("currentUrl");
String urlPath = request.getParameter("urlPath");
if (StringUtils.isNotBlank(url)) {
return url;
}
if (StringUtils.isNotBlank(currentUrl)) {
return currentUrl;
}
if (StringUtils.isNotBlank(urlPath)) {
return urlPath;
}
return null;
}
private String getHost(String urlStr) {
try {
URL url = new URL(urlStr);
return url.getHost().toLowerCase();
} catch (MalformedURLException ignored) {
}
return null;
}
}

View File

@@ -0,0 +1,39 @@
package cn.keking.web.filter;
import cn.keking.config.WatermarkConfigConstants;
import javax.servlet.*;
import java.io.IOException;
/**
* @author chenjh
* @since 2020/5/13 18:34
*/
public class WatermarkConfigFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
String watermarkTxt = request.getParameter("watermarkTxt");
request.setAttribute("watermarkTxt", watermarkTxt != null ? watermarkTxt : WatermarkConfigConstants.getWatermarkTxt());
request.setAttribute("watermarkXSpace", WatermarkConfigConstants.getWatermarkXSpace());
request.setAttribute("watermarkYSpace", WatermarkConfigConstants.getWatermarkYSpace());
request.setAttribute("watermarkFont", WatermarkConfigConstants.getWatermarkFont());
request.setAttribute("watermarkFontsize", WatermarkConfigConstants.getWatermarkFontsize());
request.setAttribute("watermarkColor", WatermarkConfigConstants.getWatermarkColor());
request.setAttribute("watermarkAlpha", WatermarkConfigConstants.getWatermarkAlpha());
request.setAttribute("watermarkWidth", WatermarkConfigConstants.getWatermarkWidth());
request.setAttribute("watermarkHeight", WatermarkConfigConstants.getWatermarkHeight());
request.setAttribute("watermarkAngle", WatermarkConfigConstants.getWatermarkAngle());
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
}
}

View File

@@ -1 +0,0 @@
app.id=file-preview

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,352 @@
/**
* @author zhixin wen <wenzhixin2010@gmail.com>
* version: 1.16.0
* https://github.com/wenzhixin/bootstrap-table/
*/
.bootstrap-table .fixed-table-toolbar::after {
content: "";
display: block;
clear: both;
}
.bootstrap-table .fixed-table-toolbar .bs-bars,
.bootstrap-table .fixed-table-toolbar .search,
.bootstrap-table .fixed-table-toolbar .columns {
position: relative;
margin-top: 10px;
margin-bottom: 10px;
}
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group {
display: inline-block;
margin-left: -1px !important;
}
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group > .btn {
border-radius: 0;
}
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group:first-child > .btn {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group:last-child > .btn {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.bootstrap-table .fixed-table-toolbar .columns .dropdown-menu {
text-align: left;
max-height: 300px;
overflow: auto;
-ms-overflow-style: scrollbar;
z-index: 1001;
}
.bootstrap-table .fixed-table-toolbar .columns label {
display: block;
padding: 3px 20px;
clear: both;
font-weight: normal;
line-height: 1.428571429;
}
.bootstrap-table .fixed-table-toolbar .columns-left {
margin-right: 5px;
}
.bootstrap-table .fixed-table-toolbar .columns-right {
margin-left: 5px;
}
.bootstrap-table .fixed-table-toolbar .pull-right .dropdown-menu {
right: 0;
left: auto;
}
.bootstrap-table .fixed-table-container {
position: relative;
clear: both;
}
.bootstrap-table .fixed-table-container .table {
width: 100%;
margin-bottom: 0 !important;
}
.bootstrap-table .fixed-table-container .table th,
.bootstrap-table .fixed-table-container .table td {
vertical-align: middle;
box-sizing: border-box;
}
.bootstrap-table .fixed-table-container .table thead th {
vertical-align: bottom;
padding: 0;
margin: 0;
}
.bootstrap-table .fixed-table-container .table thead th:focus {
outline: 0 solid transparent;
}
.bootstrap-table .fixed-table-container .table thead th.detail {
width: 30px;
}
.bootstrap-table .fixed-table-container .table thead th .th-inner {
padding: 0.75rem;
vertical-align: bottom;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.bootstrap-table .fixed-table-container .table thead th .sortable {
cursor: pointer;
background-position: right;
background-repeat: no-repeat;
padding-right: 30px !important;
}
.bootstrap-table .fixed-table-container .table thead th .both {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC");
}
.bootstrap-table .fixed-table-container .table thead th .asc {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==");
}
.bootstrap-table .fixed-table-container .table thead th .desc {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII= ");
}
.bootstrap-table .fixed-table-container .table tbody tr.selected td {
background-color: rgba(0, 0, 0, 0.075);
}
.bootstrap-table .fixed-table-container .table tbody tr.no-records-found td {
text-align: center;
}
.bootstrap-table .fixed-table-container .table tbody tr .card-view {
display: flex;
}
.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-title {
font-weight: bold;
display: inline-block;
min-width: 30%;
text-align: left !important;
}
.bootstrap-table .fixed-table-container .table .bs-checkbox {
text-align: center;
}
.bootstrap-table .fixed-table-container .table .bs-checkbox label {
margin-bottom: 0;
}
.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type="radio"],
.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type="checkbox"] {
margin: 0 auto !important;
}
.bootstrap-table .fixed-table-container .table.table-sm .th-inner {
padding: 0.3rem;
}
.bootstrap-table .fixed-table-container.fixed-height:not(.has-footer) {
border-bottom: 1px solid #dee2e6;
}
.bootstrap-table .fixed-table-container.fixed-height.has-card-view {
border-top: 1px solid #dee2e6;
border-bottom: 1px solid #dee2e6;
}
.bootstrap-table .fixed-table-container.fixed-height .fixed-table-border {
border-left: 1px solid #dee2e6;
border-right: 1px solid #dee2e6;
}
.bootstrap-table .fixed-table-container.fixed-height .table thead th {
border-bottom: 1px solid #dee2e6;
}
.bootstrap-table .fixed-table-container.fixed-height .table-dark thead th {
border-bottom: 1px solid #32383e;
}
.bootstrap-table .fixed-table-container .fixed-table-header {
overflow: hidden;
}
.bootstrap-table .fixed-table-container .fixed-table-body {
overflow-x: auto;
overflow-y: auto;
height: 100%;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading {
align-items: center;
background: #fff;
display: none;
justify-content: center;
position: absolute;
bottom: 0;
width: 100%;
z-index: 1000;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap {
align-items: baseline;
display: flex;
justify-content: center;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .loading-text {
font-size: 2rem;
margin-right: 6px;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap {
align-items: center;
display: flex;
justify-content: center;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot,
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after,
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::before {
content: "";
animation-duration: 1.5s;
animation-iteration-count: infinite;
animation-name: LOADING;
background: #212529;
border-radius: 50%;
display: block;
height: 5px;
margin: 0 4px;
opacity: 0;
width: 5px;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot {
animation-delay: 0.3s;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after {
animation-delay: 0.6s;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark {
background: #212529;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-dot,
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::after,
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::before {
background: #fff;
}
.bootstrap-table .fixed-table-container .fixed-table-footer {
overflow: hidden;
}
.bootstrap-table .fixed-table-pagination::after {
content: "";
display: block;
clear: both;
}
.bootstrap-table .fixed-table-pagination > .pagination-detail,
.bootstrap-table .fixed-table-pagination > .pagination {
margin-top: 10px;
margin-bottom: 10px;
}
.bootstrap-table .fixed-table-pagination > .pagination-detail .pagination-info {
line-height: 34px;
margin-right: 5px;
}
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list {
display: inline-block;
}
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list .btn-group {
position: relative;
display: inline-block;
vertical-align: middle;
}
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list .btn-group .dropdown-menu {
margin-bottom: 0;
}
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination {
margin: 0;
}
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination a {
padding: 6px 12px;
line-height: 1.428571429;
}
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a {
color: #c8c8c8;
}
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a::before {
content: '\2B05';
}
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a::after {
content: '\27A1';
}
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.disabled a {
pointer-events: none;
cursor: default;
}
.bootstrap-table.fullscreen {
position: fixed;
top: 0;
left: 0;
z-index: 1050;
width: 100% !important;
background: #fff;
height: calc(100vh);
overflow-y: scroll;
}
/* calculate scrollbar width */
div.fixed-table-scroll-inner {
width: 100%;
height: 200px;
}
div.fixed-table-scroll-outer {
top: 0;
left: 0;
visibility: hidden;
width: 200px;
height: 150px;
overflow: hidden;
}
@keyframes LOADING {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
to {
opacity: 0;
}
}

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,12 @@
.table-cell-input {
display: block !important;
padding: 5px !important;
margin: 0 !important;
border: 0 !important;
width: 100% !important;
box-sizing: border-box !important;
-moz-box-sizing: border-box !important;
border-radius: 0 !important;
line-height: 1 !important;
white-space: nowrap;
}

View File

@@ -0,0 +1,10 @@
/**
* bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
*
* @version v1.16.0
* @homepage https://bootstrap-table.com
* @author wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
* @license MIT
*/
.table-cell-input{display:block!important;padding:5px!important;margin:0!important;border:0!important;width:100%!important;box-sizing:border-box!important;-moz-box-sizing:border-box!important;border-radius:0!important;line-height:1!important;white-space:nowrap}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,820 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) :
typeof define === 'function' && define.amd ? define(['jquery'], factory) :
(global = global || self, factory(global.jQuery));
}(this, (function ($) { 'use strict';
$ = $ && $.hasOwnProperty('default') ? $['default'] : $;
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var check = function (it) {
return it && it.Math == Math && it;
};
// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
var global_1 =
// eslint-disable-next-line no-undef
check(typeof globalThis == 'object' && globalThis) ||
check(typeof window == 'object' && window) ||
check(typeof self == 'object' && self) ||
check(typeof commonjsGlobal == 'object' && commonjsGlobal) ||
// eslint-disable-next-line no-new-func
Function('return this')();
var fails = function (exec) {
try {
return !!exec();
} catch (error) {
return true;
}
};
// Thank's IE8 for his funny defineProperty
var descriptors = !fails(function () {
return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7;
});
var nativePropertyIsEnumerable = {}.propertyIsEnumerable;
var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
// Nashorn ~ JDK8 bug
var NASHORN_BUG = getOwnPropertyDescriptor && !nativePropertyIsEnumerable.call({ 1: 2 }, 1);
// `Object.prototype.propertyIsEnumerable` method implementation
// https://tc39.github.io/ecma262/#sec-object.prototype.propertyisenumerable
var f = NASHORN_BUG ? function propertyIsEnumerable(V) {
var descriptor = getOwnPropertyDescriptor(this, V);
return !!descriptor && descriptor.enumerable;
} : nativePropertyIsEnumerable;
var objectPropertyIsEnumerable = {
f: f
};
var createPropertyDescriptor = function (bitmap, value) {
return {
enumerable: !(bitmap & 1),
configurable: !(bitmap & 2),
writable: !(bitmap & 4),
value: value
};
};
var toString = {}.toString;
var classofRaw = function (it) {
return toString.call(it).slice(8, -1);
};
var split = ''.split;
// fallback for non-array-like ES3 and non-enumerable old V8 strings
var indexedObject = fails(function () {
// throws an error in rhino, see https://github.com/mozilla/rhino/issues/346
// eslint-disable-next-line no-prototype-builtins
return !Object('z').propertyIsEnumerable(0);
}) ? function (it) {
return classofRaw(it) == 'String' ? split.call(it, '') : Object(it);
} : Object;
// `RequireObjectCoercible` abstract operation
// https://tc39.github.io/ecma262/#sec-requireobjectcoercible
var requireObjectCoercible = function (it) {
if (it == undefined) throw TypeError("Can't call method on " + it);
return it;
};
// toObject with fallback for non-array-like ES3 strings
var toIndexedObject = function (it) {
return indexedObject(requireObjectCoercible(it));
};
var isObject = function (it) {
return typeof it === 'object' ? it !== null : typeof it === 'function';
};
// `ToPrimitive` abstract operation
// https://tc39.github.io/ecma262/#sec-toprimitive
// instead of the ES6 spec version, we didn't implement @@toPrimitive case
// and the second argument - flag - preferred type is a string
var toPrimitive = function (input, PREFERRED_STRING) {
if (!isObject(input)) return input;
var fn, val;
if (PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject(val = fn.call(input))) return val;
if (typeof (fn = input.valueOf) == 'function' && !isObject(val = fn.call(input))) return val;
if (!PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject(val = fn.call(input))) return val;
throw TypeError("Can't convert object to primitive value");
};
var hasOwnProperty = {}.hasOwnProperty;
var has = function (it, key) {
return hasOwnProperty.call(it, key);
};
var document = global_1.document;
// typeof document.createElement is 'object' in old IE
var EXISTS = isObject(document) && isObject(document.createElement);
var documentCreateElement = function (it) {
return EXISTS ? document.createElement(it) : {};
};
// Thank's IE8 for his funny defineProperty
var ie8DomDefine = !descriptors && !fails(function () {
return Object.defineProperty(documentCreateElement('div'), 'a', {
get: function () { return 7; }
}).a != 7;
});
var nativeGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
// `Object.getOwnPropertyDescriptor` method
// https://tc39.github.io/ecma262/#sec-object.getownpropertydescriptor
var f$1 = descriptors ? nativeGetOwnPropertyDescriptor : function getOwnPropertyDescriptor(O, P) {
O = toIndexedObject(O);
P = toPrimitive(P, true);
if (ie8DomDefine) try {
return nativeGetOwnPropertyDescriptor(O, P);
} catch (error) { /* empty */ }
if (has(O, P)) return createPropertyDescriptor(!objectPropertyIsEnumerable.f.call(O, P), O[P]);
};
var objectGetOwnPropertyDescriptor = {
f: f$1
};
var anObject = function (it) {
if (!isObject(it)) {
throw TypeError(String(it) + ' is not an object');
} return it;
};
var nativeDefineProperty = Object.defineProperty;
// `Object.defineProperty` method
// https://tc39.github.io/ecma262/#sec-object.defineproperty
var f$2 = descriptors ? nativeDefineProperty : function defineProperty(O, P, Attributes) {
anObject(O);
P = toPrimitive(P, true);
anObject(Attributes);
if (ie8DomDefine) try {
return nativeDefineProperty(O, P, Attributes);
} catch (error) { /* empty */ }
if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported');
if ('value' in Attributes) O[P] = Attributes.value;
return O;
};
var objectDefineProperty = {
f: f$2
};
var createNonEnumerableProperty = descriptors ? function (object, key, value) {
return objectDefineProperty.f(object, key, createPropertyDescriptor(1, value));
} : function (object, key, value) {
object[key] = value;
return object;
};
var setGlobal = function (key, value) {
try {
createNonEnumerableProperty(global_1, key, value);
} catch (error) {
global_1[key] = value;
} return value;
};
var SHARED = '__core-js_shared__';
var store = global_1[SHARED] || setGlobal(SHARED, {});
var sharedStore = store;
var functionToString = Function.toString;
// this helper broken in `3.4.1-3.4.4`, so we can't use `shared` helper
if (typeof sharedStore.inspectSource != 'function') {
sharedStore.inspectSource = function (it) {
return functionToString.call(it);
};
}
var inspectSource = sharedStore.inspectSource;
var WeakMap = global_1.WeakMap;
var nativeWeakMap = typeof WeakMap === 'function' && /native code/.test(inspectSource(WeakMap));
var shared = createCommonjsModule(function (module) {
(module.exports = function (key, value) {
return sharedStore[key] || (sharedStore[key] = value !== undefined ? value : {});
})('versions', []).push({
version: '3.6.0',
mode: 'global',
copyright: '© 2019 Denis Pushkarev (zloirock.ru)'
});
});
var id = 0;
var postfix = Math.random();
var uid = function (key) {
return 'Symbol(' + String(key === undefined ? '' : key) + ')_' + (++id + postfix).toString(36);
};
var keys = shared('keys');
var sharedKey = function (key) {
return keys[key] || (keys[key] = uid(key));
};
var hiddenKeys = {};
var WeakMap$1 = global_1.WeakMap;
var set, get, has$1;
var enforce = function (it) {
return has$1(it) ? get(it) : set(it, {});
};
var getterFor = function (TYPE) {
return function (it) {
var state;
if (!isObject(it) || (state = get(it)).type !== TYPE) {
throw TypeError('Incompatible receiver, ' + TYPE + ' required');
} return state;
};
};
if (nativeWeakMap) {
var store$1 = new WeakMap$1();
var wmget = store$1.get;
var wmhas = store$1.has;
var wmset = store$1.set;
set = function (it, metadata) {
wmset.call(store$1, it, metadata);
return metadata;
};
get = function (it) {
return wmget.call(store$1, it) || {};
};
has$1 = function (it) {
return wmhas.call(store$1, it);
};
} else {
var STATE = sharedKey('state');
hiddenKeys[STATE] = true;
set = function (it, metadata) {
createNonEnumerableProperty(it, STATE, metadata);
return metadata;
};
get = function (it) {
return has(it, STATE) ? it[STATE] : {};
};
has$1 = function (it) {
return has(it, STATE);
};
}
var internalState = {
set: set,
get: get,
has: has$1,
enforce: enforce,
getterFor: getterFor
};
var redefine = createCommonjsModule(function (module) {
var getInternalState = internalState.get;
var enforceInternalState = internalState.enforce;
var TEMPLATE = String(String).split('String');
(module.exports = function (O, key, value, options) {
var unsafe = options ? !!options.unsafe : false;
var simple = options ? !!options.enumerable : false;
var noTargetGet = options ? !!options.noTargetGet : false;
if (typeof value == 'function') {
if (typeof key == 'string' && !has(value, 'name')) createNonEnumerableProperty(value, 'name', key);
enforceInternalState(value).source = TEMPLATE.join(typeof key == 'string' ? key : '');
}
if (O === global_1) {
if (simple) O[key] = value;
else setGlobal(key, value);
return;
} else if (!unsafe) {
delete O[key];
} else if (!noTargetGet && O[key]) {
simple = true;
}
if (simple) O[key] = value;
else createNonEnumerableProperty(O, key, value);
// add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative
})(Function.prototype, 'toString', function toString() {
return typeof this == 'function' && getInternalState(this).source || inspectSource(this);
});
});
var path = global_1;
var aFunction = function (variable) {
return typeof variable == 'function' ? variable : undefined;
};
var getBuiltIn = function (namespace, method) {
return arguments.length < 2 ? aFunction(path[namespace]) || aFunction(global_1[namespace])
: path[namespace] && path[namespace][method] || global_1[namespace] && global_1[namespace][method];
};
var ceil = Math.ceil;
var floor = Math.floor;
// `ToInteger` abstract operation
// https://tc39.github.io/ecma262/#sec-tointeger
var toInteger = function (argument) {
return isNaN(argument = +argument) ? 0 : (argument > 0 ? floor : ceil)(argument);
};
var min = Math.min;
// `ToLength` abstract operation
// https://tc39.github.io/ecma262/#sec-tolength
var toLength = function (argument) {
return argument > 0 ? min(toInteger(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991
};
var max = Math.max;
var min$1 = Math.min;
// Helper for a popular repeating case of the spec:
// Let integer be ? ToInteger(index).
// If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length).
var toAbsoluteIndex = function (index, length) {
var integer = toInteger(index);
return integer < 0 ? max(integer + length, 0) : min$1(integer, length);
};
// `Array.prototype.{ indexOf, includes }` methods implementation
var createMethod = function (IS_INCLUDES) {
return function ($this, el, fromIndex) {
var O = toIndexedObject($this);
var length = toLength(O.length);
var index = toAbsoluteIndex(fromIndex, length);
var value;
// Array#includes uses SameValueZero equality algorithm
// eslint-disable-next-line no-self-compare
if (IS_INCLUDES && el != el) while (length > index) {
value = O[index++];
// eslint-disable-next-line no-self-compare
if (value != value) return true;
// Array#indexOf ignores holes, Array#includes - not
} else for (;length > index; index++) {
if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0;
} return !IS_INCLUDES && -1;
};
};
var arrayIncludes = {
// `Array.prototype.includes` method
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
includes: createMethod(true),
// `Array.prototype.indexOf` method
// https://tc39.github.io/ecma262/#sec-array.prototype.indexof
indexOf: createMethod(false)
};
var indexOf = arrayIncludes.indexOf;
var objectKeysInternal = function (object, names) {
var O = toIndexedObject(object);
var i = 0;
var result = [];
var key;
for (key in O) !has(hiddenKeys, key) && has(O, key) && result.push(key);
// Don't enum bug & hidden keys
while (names.length > i) if (has(O, key = names[i++])) {
~indexOf(result, key) || result.push(key);
}
return result;
};
// IE8- don't enum bug keys
var enumBugKeys = [
'constructor',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'toLocaleString',
'toString',
'valueOf'
];
var hiddenKeys$1 = enumBugKeys.concat('length', 'prototype');
// `Object.getOwnPropertyNames` method
// https://tc39.github.io/ecma262/#sec-object.getownpropertynames
var f$3 = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {
return objectKeysInternal(O, hiddenKeys$1);
};
var objectGetOwnPropertyNames = {
f: f$3
};
var f$4 = Object.getOwnPropertySymbols;
var objectGetOwnPropertySymbols = {
f: f$4
};
// all object keys, includes non-enumerable and symbols
var ownKeys = getBuiltIn('Reflect', 'ownKeys') || function ownKeys(it) {
var keys = objectGetOwnPropertyNames.f(anObject(it));
var getOwnPropertySymbols = objectGetOwnPropertySymbols.f;
return getOwnPropertySymbols ? keys.concat(getOwnPropertySymbols(it)) : keys;
};
var copyConstructorProperties = function (target, source) {
var keys = ownKeys(source);
var defineProperty = objectDefineProperty.f;
var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!has(target, key)) defineProperty(target, key, getOwnPropertyDescriptor(source, key));
}
};
var replacement = /#|\.prototype\./;
var isForced = function (feature, detection) {
var value = data[normalize(feature)];
return value == POLYFILL ? true
: value == NATIVE ? false
: typeof detection == 'function' ? fails(detection)
: !!detection;
};
var normalize = isForced.normalize = function (string) {
return String(string).replace(replacement, '.').toLowerCase();
};
var data = isForced.data = {};
var NATIVE = isForced.NATIVE = 'N';
var POLYFILL = isForced.POLYFILL = 'P';
var isForced_1 = isForced;
var getOwnPropertyDescriptor$1 = objectGetOwnPropertyDescriptor.f;
/*
options.target - name of the target object
options.global - target is the global object
options.stat - export as static methods of target
options.proto - export as prototype methods of target
options.real - real prototype method for the `pure` version
options.forced - export even if the native feature is available
options.bind - bind methods to the target, required for the `pure` version
options.wrap - wrap constructors to preventing global pollution, required for the `pure` version
options.unsafe - use the simple assignment of property instead of delete + defineProperty
options.sham - add a flag to not completely full polyfills
options.enumerable - export as enumerable property
options.noTargetGet - prevent calling a getter on target
*/
var _export = function (options, source) {
var TARGET = options.target;
var GLOBAL = options.global;
var STATIC = options.stat;
var FORCED, target, key, targetProperty, sourceProperty, descriptor;
if (GLOBAL) {
target = global_1;
} else if (STATIC) {
target = global_1[TARGET] || setGlobal(TARGET, {});
} else {
target = (global_1[TARGET] || {}).prototype;
}
if (target) for (key in source) {
sourceProperty = source[key];
if (options.noTargetGet) {
descriptor = getOwnPropertyDescriptor$1(target, key);
targetProperty = descriptor && descriptor.value;
} else targetProperty = target[key];
FORCED = isForced_1(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced);
// contained in target
if (!FORCED && targetProperty !== undefined) {
if (typeof sourceProperty === typeof targetProperty) continue;
copyConstructorProperties(sourceProperty, targetProperty);
}
// add a flag to not completely full polyfills
if (options.sham || (targetProperty && targetProperty.sham)) {
createNonEnumerableProperty(sourceProperty, 'sham', true);
}
// extend global
redefine(target, key, sourceProperty, options);
}
};
// `IsArray` abstract operation
// https://tc39.github.io/ecma262/#sec-isarray
var isArray = Array.isArray || function isArray(arg) {
return classofRaw(arg) == 'Array';
};
// `ToObject` abstract operation
// https://tc39.github.io/ecma262/#sec-toobject
var toObject = function (argument) {
return Object(requireObjectCoercible(argument));
};
var createProperty = function (object, key, value) {
var propertyKey = toPrimitive(key);
if (propertyKey in object) objectDefineProperty.f(object, propertyKey, createPropertyDescriptor(0, value));
else object[propertyKey] = value;
};
var nativeSymbol = !!Object.getOwnPropertySymbols && !fails(function () {
// Chrome 38 Symbol has incorrect toString conversion
// eslint-disable-next-line no-undef
return !String(Symbol());
});
var useSymbolAsUid = nativeSymbol
// eslint-disable-next-line no-undef
&& !Symbol.sham
// eslint-disable-next-line no-undef
&& typeof Symbol() == 'symbol';
var WellKnownSymbolsStore = shared('wks');
var Symbol$1 = global_1.Symbol;
var createWellKnownSymbol = useSymbolAsUid ? Symbol$1 : uid;
var wellKnownSymbol = function (name) {
if (!has(WellKnownSymbolsStore, name)) {
if (nativeSymbol && has(Symbol$1, name)) WellKnownSymbolsStore[name] = Symbol$1[name];
else WellKnownSymbolsStore[name] = createWellKnownSymbol('Symbol.' + name);
} return WellKnownSymbolsStore[name];
};
var SPECIES = wellKnownSymbol('species');
// `ArraySpeciesCreate` abstract operation
// https://tc39.github.io/ecma262/#sec-arrayspeciescreate
var arraySpeciesCreate = function (originalArray, length) {
var C;
if (isArray(originalArray)) {
C = originalArray.constructor;
// cross-realm fallback
if (typeof C == 'function' && (C === Array || isArray(C.prototype))) C = undefined;
else if (isObject(C)) {
C = C[SPECIES];
if (C === null) C = undefined;
}
} return new (C === undefined ? Array : C)(length === 0 ? 0 : length);
};
var userAgent = getBuiltIn('navigator', 'userAgent') || '';
var process = global_1.process;
var versions = process && process.versions;
var v8 = versions && versions.v8;
var match, version;
if (v8) {
match = v8.split('.');
version = match[0] + match[1];
} else if (userAgent) {
match = userAgent.match(/Edge\/(\d+)/);
if (!match || match[1] >= 74) {
match = userAgent.match(/Chrome\/(\d+)/);
if (match) version = match[1];
}
}
var v8Version = version && +version;
var SPECIES$1 = wellKnownSymbol('species');
var arrayMethodHasSpeciesSupport = function (METHOD_NAME) {
// We can't use this feature detection in V8 since it causes
// deoptimization and serious performance degradation
// https://github.com/zloirock/core-js/issues/677
return v8Version >= 51 || !fails(function () {
var array = [];
var constructor = array.constructor = {};
constructor[SPECIES$1] = function () {
return { foo: 1 };
};
return array[METHOD_NAME](Boolean).foo !== 1;
});
};
var IS_CONCAT_SPREADABLE = wellKnownSymbol('isConcatSpreadable');
var MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFF;
var MAXIMUM_ALLOWED_INDEX_EXCEEDED = 'Maximum allowed index exceeded';
// We can't use this feature detection in V8 since it causes
// deoptimization and serious performance degradation
// https://github.com/zloirock/core-js/issues/679
var IS_CONCAT_SPREADABLE_SUPPORT = v8Version >= 51 || !fails(function () {
var array = [];
array[IS_CONCAT_SPREADABLE] = false;
return array.concat()[0] !== array;
});
var SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('concat');
var isConcatSpreadable = function (O) {
if (!isObject(O)) return false;
var spreadable = O[IS_CONCAT_SPREADABLE];
return spreadable !== undefined ? !!spreadable : isArray(O);
};
var FORCED = !IS_CONCAT_SPREADABLE_SUPPORT || !SPECIES_SUPPORT;
// `Array.prototype.concat` method
// https://tc39.github.io/ecma262/#sec-array.prototype.concat
// with adding support of @@isConcatSpreadable and @@species
_export({ target: 'Array', proto: true, forced: FORCED }, {
concat: function concat(arg) { // eslint-disable-line no-unused-vars
var O = toObject(this);
var A = arraySpeciesCreate(O, 0);
var n = 0;
var i, k, length, len, E;
for (i = -1, length = arguments.length; i < length; i++) {
E = i === -1 ? O : arguments[i];
if (isConcatSpreadable(E)) {
len = toLength(E.length);
if (n + len > MAX_SAFE_INTEGER) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);
for (k = 0; k < len; k++, n++) if (k in E) createProperty(A, n, E[k]);
} else {
if (n >= MAX_SAFE_INTEGER) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);
createProperty(A, n++, E);
}
}
A.length = n;
return A;
}
});
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}
function _superPropBase(object, property) {
while (!Object.prototype.hasOwnProperty.call(object, property)) {
object = _getPrototypeOf(object);
if (object === null) break;
}
return object;
}
function _get(target, property, receiver) {
if (typeof Reflect !== "undefined" && Reflect.get) {
_get = Reflect.get;
} else {
_get = function _get(target, property, receiver) {
var base = _superPropBase(target, property);
if (!base) return;
var desc = Object.getOwnPropertyDescriptor(base, property);
if (desc.get) {
return desc.get.call(receiver);
}
return desc.value;
};
}
return _get(target, property, receiver || target);
}
/**
* When using server-side processing, the default mode of operation for
* bootstrap-table is to simply throw away any data that currently exists in the
* table and make a request to the server to get the first page of data to
* display. This is fine for an empty table, but if you already have the first
* page of data displayed in the plain HTML, it is a waste of resources. As
* such, you can use data-defer-url instead of data-url to allow you to instruct
* bootstrap-table to not make that initial request, rather it will use the data
* already on the page.
*
* @author: Ruben Suarez
* @webSite: http://rubensa.eu.org
* @update zhixin wen <wenzhixin2010@gmail.com>
*/
$.extend($.fn.bootstrapTable.defaults, {
deferUrl: undefined
});
$.BootstrapTable =
/*#__PURE__*/
function (_$$BootstrapTable) {
_inherits(_class, _$$BootstrapTable);
function _class() {
_classCallCheck(this, _class);
return _possibleConstructorReturn(this, _getPrototypeOf(_class).apply(this, arguments));
}
_createClass(_class, [{
key: "init",
value: function init() {
var _get2;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
(_get2 = _get(_getPrototypeOf(_class.prototype), "init", this)).call.apply(_get2, [this].concat(args));
if (this.options.deferUrl) {
this.options.url = this.options.deferUrl;
}
}
}]);
return _class;
}($.BootstrapTable);
})));

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,13 @@
@charset "UTF-8";
/**
* @author: Dennis Hernández
* @webSite: http://djhvscf.github.io/Blog
* @version: v2.1.1
*/
.no-filter-control {
height: 34px;
}
.filter-control {
margin: 0 2px 2px 2px;
}

View File

@@ -0,0 +1,10 @@
/**
* bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
*
* @version v1.16.0
* @homepage https://bootstrap-table.com
* @author wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
* @license MIT
*/
@charset "UTF-8";.no-filter-control{height:34px}.filter-control{margin:0 2px 2px 2px}

View File

@@ -0,0 +1,25 @@
.fixed-columns,
.fixed-columns-right {
position: absolute;
top: 0;
height: 100%;
background-color: #fff;
box-sizing: border-box;
z-index: 1;
}
.fixed-columns {
left: 0;
}
.fixed-columns .fixed-table-body {
overflow: hidden !important;
}
.fixed-columns-right {
right: 0;
}
.fixed-columns-right .fixed-table-body {
overflow-x: hidden !important;
}

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