mirror of
https://github.com/zfile-dev/zfile.git
synced 2025-04-19 05:34:52 +00:00
Compare commits
190 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b6ed1c78d | ||
|
|
5d888bb68f | ||
|
|
082bb07213 | ||
|
|
803b8cdf71 | ||
|
|
eca5f7e48b | ||
|
|
b70c37f3f0 | ||
|
|
5cb2844141 | ||
|
|
4dd6cdb4b3 | ||
|
|
c6127c029f | ||
|
|
e149039ecb | ||
|
|
37688d83cf | ||
|
|
6692016642 | ||
|
|
3f41aeda9a | ||
|
|
de86d5c47d | ||
|
|
c89e072005 | ||
|
|
b533b5e959 | ||
|
|
d35cf27d47 | ||
|
|
796c4c1fb0 | ||
|
|
1033d6c1a9 | ||
|
|
e09c6b4e58 | ||
|
|
168b0b08f3 | ||
|
|
60a6a5348c | ||
|
|
2ec8a5df1f | ||
|
|
47b5f6ac12 | ||
|
|
c64c8465f2 | ||
|
|
f636681dd8 | ||
|
|
eadd2434e0 | ||
|
|
b84c0bff42 | ||
|
|
4cb5b84bfe | ||
|
|
0351b4401c | ||
|
|
48cb14be8a | ||
|
|
eea2ff11f9 | ||
|
|
325ec1a348 | ||
|
|
93205266d3 | ||
|
|
6849a4347f | ||
|
|
cb5c6a5945 | ||
|
|
5ed45c3bb3 | ||
|
|
4442ec3165 | ||
|
|
b268a24333 | ||
|
|
84f9354d4e | ||
|
|
8dfc4f8004 | ||
|
|
1158f5c2b9 | ||
|
|
c5f0e15207 | ||
|
|
68a842ce75 | ||
|
|
3bd4f74dae | ||
|
|
4a0bdc3baf | ||
|
|
603c1b4654 | ||
|
|
1136d582df | ||
|
|
187544fc06 | ||
|
|
aa3cde8f59 | ||
|
|
e29a702b6e | ||
|
|
e4f663c9f0 | ||
|
|
fb08ef6e78 | ||
|
|
10c465d159 | ||
|
|
d15d1203b7 | ||
|
|
de48ed3b61 | ||
|
|
d22e2e872a | ||
|
|
7da1b454dc | ||
|
|
463f311dd3 | ||
|
|
73b42cf654 | ||
|
|
1fac59c4cd | ||
|
|
6ed6b4a019 | ||
|
|
7bd02437f0 | ||
|
|
57aeb5771c | ||
|
|
695c03a530 | ||
|
|
6ebc403572 | ||
|
|
7ff6fe43b5 | ||
|
|
b34f141181 | ||
|
|
87dd7b58d1 | ||
|
|
2a949db5d2 | ||
|
|
b889e91fb5 | ||
|
|
b5c757f9f0 | ||
|
|
a465f48b94 | ||
|
|
797a0a0e4c | ||
|
|
e7ff159b6d | ||
|
|
a9fbf54bb2 | ||
|
|
81f9e262f5 | ||
|
|
23bb3960ab | ||
|
|
c4a17985a4 | ||
|
|
75ddcd47f4 | ||
|
|
2dd03ae490 | ||
|
|
5b383c8741 | ||
|
|
73198d7852 | ||
|
|
fb0d9721aa | ||
|
|
b24c663fd6 | ||
|
|
6e62cfc84d | ||
|
|
eee22e9dc9 | ||
|
|
5109c51ffc | ||
|
|
66d6d311ea | ||
|
|
ef7cbdcbd7 | ||
|
|
63bcbebb4b | ||
|
|
50ce1bb6db | ||
|
|
3c88659679 | ||
|
|
d79a993eea | ||
|
|
afafb311b8 | ||
|
|
2e280e4931 | ||
|
|
ed6efac8b7 | ||
|
|
7409df85d7 | ||
|
|
4d42529c4d | ||
|
|
65224685c8 | ||
|
|
080a84986e | ||
|
|
3f8beb2f0b | ||
|
|
410a87c426 | ||
|
|
c14b8343d2 | ||
|
|
bea440f6c3 | ||
|
|
6a5fe15121 | ||
|
|
e920ab0ec0 | ||
|
|
537e3e0563 | ||
|
|
e208dc7c4c | ||
|
|
ed64910a53 | ||
|
|
5b075c3505 | ||
|
|
a8e6d9af6a | ||
|
|
2b21d8a73c | ||
|
|
e30289d21b | ||
|
|
3b6e2be7fe | ||
|
|
43c12aa8a7 | ||
|
|
c03a7710c0 | ||
|
|
1833b23d84 | ||
|
|
f3e393972d | ||
|
|
4f46c13369 | ||
|
|
f181959218 | ||
|
|
11effc0ae7 | ||
|
|
c8397e17bf | ||
|
|
ed32b9f1d4 | ||
|
|
4e184936db | ||
|
|
fe6aebfdee | ||
|
|
d65e1a442d | ||
|
|
34647793c8 | ||
|
|
e8c249b9ea | ||
|
|
d1e613dc10 | ||
|
|
1adcfee96f | ||
|
|
75f5de6b9a | ||
|
|
499942ef70 | ||
|
|
e11277ce26 | ||
|
|
5edd9e38a7 | ||
|
|
f4ffee706b | ||
|
|
bb65750278 | ||
|
|
e09167c0d0 | ||
|
|
ee6c04fa11 | ||
|
|
b31982b788 | ||
|
|
544a3d3eb2 | ||
|
|
1987bc97a9 | ||
|
|
7e878af06c | ||
|
|
766a047ee1 | ||
|
|
c1d29a46f5 | ||
|
|
08e39b3d15 | ||
|
|
e7790ac256 | ||
|
|
499f3e108c | ||
|
|
19144b653e | ||
|
|
17a87648fa | ||
|
|
ac4cef0980 | ||
|
|
71978f8003 | ||
|
|
0b3a67ec6e | ||
|
|
ee5fb54ebb | ||
|
|
4585f22817 | ||
|
|
b523453588 | ||
|
|
a8cc03c911 | ||
|
|
949c437653 | ||
|
|
84e9cce60f | ||
|
|
5720bd93ec | ||
|
|
bcae9713bc | ||
|
|
04e3023071 | ||
|
|
a09ef84629 | ||
|
|
1d29395191 | ||
|
|
3720dc6aa9 | ||
|
|
3ada172be2 | ||
|
|
15f8fbb49b | ||
|
|
547e688d38 | ||
|
|
708eb33d0e | ||
|
|
e954b725b1 | ||
|
|
c89cb4e495 | ||
|
|
286e9775f6 | ||
|
|
60513abe6a | ||
|
|
f7a8c9faa2 | ||
|
|
55e0d32ef8 | ||
|
|
4c0bacba31 | ||
|
|
61128f2677 | ||
|
|
3866526b95 | ||
|
|
250955fac9 | ||
|
|
3249266cd1 | ||
|
|
1a6235df73 | ||
|
|
430aee2b7f | ||
|
|
de2f7e4b80 | ||
|
|
6dfcc409ac | ||
|
|
78f795e1cb | ||
|
|
c69ee0f356 | ||
|
|
39475de789 | ||
|
|
bd71712765 | ||
|
|
8698686a47 | ||
|
|
762c67ee37 |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -16,7 +16,7 @@ assignees: ''
|
||||
请确认你已经做了下面这些事情,若 bug 还是未解决,那么请尽可详细地描述你的问题。
|
||||
|
||||
- 我已经安装了最新版的 ZFile
|
||||
- 我已经阅读了 ZFile 的文档:http://docs.zhaojun.im/zfile
|
||||
- 我已经阅读了 ZFile 的文档:https://docs.zfile.vip
|
||||
- 我已经搜索了已有的 Issues 列表中有关的信息
|
||||
- 我已经清理过浏览器缓存并重试
|
||||
-->
|
||||
|
||||
15
.gitignore
vendored
15
.gitignore
vendored
@@ -1,11 +1,8 @@
|
||||
HELP.md
|
||||
target/
|
||||
.mvn/wrapper/**
|
||||
!**/src/main/**
|
||||
**/src/test/**
|
||||
|
||||
mvnw
|
||||
mvnw.cmd
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
@@ -29,6 +26,12 @@ mvnw.cmd
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
/.mvn/wrapper/
|
||||
/mvnw
|
||||
/mvnw.cmd
|
||||
/.script/
|
||||
|
||||
164
API.md
164
API.md
@@ -1,164 +0,0 @@
|
||||
## API 标准
|
||||
|
||||
所有 API 均返回 `msg`, `code`, `data` 三个属性.
|
||||
|
||||
| code | 描述 |
|
||||
| :---: | :------------: |
|
||||
| 0 | 请求成功 |
|
||||
| -1 | 请求失败 |
|
||||
| -2 | 文件夹需要密码 |
|
||||
|
||||
当 `code == 0` 时, `data` 中为请求所需数据.
|
||||
|
||||
当 `code != 0` 时, 应当将 `msg` 中的属性作为参考值.
|
||||
|
||||
|
||||
## 获取文件列表
|
||||
|
||||
### 请求 URL
|
||||
|
||||
`/api/list` `GET`
|
||||
|
||||
### 参数
|
||||
|
||||
| 参数名 | 描述 | 是否必填 | 参考值 |
|
||||
| :------: | :--------: | :------: | :--------------------------: |
|
||||
| path | 路径 | 是 | `/`, `/文件夹名称` |
|
||||
| password | 文件夹密码 | 否 | 当文件夹需要密码时, |
|
||||
| page | 页数 | 否 | 默认取第一页, 每页固定 30 条 |
|
||||
|
||||
### 响应
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "操作成功",
|
||||
"code": 0,
|
||||
"data": [
|
||||
{
|
||||
"name": "密码文件夹",
|
||||
"time": "2020-01-28 13:17",
|
||||
"size": 4096,
|
||||
"type": "FOLDER",
|
||||
"path": "/",
|
||||
"url": null
|
||||
},
|
||||
{
|
||||
"name": "新建 文本文档.txt",
|
||||
"time": "2020-01-28 13:16",
|
||||
"size": 3,
|
||||
"type": "FILE",
|
||||
"path": "/",
|
||||
"url": "http://127.0.0.1:8080/file/新建 文本文档.txt"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 搜索
|
||||
|
||||
|
||||
### 请求 URL
|
||||
|
||||
`/api/search` `GET`
|
||||
|
||||
### 参数
|
||||
|
||||
| 参数名 | 描述 | 是否必填 | 参考值 |
|
||||
| :----: | :----: | :------: | :--------------------------: |
|
||||
| name | 搜索值 | 是 | 模糊匹配 |
|
||||
| page | 页数 | 否 | 默认取第一页, 每页固定 30 条 |
|
||||
|
||||
### 响应
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "操作成功",
|
||||
"code": 0,
|
||||
"data": [
|
||||
{
|
||||
"name": "密码文件夹",
|
||||
"time": "2020-01-28 13:17",
|
||||
"size": 4096,
|
||||
"type": "FOLDER",
|
||||
"path": "/",
|
||||
"url": null
|
||||
},
|
||||
{
|
||||
"name": "新建 文本文档.txt",
|
||||
"time": "2020-01-28 13:16",
|
||||
"size": 3,
|
||||
"type": "FILE",
|
||||
"path": "/",
|
||||
"url": "http://127.0.0.1:8080/file/新建 文本文档.txt"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 获取单个文件信息
|
||||
|
||||
### 请求 URL
|
||||
|
||||
`/api/directlink` `GET`
|
||||
|
||||
### 参数
|
||||
|
||||
| 参数名 | 描述 | 是否必填 | 参考值 |
|
||||
| :----: | :--------: | :------: | :------------------: |
|
||||
| path | 文件全路径 | 是 | `/新建 文本文档.txt` |
|
||||
|
||||
### 响应
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "操作成功",
|
||||
"code": 0,
|
||||
"data": {
|
||||
"name": "新建 文本文档.txt",
|
||||
"time": "2020-01-28 13:16",
|
||||
"size": 3,
|
||||
"type": "FILE",
|
||||
"path": "d:/test",
|
||||
"url": "http://127.0.0.1:8080/file/新建 文本文档.txt"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 获取系统配置
|
||||
|
||||
|
||||
### 请求 URL
|
||||
|
||||
`/api/config` `GET`
|
||||
|
||||
### 参数
|
||||
|
||||
| 参数名 | 描述 | 是否必填 | 参考值 |
|
||||
| :----: | :--------: | :------: | :-----------: |
|
||||
| path | 文件夹名称 | 是 | `/文件夹名称` |
|
||||
|
||||
### 响应
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "操作成功",
|
||||
"code": 0,
|
||||
"data": {
|
||||
"readme": null, # 文档文件名称
|
||||
"viewConfig": {
|
||||
"siteName": "站点名称", # 站点名称
|
||||
"infoEnable": false, # 是否开启右侧信息框
|
||||
"searchEnable": false, # 是否开启搜索
|
||||
"searchIgnoreCase": true, # 搜索是否忽略大小写
|
||||
"storageStrategy": "local", # 当前启用存储引擎
|
||||
"username": "2", # 用户名
|
||||
"domain": "http://127.0.0.1:8080", # 域名
|
||||
"enableCache": false, # 是否开启缓存
|
||||
"searchContainEncryptedFile": false, # 搜索是否包含加密文件夹
|
||||
"customJs": "", # 自定义 js 片段
|
||||
"customCss": "" # 自定义 css 片段
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
166
README.md
166
README.md
@@ -1,31 +1,49 @@
|
||||
# Z-File
|
||||
<p align = "center">
|
||||
<img alt="ZFile" src="https://cdn.jun6.net/2021/04/21/69a89344e2a84.png" height="150px">
|
||||
<br><br>
|
||||
基于 Java 的在线网盘程序,支持对接 S3、OneDrive、SharePoint、又拍云、本地存储、FTP 等存储源,支持在线浏览图片、播放音视频,文本文件等文件类型。
|
||||
<br><br>
|
||||
<img src="https://img.shields.io/badge/license-MIT-blue.svg?longCache=true&style=flat-square">
|
||||
<img src="https://api.codacy.com/project/badge/Grade/70b793267f7941d58cbd93f50c9a8e0a">
|
||||
<img src="https://img.shields.io/github/last-commit/zhaojun1998/zfile.svg?style=flat-square">
|
||||
<img src="https://img.shields.io/github/downloads/zhaojun1998/zfile/total?style=flat-square">
|
||||
<img src="https://img.shields.io/github/v/release/zhaojun1998/zfile?style=flat-square">
|
||||
<img src="https://img.shields.io/github/commit-activity/y/zhaojun1998/zfile?style=flat-square">
|
||||
<br>
|
||||
<img src="https://img.shields.io/github/issues/zhaojun1998/zfile?style=flat-square">
|
||||
<img src="https://img.shields.io/github/issues-closed-raw/zhaojun1998/zfile?style=flat-square">
|
||||
<img src="https://img.shields.io/github/forks/zhaojun1998/zfile?style=flat-square">
|
||||
<img src="https://img.shields.io/github/stars/zhaojun1998/zfile?style=flat-square">
|
||||
<img src="https://img.shields.io/github/watchers/zhaojun1998/zfile?style=flat-square">
|
||||
</p>
|
||||
|
||||

|
||||
[](https://www.codacy.com/manual/zhaojun1998/zfile?utm_source=github.com&utm_medium=referral&utm_content=zhaojun1998/zfile&utm_campaign=Badge_Grade)
|
||||

|
||||

|
||||
## 相关地址
|
||||
|
||||
此项目是一个在线文件目录的程序, 支持各种对象存储和本地存储, 使用定位是个人放常用工具下载, 或做公共的文件库. 不会向多账户方向开发.
|
||||
预览地址: [https://zfile.vip](https://zfile.vip)
|
||||
|
||||
前端基于 [h5ai](https://larsjung.de/h5ai/) 的原有功能使用 Vue 重新开发了一遍. 后端采用 SpringBoot, 数据库采用内嵌数据库.
|
||||
文档地址: [https://docs.zfile.vip](https://docs.zfile.vip)
|
||||
|
||||
预览地址: [https://zfile.jun6.net](https://zfile.jun6.net)
|
||||
社区地址: [https://bbs.zfile.vip](https://bbs.zfile.vip)
|
||||
|
||||
文档地址: [http://docs.zhaojun.im/zfile](http://docs.zhaojun.im/zfile)
|
||||
项目源码: [https://github.com/zhaojun1998/zfile](https://github.com/zhaojun1998/zfile)
|
||||
|
||||
前端源码: [https://github.com/zhaojun1998/zfile-vue](https://github.com/zhaojun1998/zfile-vue)
|
||||
|
||||
## 系统特色
|
||||
|
||||
* 内存缓存 (免安装)
|
||||
* 内存数据库 (免安装)
|
||||
* 个性化配置
|
||||
* 自定义目录的 readme 说明文件
|
||||
* 自定义 JS, CSS
|
||||
* 文件夹密码
|
||||
* 目录 README 说明
|
||||
* 文件直链(短链,永久直链,二维码)
|
||||
* 支持在线浏览文本文件, 视频, 图片, 音乐. (支持 FLV 和 HLS)
|
||||
* 文件/目录二维码
|
||||
* 缓存动态开启, 缓存自动刷新
|
||||
* 全局搜索
|
||||
* 支持 阿里云 OSS, FTP, 华为云 OBS, 本地存储, MINIO, OneDrive 国际/家庭/个人版, OneDrive 世纪互联版, 七牛云 KODO, 腾讯云 COS, 又拍云 USS.
|
||||
* 图片模式
|
||||
* Docker 支持
|
||||
* 隐藏指定文件夹(通配符支持)
|
||||
* 自定义 JS, CSS
|
||||
* 自定义目录 README 说明文件和密码文件名称
|
||||
* 同时挂载多个存储策略
|
||||
* 缓存动态开启, ~~缓存自动刷新 (v2.2 及以前版本支持)~~
|
||||
* ~~全局搜索 (v2.2 及以前版本支持)~~
|
||||
* 支持 S3 协议, 阿里云 OSS, FTP, 华为云 OBS, 本地存储, MINIO, OneDrive 国际/家庭/个人版/世纪互联版/SharePoint, , 七牛云 KODO, 腾讯云 COS, 又拍云 USS.
|
||||
|
||||
## 快速开始
|
||||
|
||||
@@ -34,23 +52,37 @@
|
||||
```bash
|
||||
# CentOS系统
|
||||
yum install -y java-1.8.0-openjdk unzip
|
||||
```
|
||||
|
||||
# Debian/Ubuntu系统
|
||||
```bash
|
||||
# Debian 9 / Ubuntu 14+
|
||||
apt update
|
||||
apt install -y openjdk-8-jre-headless unzip
|
||||
```
|
||||
|
||||
> 如为更新程序, 则请先执行 `rm -rf ~/zfile` 清理旧程序. 首次安装请忽略此选项.
|
||||
```bash
|
||||
# Debian 10 (Buster) 系统
|
||||
apt update && apt install -y apt-transport-https software-properties-common ca-certificates dirmngr gnupg
|
||||
wget -qO - https://adoptopenjdk.jfrog.io/adoptopenjdk/api/gpg/key/public | apt-key add -
|
||||
add-apt-repository --yes https://adoptopenjdk.jfrog.io/adoptopenjdk/deb/
|
||||
apt update && apt install -y adoptopenjdk-8-hotspot-jre
|
||||
```
|
||||
|
||||
|
||||
> 如为更新程序, 则请先执行 `~/zfile/bin/stop.sh && rm -rf ~/zfile` 清理旧程序. 首次安装请忽略此选项.
|
||||
|
||||
下载项目:
|
||||
|
||||
```bash
|
||||
wget -P ~ https://c.jun6.net/ZFILE/zfile-release.war
|
||||
cd ~
|
||||
mkdir zfile && unzip zfile-release.war -d zfile && rm -rf zfile-release.war
|
||||
chmod +x ~/zfile/bin/*.sh
|
||||
export ZFILE_INSTALL_PATH=~/zfile
|
||||
mkdir -p $ZFILE_INSTALL_PATH && cd $ZFILE_INSTALL_PATH
|
||||
wget https://c.jun6.net/ZFILE/zfile-release.war
|
||||
unzip zfile-release.war && rm -rf zfile-release.war
|
||||
chmod +x $ZFILE_INSTALL_PATH/bin/*.sh
|
||||
```
|
||||
|
||||
> 下载指定版本可以将 `zfile-release.war` 改为 `zfile-x.x.war`,如 `zfile-2.2.war`。
|
||||
|
||||
程序的目录结构为:
|
||||
```
|
||||
├── zfile
|
||||
@@ -68,84 +100,38 @@ chmod +x ~/zfile/bin/*.sh
|
||||
~/zfile/bin/start.sh
|
||||
```
|
||||
|
||||
篇幅有限, 更详细的安装教程请参考: [安装文档](http://zhaojun.im/zfile-install)
|
||||
篇幅有限, 更详细的安装教程及介绍请参考: [ZFile 文档](https://docs.zfile.vip)
|
||||
|
||||
访问地址:
|
||||
|
||||
用户前台: http://127.0.0.1:8080/#/main
|
||||
用户前台: http://127.0.0.1:8080/main
|
||||
|
||||
初始安装: http://127.0.0.1:8080/#/install
|
||||
初始安装: http://127.0.0.1:8080/install
|
||||
|
||||
管理后台: http://127.0.0.1:8080/#/admin
|
||||
管理后台: http://127.0.0.1:8080/admin
|
||||
|
||||
|
||||
## OneDrive 使用教程.
|
||||
|
||||
访问地址进行授权, 获取 accessToken 和 refreshToken:
|
||||
|
||||
|
||||
国际/家庭/个人版:
|
||||
|
||||
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=09939809-c617-43c8-a220-a93c1513c5d4&response_type=code&redirect_uri=https://zfile.jun6.net/onedrive/callback&scope=offline_access%20User.Read%20Files.ReadWrite.All
|
||||
|
||||
|
||||
世纪互联版:
|
||||
|
||||
https://login.chinacloudapi.cn/common/oauth2/v2.0/authorize?client_id=4a72d927-1907-488d-9eb2-1b465c53c1c5&response_type=code&redirect_uri=https://zfile.jun6.net/onedrive/china-callback&scope=offline_access%20User.Read%20Files.ReadWrite.All
|
||||
|
||||
|
||||
然后分别填写至访问令牌和刷新令牌即可:
|
||||
|
||||

|
||||
|
||||
## 运行环境
|
||||
|
||||
* JDK: `1.8`
|
||||
* 数据库: `h2/mysql`
|
||||
|
||||
## 预览
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## 常见问题
|
||||
|
||||
### 数据库
|
||||
|
||||
缓存默认支持 `h2` 和 `mysql`, 前者为嵌入式数据库, 无需安装, 但后者相对性能更好.
|
||||
|
||||
### 默认路径
|
||||
|
||||
默认 H2 数据库文件地址: `~/.zfile/db/`, `~` 表示用户目录, windows 为 `C:/Users/用户名/`, linux 为 `/home/用户名/`, root 用户为 `/root/`
|
||||
|
||||
### 文档文件和加密文件
|
||||
|
||||
- 目录文档显示文件名为 `readme.md`
|
||||
- 目录需要密码访问, 添加文件 `password.txt` (无法拦截此文件被下载, 但可以改名文件)
|
||||
|
||||
## 开发计划
|
||||
|
||||
- [x] API 支持 [点击查看文档](https://github.com/zhaojun1998/zfile/blob/master/API.md)
|
||||
- [x] 更方便的部署方式
|
||||
- [x] 布局优化 - 自定义操作按钮 (现为右键实现)
|
||||
- [x] 后台优化 - 设置按照其功能进行分离
|
||||
- [x] 体验优化 - 支持前后端分离部署
|
||||
- [x] 体验优化 - 文本预览更换 vscode 同款编辑器 monaco editor
|
||||
- [ ] 新功能 - 后台支持上传、编辑、删除等操作
|
||||
- [ ] 新功能 - WebDav 支持
|
||||
- [ ] 新功能 - Docker 支持
|
||||
- [ ] 新功能 - 离线下载 (aria2)
|
||||
- [ ] 体验优化 - 忽略文件列表 (正则表达式)
|
||||
- [ ] 体验优化 - 自定义支持预览的文件后缀 (正则表达式)
|
||||
- [ ] 架构调整 - 支持多存储策略
|
||||
- [ ] 体验优化 - 一键安装脚本
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## 支持作者
|
||||
|
||||
如果本项目对你有帮助,请作者喝杯咖啡吧。
|
||||
|
||||
<img src="https://cdn.jun6.net/2021/03/27/152704e91f13d.png" width="400" alt="赞助我">
|
||||
|
||||
<img src="http://cdn.jun6.net/alipay.png" width="200" height="312">
|
||||
<img src="http://cdn.jun6.net/wechat.png" width="222" height="300">
|
||||
## Stargazers over time
|
||||
|
||||
[](https://starchart.cc/zhaojun1998/zfile.svg)
|
||||
|
||||
## 开发工具赞助
|
||||
|
||||
<a href="https://www.jetbrains.com/?from=zfile"><img src="https://cdn.jun6.net/2021/04/21/26e410d60b0b0.png?1=1" width="100px"></a>
|
||||
|
||||
196
pom.xml
196
pom.xml
@@ -1,28 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.6.RELEASE</version>
|
||||
<relativePath/>
|
||||
<version>2.6.6</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<groupId>im.zhaojun</groupId>
|
||||
<artifactId>zfile</artifactId>
|
||||
<version>2.0</version>
|
||||
<version>4.0.0</version>
|
||||
<name>zfile</name>
|
||||
<packaging>war</packaging>
|
||||
<description>一个在线的文件浏览系统</description>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<log4j2.version>2.17.0</log4j2.version>
|
||||
<org.mapstruct.version>1.5.1.Final</org.mapstruct.version>
|
||||
<proguard.version>7.2.1</proguard.version>
|
||||
<proguard-maven-plugin.version>2.5.3</proguard-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- spring boot 官方相关 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -32,14 +34,6 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
@@ -50,20 +44,21 @@
|
||||
<artifactId>spring-boot-starter-cache</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>3.5.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据库驱动-->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.29</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -71,7 +66,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.1.3</version>
|
||||
<version>5.8.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 存储策略相关 API, 对象存储、FTP、 Rest API-->
|
||||
@@ -83,26 +78,36 @@
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-s3</artifactId>
|
||||
<version>1.11.699</version>
|
||||
<version>1.12.146</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.qiniu</groupId>
|
||||
<artifactId>qiniu-java-sdk</artifactId>
|
||||
<version>7.11.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
<version>3.6</version>
|
||||
<version>3.8.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 其他工具类 -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.24</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mpatric</groupId>
|
||||
<artifactId>mp3agic</artifactId>
|
||||
@@ -112,7 +117,107 @@
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.61</version>
|
||||
<version>1.2.83</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
<version>7.15.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 第三方依赖-权限相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.30.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>3.14.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-spring-boot-starter</artifactId>
|
||||
<version>3.0.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.qcloud</groupId>
|
||||
<artifactId>cos_api</artifactId>
|
||||
<version>5.6.75</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>30.1.1-jre</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${org.mapstruct.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-chain</groupId>
|
||||
<artifactId>commons-chain</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.jcraft</groupId>
|
||||
<artifactId>jsch</artifactId>
|
||||
<version>0.1.55</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.lookfirst</groupId>
|
||||
<artifactId>sardine</artifactId>
|
||||
<version>5.10</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>dev.samstevens.totp</groupId>
|
||||
<artifactId>totp-spring-boot-starter</artifactId>
|
||||
<version>1.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.beust</groupId>
|
||||
<artifactId>jcommander</artifactId>
|
||||
<version>1.82</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20200518</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpmime</artifactId>
|
||||
<version>4.5.13</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
@@ -122,6 +227,44 @@
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${org.mapstruct.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.16</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||
<version>0.2.0</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.uyoqu.framework</groupId>
|
||||
@@ -144,5 +287,4 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2022, baomidou (jobob@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.baomidou.mybatisplus.core.handlers;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.baomidou.mybatisplus.annotation.IEnum;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import org.apache.ibatis.reflection.DefaultReflectorFactory;
|
||||
import org.apache.ibatis.reflection.MetaClass;
|
||||
import org.apache.ibatis.reflection.ReflectorFactory;
|
||||
import org.apache.ibatis.reflection.invoker.Invoker;
|
||||
import org.apache.ibatis.type.BaseTypeHandler;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 自定义枚举属性转换器
|
||||
*
|
||||
* @author hubin
|
||||
* @since 2017-10-11
|
||||
*/
|
||||
public class MybatisEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
|
||||
|
||||
private static final Map<String, String> TABLE_METHOD_OF_ENUM_TYPES = new ConcurrentHashMap<>();
|
||||
private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();
|
||||
private final Class<E> enumClassType;
|
||||
private final Class<?> propertyType;
|
||||
private final Invoker getInvoker;
|
||||
|
||||
public MybatisEnumTypeHandler(Class<E> enumClassType) {
|
||||
if (enumClassType == null) {
|
||||
throw new IllegalArgumentException("Type argument cannot be null");
|
||||
}
|
||||
this.enumClassType = enumClassType;
|
||||
MetaClass metaClass = MetaClass.forClass(enumClassType, REFLECTOR_FACTORY);
|
||||
String name = "value";
|
||||
if (!IEnum.class.isAssignableFrom(enumClassType)) {
|
||||
name = findEnumValueFieldName(this.enumClassType).orElseThrow(() -> new IllegalArgumentException(String.format("Could not find @EnumValue in Class: %s.", this.enumClassType.getName())));
|
||||
}
|
||||
this.propertyType = ReflectionKit.resolvePrimitiveIfNecessary(metaClass.getGetterType(name));
|
||||
this.getInvoker = metaClass.getGetInvoker(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找标记标记EnumValue字段
|
||||
*
|
||||
* @param clazz class
|
||||
* @return EnumValue字段
|
||||
* @since 3.3.1
|
||||
*/
|
||||
public static Optional<String> findEnumValueFieldName(Class<?> clazz) {
|
||||
if (clazz != null && clazz.isEnum()) {
|
||||
String className = clazz.getName();
|
||||
return Optional.ofNullable(CollectionUtils.computeIfAbsent(TABLE_METHOD_OF_ENUM_TYPES, className, key -> {
|
||||
Optional<Field> fieldOptional = findEnumValueAnnotationField(clazz);
|
||||
return fieldOptional.map(Field::getName).orElse(null);
|
||||
}));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static Optional<Field> findEnumValueAnnotationField(Class<?> clazz) {
|
||||
return Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(EnumValue.class)).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为MP枚举处理
|
||||
*
|
||||
* @param clazz class
|
||||
* @return 是否为MP枚举处理
|
||||
* @since 3.3.1
|
||||
*/
|
||||
public static boolean isMpEnums(Class<?> clazz) {
|
||||
return clazz != null && clazz.isEnum() && (IEnum.class.isAssignableFrom(clazz) || findEnumValueFieldName(clazz).isPresent());
|
||||
}
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType)
|
||||
throws SQLException {
|
||||
if (jdbcType == null) {
|
||||
ps.setObject(i, this.getValue(parameter));
|
||||
} else {
|
||||
// see r3589
|
||||
ps.setObject(i, this.getValue(parameter), jdbcType.TYPE_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
Object value = rs.getObject(columnName);
|
||||
if (null == value && rs.wasNull()) {
|
||||
return null;
|
||||
}
|
||||
return this.valueOf(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
Object value = rs.getObject(columnIndex, this.propertyType);
|
||||
if (null == value && rs.wasNull()) {
|
||||
return null;
|
||||
}
|
||||
return this.valueOf(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
Object value = cs.getObject(columnIndex, this.propertyType);
|
||||
if (null == value && cs.wasNull()) {
|
||||
return null;
|
||||
}
|
||||
return this.valueOf(value);
|
||||
}
|
||||
|
||||
private E valueOf(Object value) {
|
||||
E[] es = this.enumClassType.getEnumConstants();
|
||||
return Arrays.stream(es).filter((e) -> equalsValue(value, getValue(e))).findAny().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 值比较
|
||||
*
|
||||
* @param sourceValue 数据库字段值
|
||||
* @param targetValue 当前枚举属性值
|
||||
* @return 是否匹配
|
||||
* @since 3.3.0
|
||||
*/
|
||||
protected boolean equalsValue(Object sourceValue, Object targetValue) {
|
||||
String sValue = StringUtils.toStringTrim(sourceValue);
|
||||
String tValue = StringUtils.toStringTrim(targetValue);
|
||||
if (sourceValue instanceof Number && targetValue instanceof Number
|
||||
&& new BigDecimal(sValue).compareTo(new BigDecimal(tValue)) == 0) {
|
||||
return true;
|
||||
}
|
||||
return Objects.equals(sValue, tValue);
|
||||
}
|
||||
|
||||
private Object getValue(Object object) {
|
||||
try {
|
||||
return this.getInvoker.invoke(object, new Object[0]);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw ExceptionUtils.mpe(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package im.zhaojun;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@EnableAsync
|
||||
@SpringBootApplication
|
||||
@EnableAspectJAutoProxy(exposeProxy = true)
|
||||
public class ZfileApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ZfileApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package im.zhaojun.aliyun.service;
|
||||
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractS3FileService;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class AliyunServiceImpl extends AbstractS3FileService implements FileService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AliyunServiceImpl.class);
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
|
||||
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
|
||||
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
|
||||
|
||||
super.domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
|
||||
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
super.bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
|
||||
|
||||
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
|
||||
|
||||
super.s3Client = AmazonS3ClientBuilder.standard()
|
||||
.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "oss")).build();
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.ALIYUN;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package im.zhaojun.common.aop;
|
||||
|
||||
import im.zhaojun.common.cache.ZFileCache;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 操作日志切面.
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class FileListCacheAspect {
|
||||
|
||||
@Resource
|
||||
private ZFileCache zFileCache;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Pointcut("execution(public * im.zhaojun.common.service.AbstractFileService.fileList(..))")
|
||||
public void pointcut() {
|
||||
}
|
||||
|
||||
@Around(value = "pointcut()")
|
||||
public Object around(ProceedingJoinPoint point) throws Throwable {
|
||||
List<FileItemDTO> result;
|
||||
|
||||
Object[] args = point.getArgs();
|
||||
String path = String.valueOf(args[0]);
|
||||
|
||||
boolean enableCache = systemConfigService.getEnableCache();
|
||||
|
||||
if (enableCache) {
|
||||
List<FileItemDTO> cacheFileList = zFileCache.get(path);
|
||||
if (CollectionUtils.isEmpty(cacheFileList)) {
|
||||
result = (List<FileItemDTO>) point.proceed();
|
||||
zFileCache.put(path, result);
|
||||
} else {
|
||||
result = cacheFileList;
|
||||
}
|
||||
} else {
|
||||
result = (List<FileItemDTO>) point.proceed();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package im.zhaojun.common.aspect;
|
||||
|
||||
import im.zhaojun.common.exception.StorageStrategyUninitializedException;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import im.zhaojun.common.util.SpringContextHolder;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class StorageStrategyInitCheckAspect {
|
||||
|
||||
@Before("@annotation(im.zhaojun.common.annotation.CheckStorageStrategyInit)")
|
||||
public void logStart() {
|
||||
SystemConfigService systemConfigService = SpringContextHolder.getBean(SystemConfigService.class);
|
||||
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
|
||||
if (currentFileService == null) {
|
||||
throw new StorageStrategyUninitializedException("存储策略尚未初始化, 请联系管理员!");
|
||||
}
|
||||
if (currentFileService.getIsUnInitialized()) {
|
||||
throw new StorageStrategyUninitializedException("存储策略异常, 请联系管理员!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package im.zhaojun.common.cache;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Component
|
||||
public class ZFileCache {
|
||||
|
||||
private ConcurrentMap<String, List<FileItemDTO>> fileCache = new ConcurrentHashMap<>();
|
||||
|
||||
private SystemConfigDTO systemConfigCache;
|
||||
|
||||
public Date lastCacheAutoRefreshDate;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
public synchronized void put(String key, List<FileItemDTO> value) {
|
||||
fileCache.put(key, value);
|
||||
}
|
||||
|
||||
public List<FileItemDTO> get(String key) {
|
||||
return fileCache.get(key);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
fileCache.clear();
|
||||
}
|
||||
|
||||
public int cacheCount() {
|
||||
return fileCache.size();
|
||||
}
|
||||
|
||||
public List<FileItemDTO> find(String key, boolean ignoreCase) {
|
||||
List<FileItemDTO> result = new ArrayList<>();
|
||||
|
||||
Collection<List<FileItemDTO>> values = fileCache.values();
|
||||
for (List<FileItemDTO> fileItemList : values) {
|
||||
for (FileItemDTO fileItemDTO : fileItemList) {
|
||||
boolean testResult;
|
||||
|
||||
if (ignoreCase) {
|
||||
testResult = StrUtil.containsIgnoreCase(fileItemDTO.getName(), key);
|
||||
} else {
|
||||
testResult = fileItemDTO.getName().contains(key);
|
||||
}
|
||||
|
||||
if (testResult) {
|
||||
result.add(fileItemDTO);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Set<String> keySet() {
|
||||
return fileCache.keySet();
|
||||
}
|
||||
|
||||
public void remove(String key) {
|
||||
fileCache.remove(key);
|
||||
}
|
||||
|
||||
public void updateConfig(SystemConfigDTO systemConfigCache) {
|
||||
this.systemConfigCache = systemConfigCache;
|
||||
}
|
||||
|
||||
public SystemConfigDTO getConfig() {
|
||||
return this.systemConfigCache;
|
||||
}
|
||||
|
||||
public void removeConfig() {
|
||||
this.systemConfigCache = null;
|
||||
}
|
||||
|
||||
public Date getLastCacheAutoRefreshDate() {
|
||||
return lastCacheAutoRefreshDate;
|
||||
}
|
||||
|
||||
public void setLastCacheAutoRefreshDate(Date lastCacheAutoRefreshDate) {
|
||||
this.lastCacheAutoRefreshDate = lastCacheAutoRefreshDate;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package im.zhaojun.common.config;
|
||||
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Configuration
|
||||
public class CacheConfig {
|
||||
|
||||
@Bean
|
||||
public ConcurrentMap<String, List<FileItemDTO>> concurrentMapCache() {
|
||||
return new ConcurrentHashMap<>();
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package im.zhaojun.common.config;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
|
||||
public class ContentTypeTextToTextJson implements ClientHttpRequestInterceptor {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ContentTypeTextToTextJson.class);
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
|
||||
throws IOException {
|
||||
URI uri = request.getURI();
|
||||
ClientHttpResponse response = execution.execute(request, body);
|
||||
HttpHeaders headers = response.getHeaders();
|
||||
headers.put("Content-Type", Collections.singletonList("application/text"));
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package im.zhaojun.common.config;
|
||||
|
||||
import im.zhaojun.common.cache.ZFileCache;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import im.zhaojun.onedrive.china.service.OneDriveChinaServiceImpl;
|
||||
import im.zhaojun.onedrive.international.service.OneDriveServiceImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
@Slf4j
|
||||
public class GlobalScheduleTask {
|
||||
|
||||
@Resource
|
||||
private ZFileCache zFileCache;
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Resource
|
||||
private OneDriveServiceImpl oneDriveServiceImpl;
|
||||
|
||||
@Resource
|
||||
private OneDriveChinaServiceImpl oneDriveChinaServiceImpl;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
/**
|
||||
* 项目启动 30 秒后, 每 15 分钟执行一次刷新 OneDrive Token 的定时任务.
|
||||
*/
|
||||
@Scheduled(fixedRate = 1000 * 60 * 10, initialDelay = 1000 * 30)
|
||||
public void autoRefreshOneDriveToken() {
|
||||
|
||||
try {
|
||||
log.debug("尝试调用 OneDrive 自动刷新 AccessToken 定时任务");
|
||||
|
||||
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
|
||||
|
||||
if (!(currentFileService instanceof OneDriveServiceImpl
|
||||
|| currentFileService instanceof OneDriveChinaServiceImpl)) {
|
||||
log.debug("当前启用存储类型, 不是 OneDrive, 跳过自动刷新 AccessToken");
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentFileService.getIsUnInitialized()) {
|
||||
log.debug("当前启用 OneDrive 未初始化成功, 跳过自动刷新 AccessToken");
|
||||
return;
|
||||
}
|
||||
|
||||
StorageTypeEnum currentStorageTypeEnum = currentFileService.getStorageTypeEnum();
|
||||
|
||||
try {
|
||||
refreshOneDriveToken(currentStorageTypeEnum);
|
||||
} catch (Exception e) {
|
||||
log.debug("刷新 " + currentStorageTypeEnum.getDescription() + " Token 失败.", e);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.debug("尝试调用 OneDrive 自动刷新 AccessToken 定时任务出现未知异常", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用刷新 OneDrive Token
|
||||
*/
|
||||
public void refreshOneDriveToken(StorageTypeEnum storageType) {
|
||||
if (Objects.equals(storageType, StorageTypeEnum.ONE_DRIVE_CHINA)) {
|
||||
oneDriveChinaServiceImpl.refreshOneDriveToken();
|
||||
} else {
|
||||
oneDriveServiceImpl.refreshOneDriveToken();
|
||||
}
|
||||
log.info("刷新 {} key 时间: {}", storageType.getDescription(), LocalDateTime.now());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package im.zhaojun.common.config;
|
||||
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Component
|
||||
public class StorageTypeFactory implements ApplicationContextAware {
|
||||
|
||||
private static Map<String, AbstractFileService> storageTypeEnumFileServiceMap;
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* 项目启动时执行
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext act) throws BeansException {
|
||||
applicationContext = act;
|
||||
|
||||
// 获取 Spring 容器中所有 FileService 类型的类
|
||||
storageTypeEnumFileServiceMap = act.getBeansOfType(AbstractFileService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定存储类型 Service
|
||||
*/
|
||||
public static AbstractFileService getStorageTypeService(StorageTypeEnum type) {
|
||||
AbstractFileService result = null;
|
||||
for (AbstractFileService fileService : storageTypeEnumFileServiceMap.values()) {
|
||||
if (fileService.getStorageTypeEnum() == type) {
|
||||
result = fileService;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package im.zhaojun.common.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Configuration
|
||||
public class ZFileConfiguration {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate(){
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
|
||||
restTemplate.setInterceptors(Collections.singletonList(new ContentTypeTextToTextJson()));
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
package im.zhaojun.common.controller;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.ZipUtil;
|
||||
import im.zhaojun.common.config.StorageTypeFactory;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.SystemMonitorInfo;
|
||||
import im.zhaojun.common.model.dto.ResultBean;
|
||||
import im.zhaojun.common.model.dto.StorageStrategyDTO;
|
||||
import im.zhaojun.common.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.FileAsyncCacheService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import im.zhaojun.common.util.FileUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* 后台管理
|
||||
* @author zhaojun
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin")
|
||||
public class AdminController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AdminController.class);
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Resource
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
private ScheduledExecutorService scheduledExecutorService;
|
||||
|
||||
/**
|
||||
* 获取系统配置
|
||||
*/
|
||||
@GetMapping("/config")
|
||||
public ResultBean getConfig() {
|
||||
SystemConfigDTO systemConfigDTO = systemConfigService.getSystemConfig();
|
||||
return ResultBean.success(systemConfigDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新系统配置
|
||||
*/
|
||||
@PostMapping("/config")
|
||||
public ResultBean updateConfig(SystemConfigDTO systemConfigDTO) throws Exception {
|
||||
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
|
||||
if (!Objects.equals(currentStorageStrategy, systemConfigDTO.getStorageStrategy())) {
|
||||
if (systemConfigService.getEnableCache()) {
|
||||
return ResultBean.error("不支持缓存开启状态下, 切换存储策略, 请先手动关闭缓存");
|
||||
}
|
||||
log.info("已将存储策略由 {} 切换为 {}",
|
||||
currentStorageStrategy.getDescription(),
|
||||
systemConfigDTO.getStorageStrategy().getDescription());
|
||||
refreshStorageStrategy();
|
||||
}
|
||||
|
||||
systemConfigDTO.setId(1);
|
||||
systemConfigService.updateSystemConfig(systemConfigDTO);
|
||||
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改管理员登陆密码
|
||||
*/
|
||||
@PostMapping("/update-pwd")
|
||||
public ResultBean updatePwd(String username, String password) {
|
||||
systemConfigService.updateUsernameAndPwd(username, password);
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定存储策略的设置
|
||||
* @param storageType 存储策略
|
||||
* @return 所有设置
|
||||
*/
|
||||
@GetMapping("/strategy-form")
|
||||
public ResultBean getFormByStorageType(StorageTypeEnum storageType) {
|
||||
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageType);
|
||||
return ResultBean.success(storageConfigList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回支持的存储引擎.
|
||||
*/
|
||||
@GetMapping("/support-strategy")
|
||||
public ResultBean supportStrategy() {
|
||||
List<StorageStrategyDTO> result = new ArrayList<>();
|
||||
StorageTypeEnum[] values = StorageTypeEnum.values();
|
||||
for (StorageTypeEnum value : values) {
|
||||
AbstractFileService storageTypeService = StorageTypeFactory.getStorageTypeService(value);
|
||||
result.add(new StorageStrategyDTO(value.getKey(),
|
||||
value.getDescription(),
|
||||
storageTypeService.getIsInitialized()));
|
||||
}
|
||||
return ResultBean.successData(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存存储策略
|
||||
* @param storageStrategyConfig 保存表单值
|
||||
* @param storageStrategy 所属策略
|
||||
* @return 操作结果
|
||||
* @throws Exception 表单解析出错异常
|
||||
*/
|
||||
@PostMapping("/storage-strategy")
|
||||
public ResultBean save(@RequestParam Map<String, String> storageStrategyConfig, StorageTypeEnum storageStrategy) throws Exception {
|
||||
// 保存设置.
|
||||
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageStrategy);
|
||||
for (StorageConfig storageConfig : storageConfigList) {
|
||||
String key = storageConfig.getKey();
|
||||
String value = storageStrategyConfig.get(key);
|
||||
storageConfig.setValue(value);
|
||||
}
|
||||
storageConfigService.updateStorageConfig(storageConfigList);
|
||||
|
||||
// 获取当前修改的存储策略 Service, 尝试调用初始化.
|
||||
AbstractFileService updateStorageStrategyService = StorageTypeFactory.getStorageTypeService(storageStrategy);
|
||||
updateStorageStrategyService.init();
|
||||
|
||||
// 如果修改的为当前启用的缓存, 则重新进行缓存.
|
||||
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
|
||||
if (Objects.equals(storageStrategy, currentStorageStrategy)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("检测到更新了当前启用的存储策略 {}, 已清理缓存.", currentStorageStrategy);
|
||||
}
|
||||
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
fileService.clearFileCache();
|
||||
fileAsyncCacheService.cacheGlobalFile();
|
||||
}
|
||||
|
||||
// 返回是否初始化成功.
|
||||
if (updateStorageStrategyService.getIsInitialized()) {
|
||||
return ResultBean.success();
|
||||
} else {
|
||||
return ResultBean.error("保存成功, 但尝试初始化异常, 请检查设置.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新存储策略
|
||||
*/
|
||||
public void refreshStorageStrategy() {
|
||||
StorageTypeEnum storageStrategy = systemConfigService.getCurrentStorageStrategy();
|
||||
refreshStorageStrategy(storageStrategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新存储策略
|
||||
*/
|
||||
private void refreshStorageStrategy(StorageTypeEnum storageStrategy) {
|
||||
if (storageStrategy == null) {
|
||||
log.info("尚未配置存储策略.");
|
||||
} else {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
fileService.init();
|
||||
fileService.clearFileCache();
|
||||
log.info("切换至存储类型: {}", storageStrategy.getDescription());
|
||||
fileAsyncCacheService.cacheGlobalFile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统日志下载
|
||||
*/
|
||||
@GetMapping("/log")
|
||||
public ResponseEntity<Object> downloadLog(HttpServletResponse response) {
|
||||
String userHome = System.getProperty("user.home");
|
||||
File fileZip = ZipUtil.zip(userHome + "/.zfile/logs");
|
||||
String currentDate = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
|
||||
return FileUtil.export(fileZip, "ZFile 诊断日志 - " + currentDate + ".zip");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统监控信息
|
||||
*/
|
||||
@GetMapping("monitor")
|
||||
public ResultBean monitor() {
|
||||
return ResultBean.success(new SystemMonitorInfo());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package im.zhaojun.common.controller;
|
||||
|
||||
import im.zhaojun.common.cache.ZFileCache;
|
||||
import im.zhaojun.common.model.dto.CacheConfigDTO;
|
||||
import im.zhaojun.common.model.dto.ResultBean;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.FileAsyncCacheService;
|
||||
import im.zhaojun.common.service.FileCacheService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin/cache")
|
||||
public class CacheController {
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Resource
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
@Resource
|
||||
private FileCacheService fileCacheService;
|
||||
|
||||
@Resource
|
||||
private ZFileCache zFileCache;
|
||||
|
||||
@PostMapping("/enable")
|
||||
public ResultBean enableCache() {
|
||||
fileCacheService.enableCache();
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
@PostMapping("/disable")
|
||||
public ResultBean disableCache() {
|
||||
fileCacheService.disableCache();
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
@GetMapping("/config")
|
||||
public ResultBean cacheConfig() {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
CacheConfigDTO cacheConfigDTO = new CacheConfigDTO();
|
||||
cacheConfigDTO.setEnableCache(systemConfigService.getEnableCache());
|
||||
cacheConfigDTO.setCacheFinish(fileAsyncCacheService.isCacheFinish());
|
||||
cacheConfigDTO.setCacheKeys(zFileCache.keySet());
|
||||
cacheConfigDTO.setCacheDirectoryCount(zFileCache.cacheCount());
|
||||
cacheConfigDTO.setLastCacheAutoRefreshDate(zFileCache.getLastCacheAutoRefreshDate());
|
||||
return ResultBean.success(cacheConfigDTO);
|
||||
}
|
||||
|
||||
/*
|
||||
@PostMapping("/refresh")
|
||||
public ResultBean refreshCache(String key) throws Exception {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
fileService.refreshCache(key);
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
@PostMapping("/clear")
|
||||
public ResultBean clearCache(String key) {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
fileService.clearFileCache();
|
||||
return ResultBean.success();
|
||||
}
|
||||
*/
|
||||
|
||||
@PostMapping("/all")
|
||||
public ResultBean cacheAll() {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
fileService.clearFileCache();
|
||||
fileAsyncCacheService.cacheGlobalFile();
|
||||
return ResultBean.success();
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package im.zhaojun.common.controller;
|
||||
|
||||
import im.zhaojun.common.model.dto.ResultBean;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.util.AudioHelper;
|
||||
import im.zhaojun.common.util.HttpUtil;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/common")
|
||||
public class CommonController {
|
||||
|
||||
@GetMapping("/support-strategy")
|
||||
public ResultBean supportStrategy() {
|
||||
return ResultBean.successData(StorageTypeEnum.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件内容, 仅限用于, txt, md, ini 等普通文本文件.
|
||||
* @param url 文件路径
|
||||
* @return 文件内容
|
||||
*/
|
||||
@GetMapping("/content")
|
||||
public ResultBean getContent(String url) {
|
||||
return ResultBean.successData(HttpUtil.getTextContent(url));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取文件内容, 仅限用于, txt, md, ini 等普通文本文件.
|
||||
* @param url 文件路径
|
||||
* @return 文件内容
|
||||
*/
|
||||
@GetMapping("/content/origin")
|
||||
public String getContentOrigin(String url) {
|
||||
return HttpUtil.getTextContent(url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检测文件是否存在
|
||||
* @param url 文件路径
|
||||
* @return 是否存在
|
||||
*/
|
||||
@GetMapping("/content/exist")
|
||||
public boolean checkFileExist(String url) {
|
||||
return HttpUtil.checkUrlExist(url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取音频文件信息
|
||||
* @param url 文件 URL
|
||||
* @return 音频信息, 标题封面等信息
|
||||
*/
|
||||
@GetMapping("/audio-info")
|
||||
public ResultBean getAudioInfo(String url) throws Exception {
|
||||
return ResultBean.success(AudioHelper.getAudioInfo(url));
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
package im.zhaojun.common.controller;
|
||||
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import im.zhaojun.common.annotation.CheckStorageStrategyInit;
|
||||
import im.zhaojun.common.exception.SearchDisableException;
|
||||
import im.zhaojun.common.model.FilePageModel;
|
||||
import im.zhaojun.common.model.constant.ZFileConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.dto.ResultBean;
|
||||
import im.zhaojun.common.model.dto.SiteConfigDTO;
|
||||
import im.zhaojun.common.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.FileAsyncCacheService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import im.zhaojun.common.service.SystemService;
|
||||
import im.zhaojun.common.util.FileComparator;
|
||||
import im.zhaojun.common.util.HttpUtil;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 前台文件管理
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Slf4j
|
||||
@RequestMapping("/api")
|
||||
@RestController
|
||||
public class FileController {
|
||||
|
||||
@Resource
|
||||
private SystemService systemService;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Resource
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
/**
|
||||
* 滚动加载每页条数.
|
||||
*/
|
||||
private static final Integer PAGE_SIZE = 30;
|
||||
|
||||
@CheckStorageStrategyInit
|
||||
@GetMapping("/list")
|
||||
public ResultBean list(@RequestParam(defaultValue = "/") String path,
|
||||
@RequestParam(defaultValue = "name") String sortBy,
|
||||
@RequestParam(defaultValue = "asc") String order,
|
||||
@RequestParam(required = false) String password,
|
||||
@RequestParam(defaultValue = "1") Integer page) throws Exception {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
List<FileItemDTO> fileItemList = fileService.fileList(StringUtils.removeDuplicateSeparator("/" + path + "/"));
|
||||
for (FileItemDTO fileItemDTO : fileItemList) {
|
||||
if (ZFileConstant.PASSWORD_FILE_NAME.equals(fileItemDTO.getName())) {
|
||||
String expectedPasswordContent = null;
|
||||
try {
|
||||
expectedPasswordContent = HttpUtil.getTextContent(fileItemDTO.getUrl() + '1');
|
||||
} catch (HttpClientErrorException httpClientErrorException) {
|
||||
log.debug("尝试重新获取密码文件缓存中链接后仍失败", httpClientErrorException);
|
||||
try {
|
||||
String fullPath = StringUtils.removeDuplicateSeparator(fileItemDTO.getPath() + "/" + fileItemDTO.getName());
|
||||
FileItemDTO fileItem = fileService.getFileItem(fullPath);
|
||||
expectedPasswordContent = HttpUtil.getTextContent(fileItem.getUrl());
|
||||
} catch (Exception e) {
|
||||
log.debug("尝试重新获取密码文件链接后仍失败, 已暂时取消密码", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Objects.equals(expectedPasswordContent, password)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (password != null && !"".equals(password)) {
|
||||
return ResultBean.error("密码错误.", ResultBean.INVALID_PASSWORD);
|
||||
}
|
||||
return ResultBean.error("此文件夹需要密码.", ResultBean.REQUIRED_PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
return ResultBean.successData(getSortedPagingData(fileItemList, page));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取系统配置信息和当前页的标题, 页面文档信息
|
||||
* @param path 路径
|
||||
*/
|
||||
@CheckStorageStrategyInit
|
||||
@GetMapping("/config")
|
||||
public ResultBean getConfig(String path) throws Exception {
|
||||
SiteConfigDTO config = systemService.getConfig(StringUtils.removeDuplicateSeparator("/" + path + "/"));
|
||||
config.setSystemConfigDTO(systemConfigService.getSystemConfig());
|
||||
return ResultBean.successData(config);
|
||||
}
|
||||
|
||||
|
||||
@CheckStorageStrategyInit
|
||||
@GetMapping("/search")
|
||||
public ResultBean search(@RequestParam(value = "name", defaultValue = "/") String name,
|
||||
@RequestParam(defaultValue = "name") String sortBy,
|
||||
@RequestParam(defaultValue = "asc") String order,
|
||||
@RequestParam(defaultValue = "1") Integer page) {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
SystemConfigDTO systemConfigDTO = systemConfigService.getSystemConfig();
|
||||
if (BooleanUtil.isFalse(systemConfigDTO.getSearchEnable())) {
|
||||
throw new SearchDisableException("搜索功能未开启");
|
||||
}
|
||||
if (!fileAsyncCacheService.isCacheFinish()) {
|
||||
throw new SearchDisableException("搜索功能缓存预热中, 请稍后再试");
|
||||
}
|
||||
List<FileItemDTO> fileItemList = fileService.search(URLUtil.decode(name));
|
||||
return ResultBean.successData(getSortedPagingData(fileItemList, page));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 过滤文件列表, 不显示密码, 文档文件.
|
||||
*/
|
||||
private void filterFileList(List<FileItemDTO> fileItemList) {
|
||||
if (fileItemList == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
fileItemList.removeIf(fileItem -> ZFileConstant.PASSWORD_FILE_NAME.equals(fileItem.getName())
|
||||
|| ZFileConstant.README_FILE_NAME.equals(fileItem.getName()));
|
||||
}
|
||||
|
||||
|
||||
private FilePageModel getSortedPagingData(List<FileItemDTO> fileItemList, Integer page) {
|
||||
ArrayList<FileItemDTO> copy = new ArrayList<>(Arrays.asList(new FileItemDTO[fileItemList.size()]));
|
||||
Collections.copy(copy, fileItemList);
|
||||
|
||||
// 排序, 先按照文件类型比较, 文件夹在前, 文件在后, 然后根据 sortBy 字段排序, 默认为升序;
|
||||
copy.sort(new FileComparator());
|
||||
filterFileList(copy);
|
||||
|
||||
int total = copy.size();
|
||||
int totalPage = (total + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
|
||||
if (page > totalPage) {
|
||||
return new FilePageModel(total, totalPage, Collections.emptyList());
|
||||
}
|
||||
|
||||
int start = (page - 1) * PAGE_SIZE;
|
||||
int end = page * PAGE_SIZE;
|
||||
end = Math.min(end, total);
|
||||
return new FilePageModel(total, totalPage, copy.subList(start, end));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定路径下的文件信息内容
|
||||
* @param path 文件全路径
|
||||
* @return 该文件的名称, 路径, 大小, 下载地址等信息.
|
||||
*/
|
||||
@CheckStorageStrategyInit
|
||||
@GetMapping("/directlink")
|
||||
public ResultBean directlink(String path) {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
return ResultBean.successData(fileService.getFileItem(path));
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package im.zhaojun.common.controller;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.dto.InstallModelDTO;
|
||||
import im.zhaojun.common.model.dto.ResultBean;
|
||||
import im.zhaojun.common.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 系统安装初始化
|
||||
* @author zhaojun
|
||||
*/
|
||||
@RestController
|
||||
public class InstallController {
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Resource
|
||||
private AdminController adminController;
|
||||
|
||||
@GetMapping("/is-installed")
|
||||
public ResultBean isInstall() {
|
||||
if (systemConfigService.getCurrentStorageStrategy() == null) {
|
||||
return ResultBean.success();
|
||||
}
|
||||
return ResultBean.error("请勿重复初始化");
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/install")
|
||||
public ResultBean install(InstallModelDTO installModelDTO) throws Exception {
|
||||
SystemConfigDTO systemConfigDTO = systemConfigService.getSystemConfig();
|
||||
|
||||
if (systemConfigDTO.getStorageStrategy() != null) {
|
||||
return ResultBean.error("请勿重复初始化.");
|
||||
}
|
||||
|
||||
systemConfigDTO.setSiteName(installModelDTO.getSiteName());
|
||||
StorageTypeEnum storageTypeEnum = installModelDTO.getStorageStrategy();
|
||||
systemConfigDTO.setStorageStrategy(storageTypeEnum);
|
||||
systemConfigDTO.setUsername(installModelDTO.getUsername());
|
||||
systemConfigDTO.setPassword(SecureUtil.md5(installModelDTO.getPassword()));
|
||||
systemConfigDTO.setDomain(installModelDTO.getDomain());
|
||||
systemConfigService.updateSystemConfig(systemConfigDTO);
|
||||
|
||||
Map<String, String> storageStrategyConfig = installModelDTO.getStorageStrategyConfig();
|
||||
|
||||
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageTypeEnum);
|
||||
for (StorageConfig storageConfig : storageConfigList) {
|
||||
String key = storageConfig.getKey();
|
||||
String value = storageStrategyConfig.get(key);
|
||||
storageConfig.setValue(value);
|
||||
}
|
||||
|
||||
storageConfigService.updateStorageConfig(storageConfigList);
|
||||
adminController.refreshStorageStrategy();
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/form")
|
||||
public ResultBean getFormByStorageType(String storageType) {
|
||||
StorageTypeEnum storageTypeEnum = StorageTypeEnum.getEnum(storageType);
|
||||
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageTypeEnum);
|
||||
storageConfigList.forEach(storageConfig -> storageConfig.setValue(null));
|
||||
return ResultBean.success(storageConfigList);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package im.zhaojun.common.controller;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Zhao Jun
|
||||
* 2020/2/9 11:17
|
||||
*/
|
||||
@Controller
|
||||
public class PageController {
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@GetMapping("/directlink/**")
|
||||
public String directlink(final HttpServletRequest request) throws MalformedURLException {
|
||||
String path = (String) request.getAttribute(
|
||||
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
|
||||
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
|
||||
AntPathMatcher apm = new AntPathMatcher();
|
||||
String filePath = apm.extractPathWithinPattern(bestMatchPattern, path);
|
||||
|
||||
if (filePath.length() > 0 && filePath.charAt(0) != '/') {
|
||||
filePath = "/" + filePath;
|
||||
}
|
||||
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
FileItemDTO fileItem = fileService.getFileItem(filePath);
|
||||
|
||||
String url = fileItem.getUrl();
|
||||
|
||||
int queryIndex = url.indexOf('?');
|
||||
|
||||
if (queryIndex != -1) {
|
||||
String origin = url.substring(0, queryIndex);
|
||||
String queryString = url.substring(queryIndex + 1);
|
||||
|
||||
url = URLUtil.encode(origin) + "?" + URLUtil.encode(queryString);
|
||||
} else {
|
||||
url = URLUtil.encode(url);
|
||||
}
|
||||
|
||||
|
||||
if (Objects.equals(fileItem.getType(), FileTypeEnum.FOLDER)) {
|
||||
return "redirect:" + fileItem.getUrl();
|
||||
} else {
|
||||
return "redirect:" + url;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package im.zhaojun.common.exception;
|
||||
|
||||
import im.zhaojun.common.model.dto.ResultBean;
|
||||
import org.apache.catalina.connector.ClientAbortException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
* @author zhaojun
|
||||
*/
|
||||
@ControllerAdvice
|
||||
public class GlobleExceptionHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(GlobleExceptionHandler.class);
|
||||
|
||||
@ExceptionHandler(SearchDisableException.class)
|
||||
@ResponseBody
|
||||
@ResponseStatus(code= HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public ResultBean searchDisableExceptionHandler(SearchDisableException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(e.getMessage(), e);
|
||||
}
|
||||
return ResultBean.error(e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
@ResponseStatus
|
||||
public ResultBean searchDisableExceptionHandler(StorageStrategyUninitializedException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(e.getMessage(), e);
|
||||
}
|
||||
return ResultBean.error(e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 不存在的文件异常
|
||||
*/
|
||||
@ExceptionHandler({NotExistFileException.class})
|
||||
@ResponseBody
|
||||
public ResultBean notExistFile(Exception ex) {
|
||||
return ResultBean.error("文件不存在");
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获 ClientAbortException 异常, 不做任何处理, 防止出现大量堆栈日志输出, 此异常不影响功能.
|
||||
*/
|
||||
@ExceptionHandler({HttpMediaTypeNotAcceptableException.class, ClientAbortException.class})
|
||||
@ResponseBody
|
||||
@ResponseStatus
|
||||
public void clientAbortException(Exception ex) {
|
||||
// if (log.isDebugEnabled()) {
|
||||
// log.debug("出现了断开异常:", ex);
|
||||
// }
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
@ResponseStatus(code= HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public ResultBean searchDisableExceptionHandler(Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(e.getMessage(), e);
|
||||
}
|
||||
return ResultBean.error("系统异常, 请联系管理员");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package im.zhaojun.common.exception;
|
||||
|
||||
/**
|
||||
* 对象存储初始化异常
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class InitializeException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -1920550904063819880L;
|
||||
|
||||
public InitializeException() {
|
||||
}
|
||||
|
||||
public InitializeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InitializeException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public InitializeException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public InitializeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package im.zhaojun.common.exception;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class SearchDisableException extends RuntimeException {
|
||||
|
||||
public SearchDisableException() {
|
||||
}
|
||||
|
||||
public SearchDisableException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SearchDisableException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public SearchDisableException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public SearchDisableException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package im.zhaojun.common.exception;
|
||||
|
||||
/**
|
||||
* 存储策略未初始化异常
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class StorageStrategyUninitializedException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 5736940575583615661L;
|
||||
|
||||
public StorageStrategyUninitializedException() {
|
||||
}
|
||||
|
||||
public StorageStrategyUninitializedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public StorageStrategyUninitializedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public StorageStrategyUninitializedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public StorageStrategyUninitializedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package im.zhaojun.common.exception;
|
||||
|
||||
/**
|
||||
* 未知的存储类型异常
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class UnknownStorageTypeException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -4853756482605773655L;
|
||||
|
||||
public UnknownStorageTypeException() {
|
||||
}
|
||||
|
||||
public UnknownStorageTypeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UnknownStorageTypeException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public UnknownStorageTypeException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public UnknownStorageTypeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package im.zhaojun.common.model;
|
||||
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class FilePageModel {
|
||||
|
||||
private int total;
|
||||
|
||||
private int totalPage;
|
||||
|
||||
private List<FileItemDTO> fileList;
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package im.zhaojun.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
public class Jvm {
|
||||
|
||||
/**
|
||||
* 当前 JVM 占用的内存总数 (M)
|
||||
*/
|
||||
private double total;
|
||||
|
||||
/**
|
||||
* JVM 最大可用内存总数 (M)
|
||||
*/
|
||||
private double max;
|
||||
|
||||
/**
|
||||
* JVM 空闲内存 (M)
|
||||
*/
|
||||
private double free;
|
||||
|
||||
/**
|
||||
* JDK 版本
|
||||
*/
|
||||
private String version;
|
||||
|
||||
public Jvm() {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
this.total = runtime.totalMemory();
|
||||
this.free = runtime.freeMemory();
|
||||
this.max = runtime.maxMemory();
|
||||
this.version = System.getProperty("java.version");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package im.zhaojun.common.model;
|
||||
|
||||
import com.sun.management.OperatingSystemMXBean;
|
||||
import lombok.Data;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
public class Mem {
|
||||
|
||||
/**
|
||||
* 内存总量
|
||||
*/
|
||||
private double total;
|
||||
|
||||
/**
|
||||
* 已用内存
|
||||
*/
|
||||
private double used;
|
||||
|
||||
/**
|
||||
* 剩余内存
|
||||
*/
|
||||
private double free;
|
||||
|
||||
public Mem() {
|
||||
OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
|
||||
// 总的物理内存+虚拟内存
|
||||
long totalVirtualMemory = operatingSystemMXBean.getTotalSwapSpaceSize();
|
||||
// 剩余的物理内存
|
||||
long freePhysicalMemorySize = operatingSystemMXBean.getFreePhysicalMemorySize();
|
||||
this.total = totalVirtualMemory;
|
||||
this.free = freePhysicalMemorySize;
|
||||
this.used = totalVirtualMemory - freePhysicalMemorySize;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package im.zhaojun.common.model;
|
||||
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Entity(name = "STORAGE_CONFIG")
|
||||
@Data
|
||||
public class StorageConfig {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
private StorageTypeEnum type;
|
||||
|
||||
@Column(name = "k")
|
||||
private String key;
|
||||
|
||||
private String title;
|
||||
|
||||
@Column(length = 4000)
|
||||
private String value;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public StorageTypeEnum getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(StorageTypeEnum type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package im.zhaojun.common.model;
|
||||
|
||||
import cn.hutool.core.date.BetweenFormater;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import lombok.Data;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
public class Sys {
|
||||
|
||||
/**
|
||||
* 项目路径
|
||||
*/
|
||||
private String projectDir;
|
||||
|
||||
/**
|
||||
* 操作系统
|
||||
*/
|
||||
private String osName;
|
||||
|
||||
/**
|
||||
* 系统架构
|
||||
*/
|
||||
private String osArch;
|
||||
|
||||
/**
|
||||
* 系统版本
|
||||
*/
|
||||
private String osVersion;
|
||||
|
||||
/**
|
||||
* 启动时间
|
||||
*/
|
||||
private String upTime;
|
||||
|
||||
public Sys() {
|
||||
this.osName = System.getProperty("os.name");
|
||||
this.osArch = System.getProperty("os.arch");
|
||||
this.osVersion = System.getProperty("os.version");
|
||||
Resource resource = new ClassPathResource("");
|
||||
try {
|
||||
this.projectDir = resource.getFile().getAbsolutePath();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
long uptime = ManagementFactory.getRuntimeMXBean().getUptime();
|
||||
this.upTime = DateUtil.formatBetween(uptime, BetweenFormater.Level.SECOND);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package im.zhaojun.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Entity(name = "SYSTEM_CONFIG")
|
||||
@Data
|
||||
public class SystemConfig {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
@Column(name = "k")
|
||||
private String key;
|
||||
|
||||
private String value;
|
||||
|
||||
private String remark;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package im.zhaojun.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
public class SystemMonitorInfo implements Serializable {
|
||||
|
||||
/**
|
||||
* 服务器基本信息
|
||||
*/
|
||||
private Sys sys;
|
||||
|
||||
/**
|
||||
* JVM 信息
|
||||
*/
|
||||
private Jvm jvm;
|
||||
|
||||
/**
|
||||
* 系统内存
|
||||
*/
|
||||
private Mem mem;
|
||||
|
||||
public SystemMonitorInfo() {
|
||||
this.jvm = new Jvm();
|
||||
this.sys = new Sys();
|
||||
this.mem = new Mem();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package im.zhaojun.common.model.constant;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class StorageConfigConstant {
|
||||
|
||||
public static final String BUCKET_NAME_KEY = "bucket-name";
|
||||
|
||||
public static final String SECRET_ID_KEY = "secretId";
|
||||
|
||||
public static final String ACCESS_KEY = "accessKey";
|
||||
|
||||
public static final String SECRET_KEY = "secretKey";
|
||||
|
||||
public static final String ENDPOINT_KEY = "endPoint";
|
||||
|
||||
public static final String BASE_PATH = "base-path";
|
||||
|
||||
public static final String DOMAIN_KEY = "domain";
|
||||
|
||||
public static final String USERNAME_KEY = "username";
|
||||
|
||||
public static final String PASSWORD_KEY = "password";
|
||||
|
||||
public static final String HOST_KEY = "host";
|
||||
|
||||
public static final String PORT_KEY = "port";
|
||||
|
||||
public static final String FILE_PATH_KEY = "filePath";
|
||||
|
||||
public static final String ACCESS_TOKEN_KEY = "accessToken";
|
||||
|
||||
public static final String REFRESH_TOKEN_KEY = "refreshToken";
|
||||
|
||||
public static final String PATH_STYLE = "pathStyle";
|
||||
|
||||
public static final String IS_PRIVATE = "isPrivate";
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package im.zhaojun.common.model.constant;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class SystemConfigConstant {
|
||||
|
||||
public static final String SITE_NAME = "siteName";
|
||||
|
||||
public static final String INFO_ENABLE = "infoEnable";
|
||||
|
||||
public static final String SEARCH_ENABLE = "searchEnable";
|
||||
|
||||
public static final String SEARCH_IGNORE_CASE = "searchIgnoreCase";
|
||||
|
||||
public static final String ENABLE_CACHE = "enableCache";
|
||||
|
||||
public static final String STORAGE_STRATEGY = "storageStrategy";
|
||||
|
||||
public static final String USERNAME = "username";
|
||||
|
||||
public static final String PASSWORD = "password";
|
||||
|
||||
public static final String DOMAIN = "domain";
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package im.zhaojun.common.model.constant;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Configuration
|
||||
public class ZFileConstant {
|
||||
|
||||
public final static String USER_HOME = System.getProperty("user.home");
|
||||
|
||||
public static final String AUDIO_TMP_PATH = "/.zfile/tmp/audio/";
|
||||
|
||||
/**
|
||||
* 页面文档文件
|
||||
*/
|
||||
public static String README_FILE_NAME = "readme.md";
|
||||
|
||||
/**
|
||||
* 密码文件
|
||||
*/
|
||||
public static String PASSWORD_FILE_NAME = "password.txt";
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setHeaderFileName(@Value("${zfile.constant.readme}") String headerFileName) {
|
||||
ZFileConstant.README_FILE_NAME = headerFileName;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setPasswordFileName(@Value("${zfile.constant.password}") String passwordFileName) {
|
||||
ZFileConstant.PASSWORD_FILE_NAME = passwordFileName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package im.zhaojun.common.model.dto;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class AudioInfoDTO {
|
||||
private String title;
|
||||
private String artist;
|
||||
private String cover;
|
||||
private String src;
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public void setArtist(String artist) {
|
||||
this.artist = artist;
|
||||
}
|
||||
|
||||
public String getCover() {
|
||||
return cover;
|
||||
}
|
||||
|
||||
public void setCover(String cover) {
|
||||
this.cover = cover;
|
||||
}
|
||||
|
||||
public String getSrc() {
|
||||
return src;
|
||||
}
|
||||
|
||||
public void setSrc(String src) {
|
||||
this.src = src;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AudioInfoDTO{" +
|
||||
"title='" + title + '\'' +
|
||||
", artist='" + artist + '\'' +
|
||||
", cover='" + cover + '\'' +
|
||||
", src='" + src + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package im.zhaojun.common.model.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
public class CacheConfigDTO {
|
||||
private Boolean enableCache;
|
||||
private Boolean cacheFinish;
|
||||
private Set<String> cacheKeys;
|
||||
private Integer cacheDirectoryCount;
|
||||
private Integer cacheFileCount;
|
||||
private Date lastCacheAutoRefreshDate;
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package im.zhaojun.common.model.dto;
|
||||
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class FileItemDTO implements Serializable {
|
||||
|
||||
private String name;
|
||||
private Date time;
|
||||
private Long size;
|
||||
private FileTypeEnum type;
|
||||
private String path;
|
||||
private String url;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Date getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(Date time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public Long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(Long size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public FileTypeEnum getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(FileTypeEnum type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FileItemDTO{" +
|
||||
"name='" + name + '\'' +
|
||||
", time=" + time +
|
||||
", size=" + size +
|
||||
", type=" + type +
|
||||
", path='" + path + '\'' +
|
||||
", url='" + url + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package im.zhaojun.common.model.dto;
|
||||
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class InstallModelDTO {
|
||||
private String siteName;
|
||||
private StorageTypeEnum storageStrategy;
|
||||
private String username;
|
||||
private String password;
|
||||
private String domain;
|
||||
private Map<String, String> storageStrategyConfig;
|
||||
|
||||
public String getSiteName() {
|
||||
return siteName;
|
||||
}
|
||||
|
||||
public void setSiteName(String siteName) {
|
||||
this.siteName = siteName;
|
||||
}
|
||||
|
||||
public StorageTypeEnum getStorageStrategy() {
|
||||
return storageStrategy;
|
||||
}
|
||||
|
||||
public void setStorageStrategy(StorageTypeEnum storageStrategy) {
|
||||
this.storageStrategy = storageStrategy;
|
||||
}
|
||||
|
||||
public Map<String, String> getStorageStrategyConfig() {
|
||||
return storageStrategyConfig;
|
||||
}
|
||||
|
||||
public void setStorageStrategyConfig(Map<String, String> storageStrategyConfig) {
|
||||
this.storageStrategyConfig = storageStrategyConfig;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
public void setDomain(String domain) {
|
||||
this.domain = domain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InstallModelDTO{" +
|
||||
"siteName='" + siteName + '\'' +
|
||||
", storageStrategy=" + storageStrategy +
|
||||
", username='" + username + '\'' +
|
||||
", password='" + password + '\'' +
|
||||
", domain='" + domain + '\'' +
|
||||
", storageStrategyConfig=" + storageStrategyConfig +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package im.zhaojun.common.model.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class ResultBean implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8276264968757808344L;
|
||||
|
||||
public static final int SUCCESS = 0;
|
||||
|
||||
public static final int FAIL = -1;
|
||||
|
||||
public static final int REQUIRED_PASSWORD = -2;
|
||||
|
||||
public static final int INVALID_PASSWORD = -3;
|
||||
|
||||
private String msg = "操作成功";
|
||||
|
||||
private int code = SUCCESS;
|
||||
|
||||
private Object data;
|
||||
|
||||
private int total;
|
||||
|
||||
private ResultBean() {
|
||||
super();
|
||||
}
|
||||
|
||||
private ResultBean(String msg, Object data, int code) {
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static ResultBean success() {
|
||||
return success("操作成功");
|
||||
}
|
||||
|
||||
public static ResultBean success(String msg) {
|
||||
return success(msg, null);
|
||||
}
|
||||
|
||||
public static ResultBean successData(Object data) {
|
||||
return success("操作成功", data);
|
||||
}
|
||||
|
||||
public static ResultBean successPage(Object data, Long total) {
|
||||
return success("操作成功", data);
|
||||
}
|
||||
|
||||
public static ResultBean success(Object data) {
|
||||
return success("操作成功", data);
|
||||
}
|
||||
|
||||
public static ResultBean success(String msg, Object data) {
|
||||
return new ResultBean(msg, data, SUCCESS);
|
||||
}
|
||||
|
||||
public static ResultBean error(String msg) {
|
||||
ResultBean resultBean = new ResultBean();
|
||||
resultBean.setCode(FAIL);
|
||||
resultBean.setMsg(msg);
|
||||
return resultBean;
|
||||
}
|
||||
|
||||
public static ResultBean error(String msg, Integer code) {
|
||||
ResultBean resultBean = new ResultBean();
|
||||
resultBean.setCode(code);
|
||||
resultBean.setMsg(msg);
|
||||
return resultBean;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(Object data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package im.zhaojun.common.model.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
public class SiteConfigDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8811196207046121740L;
|
||||
|
||||
private String readme;
|
||||
|
||||
@JsonProperty("viewConfig")
|
||||
private SystemConfigDTO systemConfigDTO;
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package im.zhaojun.common.model.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Zhao Jun
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class StorageStrategyDTO {
|
||||
|
||||
private String key;
|
||||
|
||||
private String description;
|
||||
|
||||
private boolean available;
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package im.zhaojun.common.model.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnumSerializerConvert;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 系统设置传输类
|
||||
* @author zhaojun
|
||||
*/
|
||||
@ToString
|
||||
@Data
|
||||
public class SystemConfigDTO {
|
||||
|
||||
@JsonIgnore
|
||||
private Integer id;
|
||||
|
||||
private String siteName;
|
||||
|
||||
private Boolean infoEnable;
|
||||
|
||||
private Boolean searchEnable;
|
||||
|
||||
private Boolean searchIgnoreCase;
|
||||
|
||||
@JsonSerialize(using = StorageTypeEnumSerializerConvert.class)
|
||||
private StorageTypeEnum storageStrategy;
|
||||
|
||||
private String username;
|
||||
|
||||
@JsonIgnore
|
||||
private String password;
|
||||
|
||||
private String domain;
|
||||
|
||||
private Boolean enableCache;
|
||||
|
||||
private Boolean searchContainEncryptedFile;
|
||||
|
||||
private String customJs;
|
||||
|
||||
private String customCss;
|
||||
|
||||
private String tableSize;
|
||||
|
||||
private Boolean showOperator;
|
||||
|
||||
private Boolean showDocument;
|
||||
|
||||
private String announcement;
|
||||
|
||||
private Boolean showAnnouncement;
|
||||
|
||||
private String layout;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package im.zhaojun.common.model.enums;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public enum FileTypeEnum {
|
||||
|
||||
/**
|
||||
* 文件
|
||||
*/
|
||||
FILE("File"),
|
||||
|
||||
/**
|
||||
* 文件夹
|
||||
*/
|
||||
FOLDER("Folder");
|
||||
|
||||
private String value;
|
||||
|
||||
FileTypeEnum(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package im.zhaojun.common.model.enums;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
|
||||
public enum StorageTypeEnum {
|
||||
|
||||
/**
|
||||
* 当前系统支持的所有存储策略
|
||||
*/
|
||||
LOCAL("local", "本地存储"),
|
||||
ALIYUN("aliyun", "阿里云 OSS"),
|
||||
TENCENT("tencent", "腾讯云 COS"),
|
||||
UPYUN("upyun", "又拍云 USS"),
|
||||
FTP("ftp", "FTP"),
|
||||
UFILE("ufile", "UFile"),
|
||||
HUAWEI("huawei", "华为云 OBS"),
|
||||
MINIO("minio", "MINIO"),
|
||||
S3("s3", "S3通用协议"),
|
||||
ONE_DRIVE("onedrive", "OneDrive"),
|
||||
ONE_DRIVE_CHINA("onedrive-china", "OneDrive 世纪互联"),
|
||||
QINIU("qiniu", "七牛云 KODO");
|
||||
|
||||
private String key;
|
||||
private String description;
|
||||
|
||||
private static Map<String, StorageTypeEnum> enumMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (StorageTypeEnum type : StorageTypeEnum.values()) {
|
||||
enumMap.put(type.getKey(), type);
|
||||
}
|
||||
}
|
||||
|
||||
StorageTypeEnum(String key, String description) {
|
||||
this.key = key;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public static StorageTypeEnum getEnum(String value) {
|
||||
return enumMap.get(value.toLowerCase());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package im.zhaojun.common.model.enums;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Converter(autoApply = true)
|
||||
public class StorageTypeEnumConvert implements AttributeConverter<StorageTypeEnum, String> {
|
||||
|
||||
@Override
|
||||
public String convertToDatabaseColumn(StorageTypeEnum attribute) {
|
||||
return attribute.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum convertToEntityAttribute(String dbData) {
|
||||
return StorageTypeEnum.getEnum(dbData);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package im.zhaojun.common.model.enums;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class StorageTypeEnumDeSerializerConvert implements Converter<String, StorageTypeEnum> {
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum convert(@NonNull String s) {
|
||||
return StorageTypeEnum.getEnum(s);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package im.zhaojun.common.model.enums;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class StorageTypeEnumSerializerConvert extends JsonSerializer<StorageTypeEnum> {
|
||||
|
||||
@Override
|
||||
public void serialize(StorageTypeEnum storageTypeEnum, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
|
||||
jsonGenerator.writeString(storageTypeEnum.getKey());
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package im.zhaojun.common.repository;
|
||||
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Repository
|
||||
public interface StorageConfigRepository extends JpaRepository<StorageConfig, Integer> {
|
||||
|
||||
/**
|
||||
* 根据存储类型找对应的配置信息
|
||||
* @param type 存储类型
|
||||
* @return 此类型所有的配置信息
|
||||
*/
|
||||
List<StorageConfig> findByTypeOrderById(StorageTypeEnum type);
|
||||
|
||||
/**
|
||||
* 根据存储类型找到某个 KEY 的值
|
||||
* @param type 存储类型
|
||||
* @param key KEY
|
||||
* @return KEY 对应的对象
|
||||
*/
|
||||
StorageConfig findByTypeAndKey(StorageTypeEnum type, String key);
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package im.zhaojun.common.repository;
|
||||
|
||||
import im.zhaojun.common.model.SystemConfig;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Repository
|
||||
public interface SystemConfigRepository extends JpaRepository<SystemConfig, Integer> {
|
||||
|
||||
/**
|
||||
* 查找系统设置中, 某个设置项对应的值
|
||||
* @param key 设置项
|
||||
* @return 设置值
|
||||
*/
|
||||
SystemConfig findByKey(String key);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package im.zhaojun.common.security;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class Md5PasswordEncoder implements PasswordEncoder {
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
return SecureUtil.md5(rawPassword.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
return SecureUtil.md5(rawPassword.toString()).equals(encodedPassword);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package im.zhaojun.common.security;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Configuration
|
||||
public class MyCorsFilter {
|
||||
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
|
||||
final CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.setAllowCredentials(true);
|
||||
corsConfiguration.addAllowedOrigin("*");
|
||||
corsConfiguration.addAllowedHeader("*");
|
||||
corsConfiguration.addAllowedMethod("*");
|
||||
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
|
||||
return new CorsFilter(urlBasedCorsConfigurationSource);
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
package im.zhaojun.common.security;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import im.zhaojun.common.model.dto.ResultBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.firewall.HttpFirewall;
|
||||
import org.springframework.security.web.firewall.StrictHttpFirewall;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* 自定义 Security 配置类
|
||||
* @author zhaojun
|
||||
*/
|
||||
@EnableWebSecurity
|
||||
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Resource
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// .authenticationProvider(authenticationProvider())
|
||||
.exceptionHandling()
|
||||
//未登录时,进行json格式的提示,很喜欢这种写法,不用单独写一个又一个的类
|
||||
.authenticationEntryPoint((request, response, authException) -> {
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
PrintWriter out = response.getWriter();
|
||||
out.write(objectMapper.writeValueAsString(ResultBean.error("未登录")));
|
||||
out.flush();
|
||||
out.close();
|
||||
})
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/").permitAll()
|
||||
.antMatchers("/admin/**").authenticated()
|
||||
.and()
|
||||
.formLogin() //使用自带的登录
|
||||
//登录失败,返回json
|
||||
.failureHandler((request, response, ex) -> {
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||
PrintWriter out = response.getWriter();
|
||||
String msg;
|
||||
if (ex instanceof UsernameNotFoundException || ex instanceof BadCredentialsException) {
|
||||
msg = "用户名或密码错误";
|
||||
} else {
|
||||
msg = "登录失败";
|
||||
}
|
||||
out.write(objectMapper.writeValueAsString(ResultBean.error(msg)));
|
||||
out.flush();
|
||||
out.close();
|
||||
})
|
||||
//登录成功,返回json
|
||||
.successHandler((request, response, authentication) -> {
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
PrintWriter out = response.getWriter();
|
||||
out.write(objectMapper.writeValueAsString(ResultBean.success(authentication)));
|
||||
out.flush();
|
||||
out.close();
|
||||
})
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
//没有权限,返回json
|
||||
.accessDeniedHandler((request, response, ex) -> {
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||
PrintWriter out = response.getWriter();
|
||||
out.write(objectMapper.writeValueAsString(ResultBean.error("权限不足")));
|
||||
out.flush();
|
||||
out.close();
|
||||
})
|
||||
.and()
|
||||
.logout()
|
||||
//退出成功,返回json
|
||||
.logoutSuccessHandler((request, response, authentication) -> {
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
PrintWriter out = response.getWriter();
|
||||
out.write(objectMapper.writeValueAsString(ResultBean.error("注销成功")));
|
||||
out.flush();
|
||||
out.close();
|
||||
})
|
||||
.and()
|
||||
.logout().permitAll();
|
||||
|
||||
http.cors();
|
||||
http.csrf().disable();
|
||||
http.headers().frameOptions().sameOrigin();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
|
||||
StrictHttpFirewall firewall = new StrictHttpFirewall();
|
||||
firewall.setAllowUrlEncodedPercent(true);
|
||||
return firewall;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(AuthenticationManagerBuilder web) throws Exception {
|
||||
web.userDetailsService(myUserDetailsServiceImpl()).passwordEncoder(passwordEncoder());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MyUserDetailsServiceImpl myUserDetailsServiceImpl() {
|
||||
return new MyUserDetailsServiceImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) {
|
||||
//对于在header里面增加token等类似情况,放行所有OPTIONS请求。
|
||||
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
|
||||
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public static PasswordEncoder passwordEncoder() {
|
||||
return new Md5PasswordEncoder();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package im.zhaojun.common.security;
|
||||
|
||||
import im.zhaojun.common.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class MyUserDetailsServiceImpl implements UserDetailsService {
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
/**
|
||||
* 授权的时候是对角色授权,认证的时候应该基于资源,而不是角色,因为资源是不变的,而用户的角色是会变的
|
||||
*/
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();
|
||||
if (!Objects.equals(systemConfig.getUsername(), username)) {
|
||||
throw new UsernameNotFoundException("用户名不存在");
|
||||
}
|
||||
return new User(systemConfig.getUsername(), systemConfig.getPassword(), Collections.emptyList());
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import im.zhaojun.common.cache.ZFileCache;
|
||||
import im.zhaojun.common.model.constant.ZFileConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.aop.framework.AopContext;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractFileService extends FileCacheService implements FileService {
|
||||
|
||||
private static final String SYSTEM_CONFIG_CACHE_PREFIX = "zfile-cache:";
|
||||
|
||||
@Value("${zfile.cache.timeout}")
|
||||
protected Long timeout;
|
||||
|
||||
protected boolean isInitialized = false;
|
||||
|
||||
protected String basePath;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Resource
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
@Resource
|
||||
private ZFileCache zFileCache;
|
||||
|
||||
/***
|
||||
* 获取指定路径下的文件及文件夹, 默认缓存 60 分钟,每隔 30 分钟刷新一次.
|
||||
* @param path 文件路径
|
||||
* @return 文件及文件夹列表
|
||||
* @throws Exception 获取文件列表中出现的异常
|
||||
*/
|
||||
@Override
|
||||
public abstract List<FileItemDTO> fileList(String path) throws Exception;
|
||||
|
||||
/**
|
||||
* 清理当前存储策略的缓存
|
||||
* 1. 删除全部缓存
|
||||
* 2. 标记为当前处于未完成缓存状态
|
||||
*/
|
||||
public void clearFileCache() {
|
||||
zFileCache.clear();
|
||||
fileAsyncCacheService.setCacheFinish(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化方法, 启动时自动调用实现类的此方法进行初始化.
|
||||
*/
|
||||
@PostConstruct
|
||||
public abstract void init();
|
||||
|
||||
protected boolean testConnection() {
|
||||
boolean flag = true;
|
||||
try {
|
||||
fileList("/");
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常", e);
|
||||
flag = false;
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取是否初始化成功
|
||||
* @return 初始化成功与否
|
||||
*/
|
||||
public boolean getIsUnInitialized() {
|
||||
return !isInitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取是否初始化成功
|
||||
* @return 初始化成功与否
|
||||
*/
|
||||
public boolean getIsInitialized() {
|
||||
return isInitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储策略类型
|
||||
* @return 存储策略类型枚举
|
||||
*/
|
||||
public abstract StorageTypeEnum getStorageTypeEnum();
|
||||
|
||||
/**
|
||||
* 搜索文件
|
||||
* @param name 文件名
|
||||
* @return 包含该文件名的所有文件或文件夹
|
||||
*/
|
||||
public List<FileItemDTO> search(String name) {
|
||||
boolean searchIgnoreCase = systemConfigService.getSearchIgnoreCase();
|
||||
return zFileCache.find(name, searchIgnoreCase);
|
||||
}
|
||||
|
||||
/**
|
||||
* 不是加密文件夹
|
||||
* @param list 文件夹中的内容
|
||||
* @return 返回此文件夹是否加密.
|
||||
*/
|
||||
private boolean isNotEncryptedFolder(List<FileItemDTO> list) {
|
||||
// 如果开启了 "搜索包含加密文件" 选项, 则直接返回 true.
|
||||
SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();
|
||||
if (BooleanUtil.isFalse(systemConfig.getSearchContainEncryptedFile())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 遍历文件判断是否包含
|
||||
for (FileItemDTO fileItemDTO : list) {
|
||||
if (Objects.equals(ZFileConstant.PASSWORD_FILE_NAME, fileItemDTO.getName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新缓存
|
||||
*/
|
||||
public void refreshCache(String key) throws Exception {
|
||||
zFileCache.remove(key);
|
||||
FileService currentFileService = (FileService) AopContext.currentProxy();
|
||||
currentFileService.fileList(key);
|
||||
}
|
||||
|
||||
public abstract FileItemDTO getFileItem(String path);
|
||||
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import com.amazonaws.services.s3.AmazonS3;
|
||||
import com.amazonaws.services.s3.model.ListObjectsRequest;
|
||||
import com.amazonaws.services.s3.model.ObjectListing;
|
||||
import com.amazonaws.services.s3.model.S3ObjectSummary;
|
||||
import im.zhaojun.common.exception.NotExistFileException;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public abstract class AbstractS3FileService extends AbstractFileService {
|
||||
|
||||
@Resource
|
||||
protected StorageConfigService storageConfigService;
|
||||
|
||||
protected String path;
|
||||
|
||||
protected String bucketName;
|
||||
|
||||
protected String domain;
|
||||
|
||||
protected AmazonS3 s3Client;
|
||||
|
||||
protected boolean isPrivate;
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) {
|
||||
this.path = path;
|
||||
return s3FileList(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
this.path = path;
|
||||
return s3ObjectUrl(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 S3 指定目录下的对象列表
|
||||
* @param path 路径
|
||||
* @return 指定目录下的对象列表
|
||||
*/
|
||||
public List<FileItemDTO> s3FileList(String path) {
|
||||
path = StringUtils.removeFirstSeparator(path);
|
||||
String fullPath = StringUtils.removeFirstSeparator(StringUtils.getFullPath(basePath, path));
|
||||
List<FileItemDTO> fileItemList = new ArrayList<>();
|
||||
ObjectListing objectListing = s3Client.listObjects(new ListObjectsRequest(bucketName, fullPath, "", "/", 1000));
|
||||
|
||||
for (S3ObjectSummary s : objectListing.getObjectSummaries()) {
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
if (s.getKey().equals(fullPath)) {
|
||||
continue;
|
||||
}
|
||||
fileItemDTO.setName(s.getKey().substring(fullPath.length()));
|
||||
fileItemDTO.setSize(s.getSize());
|
||||
fileItemDTO.setTime(s.getLastModified());
|
||||
fileItemDTO.setType(FileTypeEnum.FILE);
|
||||
fileItemDTO.setPath(path);
|
||||
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(path, fileItemDTO.getName())));
|
||||
fileItemList.add(fileItemDTO);
|
||||
}
|
||||
|
||||
for (String commonPrefix : objectListing.getCommonPrefixes()) {
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
if (Objects.equals(commonPrefix, "/")) {
|
||||
continue;
|
||||
}
|
||||
fileItemDTO.setName(commonPrefix.substring(fullPath.length(), commonPrefix.length() - 1));
|
||||
fileItemDTO.setType(FileTypeEnum.FOLDER);
|
||||
fileItemDTO.setPath(path);
|
||||
fileItemList.add(fileItemDTO);
|
||||
}
|
||||
|
||||
return fileItemList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象的访问链接, 如果指定了域名, 则替换为自定义域名.
|
||||
* @return S3 对象访问地址
|
||||
*/
|
||||
public String s3ObjectUrl(String path) {
|
||||
String fullPath = StringUtils.removeFirstSeparator(StringUtils.removeDuplicateSeparator(basePath + "/" + path));
|
||||
|
||||
// 如果不是私有空间, 且指定了加速域名, 则直接返回下载地址.
|
||||
if (BooleanUtil.isFalse(isPrivate) && StringUtils.isNotNullOrEmpty(domain)) {
|
||||
return StringUtils.concatPath(domain, fullPath);
|
||||
}
|
||||
|
||||
Date expirationDate = new Date(System.currentTimeMillis() + timeout * 1000);
|
||||
URL url = s3Client.generatePresignedUrl(bucketName, fullPath, expirationDate);
|
||||
|
||||
String defaultUrl = url.toExternalForm();
|
||||
if (StringUtils.isNotNullOrEmpty(domain)) {
|
||||
defaultUrl = URLUtil.complateUrl(domain, url.getFile());
|
||||
}
|
||||
return URLUtil.decode(defaultUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileItemDTO getFileItem(String path) {
|
||||
List<FileItemDTO> list;
|
||||
try {
|
||||
int end = path.lastIndexOf("/");
|
||||
list = fileList(path.substring(0, end + 1));
|
||||
} catch (Exception e) {
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
|
||||
for (FileItemDTO fileItemDTO : list) {
|
||||
String fullPath = StringUtils.concatUrl(fileItemDTO.getPath(), fileItemDTO.getName());
|
||||
if (Objects.equals(fullPath, path)) {
|
||||
return fileItemDTO;
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import im.zhaojun.common.cache.ZFileCache;
|
||||
import im.zhaojun.common.config.StorageTypeFactory;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class FileAsyncCacheService {
|
||||
|
||||
public static final String CACHE_PROCESS_PREFIX = "zfile-process-cache:";
|
||||
|
||||
private boolean cacheFinish;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
private volatile boolean stopFlag = false;
|
||||
|
||||
@Resource
|
||||
private ZFileCache zFileCache;
|
||||
|
||||
@Value("${zfile.cache.auto-refresh.enable}")
|
||||
protected boolean enableAutoRefreshCache;
|
||||
|
||||
@Value("${zfile.cache.auto-refresh.delay}")
|
||||
protected Long delay;
|
||||
|
||||
@Value("${zfile.cache.auto-refresh.interval}")
|
||||
protected Long interval;
|
||||
|
||||
@Async
|
||||
public void cacheGlobalFile() {
|
||||
stopFlag = false;
|
||||
StorageTypeEnum storageStrategy = systemConfigService.getCurrentStorageStrategy();
|
||||
|
||||
if (storageStrategy == null) {
|
||||
log.debug("尚未配置存储策略. 跳过启动缓存.");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean enableCache = systemConfigService.getEnableCache();
|
||||
if (!enableCache) {
|
||||
log.debug("存储策略 {} 未启用缓存, 跳过缓存.", storageStrategy.getDescription());
|
||||
return;
|
||||
}
|
||||
|
||||
AbstractFileService fileService = StorageTypeFactory.getStorageTypeService(storageStrategy);
|
||||
|
||||
if (fileService.getIsUnInitialized()) {
|
||||
log.debug("存储策略 {} 未初始化成功, 跳过缓存.", storageStrategy.getDescription());
|
||||
return;
|
||||
}
|
||||
|
||||
Integer cacheDirectoryCount = 0;
|
||||
|
||||
log.info("缓存 {} 所有文件开始", storageStrategy.getDescription());
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
FileService currentFileService = systemConfigService.getCurrentFileService();
|
||||
List<FileItemDTO> rootFileItems = currentFileService.fileList("/");
|
||||
ArrayDeque<FileItemDTO> queue = new ArrayDeque<>(rootFileItems);
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
FileItemDTO fileItemDTO = queue.pop();
|
||||
|
||||
if (stopFlag) {
|
||||
zFileCache.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
|
||||
String filePath = StringUtils.removeDuplicateSeparator("/" + fileItemDTO.getPath() + "/" + fileItemDTO.getName() + "/");
|
||||
|
||||
List<FileItemDTO> fileItems = currentFileService.fileList(filePath);
|
||||
queue.addAll(fileItems);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("缓存所有文件失败", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
long endTime = System.currentTimeMillis();
|
||||
|
||||
if (stopFlag) {
|
||||
log.info("缓存 {} 所有文件被强制结束, 用时: {} 秒", storageStrategy.getDescription(), ((endTime - startTime) / 1000));
|
||||
cacheFinish = false;
|
||||
stopFlag = false;
|
||||
} else {
|
||||
log.info("缓存 {} 所有文件结束, 用时: {} 秒", storageStrategy.getDescription(), ((endTime - startTime) / 1000));
|
||||
enableCacheAutoRefreshTask();
|
||||
cacheFinish = true;
|
||||
stopFlag = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void enableCacheAutoRefreshTask() {
|
||||
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
|
||||
|
||||
if (enableAutoRefreshCache) {
|
||||
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
||||
scheduledExecutorService.scheduleWithFixedDelay(() -> {
|
||||
zFileCache.setLastCacheAutoRefreshDate(new Date());
|
||||
|
||||
boolean enableCache = systemConfigService.getEnableCache();
|
||||
|
||||
if (!enableCache) {
|
||||
log.debug("当前存储引擎未开启缓存, 跳过自动刷新缓存");
|
||||
zFileCache.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("开始调用自动刷新缓存");
|
||||
|
||||
Set<String> keySet = zFileCache.keySet();
|
||||
|
||||
ArrayList<String> keys = new ArrayList<>(keySet);
|
||||
|
||||
|
||||
for (String key : keys) {
|
||||
try {
|
||||
Thread.sleep(300);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (stopFlag) {
|
||||
break;
|
||||
}
|
||||
|
||||
zFileCache.remove(key);
|
||||
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
|
||||
try {
|
||||
if (Objects.equals(currentStorageStrategy, systemConfigService.getCurrentStorageStrategy())) {
|
||||
currentFileService.fileList(key);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("刷新过程中出错 : [" + key + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (stopFlag) {
|
||||
log.debug("检测到停止 [{}] 缓存指令, 已停止自动刷新任务", currentStorageStrategy);
|
||||
scheduledExecutorService.shutdownNow();
|
||||
stopFlag = false;
|
||||
} else {
|
||||
log.debug("自动刷新缓存完成");
|
||||
}
|
||||
}, delay, interval, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
public void stopScheduled() {
|
||||
this.stopFlag = true;
|
||||
}
|
||||
|
||||
public void enableScheduled() {
|
||||
this.stopFlag = false;
|
||||
}
|
||||
|
||||
public boolean isCacheFinish() {
|
||||
return cacheFinish;
|
||||
}
|
||||
|
||||
public void setCacheFinish(boolean cacheFinish) {
|
||||
this.cacheFinish = cacheFinish;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import im.zhaojun.common.cache.ZFileCache;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class FileCacheService {
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
@Resource
|
||||
private ZFileCache zFileCache;
|
||||
|
||||
public void enableCache() {
|
||||
systemConfigService.updateCacheEnableConfig(true);
|
||||
fileAsyncCacheService.cacheGlobalFile();
|
||||
}
|
||||
|
||||
public void disableCache() {
|
||||
systemConfigService.updateCacheEnableConfig(false);
|
||||
zFileCache.clear();
|
||||
fileAsyncCacheService.setCacheFinish(false);
|
||||
fileAsyncCacheService.stopScheduled();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public interface FileService {
|
||||
|
||||
/***
|
||||
* 获取指定路径下的文件及文件夹
|
||||
* @param path 文件路径
|
||||
* @return 文件及文件夹列表
|
||||
* @throws Exception 获取文件列表中出现的异常
|
||||
*/
|
||||
List<FileItemDTO> fileList(String path) throws Exception;
|
||||
|
||||
/**
|
||||
* 获取文件下载地址
|
||||
* @param path 文件路径
|
||||
* @return 文件下载地址
|
||||
*/
|
||||
String getDownloadUrl(String path);
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.repository.StorageConfigRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class StorageConfigService {
|
||||
|
||||
@Resource
|
||||
private StorageConfigRepository storageConfigRepository;
|
||||
|
||||
public List<StorageConfig> selectStorageConfigByType(StorageTypeEnum storageTypeEnum) {
|
||||
return storageConfigRepository.findByTypeOrderById(storageTypeEnum);
|
||||
}
|
||||
|
||||
|
||||
public StorageConfig selectByTypeAndKey(StorageTypeEnum storageType, String key) {
|
||||
return storageConfigRepository.findByTypeAndKey(storageType, key);
|
||||
}
|
||||
|
||||
|
||||
public Map<String, StorageConfig> selectStorageConfigMapByKey(StorageTypeEnum storageTypeEnum) {
|
||||
Map<String, StorageConfig> map = new HashMap<>(24);
|
||||
for (StorageConfig storageConfig : selectStorageConfigByType(storageTypeEnum)) {
|
||||
map.put(storageConfig.getKey(), storageConfig);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
public void updateStorageConfig(List<StorageConfig> storageConfigList) {
|
||||
storageConfigRepository.saveAll(storageConfigList);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import im.zhaojun.common.cache.ZFileCache;
|
||||
import im.zhaojun.common.config.StorageTypeFactory;
|
||||
import im.zhaojun.common.model.SystemConfig;
|
||||
import im.zhaojun.common.model.constant.SystemConfigConstant;
|
||||
import im.zhaojun.common.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.repository.SystemConfigRepository;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SystemConfigService {
|
||||
|
||||
public static final String SYSTEM_CONFIG_CACHE_PREFIX = "zfile-config-cache:";
|
||||
|
||||
public static final String SYSTEM_CONFIG_CACHE_KEY = "1";
|
||||
|
||||
@Resource
|
||||
private ZFileCache zFileCache;
|
||||
|
||||
@Resource
|
||||
private SystemConfigRepository systemConfigRepository;
|
||||
|
||||
@Resource
|
||||
private FileCacheService fileCacheService;
|
||||
|
||||
private Class<SystemConfigDTO> systemConfigDTOClass = SystemConfigDTO.class;
|
||||
|
||||
public SystemConfigDTO getSystemConfig() {
|
||||
SystemConfigDTO cacheConfig = zFileCache.getConfig();
|
||||
if (cacheConfig != null) {
|
||||
return cacheConfig;
|
||||
}
|
||||
|
||||
SystemConfigDTO systemConfigDTO = new SystemConfigDTO();
|
||||
List<SystemConfig> systemConfigList = systemConfigRepository.findAll();
|
||||
|
||||
for (SystemConfig systemConfig : systemConfigList) {
|
||||
String key = systemConfig.getKey();
|
||||
|
||||
try {
|
||||
Field field = systemConfigDTOClass.getDeclaredField(key);
|
||||
if (field != null) {
|
||||
field.setAccessible(true);
|
||||
String strVal = systemConfig.getValue();
|
||||
Object convertVal = Convert.convert(field.getType(), strVal);
|
||||
field.set(systemConfigDTO, convertVal);
|
||||
}
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("通过反射, 将字段 {" + key + "}注入 SystemConfigDTO 时出现异常:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zFileCache.updateConfig(systemConfigDTO);
|
||||
return systemConfigDTO;
|
||||
}
|
||||
|
||||
|
||||
public void updateSystemConfig(SystemConfigDTO systemConfigDTO) throws Exception {
|
||||
List<SystemConfig> systemConfigList = new ArrayList<>();
|
||||
|
||||
Field[] fields = systemConfigDTOClass.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
String key = field.getName();
|
||||
SystemConfig systemConfig = systemConfigRepository.findByKey(key);
|
||||
if (systemConfig != null) {
|
||||
field.setAccessible(true);
|
||||
Object val = field.get(systemConfigDTO);
|
||||
if (val != null) {
|
||||
systemConfig.setValue(val.toString());
|
||||
systemConfigList.add(systemConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zFileCache.removeConfig();
|
||||
systemConfigRepository.saveAll(systemConfigList);
|
||||
}
|
||||
|
||||
|
||||
public void updateUsernameAndPwd(String username, String password) {
|
||||
SystemConfig usernameConfig = systemConfigRepository.findByKey(SystemConfigConstant.USERNAME);
|
||||
usernameConfig.setValue(username);
|
||||
systemConfigRepository.save(usernameConfig);
|
||||
|
||||
String encryptionPassword = SecureUtil.md5(password);
|
||||
SystemConfig systemConfig = systemConfigRepository.findByKey(SystemConfigConstant.PASSWORD);
|
||||
systemConfig.setValue(encryptionPassword);
|
||||
|
||||
zFileCache.removeConfig();
|
||||
|
||||
systemConfigRepository.save(systemConfig);
|
||||
}
|
||||
|
||||
|
||||
public void updateCacheEnableConfig(Boolean isEnable) {
|
||||
SystemConfig enableConfig = systemConfigRepository.findByKey(SystemConfigConstant.ENABLE_CACHE);
|
||||
enableConfig.setValue(isEnable.toString());
|
||||
systemConfigRepository.save(enableConfig);
|
||||
zFileCache.removeConfig();
|
||||
}
|
||||
|
||||
|
||||
public AbstractFileService getCurrentFileService() {
|
||||
StorageTypeEnum storageStrategy = getCurrentStorageStrategy();
|
||||
return StorageTypeFactory.getStorageTypeService(storageStrategy);
|
||||
}
|
||||
|
||||
|
||||
public StorageTypeEnum getCurrentStorageStrategy() {
|
||||
SystemConfigDTO systemConfigDTO = getSystemConfig();
|
||||
return systemConfigDTO.getStorageStrategy();
|
||||
}
|
||||
|
||||
public boolean getEnableCache() {
|
||||
SystemConfigDTO systemConfigDTO = getSystemConfig();
|
||||
return BooleanUtil.isTrue(systemConfigDTO.getEnableCache());
|
||||
}
|
||||
|
||||
public boolean getSearchIgnoreCase() {
|
||||
SystemConfigDTO systemConfigDTO = getSystemConfig();
|
||||
return BooleanUtil.isTrue(systemConfigDTO.getSearchIgnoreCase());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import im.zhaojun.common.model.SystemMonitorInfo;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class SystemMonitorService {
|
||||
|
||||
public SystemMonitorInfo systemMonitorInfo() {
|
||||
return new SystemMonitorInfo();
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import im.zhaojun.common.model.constant.ZFileConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.dto.SiteConfigDTO;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.util.HttpUtil;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SystemService {
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
/**
|
||||
* 构建指定路径下标题, 页面文档信息
|
||||
* @param path 路径
|
||||
*/
|
||||
public SiteConfigDTO getConfig(String path) throws Exception {
|
||||
|
||||
SiteConfigDTO siteConfigDTO = new SiteConfigDTO();
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
|
||||
List<FileItemDTO> fileItemList;
|
||||
|
||||
if (Objects.equals(systemConfigService.getSystemConfig().getStorageStrategy(), StorageTypeEnum.FTP)) {
|
||||
fileItemList = new ArrayList<>();
|
||||
} else {
|
||||
fileItemList = fileService.fileList(path);
|
||||
}
|
||||
|
||||
for (FileItemDTO fileItemDTO : fileItemList) {
|
||||
if (ZFileConstant.README_FILE_NAME.equalsIgnoreCase(fileItemDTO.getName())) {
|
||||
String textContent = null;
|
||||
try {
|
||||
textContent = HttpUtil.getTextContent(fileItemDTO.getUrl());
|
||||
} catch (HttpClientErrorException httpClientErrorException) {
|
||||
log.debug("尝试重新获取文档区缓存中链接后仍失败", httpClientErrorException);
|
||||
try {
|
||||
String fullPath = StringUtils.removeDuplicateSeparator(fileItemDTO.getPath() + "/" + fileItemDTO.getName());
|
||||
FileItemDTO fileItem = fileService.getFileItem(fullPath);
|
||||
textContent = HttpUtil.getTextContent(fileItem.getUrl());
|
||||
} catch (Exception e) {
|
||||
log.debug("尝试重新获取文档区链接后仍失败, 已置为空", e);
|
||||
}
|
||||
}
|
||||
siteConfigDTO.setReadme(textContent);
|
||||
}
|
||||
}
|
||||
return siteConfigDTO;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package im.zhaojun.common.util;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.mpatric.mp3agic.ID3v1;
|
||||
import com.mpatric.mp3agic.ID3v2;
|
||||
import com.mpatric.mp3agic.InvalidDataException;
|
||||
import com.mpatric.mp3agic.Mp3File;
|
||||
import com.mpatric.mp3agic.UnsupportedTagException;
|
||||
import im.zhaojun.common.model.constant.ZFileConstant;
|
||||
import im.zhaojun.common.model.dto.AudioInfoDTO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
|
||||
/**
|
||||
* 音频解析工具类
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class AudioHelper {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AudioHelper.class);
|
||||
|
||||
public static AudioInfoDTO getAudioInfo(String url) throws Exception {
|
||||
String query = new URL(URLUtil.decode(url)).getQuery();
|
||||
|
||||
if (query != null) {
|
||||
url = url.replace(query, URLUtil.encode(query));
|
||||
}
|
||||
|
||||
File file = new File(ZFileConstant.USER_HOME + ZFileConstant.AUDIO_TMP_PATH + UUID.fastUUID());
|
||||
FileUtil.mkParentDirs(file);
|
||||
HttpUtil.downloadFile(url, file);
|
||||
AudioInfoDTO audioInfoDTO = parseAudioInfo(file);
|
||||
audioInfoDTO.setSrc(url);
|
||||
file.deleteOnExit();
|
||||
return audioInfoDTO;
|
||||
}
|
||||
|
||||
private static AudioInfoDTO parseAudioInfo(File file) throws IOException, UnsupportedTagException {
|
||||
AudioInfoDTO audioInfoDTO = new AudioInfoDTO();
|
||||
audioInfoDTO.setTitle("未知歌曲");
|
||||
audioInfoDTO.setArtist("未知");
|
||||
audioInfoDTO.setCover("http://c.jun6.net/audio.png");
|
||||
|
||||
Mp3File mp3File = null;
|
||||
try {
|
||||
mp3File = new Mp3File(file);
|
||||
} catch (InvalidDataException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("无法解析的音频文件.");
|
||||
}
|
||||
}
|
||||
|
||||
if (mp3File == null) {
|
||||
return audioInfoDTO;
|
||||
}
|
||||
|
||||
ID3v1 audioTag = null;
|
||||
|
||||
if (mp3File.hasId3v2Tag()) {
|
||||
ID3v2 id3v2Tag = mp3File.getId3v2Tag();
|
||||
byte[] albumImage = id3v2Tag.getAlbumImage();
|
||||
if (albumImage != null) {
|
||||
audioInfoDTO.setCover("data:" + id3v2Tag.getAlbumImageMimeType() + ";base64," + Base64.encode(albumImage));
|
||||
}
|
||||
audioTag = id3v2Tag;
|
||||
}
|
||||
|
||||
if (audioTag != null) {
|
||||
audioInfoDTO.setTitle(audioTag.getTitle());
|
||||
audioInfoDTO.setArtist(audioTag.getArtist());
|
||||
}
|
||||
|
||||
return audioInfoDTO;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package im.zhaojun.common.util;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class FileUtil {
|
||||
|
||||
public static ResponseEntity<Object> export(File file, String fileName) {
|
||||
if (!file.exists()) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("404 FILE NOT FOUND");
|
||||
}
|
||||
|
||||
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
|
||||
if (StringUtils.isNullOrEmpty(fileName)) {
|
||||
fileName = file.getName();
|
||||
}
|
||||
|
||||
headers.setContentDispositionFormData("attachment", URLUtil.encode(fileName));
|
||||
|
||||
headers.add("Pragma", "no-cache");
|
||||
headers.add("Expires", "0");
|
||||
headers.add("Last-Modified", new Date().toString());
|
||||
headers.add("ETag", String.valueOf(System.currentTimeMillis()));
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.headers(headers)
|
||||
.contentLength(file.length())
|
||||
.contentType(mediaType)
|
||||
.body(new FileSystemResource(file));
|
||||
}
|
||||
|
||||
public static ResponseEntity<Object> export(File file) {
|
||||
if (!file.exists()) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("404 FILE NOT FOUND");
|
||||
}
|
||||
|
||||
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
headers.setContentDispositionFormData("attachment", URLUtil.encode(file.getName()));
|
||||
|
||||
headers.add("Pragma", "no-cache");
|
||||
headers.add("Expires", "0");
|
||||
headers.add("Last-Modified", new Date().toString());
|
||||
headers.add("ETag", String.valueOf(System.currentTimeMillis()));
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.headers(headers)
|
||||
.contentLength(file.length())
|
||||
.contentType(mediaType)
|
||||
.body(new FileSystemResource(file));
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package im.zhaojun.common.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpUtil {
|
||||
|
||||
public static String getTextContent(String url) {
|
||||
RestTemplate restTemplate = SpringContextHolder.getBean("restTemplate");
|
||||
String result = restTemplate.getForObject(url, String.class);
|
||||
return result == null ? "" : result;
|
||||
}
|
||||
|
||||
public static boolean checkUrlExist(String url) {
|
||||
RestTemplate restTemplate = SpringContextHolder.getBean("restTemplate");
|
||||
try {
|
||||
restTemplate.headForHeaders(url);
|
||||
return true;
|
||||
} catch (RestClientException ignored) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package im.zhaojun.common.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
@Lazy(false)
|
||||
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
|
||||
|
||||
private static ApplicationContext applicationContext = null;
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);
|
||||
|
||||
/**
|
||||
* 取得存储在静态变量中的 ApplicationContext.
|
||||
*/
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从静态变量 applicationContext 中取得 Bean, 自动转型为所赋值对象的类型.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getBean(String name) {
|
||||
return (T) applicationContext.getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从静态变量 applicationContext 中取得 Bean, 自动转型为所赋值对象的类型.
|
||||
*/
|
||||
public static <T> T getBean(Class<T> requiredType) {
|
||||
return applicationContext.getBean(requiredType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除 SpringContextHolder 中的 ApplicationContext 为 Null.
|
||||
*/
|
||||
public static void clearHolder() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
|
||||
}
|
||||
logger.info("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
|
||||
applicationContext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实现 DisposableBean 接口, 在 Context 关闭时清理静态变量.
|
||||
*/
|
||||
@Override
|
||||
public void destroy() {
|
||||
SpringContextHolder.clearHolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
|
||||
SpringContextHolder.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package im.zhaojun.common.util;
|
||||
|
||||
import cn.hutool.core.net.NetUtil;
|
||||
import im.zhaojun.common.cache.ZFileCache;
|
||||
import im.zhaojun.common.exception.InitializeException;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.FileAsyncCacheService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 项目启动监听器, 当项目启动时, 遍历当前对象存储的所有内容, 添加到缓存中.
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class StartupListener implements ApplicationListener<ApplicationStartedEvent> {
|
||||
|
||||
@Resource
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
@Resource
|
||||
private Environment environment;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(@NonNull ApplicationStartedEvent event) {
|
||||
printStartInfo();
|
||||
cacheAllFile();
|
||||
}
|
||||
|
||||
|
||||
private void printStartInfo() {
|
||||
String serverPort = environment.getProperty("server.port", "8080");
|
||||
|
||||
LinkedHashSet<String> localIps = NetUtil.localIps();
|
||||
StringBuilder indexAddr = new StringBuilder();
|
||||
StringBuilder indexAdminAddr = new StringBuilder();
|
||||
for (String localIp : localIps) {
|
||||
String addr = String.format("http://%s:%s", localIp, serverPort);
|
||||
indexAddr.append(addr).append("\t");
|
||||
indexAdminAddr.append(addr).append("/#/admin").append("\t");
|
||||
}
|
||||
log.info("ZFile started at " + indexAddr);
|
||||
log.info("ZFile Admin started at " + indexAdminAddr);
|
||||
}
|
||||
|
||||
private void cacheAllFile() {
|
||||
try {
|
||||
fileAsyncCacheService.cacheGlobalFile();
|
||||
} catch (Exception e) {
|
||||
throw new InitializeException("初始化缓存异常.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package im.zhaojun.common.util;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class StringUtils {
|
||||
|
||||
|
||||
public static final char DELIMITER = '/';
|
||||
|
||||
public static final String HTTP_PROTOCAL = "http://";
|
||||
|
||||
public static final String HTTPS_PROTOCAL = "https://";
|
||||
|
||||
/**
|
||||
* 移除 URL 中的第一个 '/'
|
||||
* @return 如 path = '/folder1/file1', 返回 'folder1/file1'
|
||||
*/
|
||||
public static String removeFirstSeparator(String path) {
|
||||
if (!"".equals(path) && path.charAt(0) == DELIMITER) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除 URL 中的最后一个 '/'
|
||||
* @return 如 path = '/folder1/file1/', 返回 '/folder1/file1'
|
||||
*/
|
||||
public static String removeLastSeparator(String path) {
|
||||
if (!"".equals(path) && path.charAt(path.length() - 1) == DELIMITER) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static String concatUrl(String path, String name) {
|
||||
return removeDuplicateSeparator(DELIMITER + path + DELIMITER + name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将域名和路径组装成 URL, 主要用来处理分隔符 '/'
|
||||
* @param domain 域名
|
||||
* @param path 路径
|
||||
* @return URL
|
||||
*/
|
||||
public static String concatPath(String domain, String path) {
|
||||
if (path != null && path.length() > 1 && path.charAt(0) != DELIMITER) {
|
||||
path = DELIMITER + path;
|
||||
}
|
||||
|
||||
if (domain != null && domain.charAt(domain.length() - 1) == DELIMITER) {
|
||||
domain = domain.substring(0, domain.length() - 2);
|
||||
}
|
||||
|
||||
return domain + path;
|
||||
}
|
||||
|
||||
public static String removeDuplicateSeparator(String path) {
|
||||
if (path == null || path.length() < 2) {
|
||||
return path;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (path.indexOf(HTTP_PROTOCAL) == 0) {
|
||||
sb.append(HTTP_PROTOCAL);
|
||||
} else if (path.indexOf(HTTPS_PROTOCAL) == 0) {
|
||||
sb.append(HTTPS_PROTOCAL);
|
||||
}
|
||||
|
||||
for (int i = sb.length(); i < path.length() - 1; i++) {
|
||||
char current = path.charAt(i);
|
||||
char next = path.charAt(i + 1);
|
||||
if (!(current == DELIMITER && next == DELIMITER)) {
|
||||
sb.append(current);
|
||||
}
|
||||
}
|
||||
sb.append(path.charAt(path.length() - 1));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static boolean isNullOrEmpty(String s) {
|
||||
return s == null || "".equals(s);
|
||||
}
|
||||
|
||||
public static boolean isNotNullOrEmpty(String s) {
|
||||
return !isNullOrEmpty(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 basePath + path 的全路径地址.
|
||||
* @return basePath + path 的全路径地址.
|
||||
*/
|
||||
public static String getFullPath(String basePath, String path) {
|
||||
basePath = ObjectUtil.defaultIfNull(basePath, "");
|
||||
path = ObjectUtil.defaultIfNull(path, "");
|
||||
return StringUtils.removeDuplicateSeparator(basePath + "/" + path);
|
||||
}
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
package im.zhaojun.ftp.service;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.extra.ftp.Ftp;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import org.apache.commons.net.ftp.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class FtpServiceImpl extends AbstractFileService implements FileService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(FtpServiceImpl.class);
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
private Ftp ftp;
|
||||
|
||||
private String domain;
|
||||
|
||||
private String host;
|
||||
|
||||
private String port;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
host = stringStorageConfigMap.get(StorageConfigConstant.HOST_KEY).getValue();
|
||||
port = stringStorageConfigMap.get(StorageConfigConstant.PORT_KEY).getValue();
|
||||
username = stringStorageConfigMap.get(StorageConfigConstant.USERNAME_KEY).getValue();
|
||||
password = stringStorageConfigMap.get(StorageConfigConstant.PASSWORD_KEY).getValue();
|
||||
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
|
||||
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();;
|
||||
if (Objects.isNull(host) || Objects.isNull(port) || Objects.isNull(username) || Objects.isNull(password)) {
|
||||
isInitialized = false;
|
||||
} else {
|
||||
ftp = new Ftp(host, Integer.parseInt(port), username, password, StandardCharsets.UTF_8);
|
||||
ftp.getClient().configure(new FTPClientConfig(FTPClientConfig.SYST_UNIX));
|
||||
ftp.getClient().type(FTP.BINARY_FILE_TYPE);
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) {
|
||||
ftp.reconnectIfTimeout();
|
||||
String fullPath = StringUtils.getFullPath(basePath, path);
|
||||
ftp.cd(fullPath);
|
||||
FTPFile[] ftpFiles = new FTPFile[]{};
|
||||
try {
|
||||
ftp.getClient().changeWorkingDirectory("/");
|
||||
ftpFiles = ftp.getClient().listFiles(fullPath);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// ignore
|
||||
}
|
||||
|
||||
List<FileItemDTO> fileItemList = new ArrayList<>();
|
||||
|
||||
for (FTPFile ftpFile : ftpFiles) {
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
fileItemDTO.setName(ftpFile.getName());
|
||||
fileItemDTO.setSize(ftpFile.getSize());
|
||||
fileItemDTO.setTime(ftpFile.getTimestamp().getTime());
|
||||
fileItemDTO.setType(ftpFile.isDirectory() ? FileTypeEnum.FOLDER : FileTypeEnum.FILE);
|
||||
fileItemDTO.setPath(path);
|
||||
if (ftpFile.isFile()) {
|
||||
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(path, fileItemDTO.getName())));
|
||||
}
|
||||
fileItemList.add(fileItemDTO);
|
||||
}
|
||||
return fileItemList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
String fullPath = StringUtils.getFullPath(basePath, path);
|
||||
if (StringUtils.isNullOrEmpty(domain)) {
|
||||
return "ftp://"
|
||||
+ URLUtil.encodeQuery(username)
|
||||
+ ":"
|
||||
+ URLUtil.encodeQuery(password)
|
||||
+ "@"
|
||||
+ host + ":" + port + fullPath;
|
||||
}
|
||||
|
||||
return URLUtil.complateUrl(domain, fullPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.FTP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileItemDTO getFileItem(String path) {
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
fileItemDTO.setUrl(getDownloadUrl(path));
|
||||
return fileItemDTO;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean testConnection() {
|
||||
// FTPClient ftpClient = ftp.getClient();
|
||||
// try {
|
||||
// ftpClient.connect(host, Integer.parseInt(port));
|
||||
// return ftpClient.login(username, password);
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// return false;
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ifDisConnectionReConnection() {
|
||||
FTPClient ftpClient = ftp.getClient();
|
||||
try {
|
||||
// 验证FTP服务器是否登录成功
|
||||
int replyCode = ftpClient.getReplyCode();
|
||||
|
||||
// ftpClient.reinitialize();
|
||||
if (!FTPReply.isPositiveCompletion(replyCode)) {
|
||||
System.out.println("断开了连接, 已尝试重新连接.");
|
||||
ftpClient.connect(host, Integer.parseInt(port));
|
||||
ftpClient.login(username, password);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package im.zhaojun.huawei.service;
|
||||
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractS3FileService;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class HuaweiServiceImpl extends AbstractS3FileService implements FileService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(HuaweiServiceImpl.class);
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
|
||||
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
|
||||
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
|
||||
|
||||
bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
|
||||
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
|
||||
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
|
||||
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
|
||||
s3Client = AmazonS3ClientBuilder.standard()
|
||||
.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "obs")).build();
|
||||
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.HUAWEI;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package im.zhaojun.local.controller;
|
||||
|
||||
import im.zhaojun.common.util.FileUtil;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import im.zhaojun.local.service.LocalServiceImpl;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Controller
|
||||
public class LocalController {
|
||||
|
||||
@Resource
|
||||
private LocalServiceImpl localServiceImpl;
|
||||
|
||||
@GetMapping("/file/**")
|
||||
@ResponseBody
|
||||
public ResponseEntity<Object> downAttachment(final HttpServletRequest request) {
|
||||
String path = (String) request.getAttribute(
|
||||
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
|
||||
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
|
||||
AntPathMatcher apm = new AntPathMatcher();
|
||||
String filePath = apm.extractPathWithinPattern(bestMatchPattern, path);
|
||||
|
||||
return FileUtil.export(new File(StringUtils.concatPath(localServiceImpl.getFilePath(), filePath)));
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package im.zhaojun.local.service;
|
||||
|
||||
import im.zhaojun.common.exception.NotExistFileException;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.SystemConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.constant.SystemConfigConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.repository.SystemConfigRepository;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class LocalServiceImpl extends AbstractFileService implements FileService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LocalServiceImpl.class);
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Resource
|
||||
private SystemConfigRepository systemConfigRepository;
|
||||
|
||||
private String filePath;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
filePath = stringStorageConfigMap.get(StorageConfigConstant.FILE_PATH_KEY).getValue();
|
||||
if (Objects.isNull(filePath)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) {
|
||||
List<FileItemDTO> fileItemList = new ArrayList<>();
|
||||
|
||||
String fullPath = StringUtils.concatPath(filePath, path);
|
||||
|
||||
File file = new File(fullPath);
|
||||
File[] files = file.listFiles();
|
||||
|
||||
if (files == null) {
|
||||
return fileItemList;
|
||||
}
|
||||
for (File f : files) {
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
fileItemDTO.setType(f.isDirectory() ? FileTypeEnum.FOLDER : FileTypeEnum.FILE);
|
||||
fileItemDTO.setTime(new Date(f.lastModified()));
|
||||
fileItemDTO.setSize(f.length());
|
||||
fileItemDTO.setName(f.getName());
|
||||
fileItemDTO.setPath(path);
|
||||
if (f.isFile()) {
|
||||
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(path, f.getName())));
|
||||
}
|
||||
fileItemList.add(fileItemDTO);
|
||||
}
|
||||
|
||||
return fileItemList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
SystemConfig usernameConfig = systemConfigRepository.findByKey(SystemConfigConstant.DOMAIN);
|
||||
return StringUtils.removeDuplicateSeparator(usernameConfig.getValue() + "/file/" + path);
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.LOCAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileItemDTO getFileItem(String path) {
|
||||
String fullPath = StringUtils.concatPath(filePath, path);
|
||||
|
||||
File file = new File(fullPath);
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
fileItemDTO.setType(file.isDirectory() ? FileTypeEnum.FOLDER : FileTypeEnum.FILE);
|
||||
fileItemDTO.setTime(new Date(file.lastModified()));
|
||||
fileItemDTO.setSize(file.length());
|
||||
fileItemDTO.setName(file.getName());
|
||||
fileItemDTO.setPath(filePath);
|
||||
if (file.isFile()) {
|
||||
fileItemDTO.setUrl(getDownloadUrl(path));
|
||||
}
|
||||
|
||||
return fileItemDTO;
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package im.zhaojun.minio;
|
||||
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractS3FileService;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class MinIOServiceImpl extends AbstractS3FileService implements FileService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MinIOServiceImpl.class);
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
|
||||
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
|
||||
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
|
||||
bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
|
||||
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
|
||||
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
|
||||
s3Client = AmazonS3ClientBuilder.standard()
|
||||
.withPathStyleAccessEnabled(true)
|
||||
.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "minio")).build();
|
||||
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.MINIO;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package im.zhaojun.onedrive.china.service;
|
||||
|
||||
import im.zhaojun.common.config.GlobalScheduleTask;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.onedrive.common.service.AbstractOneDriveService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OneDriveChinaServiceImpl extends AbstractOneDriveService implements FileService {
|
||||
|
||||
@Resource
|
||||
private GlobalScheduleTask globalScheduleTask;
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Value("${zfile.onedrive-china.clientId}")
|
||||
private String clientId;
|
||||
|
||||
@Value("${zfile.onedrive-china.redirectUri}")
|
||||
private String redirectUri;
|
||||
|
||||
@Value("${zfile.onedrive-china.clientSecret}")
|
||||
private String clientSecret;
|
||||
|
||||
@Value("${zfile.onedrive-china.scope}")
|
||||
private String scope;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
String accessToken = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_TOKEN_KEY).getValue();
|
||||
String refreshToken = stringStorageConfigMap.get(StorageConfigConstant.REFRESH_TOKEN_KEY).getValue();
|
||||
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
|
||||
if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
refreshOneDriveToken();
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.ONE_DRIVE_CHINA;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGraphEndPoint() {
|
||||
return "microsoftgraph.chinacloudapi.cn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticateEndPoint() {
|
||||
return "login.partner.microsoftonline.cn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package im.zhaojun.onedrive.common.config;
|
||||
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.onedrive.china.service.OneDriveChinaServiceImpl;
|
||||
import im.zhaojun.onedrive.international.service.OneDriveServiceImpl;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Configuration
|
||||
public class OneDriveConfig {
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private OneDriveServiceImpl oneDriveServiceImpl;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private OneDriveChinaServiceImpl oneDriveChinaServiceImpl;
|
||||
|
||||
@Bean
|
||||
public RestTemplate oneDriveRestTemplate() {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
ClientHttpRequestInterceptor interceptor = (httpRequest, bytes, clientHttpRequestExecution) -> {
|
||||
String host = httpRequest.getURI().getHost();
|
||||
StorageTypeEnum type;
|
||||
if (oneDriveChinaServiceImpl.getGraphEndPoint().contains(host)) {
|
||||
type = StorageTypeEnum.ONE_DRIVE_CHINA;
|
||||
} else if (oneDriveServiceImpl.getGraphEndPoint().contains(host)) {
|
||||
type = StorageTypeEnum.ONE_DRIVE;
|
||||
} else {
|
||||
return clientHttpRequestExecution.execute(httpRequest, bytes);
|
||||
}
|
||||
|
||||
StorageConfig accessTokenConfig =
|
||||
storageConfigService.selectByTypeAndKey(type, StorageConfigConstant.ACCESS_TOKEN_KEY);
|
||||
|
||||
String tokenValue = String.format("%s %s", "Bearer", accessTokenConfig.getValue());
|
||||
httpRequest.getHeaders().add("Authorization", tokenValue);
|
||||
return clientHttpRequestExecution.execute(httpRequest, bytes);
|
||||
};
|
||||
restTemplate.setInterceptors(Collections.singletonList(interceptor));
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package im.zhaojun.onedrive.common.controller;
|
||||
|
||||
import im.zhaojun.onedrive.china.service.OneDriveChinaServiceImpl;
|
||||
import im.zhaojun.onedrive.common.model.OneDriveToken;
|
||||
import im.zhaojun.onedrive.international.service.OneDriveServiceImpl;
|
||||
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 javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/onedrive")
|
||||
public class OneDriveController {
|
||||
|
||||
@Resource
|
||||
private OneDriveServiceImpl oneDriveServiceImpl;
|
||||
|
||||
@Resource
|
||||
private OneDriveChinaServiceImpl oneDriveChinaServiceImpl;
|
||||
|
||||
@GetMapping("/callback")
|
||||
public String onedriveCallback(String code, Model model) {
|
||||
OneDriveToken oneDriveToken = oneDriveServiceImpl.getToken(code);
|
||||
model.addAttribute("accessToken", oneDriveToken.getAccessToken());
|
||||
model.addAttribute("refreshToken", oneDriveToken.getRefreshToken());
|
||||
return "callback";
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/china-callback")
|
||||
public String onedriveChinaCallback(String code, Model model) {
|
||||
OneDriveToken oneDriveToken = oneDriveChinaServiceImpl.getToken(code);
|
||||
model.addAttribute("accessToken", oneDriveToken.getAccessToken());
|
||||
model.addAttribute("refreshToken", oneDriveToken.getRefreshToken());
|
||||
return "callback";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
package im.zhaojun.onedrive.common.service;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.repository.StorageConfigRepository;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import im.zhaojun.onedrive.common.model.OneDriveToken;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Zhao Jun
|
||||
* 2020/1/29 11:54
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractOneDriveService extends AbstractFileService {
|
||||
|
||||
protected static final String DRIVER_INFO_URL = "https://{graphEndPoint}/v1.0/me/drives";
|
||||
|
||||
protected static final String DRIVER_ROOT_URL = "https://{graphEndPoint}/v1.0/me/drive/root/children";
|
||||
|
||||
protected static final String DRIVER_ITEMS_URL = "https://{graphEndPoint}/v1.0/me/drive/root:{path}:/children";
|
||||
|
||||
protected static final String DRIVER_ITEM_URL = "https://{graphEndPoint}/v1.0/me/drive/root:{path}";
|
||||
|
||||
protected static final String AUTHENTICATE_URL = "https://{authenticateEndPoint}/common/oauth2/v2.0/token";
|
||||
|
||||
@Resource
|
||||
private RestTemplate oneDriveRestTemplate;
|
||||
|
||||
@Resource
|
||||
private StorageConfigRepository storageConfigRepository;
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
public OneDriveToken getRefreshToken() {
|
||||
StorageConfig refreshStorageConfig =
|
||||
storageConfigRepository.findByTypeAndKey(this.getStorageTypeEnum(), StorageConfigConstant.REFRESH_TOKEN_KEY);
|
||||
|
||||
String param = "client_id=" + getClientId() +
|
||||
"&redirect_uri=" + getRedirectUri() +
|
||||
"&client_secret=" + getClientSecret() +
|
||||
"&refresh_token=" + refreshStorageConfig.getValue() +
|
||||
"&grant_type=refresh_token";
|
||||
|
||||
String fullAuthenticateUrl = AUTHENTICATE_URL.replace("{authenticateEndPoint}", getAuthenticateEndPoint());
|
||||
HttpRequest post = HttpUtil.createPost(fullAuthenticateUrl);
|
||||
|
||||
post.body(param, "application/x-www-form-urlencoded");
|
||||
HttpResponse response = post.execute();
|
||||
return JSONObject.parseObject(response.body(), OneDriveToken.class);
|
||||
}
|
||||
|
||||
public OneDriveToken getToken(String code) {
|
||||
String param = "client_id=" + getClientId() +
|
||||
"&redirect_uri=" + getRedirectUri() +
|
||||
"&client_secret=" + getClientSecret() +
|
||||
"&code=" + code +
|
||||
"&scope=" + getScope() +
|
||||
"&grant_type=authorization_code";
|
||||
|
||||
String fullAuthenticateUrl = AUTHENTICATE_URL.replace("{authenticateEndPoint}", getAuthenticateEndPoint());
|
||||
HttpRequest post = HttpUtil.createPost(fullAuthenticateUrl);
|
||||
|
||||
post.body(param, "application/x-www-form-urlencoded");
|
||||
HttpResponse response = post.execute();
|
||||
return JSONObject.parseObject(response.body(), OneDriveToken.class);
|
||||
}
|
||||
|
||||
public String getUserInfo() {
|
||||
return oneDriveRestTemplate.getForObject(DRIVER_INFO_URL, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) {
|
||||
path = StringUtils.removeFirstSeparator(path);
|
||||
String fullPath = StringUtils.getFullPath(basePath, path);
|
||||
|
||||
List<FileItemDTO> result = new ArrayList<>();
|
||||
String nextLink = null;
|
||||
|
||||
do {
|
||||
|
||||
String requestUrl;
|
||||
|
||||
if (nextLink != null) {
|
||||
nextLink = nextLink.replace("+", "%2B");
|
||||
requestUrl = URLUtil.decode(nextLink);
|
||||
}else if ("/".equalsIgnoreCase(fullPath) || "".equalsIgnoreCase(fullPath)) {
|
||||
requestUrl = DRIVER_ROOT_URL;
|
||||
} else {
|
||||
requestUrl = DRIVER_ITEMS_URL;
|
||||
}
|
||||
fullPath = StringUtils.removeLastSeparator(fullPath);
|
||||
|
||||
ResponseEntity<String> responseEntity;
|
||||
try {
|
||||
responseEntity = oneDriveRestTemplate.getForEntity(requestUrl, String.class, getGraphEndPoint(), fullPath);
|
||||
} catch (HttpClientErrorException e) {
|
||||
log.debug("调用 OneDrive 时出现了网络异常: {} , 已尝试重新刷新 token 后再试.", e.getMessage());
|
||||
refreshOneDriveToken();
|
||||
responseEntity = oneDriveRestTemplate.getForEntity(requestUrl, String.class, getGraphEndPoint(), fullPath);
|
||||
}
|
||||
|
||||
String body = responseEntity.getBody();
|
||||
|
||||
JSONObject root = JSON.parseObject(body);
|
||||
|
||||
nextLink = root.getString("@odata.nextLink");
|
||||
|
||||
JSONArray fileList = root.getJSONArray("value");
|
||||
|
||||
for (int i = 0; i < fileList.size(); i++) {
|
||||
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
JSONObject fileItem = fileList.getJSONObject(i);
|
||||
fileItemDTO.setName(fileItem.getString("name"));
|
||||
fileItemDTO.setSize(fileItem.getLong("size"));
|
||||
fileItemDTO.setTime(fileItem.getDate("lastModifiedDateTime"));
|
||||
|
||||
if (fileItem.containsKey("file")) {
|
||||
fileItemDTO.setUrl(fileItem.getString("@microsoft.graph.downloadUrl"));
|
||||
fileItemDTO.setType(FileTypeEnum.FILE);
|
||||
} else {
|
||||
fileItemDTO.setType(FileTypeEnum.FOLDER);
|
||||
}
|
||||
|
||||
fileItemDTO.setPath(path);
|
||||
result.add(fileItemDTO);
|
||||
}
|
||||
} while (nextLink != null);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileItemDTO getFileItem(String path) {
|
||||
|
||||
String fullPath = StringUtils.getFullPath(basePath, path);
|
||||
|
||||
String requestUrl;
|
||||
|
||||
ResponseEntity<String> responseEntity = oneDriveRestTemplate.getForEntity(DRIVER_ITEM_URL, String.class, getGraphEndPoint(), fullPath);
|
||||
String body = responseEntity.getBody();
|
||||
|
||||
JSONObject fileItem = JSON.parseObject(body);
|
||||
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
fileItemDTO.setName(fileItem.getString("name"));
|
||||
fileItemDTO.setSize(fileItem.getLong("size"));
|
||||
fileItemDTO.setTime(fileItem.getDate("lastModifiedDateTime"));
|
||||
|
||||
if (fileItem.containsKey("file")) {
|
||||
fileItemDTO.setUrl(fileItem.getString("@microsoft.graph.downloadUrl"));
|
||||
fileItemDTO.setType(FileTypeEnum.FILE);
|
||||
} else {
|
||||
fileItemDTO.setType(FileTypeEnum.FOLDER);
|
||||
}
|
||||
|
||||
fileItemDTO.setPath(path);
|
||||
return fileItemDTO;
|
||||
}
|
||||
|
||||
|
||||
public abstract String getGraphEndPoint();
|
||||
|
||||
public abstract String getAuthenticateEndPoint();
|
||||
|
||||
public abstract String getClientId();
|
||||
|
||||
public abstract String getRedirectUri();
|
||||
|
||||
public abstract String getClientSecret();
|
||||
|
||||
public abstract String getScope();
|
||||
|
||||
public void refreshOneDriveToken() {
|
||||
OneDriveToken refreshToken = getRefreshToken();
|
||||
|
||||
if (refreshToken.getAccessToken() == null || refreshToken.getRefreshToken() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
StorageConfig accessTokenConfig =
|
||||
storageConfigService.selectByTypeAndKey(this.getStorageTypeEnum(), StorageConfigConstant.ACCESS_TOKEN_KEY);
|
||||
StorageConfig refreshTokenConfig =
|
||||
storageConfigService.selectByTypeAndKey(this.getStorageTypeEnum(), StorageConfigConstant.REFRESH_TOKEN_KEY);
|
||||
accessTokenConfig.setValue(refreshToken.getAccessToken());
|
||||
refreshTokenConfig.setValue(refreshToken.getRefreshToken());
|
||||
|
||||
storageConfigService.updateStorageConfig(Arrays.asList(accessTokenConfig, refreshTokenConfig));
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package im.zhaojun.onedrive.international.service;
|
||||
|
||||
import im.zhaojun.common.config.GlobalScheduleTask;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.onedrive.common.service.AbstractOneDriveService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OneDriveServiceImpl extends AbstractOneDriveService implements FileService {
|
||||
|
||||
@Resource
|
||||
private GlobalScheduleTask globalScheduleTask;
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Value("${zfile.onedrive.clientId}")
|
||||
protected String clientId;
|
||||
|
||||
@Value("${zfile.onedrive.redirectUri}")
|
||||
protected String redirectUri;
|
||||
|
||||
@Value("${zfile.onedrive.clientSecret}")
|
||||
protected String clientSecret;
|
||||
|
||||
@Value("${zfile.onedrive.scope}")
|
||||
protected String scope;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
String accessToken = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_TOKEN_KEY).getValue();
|
||||
String refreshToken = stringStorageConfigMap.get(StorageConfigConstant.REFRESH_TOKEN_KEY).getValue();
|
||||
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
|
||||
if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
refreshOneDriveToken();
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.ONE_DRIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGraphEndPoint() {
|
||||
return "graph.microsoft.com";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticateEndPoint() {
|
||||
return "login.microsoftonline.com";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package im.zhaojun.qiniu.service;
|
||||
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractS3FileService;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class QiniuServiceImpl extends AbstractS3FileService implements FileService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(QiniuServiceImpl.class);
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
|
||||
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
|
||||
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
|
||||
|
||||
bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
|
||||
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
|
||||
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
|
||||
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
|
||||
s3Client = AmazonS3ClientBuilder.standard()
|
||||
.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "kodo")).build();
|
||||
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.QINIU;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package im.zhaojun.s3;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractS3FileService;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class S3ServiceImpl extends AbstractS3FileService implements FileService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(S3ServiceImpl.class);
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
|
||||
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
|
||||
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
|
||||
|
||||
super.domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
|
||||
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
super.bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
|
||||
super.isPrivate = Convert.toBool(stringStorageConfigMap.get(StorageConfigConstant.IS_PRIVATE).getValue(), true);
|
||||
|
||||
String pathStyle = stringStorageConfigMap.get(StorageConfigConstant.PATH_STYLE).getValue();
|
||||
|
||||
boolean isPathStyle = "path-style".equals(pathStyle);
|
||||
|
||||
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
|
||||
s3Client = AmazonS3ClientBuilder.standard()
|
||||
.withPathStyleAccessEnabled(isPathStyle)
|
||||
.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "")).build();
|
||||
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.S3;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package im.zhaojun.tencent;
|
||||
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractS3FileService;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class TencentServiceImpl extends AbstractS3FileService implements FileService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TencentServiceImpl.class);
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
String secretId = stringStorageConfigMap.get(StorageConfigConstant.SECRET_ID_KEY).getValue();
|
||||
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
|
||||
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
|
||||
bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
|
||||
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
|
||||
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
|
||||
if (Objects.isNull(secretId) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
BasicAWSCredentials credentials = new BasicAWSCredentials(secretId, secretKey);
|
||||
s3Client = AmazonS3ClientBuilder.standard()
|
||||
.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "cos")).build();
|
||||
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.TENCENT;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package im.zhaojun.ufile.service;
|
||||
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.upyun.service.UpYunServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class UFileServiceImpl extends UpYunServiceImpl {
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.UFILE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
package im.zhaojun.upyun.service;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import com.UpYun;
|
||||
import im.zhaojun.common.exception.NotExistFileException;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class UpYunServiceImpl extends AbstractFileService implements FileService {
|
||||
|
||||
private static final String END_MARK = "g2gCZAAEbmV4dGQAA2VvZg";
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
private String domain;
|
||||
|
||||
private UpYun upYun;
|
||||
|
||||
private String basePath;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
String bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
|
||||
String username = stringStorageConfigMap.get(StorageConfigConstant.USERNAME_KEY).getValue();
|
||||
String password = stringStorageConfigMap.get(StorageConfigConstant.PASSWORD_KEY).getValue();
|
||||
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
|
||||
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
basePath = ObjectUtil.defaultIfNull(basePath, "");
|
||||
|
||||
if (Objects.isNull(bucketName) || Objects.isNull(username) || Objects.isNull(password)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
upYun = new UpYun(bucketName, username, password);
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) throws Exception {
|
||||
ArrayList<FileItemDTO> fileItemList = new ArrayList<>();
|
||||
String nextMark = null;
|
||||
|
||||
do {
|
||||
HashMap<String, String> hashMap = new HashMap<>(24);
|
||||
hashMap.put("x-list-iter", nextMark);
|
||||
hashMap.put("x-list-limit", "100");
|
||||
UpYun.FolderItemIter folderItemIter = upYun.readDirIter(URLUtil.encode(basePath + path), hashMap);
|
||||
nextMark = folderItemIter.iter;
|
||||
ArrayList<UpYun.FolderItem> folderItems = folderItemIter.files;
|
||||
if (folderItems != null) {
|
||||
for (UpYun.FolderItem folderItem : folderItems) {
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
fileItemDTO.setName(folderItem.name);
|
||||
fileItemDTO.setSize(folderItem.size);
|
||||
fileItemDTO.setTime(folderItem.date);
|
||||
fileItemDTO.setPath(path);
|
||||
|
||||
if ("folder".equals(folderItem.type)) {
|
||||
fileItemDTO.setType(FileTypeEnum.FOLDER);
|
||||
} else {
|
||||
fileItemDTO.setType(FileTypeEnum.FILE);
|
||||
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(basePath + path, fileItemDTO.getName())));
|
||||
}
|
||||
fileItemList.add(fileItemDTO);
|
||||
}
|
||||
}
|
||||
} while (!END_MARK.equals(nextMark));
|
||||
return fileItemList;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
return URLUtil.complateUrl(domain, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.UPYUN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileItemDTO getFileItem(String path) {
|
||||
List<FileItemDTO> list;
|
||||
try {
|
||||
int end = path.lastIndexOf("/");
|
||||
list = fileList(path.substring(0, end));
|
||||
} catch (Exception e) {
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
|
||||
for (FileItemDTO fileItemDTO : list) {
|
||||
String fullPath = StringUtils.concatUrl(fileItemDTO.getPath(), fileItemDTO.getName());
|
||||
if (Objects.equals(fullPath, path)) {
|
||||
return fileItemDTO;
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
}
|
||||
43
src/main/java/im/zhaojun/zfile/ZfileApplication.java
Normal file
43
src/main/java/im/zhaojun/zfile/ZfileApplication.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package im.zhaojun.zfile;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true)
|
||||
@ServletComponentScan(basePackages = "im.zhaojun.zfile.*.filter")
|
||||
@ComponentScan(basePackages = "im.zhaojun.zfile.*")
|
||||
public class ZfileApplication {
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ZfileApplication.class, args);
|
||||
}
|
||||
|
||||
@Value("${spring.datasource.driver-class-name}")
|
||||
private String datasourceDriveClassName;
|
||||
|
||||
@Value("${spring.datasource.url}")
|
||||
private String datasourceUrl;
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (StrUtil.equals(datasourceDriveClassName, "org.sqlite.JDBC")) {
|
||||
String path = datasourceUrl.replace("jdbc:sqlite:", "");
|
||||
String folderPath = FileUtil.getParent(path, 1);
|
||||
if (!FileUtil.exist(folderPath)) {
|
||||
FileUtil.mkdir(folderPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package im.zhaojun.common.annotation;
|
||||
package im.zhaojun.zfile.admin.annoation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -6,10 +6,12 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 标记注解, 用于在调用前检查是否已存储策略
|
||||
* Referer 校验注解,标注了此注解的请求,会被校验 Referer 是否符合要求.
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface CheckStorageStrategyInit {
|
||||
}
|
||||
public @interface RefererCheck {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package im.zhaojun.zfile.admin.annoation;
|
||||
|
||||
import im.zhaojun.zfile.admin.model.enums.StorageParamTypeEnum;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 标记存储类型参数名称
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface StorageParamItem {
|
||||
|
||||
/**
|
||||
* 字段显示排序值, 值越小, 越靠前. 默认为 99
|
||||
*/
|
||||
int order() default 99;
|
||||
|
||||
/**
|
||||
* 参数键, 如果为空, 则使用字段名称.
|
||||
*/
|
||||
String key() default "";
|
||||
|
||||
/**
|
||||
* 参数名称, 用于网页上显示名称.
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* 字段类型, 默认为 input, 可选值为: input, select, switch.
|
||||
*/
|
||||
StorageParamTypeEnum type() default StorageParamTypeEnum.INPUT;
|
||||
|
||||
/**
|
||||
* 当 {@link #type} 为 select 时, 选项的值.
|
||||
*/
|
||||
StorageParamSelectOption[] options() default {};
|
||||
|
||||
/**
|
||||
* 当 {@link #type} 为 select 时, 选项的值. 通过 {@link StorageParamSelect#getOptions)} 方法获取选项值.
|
||||
*/
|
||||
Class<? extends StorageParamSelect> optionsClass() default StorageParamSelect.class;
|
||||
|
||||
/**
|
||||
* 参数值是否可以为空. 如不为空,则抛出异常.
|
||||
*/
|
||||
boolean required() default true;
|
||||
|
||||
/**
|
||||
* 如果填写值为空,则给予默认值.
|
||||
* 支持 ${xxx} 变量, 会读取配置文件中的值, 如获取失败, 会默认为空.
|
||||
*/
|
||||
String defaultValue() default "";
|
||||
|
||||
/**
|
||||
* 参数描述信息, 用户在用户填写时, 进行提示.
|
||||
*/
|
||||
String description() default "";
|
||||
|
||||
/**
|
||||
* 参数下方的提示链接, 如果为空, 则不显示.
|
||||
*/
|
||||
String link() default "";
|
||||
|
||||
/**
|
||||
* 参数下方的提示链接文件信息, 如果为空, 则默认为链接地址.
|
||||
*/
|
||||
String linkName() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package im.zhaojun.zfile.admin.annoation;
|
||||
|
||||
import im.zhaojun.zfile.admin.model.param.IStorageParam;
|
||||
import im.zhaojun.zfile.admin.annoation.model.StorageSourceParamDef;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 存储源参数下拉值接口.
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
public interface StorageParamSelect {
|
||||
|
||||
/**
|
||||
* 获取存储源参数下拉选项列表.
|
||||
*
|
||||
* @param storageParamItem
|
||||
* 存储源下拉参数定义
|
||||
*
|
||||
* @param targetParam
|
||||
* 存储源参数
|
||||
*
|
||||
* @return 存储源参数下拉选项列表
|
||||
*/
|
||||
List<StorageSourceParamDef.Options> getOptions(StorageParamItem storageParamItem, IStorageParam targetParam);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package im.zhaojun.zfile.admin.annoation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 标记存储类型参数类型为 select 时, 数据的下拉值.
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface StorageParamSelectOption {
|
||||
|
||||
/**
|
||||
* 选项显示值
|
||||
*/
|
||||
String label();
|
||||
|
||||
/**
|
||||
* 选项存储值
|
||||
*/
|
||||
String value();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package im.zhaojun.zfile.admin.annoation.model;
|
||||
|
||||
import im.zhaojun.zfile.admin.annoation.StorageParamSelectOption;
|
||||
import im.zhaojun.zfile.admin.model.enums.StorageParamTypeEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 存储源参数定义, 包含参数名称、描述、必填、默认值等信息.
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class StorageSourceParamDef {
|
||||
|
||||
/**
|
||||
* 字段显示排序值, 值越小, 越靠前.
|
||||
*/
|
||||
private int order;
|
||||
|
||||
/**
|
||||
* 参数 key
|
||||
*/
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 参数名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 参数描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 是否必填
|
||||
*/
|
||||
private boolean required;
|
||||
|
||||
/**
|
||||
* 默认值
|
||||
*/
|
||||
private String defaultValue;
|
||||
|
||||
/**
|
||||
* 链接地址
|
||||
*/
|
||||
private String link;
|
||||
|
||||
/**
|
||||
* 链接名称
|
||||
*/
|
||||
private String linkName;
|
||||
|
||||
/**
|
||||
* 字段类型, 默认为 input, 可选值为: input, select, switch.
|
||||
*/
|
||||
private StorageParamTypeEnum type;
|
||||
|
||||
/**
|
||||
* 当 {@link #type} 为 select 时, 选项的值.
|
||||
*/
|
||||
private List<Options> options;
|
||||
|
||||
@Getter
|
||||
public static class Options {
|
||||
|
||||
private final String label;
|
||||
|
||||
private final String value;
|
||||
|
||||
public Options(String value) {
|
||||
this.label = value;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Options(String label, String value) {
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
}
|
||||
public Options(StorageParamSelectOption storageParamSelectOption) {
|
||||
this.label = storageParamSelectOption.label();
|
||||
this.value = storageParamSelectOption.value();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package im.zhaojun.zfile.admin.annoation.select.impl;
|
||||
|
||||
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
|
||||
import im.zhaojun.zfile.admin.annoation.StorageParamSelect;
|
||||
import im.zhaojun.zfile.admin.model.param.IStorageParam;
|
||||
import im.zhaojun.zfile.admin.annoation.model.StorageSourceParamDef;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 编码格式动态参数.
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class EncodingStorageParamSelect implements StorageParamSelect {
|
||||
|
||||
@Override
|
||||
public List<StorageSourceParamDef.Options> getOptions(StorageParamItem storageParamItem, IStorageParam targetParam) {
|
||||
List<StorageSourceParamDef.Options> options = new ArrayList<>();
|
||||
|
||||
for (String name : Charset.availableCharsets().keySet()) {
|
||||
StorageSourceParamDef.Options option = new StorageSourceParamDef.Options(name);
|
||||
options.add(option);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package im.zhaojun.zfile.admin.constant;
|
||||
|
||||
/**
|
||||
* 存储源设置字段常量.
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class StorageConfigConstant {
|
||||
|
||||
public static final String ACCESS_TOKEN_KEY = "accessToken";
|
||||
|
||||
public static final String REFRESH_TOKEN_KEY = "refreshToken";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package im.zhaojun.zfile.admin.constant;
|
||||
|
||||
/**
|
||||
* 系统设置字段常量.
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class SystemConfigConstant {
|
||||
|
||||
public static final String USERNAME = "username";
|
||||
|
||||
public static final String PASSWORD = "password";
|
||||
|
||||
public static final String LOGIN_VERIFY_MODE = "loginVerifyMode";
|
||||
|
||||
public static final String RSA_HEX_KEY = "rsaHexKey";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package im.zhaojun.zfile.admin.controller;
|
||||
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiSort;
|
||||
import im.zhaojun.zfile.home.model.dto.CacheInfoDTO;
|
||||
import im.zhaojun.zfile.admin.service.StorageSourceService;
|
||||
import im.zhaojun.zfile.common.util.AjaxJson;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 存储源缓存维护接口
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@RestController
|
||||
@Api(tags = "存储源模块-缓存")
|
||||
@ApiSort(5)
|
||||
@RequestMapping("/admin/cache")
|
||||
public class CacheController {
|
||||
|
||||
@Resource
|
||||
private StorageSourceService storageSourceService;
|
||||
|
||||
@ApiOperationSupport(order = 1)
|
||||
@ApiOperation(value = "启用存储源缓存", notes = "开启缓存后,N 秒内重复请求相同文件夹,不会重复调用 API。" +
|
||||
"参数 N 在配置文件中设置 {zfile.cache.timeout},默认为 1800 秒。")
|
||||
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
|
||||
@PostMapping("/{storageId}/enable")
|
||||
public AjaxJson<Void> enableCache(@PathVariable("storageId") Integer storageId) {
|
||||
storageSourceService.updateCacheStatus(storageId, true);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
@ApiOperationSupport(order = 2)
|
||||
@ApiOperation(value = "禁用存储源缓存")
|
||||
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
|
||||
@PostMapping("/{storageId}/disable")
|
||||
public AjaxJson<Void> disableCache(@PathVariable("storageId") Integer storageId) {
|
||||
storageSourceService.updateCacheStatus(storageId, false);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
@ApiOperationSupport(order = 3)
|
||||
@ApiOperation(value = "查看存储源缓存", notes = "可查看存储源缓存的所有目录路径")
|
||||
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
|
||||
@GetMapping("/{storageId}/info")
|
||||
public AjaxJson<CacheInfoDTO> cacheInfo(@PathVariable("storageId") Integer storageId) {
|
||||
CacheInfoDTO cacheInfo = storageSourceService.findCacheInfo(storageId);
|
||||
return AjaxJson.getSuccessData(cacheInfo);
|
||||
}
|
||||
|
||||
|
||||
@ApiOperationSupport(order = 4)
|
||||
@ApiOperation(value = "刷新存储源缓存", notes = "刷新存储源缓存路径,系统会重新预热此路径的内容")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true),
|
||||
@ApiImplicitParam(paramType = "body", name = "key", value = "缓存 key", required = true)
|
||||
})
|
||||
@PostMapping("/{storageId}/refresh")
|
||||
public AjaxJson<Void> refreshCache(@PathVariable("storageId") Integer storageId, String key) throws Exception {
|
||||
storageSourceService.refreshCache(storageId, key);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
@ApiOperationSupport(order = 5)
|
||||
@ApiOperation(value = "开启缓存自动刷新", notes = "开启后每隔 N 秒检测到期的缓存, 对于过期缓存尝试调用 API, 重新写入缓存." +
|
||||
"参数 N 在配置文件中设置 {zfile.cache.auto-refresh-interval},默认为 5 秒。")
|
||||
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
|
||||
@PostMapping("/{storageId}/auto-refresh/start")
|
||||
public AjaxJson<Void> enableAutoRefresh(@PathVariable("storageId") Integer storageId) {
|
||||
storageSourceService.startAutoCacheRefresh(storageId);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
@ApiOperationSupport(order = 5)
|
||||
@ApiOperation(value = "关闭缓存自动刷新")
|
||||
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
|
||||
@PostMapping("/{storageId}/auto-refresh/stop")
|
||||
public AjaxJson<Void> disableAutoRefresh(@PathVariable("storageId") Integer storageId) {
|
||||
storageSourceService.stopAutoCacheRefresh(storageId);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
@ApiOperationSupport(order = 6)
|
||||
@ApiOperation(value = "清空缓存")
|
||||
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
|
||||
@PostMapping("/{storageId}/clear")
|
||||
public AjaxJson<Void> clearCache(@PathVariable("storageId") Integer storageId) {
|
||||
storageSourceService.clearCache(storageId);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package im.zhaojun.zfile.admin.controller;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.ZipUtil;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiSort;
|
||||
import im.zhaojun.zfile.common.util.FileResponseUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 获取系统日志接口
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Api(tags = "日志")
|
||||
@ApiSort(8)
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/admin")
|
||||
public class LogController {
|
||||
|
||||
@Value("${zfile.log.path}")
|
||||
private String zfileLogPath;
|
||||
|
||||
@GetMapping("/log/download")
|
||||
@ApiOperation(value = "下载系统日志")
|
||||
public ResponseEntity<Resource> downloadLog() {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("下载诊断日志");
|
||||
}
|
||||
|
||||
File fileZip = ZipUtil.zip(zfileLogPath);
|
||||
String currentDate = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
|
||||
return FileResponseUtil.exportSingleThread(fileZip, "ZFile 诊断日志 - " + currentDate + ".zip");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package im.zhaojun.zfile.admin.controller.link;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiSort;
|
||||
import im.zhaojun.zfile.admin.convert.DownloadLogConvert;
|
||||
import im.zhaojun.zfile.admin.model.entity.DownloadLog;
|
||||
import im.zhaojun.zfile.admin.model.entity.StorageSource;
|
||||
import im.zhaojun.zfile.admin.model.request.link.QueryDownloadLogRequest;
|
||||
import im.zhaojun.zfile.admin.model.result.link.DownloadLogResult;
|
||||
import im.zhaojun.zfile.admin.service.DownloadLogService;
|
||||
import im.zhaojun.zfile.admin.service.StorageSourceService;
|
||||
import im.zhaojun.zfile.common.util.AjaxJson;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 直链下载日志接口
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Api(tags = "直链日志管理")
|
||||
@ApiSort(7)
|
||||
@Controller
|
||||
@RequestMapping("/admin/download/log")
|
||||
public class DownloadLogManagerController {
|
||||
|
||||
@Resource
|
||||
private StorageSourceService storageSourceService;
|
||||
|
||||
@Resource
|
||||
private DownloadLogConvert downloadLogConvert;
|
||||
|
||||
@Resource
|
||||
private DownloadLogService downloadLogService;
|
||||
|
||||
@ApiOperationSupport(order = 1)
|
||||
@GetMapping("/list")
|
||||
@ApiOperation(value = "直链下载日志")
|
||||
@ResponseBody
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(paramType = "query", name = "key", value = "直链 key"),
|
||||
@ApiImplicitParam(paramType = "query", name = "page", value = "分页页数"),
|
||||
@ApiImplicitParam(paramType = "query", name = "limit", value = "每页条数"),
|
||||
@ApiImplicitParam(paramType = "query", name = "orderBy", defaultValue = "createDate", value = "排序字段"),
|
||||
@ApiImplicitParam(paramType = "query", name = "orderDirection", defaultValue = "desc", value = "排序顺序")
|
||||
})
|
||||
public AjaxJson<?> list(QueryDownloadLogRequest queryDownloadLogRequest,
|
||||
@RequestParam(required = false, defaultValue = "create_time") String orderBy,
|
||||
@RequestParam(required = false, defaultValue = "desc") String orderDirection) {
|
||||
Page<DownloadLog> pages = Page.of(queryDownloadLogRequest.getPage(), queryDownloadLogRequest.getLimit());
|
||||
boolean asc = Objects.equals(orderDirection, "asc");
|
||||
pages.addOrder(new OrderItem(orderBy, asc));
|
||||
|
||||
DownloadLog downloadLog = new DownloadLog();
|
||||
QueryWrapper<DownloadLog> queryWrapper = new QueryWrapper<>(downloadLog);
|
||||
|
||||
if (StrUtil.isNotEmpty(queryDownloadLogRequest.getStorageKey())) {
|
||||
queryWrapper.eq("storage_key", queryDownloadLogRequest.getStorageKey());
|
||||
}
|
||||
if (StrUtil.isNotEmpty(queryDownloadLogRequest.getPath())) {
|
||||
queryWrapper.like("path", queryDownloadLogRequest.getPath());
|
||||
}
|
||||
if (StrUtil.isNotEmpty(queryDownloadLogRequest.getShortKey())) {
|
||||
queryWrapper.like("short_key", queryDownloadLogRequest.getShortKey());
|
||||
}
|
||||
if (StrUtil.isNotEmpty(queryDownloadLogRequest.getIp())) {
|
||||
queryWrapper.like("ip", queryDownloadLogRequest.getIp());
|
||||
}
|
||||
if (StrUtil.isNotEmpty(queryDownloadLogRequest.getReferer())) {
|
||||
queryWrapper.like("referer", queryDownloadLogRequest.getReferer());
|
||||
}
|
||||
if (StrUtil.isNotEmpty(queryDownloadLogRequest.getUserAgent())) {
|
||||
queryWrapper.like("user_agent", queryDownloadLogRequest.getUserAgent());
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(queryDownloadLogRequest.getDateFrom())) {
|
||||
queryWrapper.ge("create_time", queryDownloadLogRequest.getDateFrom());
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(queryDownloadLogRequest.getDateTo())) {
|
||||
queryWrapper.le("create_time", queryDownloadLogRequest.getDateFrom());
|
||||
}
|
||||
Page<DownloadLog> selectResult = downloadLogService.page(pages, queryWrapper);
|
||||
|
||||
long total = selectResult.getTotal();
|
||||
List<DownloadLog> records = selectResult.getRecords();
|
||||
|
||||
Map<String, StorageSource> cache = new HashMap<>();
|
||||
|
||||
Stream<DownloadLogResult> shortLinkResultList = records.stream().map(model -> {
|
||||
String storageKey = model.getStorageKey();
|
||||
StorageSource storageSource;
|
||||
if (cache.containsKey(storageKey)) {
|
||||
storageSource = cache.get(storageKey);
|
||||
} else {
|
||||
storageSource = storageSourceService.findByStorageKey(storageKey);
|
||||
cache.put(storageKey, storageSource);
|
||||
}
|
||||
return downloadLogConvert.entityToResultList(model, storageSource);
|
||||
});
|
||||
return AjaxJson.getPageData(total, shortLinkResultList);
|
||||
}
|
||||
|
||||
|
||||
@ApiOperationSupport(order = 2)
|
||||
@DeleteMapping("/delete/{id}")
|
||||
@ApiOperation(value = "删除直链")
|
||||
@ApiImplicitParam(paramType = "path", name = "id", value = "直链 id", required = true)
|
||||
@ResponseBody
|
||||
public AjaxJson<Void> deleteById(@PathVariable Integer id) {
|
||||
downloadLogService.removeById(id);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
@ApiOperationSupport(order = 3)
|
||||
@DeleteMapping("/delete/batch")
|
||||
@ResponseBody
|
||||
@ApiImplicitParam(paramType = "query", name = "ids", value = "直链 id", required = true)
|
||||
@ApiOperation(value = "批量删除直链")
|
||||
public AjaxJson<Void> batchDelete(@RequestParam("id[]") Integer[] ids) {
|
||||
downloadLogService.removeBatchByIds(Arrays.asList(ids));
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package im.zhaojun.zfile.admin.controller.link;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiSort;
|
||||
import im.zhaojun.zfile.admin.convert.ShortLinkConvert;
|
||||
import im.zhaojun.zfile.admin.model.entity.ShortLink;
|
||||
import im.zhaojun.zfile.admin.model.entity.StorageSource;
|
||||
import im.zhaojun.zfile.admin.model.result.link.ShortLinkResult;
|
||||
import im.zhaojun.zfile.admin.service.ShortLinkService;
|
||||
import im.zhaojun.zfile.admin.service.StorageSourceService;
|
||||
import im.zhaojun.zfile.common.util.AjaxJson;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 直链管理接口
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Api(tags = "直链管理")
|
||||
@ApiSort(7)
|
||||
@Controller
|
||||
@RequestMapping("/admin")
|
||||
public class ShortLinkManagerController {
|
||||
|
||||
@Resource
|
||||
private ShortLinkService shortLinkService;
|
||||
|
||||
@Resource
|
||||
private StorageSourceService storageSourceService;
|
||||
|
||||
@Resource
|
||||
private ShortLinkConvert shortLinkConvert;
|
||||
|
||||
@ApiOperationSupport(order = 1)
|
||||
@GetMapping("/link/list")
|
||||
@ApiOperation(value = "搜索短链")
|
||||
@ResponseBody
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(paramType = "query", name = "key", value = "短链 key"),
|
||||
@ApiImplicitParam(paramType = "query", name = "storageId", value = "存储源 ID"),
|
||||
@ApiImplicitParam(paramType = "query", name = "url", value = "短链 url"),
|
||||
@ApiImplicitParam(paramType = "query", name = "dateFrom", value = "短链创建时间从"),
|
||||
@ApiImplicitParam(paramType = "query", name = "dateTo", value = "短链创建时间至"),
|
||||
@ApiImplicitParam(paramType = "query", name = "page", value = "分页页数"),
|
||||
@ApiImplicitParam(paramType = "query", name = "limit", value = "每页条数"),
|
||||
@ApiImplicitParam(paramType = "query", name = "orderBy", defaultValue = "createDate", value = "排序字段"),
|
||||
@ApiImplicitParam(paramType = "query", name = "orderDirection", defaultValue = "desc", value = "排序顺序")
|
||||
})
|
||||
public AjaxJson<?> list(String key, String storageId,
|
||||
String url,
|
||||
String dateFrom,
|
||||
String dateTo,
|
||||
Integer page,
|
||||
Integer limit,
|
||||
@RequestParam(required = false, defaultValue = "create_date") String orderBy,
|
||||
@RequestParam(required = false, defaultValue = "desc") String orderDirection) {
|
||||
Page<ShortLink> pages = Page.of(page, limit);
|
||||
boolean asc = Objects.equals(orderDirection, "asc");
|
||||
pages.addOrder(new OrderItem(orderBy, asc));
|
||||
QueryWrapper<ShortLink> queryWrapper = new QueryWrapper<>(new ShortLink());
|
||||
if (StrUtil.isNotEmpty(storageId)) {
|
||||
queryWrapper.eq("storage_id", storageId);
|
||||
}
|
||||
if (StrUtil.isNotEmpty(key)) {
|
||||
queryWrapper.like("short_key", key);
|
||||
}
|
||||
if (StrUtil.isNotEmpty(url)) {
|
||||
queryWrapper.like("url", url);
|
||||
}
|
||||
if (StrUtil.isNotEmpty(dateFrom)) {
|
||||
queryWrapper.ge("create_date", dateFrom);
|
||||
}
|
||||
if (StrUtil.isNotEmpty(dateTo)) {
|
||||
queryWrapper.le("create_date", dateTo);
|
||||
}
|
||||
Page<ShortLink> selectResult = shortLinkService.page(pages, queryWrapper);
|
||||
|
||||
long total = selectResult.getTotal();
|
||||
List<ShortLink> records = selectResult.getRecords();
|
||||
|
||||
Map<Integer, StorageSource> cache = new HashMap<>();
|
||||
|
||||
Stream<ShortLinkResult> shortLinkResultList = records.stream().map(shortLink -> {
|
||||
Integer shortLinkStorageId = shortLink.getStorageId();
|
||||
StorageSource storageSource;
|
||||
if (cache.containsKey(shortLinkStorageId)) {
|
||||
storageSource = cache.get(shortLinkStorageId);
|
||||
} else {
|
||||
storageSource = storageSourceService.findById(shortLinkStorageId);
|
||||
cache.put(shortLinkStorageId, storageSource);
|
||||
}
|
||||
return shortLinkConvert.entityToResultList(shortLink, storageSource);
|
||||
});
|
||||
return AjaxJson.getPageData(total, shortLinkResultList);
|
||||
}
|
||||
|
||||
|
||||
@ApiOperationSupport(order = 2)
|
||||
@DeleteMapping("/link/delete/{id}")
|
||||
@ApiOperation(value = "删除短链")
|
||||
@ApiImplicitParam(paramType = "path", name = "id", value = "短链 id", required = true)
|
||||
@ResponseBody
|
||||
public AjaxJson<Void> deleteById(@PathVariable Integer id) {
|
||||
shortLinkService.removeById(id);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
@ApiOperationSupport(order = 3)
|
||||
@DeleteMapping("/link/delete/batch")
|
||||
@ResponseBody
|
||||
@ApiImplicitParam(paramType = "query", name = "ids", value = "短链 id", required = true)
|
||||
@ApiOperation(value = "批量删除短链")
|
||||
public AjaxJson<Void> batchDelete(@RequestParam("id[]") Integer[] ids) {
|
||||
shortLinkService.removeBatchByIds(Arrays.asList(ids));
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user