Compare commits

...

20 Commits

Author SHA1 Message Date
陈精华
1d39360c60 4.4.0版本发布 2025-01-16 10:44:41 +08:00
陈精华
8c763599fe !312 update server/src/main/resources/web/main/record.ftl.
Merge pull request !312 from 高雄/N/A
2025-01-15 03:28:17 +00:00
高雄
9632f6070c update server/src/main/resources/web/main/record.ftl.
更新日志文档

Signed-off-by: 高雄 <admin@cxcp.com>
2025-01-14 01:23:13 +00:00
陈精华
cc63659650 Merge pull request #593 from zp96324511/patch-1
容易引起歧义的环境变量命名
2024-09-30 16:05:29 +08:00
阿鹏
177f389814 容易引起歧义的环境变量命名 2024-09-14 12:09:24 +08:00
陈精华
406e9ea6ee Merge pull request #584 from gitchenjh/master
docker基础镜像调整为kkfileview-base
2024-08-27 10:36:59 +08:00
陈精华
bb461cd74a docker基础镜像调整为kkfileview-base 2024-08-26 10:29:27 +08:00
陈精华
782509376c Merge pull request #581 from wayne-cheng/optimize-dockerfile
优化Dockerfile,支持真正的跨平台构建镜像
2024-08-22 14:41:17 +08:00
郑威
63dc58d088 :construction_worker:优化Dockerfile,支持真正的跨平台构建镜像 2024-08-21 11:12:38 +08:00
陈精华
77f5adb19f !299 修复压缩获取路径错误,图片合集路径错误,水印问题等BUG
Merge pull request !299 from 高雄/yashuoba
2024-07-02 02:20:08 +00:00
陈精华
7abfb67451 !289 修复前端解析xlsx 包含emf格式文件错误
Merge pull request !289 from 高雄/lockkkk
2024-06-25 02:05:43 +00:00
gaoxiongzaq
c8dc638c29 修复压缩获取路径错误,图片合集路径错误,水印问题等BUG 2024-05-27 14:30:31 +08:00
gaoxiongzaq
48ac926289 修复压缩获取路径错误,图片合集路径错误,水印问题等BUG 2024-05-27 14:21:11 +08:00
陈精华
0a4ae41b0c !296 新增PDF线程管理,超时管理,内存缓存管理,更新PDF解析组件版本
Merge pull request !296 from 高雄/pdfddd
2024-05-27 03:32:27 +00:00
gaoxiongzaq
bb0139bee6 新增PDF线程管理,超时管理,内存缓存管理,更新PDF解析组件版本 2024-05-20 10:12:11 +08:00
陈精华
7bf07cb64c !286 修复远程文件,文件名带有穿越的BUG
Merge pull request !286 from 高雄/chuanyue
2024-04-17 02:40:52 +00:00
陈精华
ab370e66a5 Merge pull request #554 from kekingcn/kl
重构解压缩逻辑,fix  #553
2024-04-17 10:32:19 +08:00
kl
421a2760d5 重构解压缩逻辑,fix #553 2024-04-16 20:04:50 +08:00
gaoxiongzaq
9150346926 修复前端解析xlsx 包含emf格式文件错误 2024-03-28 11:26:25 +08:00
gaoxiongzaq
b65a04857c 修复远程文件文件名带有穿越漏洞的BUG 2024-03-27 08:55:28 +08:00
30 changed files with 1350 additions and 7863 deletions

View File

@@ -1,5 +1,4 @@
FROM keking/kkfileview-jdk:latest FROM keking/kkfileview-base:4.4.0
MAINTAINER chenjh "842761733@qq.com"
ADD server/target/kkFileView-*.tar.gz /opt/ ADD server/target/kkFileView-*.tar.gz /opt/
ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-4.4.0-beta/bin ENV KKFILEVIEW_BIN_FOLDER=/opt/kkFileView-4.4.0/bin
ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dspring.config.location=/opt/kkFileView-4.4.0-beta/config/application.properties","-jar","/opt/kkFileView-4.4.0-beta/bin/kkFileView-4.4.0-beta.jar"] ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dspring.config.location=/opt/kkFileView-4.4.0/config/application.properties","-jar","/opt/kkFileView-4.4.0/bin/kkFileView-4.4.0.jar"]

View File

@@ -0,0 +1,28 @@
FROM ubuntu:24.04
RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.aliyun.com@g' /etc/apt/sources.list.d/ubuntu.sources &&\
sed -i 's@//security.ubuntu.com@//mirrors.aliyun.com@g' /etc/apt/sources.list.d/ubuntu.sources &&\
sed -i 's@//ports.ubuntu.com@//mirrors.aliyun.com@g' /etc/apt/sources.list.d/ubuntu.sources &&\
apt-get update &&\
export DEBIAN_FRONTEND=noninteractive &&\
apt-get install -y --no-install-recommends openjdk-8-jre tzdata locales xfonts-utils fontconfig libreoffice-nogui &&\
echo 'Asia/Shanghai' > /etc/timezone &&\
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\
localedef -i zh_CN -c -f UTF-8 -A /usr/share/locale/locale.alias zh_CN.UTF-8 &&\
locale-gen zh_CN.UTF-8 &&\
apt-get install -y --no-install-recommends ttf-mscorefonts-installer &&\
apt-get install -y --no-install-recommends ttf-wqy-microhei ttf-wqy-zenhei xfonts-wqy &&\
apt-get autoremove -y &&\
apt-get clean &&\
rm -rf /var/lib/apt/lists/*
# 内置一些常用的中文字体,避免普遍性乱码
ADD fonts/* /usr/share/fonts/chinese/
RUN cd /usr/share/fonts/chinese &&\
# 安装字体
mkfontscale &&\
mkfontdir &&\
fc-cache -fv
ENV LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8

View File

@@ -0,0 +1,50 @@
# 构建说明
由于 kkfileview 的基础运行环境很少变动且制作耗时较久 kkfileview 本身代码开发会频繁改动因此把制作其 Docker 镜像的步骤拆分为两次
首先制作 kkfileview 的基础镜像kkfileview-base
然后使用 kkfileview-base 作为基础镜像进行构建加快 kkfileview docker 镜像构建与发布
执行如下命令即可构建基础镜像
> 这里镜像 tag 4.4.0 为例本项目所维护的 Dockerfile 文件考虑了跨平台兼容性 如果你需要用到 arm64 架构镜像, 则在arm64 架构机器上同样执行下面的构建命令即可
```shell
docker build --tag keking/kkfileview-base:4.4.0 .
```
## 跨平台构建
`docker buildx` 支持在一台机器上构建出多种平台架构的镜像推荐使用该能力进行跨平台的镜像构建
例如执行 `docker buildx build` 命令时加上 `--platform=linux/arm64` 参数即可构建出 arm64 架构镜像这极大方便了那些没有arm64 架构机器却想构建 arm64 架构镜像的用户
> 当前本项目仅支持构建 linux/amd64 linux/arm64 两种平台架构的镜像
> buildx builder driver 可以使用默认的 `docker` 类型, 若使用 `docker-container` 类型可以支持并行构建多种架构, 本文不再赘述, 有兴趣可以自行了解参考 [Docker Buildx | Docker Documentation](https://docs.docker.com/buildx/working-with-buildx/#build-multi-platform-images)
**前提要求**
以当前机器为 amd64 (x86_64)架构为例需要开启 docker buildx 特性以及开启 Linux QEMU 用户模式
> 使用 WSL2 Windows 用户如果安装了最新的 DockerDesktop, 则这些前提要求已满足, 无需额外下述设置
1. 安装 docker buildx 客户端插件
> docker 版本要求 >=19.03
若已安装, 则跳过详情参考 https://github.com/docker/buildx
2. 开启 QEMU 的用户模式功能, 并安装其它平台的模拟器:
> Linux 内核要求 >=4.8
使用 `tonistiigi/binfmt` 镜像可快速开启并安装模拟器执行下面命令:
```shell
docker run --privileged --rm tonistiigi/binfmt --install all
```
现在就可以愉快地开始构建了构建命令示例:
```shell
docker buildx build --platform=linux/amd64,linux/arm64 -t keking/kkfileview-base:4.4.0 --push .
```

View File

@@ -0,0 +1,53 @@
# Build Instructions
Since the base runtime environment for kkfileview rarely changes and takes a long time to build, while the kkfileview code itself is frequently updated, the process of building its Docker image is split into two steps:
First, create the base image for kkfileview (kkfileview-base).
Then, use kkfileview-base as the base image to build and speed up the kkfileview Docker image build and release process.
To build the base image, run the following command:
> In this example, the image tag is 4.4.0. The Dockerfile maintained in this project considers cross-platform compatibility. If you need an arm64 architecture image, run the same build command on an arm64 architecture machine.
```shell
docker build --tag keking/kkfileview-base:4.4.0 .
```
## Cross-Platform Build
`docker buildx` supports building images for multiple platform architectures on a single machine. It is recommended to use this capability for cross-platform image builds.
For example, adding the `--platform=linux/arm64` parameter when executing the `docker buildx build` command will allow you to build an arm64 architecture image. This is particularly convenient for users who want to build arm64 images but don't have an arm64 machine.
> Currently, this project only supports building images for the linux/amd64 and linux/arm64 architectures.
> The buildx builder driver can use the default `docker` type, but if you use the `docker-container` type, you can build multiple architectures in parallel. This README will not cover that in detail, you can learn more on your own. Refer to [Docker Buildx | Docker Documentation](https://docs.docker.com/buildx/working-with-buildx/#build-multi-platform-images)
**Prerequisites**
Assuming the current machine is amd64 (x86_64) architecture, you'll need to enable the docker buildx feature and enable Linux QEMU user mode:
> Windows users with WSL2 who have installed a recent version of Docker Desktop will already meet these prerequisites, so no additional setup is required.
1. Install the docker buildx client plugin:
> Docker version >=19.03 is required.
If it's already installed, you can skip this step. For more details, refer to https://github.com/docker/buildx.
2. Enable QEMU user mode and install emulators for other platforms:
> Linux kernel version >=4.8 is required.
You can quickly enable and install emulators using the tonistiigi/binfmt image by running the following command:
```shell
docker run --privileged --rm tonistiigi/binfmt --install all
```
Now you can enjoy the building. Heres an example build command:
```shell
docker buildx build --platform=linux/amd64,linux/arm64 -t keking/kkfileview-base:4.4.0 --push .
```

View File

@@ -1,38 +0,0 @@
FROM ubuntu:20.04
MAINTAINER chenjh "842761733@qq.com"
# 内置一些常用的中文字体,避免普遍性乱码
COPY fonts/* /usr/share/fonts/chinese/
RUN apt-get clean && apt-get update &&\
sed -i 's/http:\/\/archive.ubuntu.com/https:\/\/mirrors.aliyun.com/g' /etc/apt/sources.list &&\
sed -i 's/# deb/deb/g' /etc/apt/sources.list &&\
apt-get install -y --reinstall ca-certificates &&\
apt-get clean && apt-get update &&\
apt-get install -y locales 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 &&\
export DEBIAN_FRONTEND=noninteractive &&\
apt-get install -y tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\
apt-get install -y fontconfig ttf-mscorefonts-installer ttf-wqy-microhei ttf-wqy-zenhei xfonts-wqy &&\
apt-get install -y wget &&\
cd /tmp &&\
wget https://kkview.cn/resource/server-jre-8u251-linux-x64.tar.gz &&\
tar -zxf /tmp/server-jre-8u251-linux-x64.tar.gz && mv /tmp/jdk1.8.0_251 /usr/local/ &&\
# 安装 libreoffice
apt-get install -y libxrender1 libxinerama1 libxt6 libxext-dev libfreetype6-dev libcairo2 libcups2 libx11-xcb1 libnss3 &&\
wget https://downloadarchive.documentfoundation.org/libreoffice/old/7.5.3.2/deb/x86_64/LibreOffice_7.5.3.2_Linux_x86-64_deb.tar.gz -cO libreoffice_deb.tar.gz &&\
tar -zxf /tmp/libreoffice_deb.tar.gz && cd /tmp/LibreOffice_7.5.3.2_Linux_x86-64_deb/DEBS &&\
dpkg -i *.deb &&\
# 清理临时文件
rm -rf /tmp/* && rm -rf /var/lib/apt/lists/* &&\
cd /usr/share/fonts/chinese &&\
mkfontscale &&\
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
CMD ["/bin/bash"]

View File

@@ -1,77 +0,0 @@
FROM arm64v8/ubuntu:20.04
MAINTAINER chenjh "842761733@qq.com"
# 内置一些常用的中文字体,避免普遍性乱码
COPY fonts/* /usr/share/fonts/chinese/
RUN apt-get clean && apt-get update &&\
sed -i 's/http:\/\/archive.ubuntu.com/https:\/\/mirrors.aliyun.com/g' /etc/apt/sources.list &&\
sed -i 's/# deb/deb/g' /etc/apt/sources.list &&\
apt-get install -y --reinstall ca-certificates &&\
apt-get clean && apt-get update &&\
apt-get install -y locales 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 &&\
export DEBIAN_FRONTEND=noninteractive &&\
apt-get install -y tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\
apt-get install -y fontconfig ttf-mscorefonts-installer ttf-wqy-microhei ttf-wqy-zenhei xfonts-wqy &&\
apt-get install -y wget
# 安装 arm64-jre8
RUN apt-get install -y openjdk-8-jre
# 编译 libreoffice
RUN apt-get install -y git build-essential zip ccache junit4 libkrb5-dev nasm graphviz python3 python3-dev qtbase5-dev libkf5coreaddons-dev libkf5i18n-dev libkf5config-dev libkf5windowsystem-dev libkf5kio-dev autoconf libcups2-dev libfontconfig1-dev gperf default-jdk doxygen libxslt1-dev xsltproc libxml2-utils libxrandr-dev libx11-dev bison flex libgtk-3-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev ant ant-optional libnss3-dev libavahi-client-dev libxt-dev &&\
# 安装 ccache重复编译时加快速度
apt-get install ccache &&\
ccache -M 10G &&\
# clone主代码
mkdir /opt/libreoffice
WORKDIR /opt/libreoffice
RUN git clone --depth=1 --branch libreoffice-7-5 git://go.suokunlong.cn/lo/core ./libreoffice-7-5
# 配置&抓取子模块
WORKDIR /opt/libreoffice/libreoffice-7-5
RUN git submodule init &&\
git config --unset-all submodule.dictionaries.active &&\
git config --unset-all submodule.dictionaries.url &&\
git config --unset-all submodule.helpcontent2.active &&\
git config --unset-all submodule.helpcontent2.url &&\
git submodule update --progress --depth=1 &&\
# 下载第三方依赖
mkdir -p /opt/libreoffice/ext &&\
wget --recursive --no-parent --no-check-certificate -P /opt/libreoffice/ext https://go.suokunlong.cn:88/dl/libreoffice/external_tarballs/
RUN mv /opt/libreoffice/ext/go.suokunlong.cn:88/dl/libreoffice/external_tarballs/* /opt/libreoffice/ext
# 配置编译选项
RUN cat << EOF > autogen.input \
&& echo "--without-help" >> autogen.input \
&& echo "--without-helppack-integration" >> autogen.input \
&& echo "--with-lang=zh-CN zh-TW" >> autogen.input \
&& echo "--disable-online-update" >> autogen.input \
&& echo "--disable-breakpad" >> autogen.input \
&& echo "--disable-odk" >> autogen.input \
&& echo "--without-doxygen" >> autogen.input \
&& echo "--with-external-tar=/opt/libreoffice/ext" >> autogen.input \
&& echo "--without-java" >> autogen.input \
&& echo "--enable-firebird-sdbc" >> autogen.input \
&& echo "--without-system-firebird" >> autogen.input \
&& echo "--enable-python=internal" >> autogen.input
# 预编译
RUN ./autogen.sh
# 因为libreoffice的安全策略不允许root用户执行编译操作可以改Makefile文件解决所以新建用户
RUN useradd libreoffice
# 切换用户
RUN su libreoffice
# 在普通用户下编译
RUN make || true
# !!!编译40分钟左右会报错此时需要执行以下操作重新编译
RUN cp ./workdir/UnpackedTarball/python3/build/lib.linux-aarch64-3.8/_sysconfigdata__linux_aarch64-linux-gnu.py ./workdir/UnpackedTarball/python3/build/lib.linux-aarch64-3.8/_sysconfigdata__linux_aarch64-unknown-linux-gnu.py
# 重新编译
RUN make &&\
make install
RUN ln -s /usr/local/lib/libreoffice/program/soffice /usr/bin/libreoffice
# 清理临时文件
RUN rm -rf /tmp/* && rm -rf /var/lib/apt/lists/* &&\
cd /usr/share/fonts/chinese &&\
mkfontscale &&\
mkfontdir &&\
fc-cache -fv
ENV LANG zh_CN.UTF-8
ENV LC_ALL zh_CN.UTF-8
CMD ["/bin/bash"]

View File

@@ -1,5 +0,0 @@
# 执行如下命令构建基础镜像加快kkfileview docker镜像构建与发布
docker build --tag keking/kkfileview-jdk:4.3.0 .
# arm64架构执行如下命令
docker build -f Dockerfile_arm64 --tag keking/kkfileview-jdk:4.3.0 .

View File

@@ -6,7 +6,7 @@
<groupId>cn.keking</groupId> <groupId>cn.keking</groupId>
<artifactId>kkFileView-parent</artifactId> <artifactId>kkFileView-parent</artifactId>
<version>4.4.0-beta</version> <version>4.4.0</version>
<properties> <properties>
<java.version>1.8</java.version> <java.version>1.8</java.version>
@@ -22,7 +22,7 @@
<antlr.version>2.7.7</antlr.version> <antlr.version>2.7.7</antlr.version>
<concurrentlinkedhashmap.version>1.4.2</concurrentlinkedhashmap.version> <concurrentlinkedhashmap.version>1.4.2</concurrentlinkedhashmap.version>
<rocksdb.version>5.17.2</rocksdb.version> <rocksdb.version>5.17.2</rocksdb.version>
<pdfbox.version>2.0.29</pdfbox.version> <pdfbox.version>3.0.2</pdfbox.version>
<jai-imageio.version>1.4.0</jai-imageio.version> <jai-imageio.version>1.4.0</jai-imageio.version>
<jbig2-imageio.version>3.0.4</jbig2-imageio.version> <jbig2-imageio.version>3.0.4</jbig2-imageio.version>
<galimatias.version>0.2.1</galimatias.version> <galimatias.version>0.2.1</galimatias.version>

View File

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

View File

@@ -8,7 +8,7 @@ install_redhat() {
yum install -y libSM.x86_64 libXrender.x86_64 libXext.x86_64 yum install -y libSM.x86_64 libXrender.x86_64 libXext.x86_64
yum groupinstall -y "X Window System" yum groupinstall -y "X Window System"
yum localinstall -y *.rpm yum localinstall -y *.rpm
echo 'install finshed...' echo 'install finished...'
else else
echo 'download package error...' echo 'download package error...'
fi fi
@@ -20,7 +20,7 @@ install_ubuntu() {
if [ $? -eq 0 ];then if [ $? -eq 0 ];then
apt-get install -y libxinerama1 libcairo2 libcups2 libx11-xcb1 apt-get install -y libxinerama1 libcairo2 libcups2 libx11-xcb1
dpkg -i *.deb dpkg -i *.deb
echo 'install finshed...' echo 'install finished...'
else else
echo 'download package error...' echo 'download package error...'
fi fi

View File

@@ -7,4 +7,4 @@ echo Please check log file in ../log/kkFileView.log for more information
echo You can get help in our official home site: https://kkview.cn echo You can get help in our official home site: https://kkview.cn
echo If you need further help, please join our kk opensource community: https://t.zsxq.com/09ZHSXbsQ echo If you need further help, please join our kk opensource community: https://t.zsxq.com/09ZHSXbsQ
echo If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers echo If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers
java -Dspring.config.location=..\config\application.properties -jar kkFileView-4.4.0-beta.jar -> ..\log\kkFileView.log java -Dspring.config.location=..\config\application.properties -jar kkFileView-4.4.0.jar -> ..\log\kkFileView.log

View File

@@ -9,7 +9,7 @@
# Description: v1.1修改进程启动机制为pid形式 # Description: v1.1修改进程启动机制为pid形式
############################# #############################
# #
DIR_HOME=("/opt/openoffice.org3" "/opt/libreoffice" "/opt/libreoffice6.1" "/opt/libreoffice7.0" "/opt/libreoffice7.1" "/opt/libreoffice7.2" "/opt/libreoffice7.3" "/opt/libreoffice7.4" "/opt/libreoffice7.5" "/opt/libreoffice7.6" "/opt/openoffice4" "/usr/lib/openoffice" "/usr/lib/libreoffice") DIR_HOME=("/opt/openoffice.org3" "/opt/libreoffice" "/opt/libreoffice6.1" "/opt/libreoffice7.0" "/opt/libreoffice7.1" "/opt/libreoffice7.2" "/opt/libreoffice7.3" "/opt/libreoffice7.4" "/opt/libreoffice7.5" "/opt/libreoffice7.6" "/opt/libreoffice24.2" "/opt/libreoffice24.8" "/opt/libreoffice25.2" "/opt/openoffice4" "/usr/lib/openoffice" "/usr/lib/libreoffice")
FLAG= FLAG=
OFFICE_HOME= OFFICE_HOME=
KKFILEVIEW_BIN_FOLDER=$(cd "$(dirname "$0")" || exit 1 ;pwd) KKFILEVIEW_BIN_FOLDER=$(cd "$(dirname "$0")" || exit 1 ;pwd)
@@ -29,7 +29,7 @@ if [ -s "${PID_FILE}" ]; then
else else
cd "$KKFILEVIEW_BIN_FOLDER" || exit 1 cd "$KKFILEVIEW_BIN_FOLDER" || exit 1
echo "Using KKFILEVIEW_BIN_FOLDER $KKFILEVIEW_BIN_FOLDER" echo "Using KKFILEVIEW_BIN_FOLDER $KKFILEVIEW_BIN_FOLDER"
grep 'office\.home' ../config/application.properties | grep '!^#' grep 'office\.home' ../config/application.properties | grep -v '^#' | grep -v 'default'
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo "Using customized office.home" echo "Using customized office.home"
else else
@@ -51,7 +51,7 @@ else
## 启动kkFileView ## 启动kkFileView
echo "Starting kkFileView..." echo "Starting kkFileView..."
nohup java -Dfile.encoding=UTF-8 -Dspring.config.location=../config/application.properties -jar kkFileView-4.4.0-beta.jar > ../log/kkFileView.log 2>&1 & nohup java -Dfile.encoding=UTF-8 -Dspring.config.location=../config/application.properties -jar kkFileView-4.4.0.jar > ../log/kkFileView.log 2>&1 &
echo "Please execute ./showlog.sh to check log for more information" echo "Please execute ./showlog.sh to check log for more information"
echo "You can get help in our official home site: https://kkview.cn" echo "You can get help in our official home site: https://kkview.cn"
echo "If you need further help, please join our kk opensource community: https://t.zsxq.com/09ZHSXbsQ" echo "If you need further help, please join our kk opensource community: https://t.zsxq.com/09ZHSXbsQ"

View File

@@ -3,7 +3,7 @@ server.port = ${KK_SERVER_PORT:8012}
server.servlet.context-path= ${KK_CONTEXT_PATH:/} server.servlet.context-path= ${KK_CONTEXT_PATH:/}
server.servlet.encoding.charset = utf-8 server.servlet.encoding.charset = utf-8
#启用GZIP压缩功能 #启用GZIP压缩功能
server.compression.enable= true server.compression.enabled = true
#允许压缩的响应缓冲区最小字节数默认2048 #允许压缩的响应缓冲区最小字节数默认2048
server.compression.min-response-size = 2048 server.compression.min-response-size = 2048
#压缩格式 #压缩格式
@@ -110,6 +110,14 @@ convertMedias = ${KK_CONVERTMEDIAS:avi,mov,wmv,mkv,3gp,rm}
#PDF预览模块设置 #PDF预览模块设置
#配置PDF文件生成图片的像素大小dpi 越高图片质量越清晰同时也会消耗更多的计算资源 #配置PDF文件生成图片的像素大小dpi 越高图片质量越清晰同时也会消耗更多的计算资源
pdf2jpg.dpi = ${KK_PDF2JPG_DPI:144} pdf2jpg.dpi = ${KK_PDF2JPG_DPI:144}
#PDF转换超时设置 (低于50页) 温馨提示这里数字仅供参考
pdf.timeout =${KK_pdf_TIMEOUT:90}
#PDF转换超时设置 (高于50小于200页)
pdf.timeout80 =${KK_PDF_TIMEOUT80:180}
#PDF转换超时设置 (大于200页)
pdf.timeout200 =${KK_PDF_TIMEOUT200:300}
#PDF转换线程设置
pdf.thread =${KK_PDF_THREAD:5}
#是否禁止演示模式 #是否禁止演示模式
pdf.presentationMode.disable = ${KK_PDF_PRESENTATION_MODE_DISABLE:true} pdf.presentationMode.disable = ${KK_PDF_PRESENTATION_MODE_DISABLE:true}
#是否禁止打开文件 #是否禁止打开文件
@@ -154,7 +162,7 @@ watermark.angle = ${WATERMARK_ANGLE:10}
#首页功能设置 #首页功能设置
#是否禁用首页文件上传 #是否禁用首页文件上传
file.upload.disable = ${KK_FILE_UPLOAD_ENABLED:false} file.upload.disable = ${KK_FILE_UPLOAD_DISABLE:false}
# 备案信息默认为空 # 备案信息默认为空
beian = ${KK_BEIAN:default} beian = ${KK_BEIAN:default}
#禁止上传类型 #禁止上传类型

View File

@@ -67,6 +67,10 @@ public class ConfigConstants {
private static String homePagination; private static String homePagination;
private static String homePageSize; private static String homePageSize;
private static String homeSearch; private static String homeSearch;
private static int pdfTimeout;
private static int pdfTimeout80;
private static int pdfTimeout200;
private static int pdfThread;
public static final String DEFAULT_CACHE_ENABLED = "true"; 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,xbrl"; public static final String DEFAULT_TXT_TYPE = "txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd,xbrl";
@@ -107,6 +111,10 @@ public class ConfigConstants {
public static final String DEFAULT_HOME_PAGINATION = "true"; public static final String DEFAULT_HOME_PAGINATION = "true";
public static final String DEFAULT_HOME_PAGSIZE = "15"; public static final String DEFAULT_HOME_PAGSIZE = "15";
public static final String DEFAULT_HOME_SEARCH = "true"; public static final String DEFAULT_HOME_SEARCH = "true";
public static final String DEFAULT_PDF_TIMEOUT = "90";
public static final String DEFAULT_PDF_TIMEOUT80 = "180";
public static final String DEFAULT_PDF_TIMEOUT200 = "300";
public static final String DEFAULT_PDF_THREAD = "5";
public static Boolean isCacheEnabled() { public static Boolean isCacheEnabled() {
return cacheEnabled; return cacheEnabled;
@@ -580,6 +588,65 @@ public class ConfigConstants {
ConfigConstants.cadThread = cadThread; ConfigConstants.cadThread = cadThread;
} }
/**
* 以下为pdf转换模块设置
*/
public static int getPdfTimeout() {
return pdfTimeout;
}
@Value("${pdf.timeout:90}")
public void setPdfTimeout(int pdfTimeout) {
setPdfTimeoutValue(pdfTimeout);
}
public static void setPdfTimeoutValue(int pdfTimeout) {
ConfigConstants.pdfTimeout = pdfTimeout;
}
public static int getPdfTimeout80() {
return pdfTimeout80;
}
@Value("${pdf.timeout80:180}")
public void setPdfTimeout80(int pdfTimeout80) {
setPdfTimeout80Value(pdfTimeout80);
}
public static void setPdfTimeout80Value(int pdfTimeout80) {
ConfigConstants.pdfTimeout80 = pdfTimeout80;
}
public static int getPdfTimeout200() {
return pdfTimeout200;
}
@Value("${pdf.timeout200:300}")
public void setPdfTimeout200(int pdfTimeout200) {
setPdfTimeout200Value(pdfTimeout200);
}
public static void setPdfTimeout200Value(int pdfTimeout200) {
ConfigConstants.pdfTimeout200 = pdfTimeout200;
}
public static int getPdfThread() {
return pdfThread;
}
@Value("${pdf.thread:5}")
public void setPdfThread(int pdfThread) {
setPdfThreadValue(pdfThread);
}
public static void setPdfThreadValue(int pdfThread) {
ConfigConstants.pdfThread = pdfThread;
}
/** /**
* 以下为OFFICE转换模块设置 * 以下为OFFICE转换模块设置
*/ */

View File

@@ -78,6 +78,10 @@ public class ConfigRefreshComponent {
String homePagination; String homePagination;
String homePageSize; String homePageSize;
String homeSearch; String homeSearch;
int pdfTimeout;
int pdfTimeout80;
int pdfTimeout200;
int pdfThread;
while (true) { while (true) {
FileReader fileReader = new FileReader(configFilePath); FileReader fileReader = new FileReader(configFilePath);
BufferedReader bufferedReader = new BufferedReader(fileReader); BufferedReader bufferedReader = new BufferedReader(fileReader);
@@ -126,6 +130,10 @@ public class ConfigRefreshComponent {
homePageSize = properties.getProperty("home.pagesize", ConfigConstants.DEFAULT_HOME_PAGSIZE); homePageSize = properties.getProperty("home.pagesize", ConfigConstants.DEFAULT_HOME_PAGSIZE);
homeSearch = properties.getProperty("home.search", ConfigConstants.DEFAULT_HOME_SEARCH); homeSearch = properties.getProperty("home.search", ConfigConstants.DEFAULT_HOME_SEARCH);
cadThread = Integer.parseInt(properties.getProperty("cad.thread", ConfigConstants.DEFAULT_CAD_THREAD)); cadThread = Integer.parseInt(properties.getProperty("cad.thread", ConfigConstants.DEFAULT_CAD_THREAD));
pdfTimeout = Integer.parseInt(properties.getProperty("pdf.timeout", ConfigConstants.DEFAULT_PDF_TIMEOUT));
pdfTimeout80 = Integer.parseInt(properties.getProperty("pdf.timeout80", ConfigConstants.DEFAULT_PDF_TIMEOUT80));
pdfTimeout200 = Integer.parseInt(properties.getProperty("pdf.timeout200", ConfigConstants.DEFAULT_PDF_TIMEOUT200));
pdfThread = Integer.parseInt(properties.getProperty("pdf.thread", ConfigConstants.DEFAULT_PDF_THREAD));
prohibitArray = prohibit.split(","); prohibitArray = prohibit.split(",");
ConfigConstants.setCacheEnabledValueValue(cacheEnabled); ConfigConstants.setCacheEnabledValueValue(cacheEnabled);
@@ -169,6 +177,10 @@ public class ConfigRefreshComponent {
ConfigConstants.setHomePaginationValue(homePagination); ConfigConstants.setHomePaginationValue(homePagination);
ConfigConstants.setHomePageSizeValue(homePageSize); ConfigConstants.setHomePageSizeValue(homePageSize);
ConfigConstants.setHomeSearchValue(homeSearch); ConfigConstants.setHomeSearchValue(homeSearch);
ConfigConstants.setPdfTimeoutValue(pdfTimeout);
ConfigConstants.setPdfTimeout80Value(pdfTimeout80);
ConfigConstants.setPdfTimeout200Value(pdfTimeout200);
ConfigConstants.setPdfThreadValue(pdfThread);
setWatermarkConfig(properties); setWatermarkConfig(properties);
bufferedReader.close(); bufferedReader.close();
fileReader.close(); fileReader.close();

View File

@@ -12,12 +12,19 @@ import net.sf.sevenzipjbinding.SevenZipException;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream; import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive; import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem; import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import java.io.*; import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -37,73 +44,70 @@ public class CompressFileReader {
public String unRar(String filePath, String filePassword, String fileName, FileAttribute fileAttribute) throws Exception { public String unRar(String filePath, String filePassword, String fileName, FileAttribute fileAttribute) throws Exception {
List<String> imgUrls = new ArrayList<>(); List<String> imgUrls = new ArrayList<>();
String baseUrl = BaseUrlFilter.getBaseUrl(); String baseUrl = BaseUrlFilter.getBaseUrl();
String packagePath = "_"; //防止文件名重复 压缩包统一生成文件添加_符号 String packagePath = "_";
String folderName = filePath.replace(fileDir, ""); //修复压缩包 多重目录获取路径错误 String folderName = filePath.replace(fileDir, ""); //修复压缩包 多重目录获取路径错误
if (fileAttribute.isCompressFile()) { //压缩包文件 直接赋予路径 不予下载 if (fileAttribute.isCompressFile()) {
folderName = "_decompression" + folderName; //重新修改多重压缩包 生成文件路径 folderName = "_decompression" + folderName;
} }
RandomAccessFile randomAccessFile = null; Path folderPath = Paths.get(fileDir, folderName + packagePath);
IInArchive inArchive = null; Files.createDirectories(folderPath);
try {
randomAccessFile = new RandomAccessFile(filePath, "r"); try (RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "r");
inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile)); IInArchive inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile))) {
ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface(); ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
final String[] str = {null};
for (final ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) { for (final ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
if (!item.isFolder()) { if (!item.isFolder()) {
ExtractOperationResult result; final Path filePathInsideArchive = getFilePathInsideArchive(item, folderPath);
String finalFolderName = folderName; ExtractOperationResult result = item.extractSlow(data -> {
result = item.extractSlow(data -> { try (OutputStream out = new BufferedOutputStream(new FileOutputStream(filePathInsideArchive.toFile(), true))) {
try { out.write(data);
str[0] = RarUtils.getUtf8String(item.getPath()); } catch (IOException e) {
if (RarUtils.isMessyCode(str[0])) { throw new RuntimeException(e);
str[0] = new String(item.getPath().getBytes(StandardCharsets.ISO_8859_1), "gbk");
}
str[0] = str[0].replace("\\", File.separator); //Linux 下路径错误
String str1 = str[0].substring(0, str[0].lastIndexOf(File.separator) + 1);
File file = new File(fileDir, finalFolderName + packagePath + File.separator + str1);
if (!file.exists()) {
file.mkdirs();
}
OutputStream out = new FileOutputStream(fileDir + finalFolderName + packagePath + File.separator + str[0], true);
IOUtils.write(data, out);
out.close();
} catch (Exception e) {
e.printStackTrace();
return Integer.parseInt(null);
} }
return data.length; return data.length;
}, filePassword); }, filePassword);
if (result == ExtractOperationResult.OK) { if (result != ExtractOperationResult.OK) {
FileType type = FileType.typeFromUrl(str[0]); ExtractOperationResult result1 = ExtractOperationResult.valueOf("WRONG_PASSWORD");
if (type.equals(FileType.PICTURE)) { if (result1.equals(result)) {
imgUrls.add(baseUrl + folderName + packagePath + "/" + str[0].replace("\\", "/")); throw new Exception("Password");
} }else {
fileHandlerService.putImgCache(fileName + packagePath, imgUrls); throw new Exception("Failed to extract RAR file.");
} else {
return null;
}
}
}
return folderName + packagePath;
} catch (Exception e) {
throw new Exception(e);
} finally {
if (inArchive != null) {
try {
inArchive.close();
} catch (SevenZipException e) {
System.err.println("Error closing archive: " + e);
}
}
if (randomAccessFile != null) {
try {
randomAccessFile.close();
} catch (IOException e) {
System.err.println("Error closing file: " + e);
}
}
} }
} }
FileType type = FileType.typeFromUrl(filePathInsideArchive.toString());
if (type.equals(FileType.PICTURE)) { //图片缓存到集合,为了特殊符号需要进行编码
imgUrls.add(baseUrl + URLEncoder.encode(fileName + packagePath+"/"+ folderPath.relativize(filePathInsideArchive).toString().replace("\\", "/"), "UTF-8"));
}
}
}
fileHandlerService.putImgCache(fileName + packagePath, imgUrls);
} catch (Exception e) {
throw new Exception("Error processing RAR file: " + e.getMessage(), e);
}
return folderName + packagePath;
}
private Path getFilePathInsideArchive(ISimpleInArchiveItem item, Path folderPath) throws SevenZipException, UnsupportedEncodingException {
String insideFileName = RarUtils.getUtf8String(item.getPath());
if (RarUtils.isMessyCode(insideFileName)) {
insideFileName = new String(item.getPath().getBytes(StandardCharsets.ISO_8859_1), "gbk");
}
// 正规化路径并验证是否安全
Path normalizedPath = folderPath.resolve(insideFileName).normalize();
if (!normalizedPath.startsWith(folderPath)) {
throw new SecurityException("Unsafe path detected: " + insideFileName);
}
try {
Files.createDirectories(normalizedPath.getParent());
} catch (IOException e) {
throw new RuntimeException("Failed to create directory: " + normalizedPath.getParent(), e);
}
return normalizedPath;
}
} }

View File

@@ -14,8 +14,8 @@ import com.aspose.cad.*;
import com.aspose.cad.fileformats.cad.CadDrawTypeMode; import com.aspose.cad.fileformats.cad.CadDrawTypeMode;
import com.aspose.cad.fileformats.tiff.enums.TiffExpectedFormat; import com.aspose.cad.fileformats.tiff.enums.TiffExpectedFormat;
import com.aspose.cad.imageoptions.*; import com.aspose.cad.imageoptions.*;
import com.itextpdf.text.pdf.PdfReader;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType; import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.rendering.PDFRenderer;
@@ -37,7 +37,10 @@ import java.io.*;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@@ -236,9 +239,9 @@ public class FileHandlerService implements InitializingBean {
boolean forceUpdatedCache = fileAttribute.forceUpdatedCache(); boolean forceUpdatedCache = fileAttribute.forceUpdatedCache();
boolean usePasswordCache = fileAttribute.getUsePasswordCache(); boolean usePasswordCache = fileAttribute.getUsePasswordCache();
String filePassword = fileAttribute.getFilePassword(); String filePassword = fileAttribute.getFilePassword();
String pdfPassword = null; PDDocument doc;
PDDocument doc = null; final String[] pdfPassword = {null};
PdfReader pdfReader = null; final int[] pageCount = new int[1];
if (!forceUpdatedCache) { if (!forceUpdatedCache) {
List<String> cacheResult = this.loadPdf2jpgCache(pdfFilePath); List<String> cacheResult = this.loadPdf2jpgCache(pdfFilePath);
if (!CollectionUtils.isEmpty(cacheResult)) { if (!CollectionUtils.isEmpty(cacheResult)) {
@@ -246,64 +249,77 @@ public class FileHandlerService implements InitializingBean {
} }
} }
List<String> imageUrls = new ArrayList<>(); List<String> imageUrls = new ArrayList<>();
try {
File pdfFile = new File(fileNameFilePath); File pdfFile = new File(fileNameFilePath);
if (!pdfFile.exists()) { if (!pdfFile.exists()) {
return null; return null;
} }
doc = PDDocument.load(pdfFile, filePassword);
doc.setResourceCache(new NotResourceCache());
int pageCount = doc.getNumberOfPages();
PDFRenderer pdfRenderer = new PDFRenderer(doc);
int index = pdfFilePath.lastIndexOf("."); int index = pdfFilePath.lastIndexOf(".");
String folder = pdfFilePath.substring(0, index); String folder = pdfFilePath.substring(0, index);
File path = new File(folder); File path = new File(folder);
if (!path.exists() && !path.mkdirs()) { if (!path.exists() && !path.mkdirs()) {
logger.error("创建转换文件【{}】目录失败,请检查目录权限!", folder); logger.error("创建转换文件【{}】目录失败,请检查目录权限!", folder);
} }
String imageFilePath;
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
imageFilePath = folder + File.separator + pageIndex + PDF2JPG_IMAGE_FORMAT;
BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, ConfigConstants.getPdf2JpgDpi(), ImageType.RGB);
ImageIOUtil.writeImage(image, imageFilePath, ConfigConstants.getPdf2JpgDpi());
String imageUrl = this.getPdf2jpgUrl(pdfFilePath, pageIndex);
imageUrls.add(imageUrl);
}
try { try {
if (!ObjectUtils.isEmpty(filePassword)) { //获取到密码 判断是否是加密文件 doc = Loader.loadPDF(pdfFile, filePassword);
pdfReader = new PdfReader(fileNameFilePath); //读取PDF文件 通过异常获取该文件是否有密码字符 doc.setResourceCache(new NotResourceCache());
} pageCount[0] = doc.getNumberOfPages();
} catch (Exception e) { //获取异常方法 判断是否有加密字符串 } catch (IOException e) {
Throwable[] throwableArray = ExceptionUtils.getThrowables(e); Throwable[] throwableArray = ExceptionUtils.getThrowables(e);
for (Throwable throwable : throwableArray) { for (Throwable throwable : throwableArray) {
if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) { if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) {
if (e.getMessage().toLowerCase().contains(PDF_PASSWORD_MSG)) { if (e.getMessage().toLowerCase().contains(PDF_PASSWORD_MSG)) {
pdfPassword = PDF_PASSWORD_MSG; //查询到该文件是密码文件 输出带密码的值 pdfPassword[0] = PDF_PASSWORD_MSG; //查询到该文件是密码文件 输出带密码的值
} }
} }
} }
if (!PDF_PASSWORD_MSG.equals(pdfPassword)) { //该文件异常 错误原因非密码原因输出错误 if (!PDF_PASSWORD_MSG.equals(pdfPassword[0])) { //该文件异常 错误原因非密码原因输出错误
logger.error("Convert pdf exception, pdfFilePath{}", pdfFilePath, e); logger.error("Convert pdf exception, pdfFilePath{}", pdfFilePath, e);
} }
throw new Exception(e);
} finally {
if (pdfReader != null) { //关闭
pdfReader.close();
} }
Callable <List<String>> call = () -> {
try {
String imageFilePath;
BufferedImage image = null;
PDFRenderer pdfRenderer = new PDFRenderer(doc);
pdfRenderer.setSubsamplingAllowed(true);
for (int pageIndex = 0; pageIndex < pageCount[0]; pageIndex++) {
imageFilePath = folder + File.separator + pageIndex + PDF2JPG_IMAGE_FORMAT;
image = pdfRenderer.renderImageWithDPI(pageIndex, ConfigConstants.getPdf2JpgDpi(), ImageType.RGB);
ImageIOUtil.writeImage(image, imageFilePath, ConfigConstants.getPdf2JpgDpi());
String imageUrl = this.getPdf2jpgUrl(pdfFilePath, pageIndex);
imageUrls.add(imageUrl);
} }
image.flush();
if (usePasswordCache || !PDF_PASSWORD_MSG.equals(pdfPassword)) { //加密文件 判断是否启用缓存命令
this.addPdf2jpgCache(pdfFilePath, pageCount);
}
} catch (IOException e) { } catch (IOException e) {
if (!e.getMessage().contains(PDF_PASSWORD_MSG)) {
logger.error("Convert pdf to jpg exception, pdfFilePath{}", pdfFilePath, e);
}
throw new Exception(e); throw new Exception(e);
} finally { } finally {
if (doc != null) { //关闭
doc.close(); doc.close();
} }
return imageUrls;
};
Future<List<String>> result = pool.submit(call);
int pdftimeout;
if(pageCount[0] <=50){
pdftimeout = ConfigConstants.getPdfTimeout();
}else if(pageCount[0] <=200){
pdftimeout = ConfigConstants.getPdfTimeout80();
}else {
pdftimeout = ConfigConstants.getPdfTimeout200();
}
try {
result.get(pdftimeout, TimeUnit.SECONDS);
// 如果在超时时间内没有数据返回则抛出TimeoutException异常
} catch (InterruptedException | ExecutionException e) {
throw new Exception(e);
} catch (TimeoutException e) {
throw new Exception("overtime");
} finally {
//关闭
doc.close();
}
if (usePasswordCache || ObjectUtils.isEmpty(filePassword)) { //加密文件 判断是否启用缓存命令
this.addPdf2jpgCache(pdfFilePath, pageCount[0]);
} }
return imageUrls; return imageUrls;
} }
@@ -458,11 +474,7 @@ public class FileHandlerService implements InitializingBean {
boolean isCompressFile = !ObjectUtils.isEmpty(compressFileKey); boolean isCompressFile = !ObjectUtils.isEmpty(compressFileKey);
if (isCompressFile) { //判断是否使用特定压缩包符号 if (isCompressFile) { //判断是否使用特定压缩包符号
try { try {
// http://127.0.0.1:8012/各类型文件1 - 副本.zip_/各类型文件/正常预览/PPT转的PDF.pdf?kkCompressfileKey=各类型文件1 - 副本.zip_ originFileName = URLDecoder.decode(originFileName, uriEncoding); //转义的文件名 解下出原始文件名
// http://127.0.0.1:8012/preview/各类型文件1 - 副本.zip_/各类型文件/正常预览/PPT转的PDF.pdf?kkCompressfileKey=各类型文件1 - 副本.zip_ 获取路径就会错误 需要下面的方法
String urlStrr = getSubString(compressFilePath, compressFileKey); //反代情况下添加前缀,只获取有压缩包字符的路径
originFileName = compressFileKey + urlStrr.trim(); //拼接完整路径
originFileName = URLDecoder.decode(originFileName, uriEncoding); //压缩包文件中文编码问题
attribute.setSkipDownLoad(true); attribute.setSkipDownLoad(true);
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
e.printStackTrace(); e.printStackTrace();

View File

@@ -2,9 +2,12 @@ package cn.keking.service.cache;
import org.apache.pdfbox.cos.COSObject; import org.apache.pdfbox.cos.COSObject;
import org.apache.pdfbox.pdmodel.DefaultResourceCache; import org.apache.pdfbox.pdmodel.DefaultResourceCache;
import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDPropertyList;
import org.apache.pdfbox.pdmodel.graphics.PDXObject; import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
import java.io.IOException; import org.apache.pdfbox.pdmodel.graphics.pattern.PDAbstractPattern;
import org.apache.pdfbox.pdmodel.graphics.shading.PDShading;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
/** /**
* @author: Sawyer.Yong * @author: Sawyer.Yong
@@ -14,7 +17,21 @@ import java.io.IOException;
public class NotResourceCache extends DefaultResourceCache { public class NotResourceCache extends DefaultResourceCache {
@Override @Override
public void put(COSObject indirect, PDXObject xobject) throws IOException { public void put(COSObject indirect, PDColorSpace colorSpace) {
// do nothing }
@Override
public void put(COSObject indirect, PDExtendedGraphicsState extGState) {
}
@Override
public void put(COSObject indirect, PDShading shading) {
}
@Override
public void put(COSObject indirect, PDAbstractPattern pattern) {
}
@Override
public void put(COSObject indirect, PDPropertyList propertyList) {
}
@Override
public void put(COSObject indirect, PDXObject xobject) {
} }
} }

View File

@@ -10,6 +10,7 @@ import cn.keking.service.CompressFileReader;
import cn.keking.utils.KkFileUtils; import cn.keking.utils.KkFileUtils;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.poi.EncryptedDocumentException; import org.apache.poi.EncryptedDocumentException;
import org.slf4j.Logger;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@@ -28,6 +29,9 @@ public class CompressFilePreviewImpl implements FilePreview {
private final CompressFileReader compressFileReader; private final CompressFileReader compressFileReader;
private final OtherFilePreviewImpl otherFilePreview; private final OtherFilePreviewImpl otherFilePreview;
private static final String Rar_PASSWORD_MSG = "password"; private static final String Rar_PASSWORD_MSG = "password";
private static final Logger logger = org.slf4j.LoggerFactory.getLogger(CompressFileReader.class);
public CompressFilePreviewImpl(FileHandlerService fileHandlerService, CompressFileReader compressFileReader, OtherFilePreviewImpl otherFilePreview) { public CompressFilePreviewImpl(FileHandlerService fileHandlerService, CompressFileReader compressFileReader, OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService; this.fileHandlerService = fileHandlerService;
this.compressFileReader = compressFileReader; this.compressFileReader = compressFileReader;
@@ -36,9 +40,9 @@ public class CompressFilePreviewImpl implements FilePreview {
@Override @Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) { public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String fileName=fileAttribute.getName(); String fileName = fileAttribute.getName();
String filePassword = fileAttribute.getFilePassword(); String filePassword = fileAttribute.getFilePassword();
boolean forceUpdatedCache=fileAttribute.forceUpdatedCache(); boolean forceUpdatedCache = fileAttribute.forceUpdatedCache();
String fileTree = null; String fileTree = null;
// 判断文件名是否存在(redis缓存读取) // 判断文件名是否存在(redis缓存读取)
if (forceUpdatedCache || !StringUtils.hasText(fileHandlerService.getConvertedFile(fileName)) || !ConfigConstants.isCacheEnabled()) { if (forceUpdatedCache || !StringUtils.hasText(fileHandlerService.getConvertedFile(fileName)) || !ConfigConstants.isCacheEnabled()) {
@@ -48,16 +52,13 @@ public class CompressFilePreviewImpl implements FilePreview {
} }
String filePath = response.getContent(); String filePath = response.getContent();
try { try {
fileTree = compressFileReader.unRar(filePath, filePassword,fileName, fileAttribute); fileTree = compressFileReader.unRar(filePath, filePassword, fileName, fileAttribute);
} catch (Exception e) { } catch (Exception e) {
Throwable[] throwableArray = ExceptionUtils.getThrowables(e);
for (Throwable throwable : throwableArray) {
if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) {
if (e.getMessage().toLowerCase().contains(Rar_PASSWORD_MSG)) { if (e.getMessage().toLowerCase().contains(Rar_PASSWORD_MSG)) {
model.addAttribute("needFilePassword", true); model.addAttribute("needFilePassword", true);
return EXEL_FILE_PREVIEW_PAGE; return EXEL_FILE_PREVIEW_PAGE;
} }else {
} logger.error("Error processing RAR file: " + e.getMessage(), e);
} }
} }
if (!ObjectUtils.isEmpty(fileTree)) { if (!ObjectUtils.isEmpty(fileTree)) {
@@ -69,8 +70,8 @@ public class CompressFilePreviewImpl implements FilePreview {
// 加入缓存 // 加入缓存
fileHandlerService.addConvertedFile(fileName, fileTree); fileHandlerService.addConvertedFile(fileName, fileTree);
} }
}else { } else {
return otherFilePreview.notSupportedFile(model, fileAttribute, "压缩文件密码错误! 压缩文件损坏! 压缩文件类型不受支持!"); return otherFilePreview.notSupportedFile(model, fileAttribute, "压缩文件无法处理!");
} }
} else { } else {
fileTree = fileHandlerService.getConvertedFile(fileName); fileTree = fileHandlerService.getConvertedFile(fileName);

View File

@@ -86,6 +86,9 @@ public class LocalOfficeUtils {
"/opt/libreoffice7.4", "/opt/libreoffice7.4",
"/opt/libreoffice7.5", "/opt/libreoffice7.5",
"/opt/libreoffice7.6", "/opt/libreoffice7.6",
"/opt/libreoffice24.2",
"/opt/libreoffice24.8",
"/opt/libreoffice25.2",
"/usr/lib64/libreoffice", "/usr/lib64/libreoffice",
"/usr/lib/libreoffice", "/usr/lib/libreoffice",
"/usr/local/lib64/libreoffice", "/usr/local/lib64/libreoffice",

View File

@@ -77,7 +77,7 @@ public class RarUtils {
} }
public static String specialSymbols(String str) { public static String specialSymbols(String str) {
//去除压缩包文件字符串中特殊符号 //去除压缩包文件字符串中特殊符号
Pattern p = Pattern.compile("\\s|\t|\r|\n|\\+|#|&|=|\\p{P}"); Pattern p = Pattern.compile("\\s|\t|\r|\n|\\+|#|&|=|<EFBFBD>|\\p{P}");
// Pattern p = Pattern.compile("\\s|\\+|#|&|=|\\p{P}"); // Pattern p = Pattern.compile("\\s|\\+|#|&|=|\\p{P}");
Matcher m = p.matcher(str); Matcher m = p.matcher(str);
return m.replaceAll(""); return m.replaceAll("");

View File

@@ -79,7 +79,9 @@ public class WebUtils {
urlStr = clearFullfilenameParam(urlStr); urlStr = clearFullfilenameParam(urlStr);
} else { } else {
fullFileName = getFileNameFromURL(urlStr); //获取文件名 fullFileName = getFileNameFromURL(urlStr); //获取文件名
}
if (KkFileUtils.isIllegalFileName(fullFileName)) { //判断文件名是否带有穿越漏洞
return null;
} }
if (!UrlEncoderUtils.hasUrlEncoded(fullFileName)) { //判断文件名是否转义 if (!UrlEncoderUtils.hasUrlEncoded(fullFileName)) { //判断文件名是否转义
try { try {

View File

@@ -213,6 +213,7 @@ public class FileController {
String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "url"); String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "url");
return ReturnResponse.failure(errorMsg); return ReturnResponse.failure(errorMsg);
} }
fileUrl = fileUrl.replaceAll("http://", "");
if (KkFileUtils.isIllegalFileName(fileUrl)) { if (KkFileUtils.isIllegalFileName(fileUrl)) {
return ReturnResponse.failure("不允许访问的路径:"); return ReturnResponse.failure("不允许访问的路径:");
} }

View File

@@ -21,6 +21,7 @@ import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@@ -76,7 +77,11 @@ public class OnlinePreviewController {
model.addAttribute("file", fileAttribute); model.addAttribute("file", fileAttribute);
FilePreview filePreview = previewFactory.get(fileAttribute); FilePreview filePreview = previewFactory.get(fileAttribute);
logger.info("预览文件url{}previewType{}", fileUrl, fileAttribute.getType()); logger.info("预览文件url{}previewType{}", fileUrl, fileAttribute.getType());
return filePreview.filePreviewHandle(WebUtils.urlEncoderencode(fileUrl), model, fileAttribute); //统一在这里处理 url fileUrl =WebUtils.urlEncoderencode(fileUrl);
if (ObjectUtils.isEmpty(fileUrl)) {
return otherFilePreview.notSupportedFile(model, "非法路径,不允许访问");
}
return filePreview.filePreviewHandle(fileUrl, model, fileAttribute); //统一在这里处理 url
} }
@GetMapping( "/picturesPreview") @GetMapping( "/picturesPreview")

View File

@@ -7,7 +7,7 @@
|_|\_\ |_|\_\ |_| |_| |_| \___| \/ |_| \___| \_/\_/ |_|\_\ |_|\_\ |_| |_| |_| \___| \/ |_| \___| \_/\_/
=> Spring Boot :: ${spring-boot.version} => Spring Boot :: ${spring-boot.version}
=> kkFileView :: 4.4.0-beta => kkFileView :: 4.4.0
=> Home site :: https://kkview.cn => Home site :: https://kkview.cn
=> Github :: https://github.com/kekingcn/kkFileView => Github :: https://github.com/kekingcn/kkFileView
=> Gitee :: https://gitee.com/kekingcn/file-online-preview => Gitee :: https://gitee.com/kekingcn/file-online-preview

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@
<html> <html>
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<title>压缩包预览</title> <title>${file.name}压缩包预览</title>
<script src="js/jquery-3.6.1.min.js"></script> <script src="js/jquery-3.6.1.min.js"></script>
<#include "*/commonHeader.ftl"> <#include "*/commonHeader.ftl">
<script src="js/base64.min.js" type="text/javascript"></script> <script src="js/base64.min.js" type="text/javascript"></script>
@@ -49,14 +49,28 @@
onClick: chooseNode, onClick: chooseNode,
} }
}; };
function isNotEmpty(value) {
return value !== null && value !== undefined && value !== '' && value !== 0 && !(value instanceof Array && value.length === 0) && !isNaN(value);
}
function getQueryParam(url, param) {
var urlObj = new URL(url);
return urlObj.searchParams.get(param);
}
var currentUrl = window.location.href;
var keyword = getQueryParam(currentUrl, 'watermarkTxt');
function chooseNode(event, treeId, treeNode) { function chooseNode(event, treeId, treeNode) {
if (!treeNode.isParent) { if (!treeNode.isParent) {
var path = '${baseUrl}' + treeNode.id + "?kkCompressfileKey=" + encodeURIComponent('${fileTree}')+"&kkCompressfilepath=" + encodeURIComponent(treeNode.id)+"&fullfilename="+encodeURIComponent(treeNode.name); var path = '${baseUrl}'+treeNode.id+"?kkCompressfileKey="+'${fileTree}'+"&kkCompressfilepath="+encodeURIComponent(treeNode.id)+"&fullfilename="+encodeURIComponent(treeNode.name);
location.href = "${baseUrl}onlinePreview?url=" + encodeURIComponent(Base64.encode(path)); if (isNotEmpty(keyword)){
location.href = "${baseUrl}onlinePreview?url=" + encodeURIComponent(Base64.encode(path))+"&watermarkTxt="+keyword;
}else{
location.href = "${baseUrl}onlinePreview?url=" + encodeURIComponent(Base64.encode(path));}
} }
} }
$(document).ready(function () { $(document).ready(function () {
var url = '${fileTree}'; var url = "http://"+'${fileTree}'; //添加http协议方法
$.ajax({ $.ajax({
type: "get", type: "get",
url: "${baseUrl}directory?urls="+encodeURIComponent(Base64.encode(url)), url: "${baseUrl}directory?urls="+encodeURIComponent(Base64.encode(url)),
@@ -66,6 +80,9 @@
} }
}); });
}); });
window.onload = function () {
initWaterMark();
}
</script> </script>
</body> </body>
</html> </html>

View File

@@ -33,6 +33,63 @@
<div class="page-header"> <div class="page-header">
<h1>版本发布记录</h1> <h1>版本发布记录</h1>
</div> </div>
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">2025年01月16日v4.4.0版本</h3>
</div>
<div class="panel-body">
<div>
<h4>优化</h4>
1. 优化 OFD 移动端预览 页面不自适应 <br>
2. 更新 xlsx 前端解析组件,加速解析速度 <br>
3. 升级 CAD 组件 <br>
4. office 功能调整,支持批注、转换页码限制、生成水印等功能 <br>
5. 升级 markdown 组件 <br>
6. 升级 dcm 解析组件 <br>
7. 升级 PDF.JS 解析组件 <br>
8. 更换视频播放插件为 ckplayer <br>
9. tif 解析更加智能化,支持被修改的图片格式 <br>
10. 针对大小文本文件检测字符编码的正确率,处理并发隐患 <br>
11. 重构下载文件的代码,新增通用的文件服务器认证访问的设计 <br>
12. 更新 bootstrap 组件,并精简掉不需要的文件 <br>
13. 更新 epub 版本,优化 epub 显示效果 <br>
14. 解决定时清除缓存时,对于多媒体类型文件,只删除了磁盘缓存文件 <br>
15. 自动检测已安装 Office 组件,增加 LibreOffice 7.5 & 7.6 版本默认路径 <br>
16. 修改 drawio 默认为预览模式 <br>
17. 新增 PDF 线程管理、超时管理、内存缓存管理,更新 PDF 解析组件版本 <br>
18. 优化 Dockerfile支持真正的跨平台构建镜像 <br>
<br>
<h4>新增</h4>
1. xlsx 新增支持打印功能 <br>
2. 配置文件新增启用 GZIP 压缩 <br>
3. CAD 格式新增支持转换成 SVG 和 TIF 格式,新增超时结束、线程管理 <br>
4. 新增删除文件使用验证码校验 <br>
5. 新增 xbrl 格式预览支持 <br>
6. PDF 预览新增控制签名、绘图、插图控制、搜索定位页码、定义显示内容等功能 <br>
7. 新增 CSV 格式前端解析支持 <br>
8. 新增 ARM64 下的 Docker 镜像支持 <br>
9. 新增 Office 预览支持转换超时属性设置功能 <br>
10. 新增预览文件 host 黑名单机制 <br>
<br>
<h4>修复</h4>
1. 修复 forceUpdatedCache 属性设置,但本地缓存文件不更新的问题 <br>
2. 修复 PDF 解密加密文件转换成功后后台报错的问题 <br>
3. 修复 BPMN 不支持跨域的问题 <br>
4. 修复压缩包二级反代特殊符号错误问题 <br>
5. 修复视频跨域配置导致视频无法预览的问题 <br>
6. 修复 TXT 文本类分页二次加载问题 <br>
7. 修复 Drawio 缺少 Base64 组件的问题 <br>
8. 修复 Markdown 被转义问题 <br>
9. 修复 EPUB 跨域报错问题 <br>
10. 修复 URL 特殊符号问题 <br>
11. 修复压缩包穿越漏洞 <br>
12. 修复压缩获取路径错误、图片合集路径错误、水印问题等 BUG <br>
13. 修复前端解析 XLSX 包含 EMF 格式文件错误问题 <br>
</div>
</div>
</div>
<div class="panel panel-success"> <div class="panel panel-success">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">2024年04月15日v4.4.0-beta版本</h3> <h3 class="panel-title">2024年04月15日v4.4.0-beta版本</h3>

View File

@@ -123,7 +123,7 @@
title: exportJson.info.name, title: exportJson.info.name,
userInfo: exportJson.info.name.creator, userInfo: exportJson.info.name.creator,
}); });
}); }, 1000);
} }
loadText(); loadText();
// 打印时获取luckysheet指定区域html内容拼接至div隐藏luckysheet容器并显示打印区域html // 打印时获取luckysheet指定区域html内容拼接至div隐藏luckysheet容器并显示打印区域html