Compare commits

...

140 Commits
0.2 ... 0.9

Author SHA1 Message Date
zhaojun1998
4c914793b0 📝 更新文档 2020-01-29 13:43:19 +08:00
zhaojun1998
5698cfb2d3 🔖 发布 0.9 版 2020-01-29 13:41:42 +08:00
zhaojun1998
3cd5f8f9a7 调整显示顺序 2020-01-29 12:58:03 +08:00
zhaojun1998
76747771de OneDrive 世纪互联支持 2020-01-29 12:53:38 +08:00
zhaojun1998
cfacd39210 优化代码, 增强健壮性 2020-01-29 12:52:07 +08:00
zhaojun1998
90cd13f2c3 🔖 发布 0.8 版 2020-01-28 15:19:04 +08:00
zhaojun1998
018a68246e 💄 更新页面 2020-01-28 15:16:27 +08:00
zhaojun1998
b6a2e3ccb8 添加获取指定路径文件信息的 API 2020-01-28 14:55:06 +08:00
zhaojun1998
38b811f8e6 🐛 新增 '搜索包含加密文件' 支持. 2020-01-28 13:25:28 +08:00
zhaojun1998
6922fa2195 添加自定义 JS, CSS 支持 2020-01-28 13:14:23 +08:00
zhaojun1998
4bca6cf7a5 移除尾部说明文件支持 2020-01-28 13:13:52 +08:00
zhaojun1998
c3484426ab 🐛 修复 OneDrive 教育版和部分国际版无法正常获取文件的 BUG 2020-01-28 10:18:36 +08:00
zhaojun1998
0455bd366c 🔖 发布 0.7.1 版 2020-01-26 13:42:19 +08:00
zhaojun1998
bbe3c053f8 OneDrive 回调页面友好提示 2020-01-26 13:41:15 +08:00
zhaojun1998
f47708f45d Merge remote-tracking branch 'origin/master' 2020-01-26 13:38:24 +08:00
zhaojun1998
2e7a7b8cec 🐛 修复文件夹包含特殊字符编码 BUG 2020-01-26 13:35:50 +08:00
zhaojun1998
9e067dbce9 🐛 修复国际版 OneDrive, 无法获取子目录的 BUG 2020-01-26 13:35:27 +08:00
zhaojun1998
a4a236e488 🐛 修复 OneDrive 列目录, 文件数超出 200 个无法显示的 BUG. 2020-01-26 13:21:49 +08:00
赵俊
7d5b0431f5 Update README.md 2020-01-25 18:10:22 +08:00
zhaojun1998
a758c8cc6d 📝 更新文档 2020-01-25 18:06:13 +08:00
zhaojun1998
21a64ec0f3 🐛 修复循环依赖 BUG 2020-01-25 18:00:38 +08:00
zhaojun1998
3f241d129a 📝 更新文档 2020-01-25 18:00:14 +08:00
zhaojun1998
fa5f16c61f 🔖 发布 0.7 版 2020-01-24 19:00:56 +08:00
zhaojun1998
492b22506d 🐛 修复切换存储策略时没有重新缓存的 BUG 2020-01-24 18:51:20 +08:00
zhaojun1998
a12f685340 🐛 修复 FTP 初始化显示错误 2020-01-24 18:44:59 +08:00
zhaojun1998
2ee3f3dd66 💄 更新页面 2020-01-24 18:39:20 +08:00
zhaojun1998
245937e773 搜索忽略大小写支持 2020-01-24 18:37:58 +08:00
zhaojun1998
aef34facbd 🐛 修复更改系统设置时, 误清理缓存的 BUG 2020-01-24 18:37:32 +08:00
zhaojun1998
14bb5e15e3 替换 onedrive 授权重定向地址, 配置文件添加元数据描述 2020-01-24 17:40:01 +08:00
zhaojun1998
12371f06dd 🐛 修复 S3 协议中, 某些情况下出现了空文件名的 BUG 2020-01-24 17:28:48 +08:00
zhaojun1998
28e43e968f 添加分页, 修复本地存储下载错误 2020-01-24 17:27:12 +08:00
zhaojun1998
669b413ff0 优化存储策略功能, 添加是否初始化成功标识 2020-01-24 17:13:49 +08:00
zhaojun1998
f32e5e8f9e 🐛 修复切换存储引擎时, 没有清空原引擎缓存的 BUG. 2020-01-24 10:46:00 +08:00
zhaojun1998
3719378614 ✏️ 修复拼写错误 2020-01-24 10:28:21 +08:00
zhaojun1998
40c759078e 🐛 MINIO 修改 URL 路径风格指定为 path-style, 防止配置域名情况下, 找不到域名的 BUG. 2020-01-24 10:27:53 +08:00
zhaojun1998
e37e778e1a 优化日志输出 2020-01-23 10:53:16 +08:00
zhaojun1998
031607402a 本地存储, 文件不存在时, 给与友好提示 2020-01-20 22:48:04 +08:00
zhaojun1998
6c9150466c 优化日志配置 2020-01-20 21:36:58 +08:00
zhaojun1998
be633ebe1a OneDrive 支持 2020-01-20 21:36:13 +08:00
zhaojun1998
9715cf922a 优化日志输出 2020-01-20 21:35:41 +08:00
zhaojun1998
f6163c7e19 优化代码结构 2020-01-20 21:35:20 +08:00
zhaojun1998
dcc4cb19ad OneDrive 支持 2020-01-19 21:58:02 +08:00
zhaojun1998
ad0ad12c08 搜索功能支持分页 2020-01-18 22:57:29 +08:00
zhaojun1998
74c935cdf0 抽取通用代码 2020-01-17 23:28:23 +08:00
zhaojun1998
1876e692f2 💄 更改默认排序器 2020-01-16 22:24:46 +08:00
zhaojun1998
f198b34324 🔖 升级版本为 0.6 2020-01-12 11:18:50 +08:00
zhaojun1998
3095e0c8d9 💄 更新页面, 优化缓存页面展示 2020-01-12 11:15:01 +08:00
zhaojun1998
594246127d 🐛 修复更改策略时, 未正确启用和关闭缓存的 BUG 2020-01-12 11:14:20 +08:00
zhaojun1998
f6c5f7a91b 🔧 修改配置配置文件, 将日志改为单独配置 2020-01-12 11:13:02 +08:00
zhaojun1998
2a765fff7e 🐛 获取缓存是否开启时的 NPE 问题 2020-01-11 23:20:12 +08:00
zhaojun1998
28f958878b 🔊 更新日志级别 2020-01-11 23:02:19 +08:00
zhaojun1998
368f3a90eb 移除无用依赖 2020-01-11 22:53:19 +08:00
zhaojun1998
98b14abbfc 🔧 修改配置文件, 去除默认缓存时间. 2020-01-11 22:49:55 +08:00
zhaojun1998
7c04c3d6b8 优化代码结构 2020-01-11 22:48:28 +08:00
zhaojun1998
921cb1a115 通过反射优化代码 2020-01-08 22:34:15 +08:00
zhaojun1998
9371968c3b 增强根据 value 获取枚举的功能 2020-01-08 22:33:30 +08:00
zhaojun1998
47e88849ac 🐛 修复当前存储引擎为空时, 与新设置的存储引擎比较出现的 NPE 2020-01-08 22:32:47 +08:00
zhaojun1998
2f0f41f413 去除无效输出 2020-01-08 21:31:24 +08:00
zhaojun1998
7667765abc 🐛 修复本地存储的文件名, 包含 &?=[] 等特殊字符时出现的问题 2020-01-08 21:28:05 +08:00
zhaojun1998
b2a2e69af5 🔒 关闭 URL 部分校验, 允许中文文件名 2020-01-08 21:22:03 +08:00
zhaojun1998
7c729a72e2 🐛 本地存储, 获取文件不存在返回状态码 404 2020-01-07 22:57:59 +08:00
zhaojun1998
5495abc881 更新未知歌曲默认封面 2020-01-06 23:00:07 +08:00
zhaojun1998
797cd4fc06 💄 更新页面 2020-01-05 16:11:02 +08:00
zhaojun1998
8148d182cf 更新缓存策略刷新时间 2020-01-05 16:01:50 +08:00
zhaojun1998
7e8cab90d0 🔖 升级版本为 0.5 2020-01-05 15:58:46 +08:00
zhaojun1998
4d5743dc0b 💄 更新页面 2020-01-05 15:56:51 +08:00
zhaojun1998
1a326cc17d 💄 更新页面 2020-01-05 15:56:38 +08:00
zhaojun1998
cc993d8e65 优化缓存策略, 开启/关闭缓存后同步控制自动刷新策略 2020-01-05 15:56:17 +08:00
zhaojun1998
f128882034 添加文件获取和判断是否存在接口 2020-01-05 15:54:59 +08:00
zhaojun1998
31dbb902c3 添加文件获取和判断是否存在接口 2020-01-05 15:54:42 +08:00
zhaojun1998
c849057aaa 💄 更新页面 2020-01-03 17:00:15 +08:00
zhaojun1998
7b288b795c 🐛 修复切换缓存时, 出现的异常 BUG 2020-01-03 16:24:53 +08:00
zhaojun1998
316566d479 📝 更新文档, 修复拼写错误 2020-01-03 16:16:05 +08:00
zhaojun1998
e01ce28eb8 💄 更新页面 2020-01-03 15:59:41 +08:00
zhaojun1998
9b7528b61c 💄 更新页面 2020-01-03 15:49:22 +08:00
zhaojun1998
bd22cfd34c 添加检测缓存管理功能 2020-01-03 15:48:42 +08:00
zhaojun1998
4aa9839c6b 🔖 升级版本为 0.4 2020-01-03 15:28:58 +08:00
zhaojun1998
5eeea23703 缓存功能优化, 更高效的管理缓存. 2020-01-03 15:27:45 +08:00
zhaojun1998
6997b15dd0 修复系统设置缓存读取到远程的 BUG 2020-01-02 18:45:19 +08:00
zhaojun1998
326c954c36 💄 更新页面 2020-01-02 18:44:36 +08:00
zhaojun1998
ba5801bea2 🔖 升级版本为 0.3.1 2020-01-02 18:27:30 +08:00
zhaojun1998
e2b0c29e2d 更改日志级别 2020-01-02 18:23:24 +08:00
zhaojun1998
845a380a7e 未开启缓存, 不允许搜索. 2020-01-02 18:22:54 +08:00
zhaojun1998
87229f225e 系统配置添加缓存, 优化性能 2020-01-02 18:20:06 +08:00
zhaojun1998
de947e510c 缓存功能优化, 修改设置后自动修改缓存, 且缓存未完成, 搜索功能暂时禁用. 2020-01-02 18:17:11 +08:00
zhaojun1998
2a367afc37 优化代码, 参数不完整时, 不进行初始化 2020-01-02 18:15:05 +08:00
zhaojun1998
5fb945cebc 🔖 升级版本为 0.3 2020-01-02 14:51:22 +08:00
zhaojun1998
7acedfef38 🎨 修改名称 2020-01-02 14:48:56 +08:00
zhaojun1998
5e198b7ce7 开启 gzip 压缩 2020-01-02 14:48:05 +08:00
zhaojun1998
c3e7e622e2 🎨 缓存修改使用本地 caffeine 2020-01-02 14:46:55 +08:00
zhaojun1998
aec73f5cc7 💄 更新页面 2020-01-02 14:45:21 +08:00
zhaojun1998
9236f11044 优化生产环境配置 2020-01-02 14:40:36 +08:00
zhaojun1998
02973a3c21 更新页面, 支持全局搜索功能 2020-01-01 23:20:12 +08:00
zhaojun1998
e60c64c8fc 📝 更新文档 2019-12-30 22:23:52 +08:00
zhaojun1998
52db0c1515 💄 更新页面 2019-12-29 19:59:48 +08:00
zhaojun1998
0ab2f3b015 移除依赖 2019-12-29 19:58:56 +08:00
zhaojun1998
dd588fe218 🐛 修复某些 S3 协议的存储策略, 生成对象下载地址时, 因重复 encode 造成的 BUG 2019-12-29 19:51:04 +08:00
zhaojun1998
dce1b42e8e 💄 更新页面 2019-12-29 19:14:35 +08:00
zhaojun1998
0e9dd7d5a8 💄 更新页面 2019-12-29 19:14:08 +08:00
zhaojun1998
f5bebd2500 所有存储引擎都支持基路径 2019-12-29 19:10:37 +08:00
zhaojun1998
6a54150868 🏗️ 将缓存框架由 Spring Boot Cache 改为 Alibaba JetCache, 支持缓存自动刷新 2019-12-29 19:09:50 +08:00
zhaojun1998
099c09b625 🎨 抽象基于 S3 API 的存储 2019-12-28 23:49:16 +08:00
zhaojun1998
45e117a05a 腾讯云和七牛云支持基路径 2019-12-28 23:20:56 +08:00
zhaojun1998
14ba1027ae 🎨 修改获取当前存储策略为获取抽象类, 而不是接口 2019-12-28 23:20:26 +08:00
zhaojun1998
04a2ff9542 🐛 修复获取文本内容时的 BUG 2019-12-28 23:20:15 +08:00
zhaojun1998
040e92a433 🎨 抽取 S3 通用 API 2019-12-28 23:19:53 +08:00
zhaojun1998
c739878890 🎨 修改获取当前存储策略为获取抽象类, 而不是接口 2019-12-28 23:19:31 +08:00
zhaojun1998
65616e045b 🎨 优化代码, 将通用方法抽取为抽象类 2019-12-28 23:17:56 +08:00
zhaojun1998
9aaf1494b1 🎨 抽取通用变量 2019-12-28 23:15:34 +08:00
zhaojun1998
74eaaad72d 🐛 修复音乐文件获取封面时包含特殊字符异常的 BUG 2019-12-26 20:19:39 +08:00
zhaojun1998
b65ccc95e2 初始化存储引擎后, 增加测试连接机制 2019-12-25 21:57:22 +08:00
zhaojun1998
e334acd508 🎨 增加启动时显示项目访问地址的功能 2019-12-24 22:11:45 +08:00
zhaojun1998
844cbcc03a 📝 更新文档 2019-12-23 22:11:23 +08:00
zhaojun1998
debaa72938 📝 更新文档 2019-12-21 13:44:05 +08:00
zhaojun1998
d293105521 💩 改善代码, 通过 codacy 校验 2019-12-21 12:31:53 +08:00
zhaojun1998
8e7d4432a3 💩 改善代码, 通过 P3C 校验 2019-12-21 12:08:24 +08:00
zhaojun1998
0e6bcbfa11 接口缓存降级为实现类缓存 2019-12-21 11:06:29 +08:00
zhaojun1998
e8122b4ed0 🗃️ 更新数据库脚本 2019-12-21 11:03:05 +08:00
zhaojun1998
b3a0f4585b 代码优化 2019-12-21 11:01:49 +08:00
zhaojun1998
12bae1ef53 MINIO 添加基路径支持 2019-12-21 11:00:47 +08:00
zhaojun1998
4d9357b11f 🎨 修改文件头, 文件尾, 密码文件的名称为可配置 2019-12-14 22:21:53 +08:00
zhaojun1998
2aa9ccc389 🎨 修改文件头, 文件尾, 密码文件的名称为可配置 2019-12-14 22:01:04 +08:00
zhaojun1998
d81d795095 Merge remote-tracking branch 'origin/master' 2019-12-11 22:40:09 +08:00
zhaojun1998
9af89ecd8c 📝 更新文档 2019-12-11 22:39:30 +08:00
赵俊
bb381a98b9 Create LICENSE 2019-12-10 21:22:32 +08:00
zhaojun1998
14828e7f34 🗃️ 更新数据库文档 2019-12-10 21:11:53 +08:00
zhaojun1998
13c091bcf4 🔖 发布 0.2.1 版 2019-12-09 22:51:12 +08:00
zhaojun1998
6842a31402 💄 更新页面 2019-12-09 22:49:57 +08:00
zhaojun1998
eb99a3e340 UpYun 添加基路径支持 2019-12-09 22:47:43 +08:00
zhaojun1998
403dd4f2e1 优化本地存储, 不适用缓存 2019-12-09 22:46:59 +08:00
zhaojun1998
ff7feedb2f 👽 修改分页条数为每页 30 条 2019-12-09 22:45:19 +08:00
zhaojun1998
2861faeacd 🗃️ 优化数据库脚本 2019-12-09 22:44:41 +08:00
zhaojun1998
8532e91386 🎨 去除无用依赖 2019-12-08 20:36:03 +08:00
zhaojun1998
83cb080f35 🐛 优化文本读取方式, 避免本地存储引发的 BUG 2019-12-08 20:35:23 +08:00
zhaojun1998
682da819a8 🐛 修复本地存储 https 链接拼接错误 BUG 2019-12-08 19:01:50 +08:00
zhaojun1998
0c58869158 💄 更新页面 2019-12-08 19:01:26 +08:00
zhaojun1998
92396c3631 🐛 修复音乐包含特殊字符时,无法获取封面和歌手的 BUG 2019-12-08 18:10:51 +08:00
zhaojun1998
6285633ad4 📝 更新文档 2019-12-08 14:02:19 +08:00
zhaojun1998
7dadb24727 📝 更新文档 2019-12-08 14:00:26 +08:00
zhaojun1998
3c278cf176 🔖 发布 0.2 版本 2019-12-08 13:38:05 +08:00
107 changed files with 3079 additions and 1587 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 ZhaoJun
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

105
README.md
View File

@@ -1,50 +1,89 @@
# Z-File
![https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square](https://img.shields.io/badge/license-MIT-blue.svg?longCache=true&style=flat-square)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/70b793267f7941d58cbd93f50c9a8e0a)](https://www.codacy.com/manual/zhaojun1998/zfile?utm_source=github.com&utm_medium=referral&utm_content=zhaojun1998/zfile&utm_campaign=Badge_Grade)
![https://img.shields.io/badge/springboot-2.0.6-orange.svg?style=flat-square](https://img.shields.io/badge/springboot-2.0.6-yellow.svg?longCache=true&style=popout-square)
![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/zhaojun1998/zfile.svg?style=flat-square)
此项目是一个在线文件目录的程序, 支持各种对象存储和本地存储, 使用定位是个人放常用工具下载, 或做公共的文件库. 不会向多账户方向开发.
前端基于 [h5ai](https://larsjung.de/h5ai/) 的原有功能使用 Vue 重新开发了一遍. 后端采用 SpringBoot, 数据库采用内嵌数据库.
预览地址: [http://zfile.jun6.net](http://zfile.jun6.net)
预览地址: [https://zfile.jun6.net](https://zfile.jun6.net)
## 系统特色
* 内存缓存 (免安装)
* 内存数据库 (免安装)
* 个性化配置
* 自定义目录的 header 和 footer 说明文件
* 自定义目录的 header 说明文件
* 自定义 JS, CSS
* 文件夹密码
* 支持在线浏览文本文件, 视频, 图片, 音乐.
* 支持在线浏览文本文件, 视频, 图片, 音乐. (支持 FLV 和 HLS)
* 文件/目录二维码
* 缓存动态开启, 缓存自动刷新
* 全局搜索
* 支持 阿里云 OSS, FTP, 华为云 OBS, 本地存储, MINIO, OneDrive 国际/家庭/个人版, OneDrive 世纪互联版, 七牛云 KODO, 腾讯云 COS, 又拍云 USS.
## 快速开始
安装 JDK 1.8 :
安装依赖环境:
```bash
yum instal -y java # 适用于 Centos 7.x
```
# CentOS系统
yum install -y java-1.8.0-openjdk unzip
> 其他系统的 JDK 安装教程, 后续我也都会补上. 大家也可执行搜索安装方式, 应该不是很难.
# Debian/Ubuntu系统
apt update
apt install -y default-jdk unzip
```
下载项目:
```bash
wget https://github.com/zhaojun1998/zfile/releases/download/0.1/zfile-0.1.jar
wget -P ~ https://c.jun6.net/ZFILE/zfile-0.9.war
cd ~
mkdir zfile && unzip zfile-0.9.war -d zfile && rm -rf zfile-0.9.war
chmod +x ~/zfile/bin/*.sh
```
程序的目录结构为:
```
├── zfile
├── META-INF
├── WEB-INF
└── bin
├── start.sh # 启动脚本
└── stop.sh # 停止脚本
├── restart.sh # 重启脚本
```
启动项目:
```bash
java -jar zfile-0.1.jar
## 高级启动
java -jar zfile-0.1.jar --server.port=18777
~/zfile/bin/start.sh
```
> `--server.port` 为指定端口, 默认为 `8080`
> 其他参数, 后面我会详细补充至文档, 最晚本周六日.
停止项目:
```bash
~/zfile/bin/stop.sh
```
重启项目:
```bash
~/zfile/bin/restart.sh
```
修改配置文件:
```bash
vim ~/zfile/WEB-INF/classes/application.yml
```
> 默认启动端口为 8080, 如需请配置文件请编辑上述文件, 修改后重启程序生效.
访问地址:
@@ -55,40 +94,58 @@ java -jar zfile-0.1.jar --server.port=18777
管理后台: 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/onedirve/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/onedirve/china-callback&scope=offline_access%20User.Read%20Files.ReadWrite.All
然后分别填写至访问令牌和刷新令牌即可:
![http://cdn.jun6.net/2020-01-24_18-57-06.png](http://cdn.jun6.net/2020-01-24_18-57-06.png)
## 运行环境
* JDK: `1.8`
* 缓存: `caffeine/redis`
* 缓存: `caffeine`
* 数据库: `h2/mysql`
## 常见问题
### 缓存
缓存默认支持 `caffeine``redis`, 前者为内存缓存, 无需安装, 但后者相对性能更好.
### 数据库
缓存默认支持 `h2``mysql`, 前者为嵌入式数据库, 无需安装, 但后者相对性能更好.
### 默认路径
默认 H2 数据库文件地址: `~/.zfile/db/`, `~` 表示用户目录, windows 为 `C:/Users/用户名/`, linux 为 `/home/用户名/`, root 用户为 `/root/`
### 头尾文件和加密文件
- 目录头部显示文件名为 `header.md`
- 目录底部显示文件名为 `footer.md`
- 目录需要密码访问, 添加文件 `password.txt` (无法拦截此文件被下载, 但可以改名文件)
## TODO
- 全局搜索功能
- 文本预览更换更好用的编辑器
- 后台支持上传、编辑、删除等操作
- API 支持
- 更方便的部署方式
## 支持作者
如果本项目对你有帮助,请作者喝杯咖啡吧。
<img src="http://cdn.jun6.net/alipay.png" width="200" height="312">
<img src="http://cdn.jun6.net/wechat.png" width="222" height="300">

172
pom.xml
View File

@@ -12,8 +12,9 @@
<groupId>im.zhaojun</groupId>
<artifactId>zfile</artifactId>
<version>0.1</version>
<version>0.9</version>
<name>zfile</name>
<packaging>war</packaging>
<description>一个在线的文件浏览系统</description>
<properties>
@@ -21,10 +22,8 @@
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- spring boot 官方相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
@@ -43,13 +42,25 @@
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</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>io.minio</groupId>
<artifactId>minio</artifactId>
<version>3.0.10</version>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 工具类 -->
@@ -59,80 +70,33 @@
<version>4.5.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.7.0</version>
</dependency>
<!-- 对象存储 API -->
<!-- 存储策略相关 API, 对象存储、FTP、 Rest API-->
<dependency>
<groupId>com.upyun</groupId>
<artifactId>java-sdk</artifactId>
<version>4.1.3</version>
</dependency>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>7.2.23</version>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.699</version>
</dependency>
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>esdk-obs-java</artifactId>
<version>3.19.5</version>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.8</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.5.0</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.nuxeo.onedrive</groupId>-->
<!-- <artifactId>onedrive-java-client</artifactId>-->
<!-- <version>1.0</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>5.6.3</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 其他工具类 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org</groupId>
<artifactId>jaudiotagger</artifactId>
<version>2.0.3</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -142,55 +106,53 @@
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.5.14</version>
<exclusions>
<exclusion>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-redis</artifactId>
</exclusion>
<exclusion>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</exclusion>
<exclusion>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.1-jre</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<repositories>
<!-- <repository>-->
<!-- <id>nuxeo</id>-->
<!-- <name>nuxeo</name>-->
<!-- <url>http://maven.nuxeo.org/nexus/content/groups/public</url>-->
<!-- </repository>-->
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.uyoqu.framework</groupId>
<artifactId>maven-plugin-starter</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>bin</goal>
</goals>
</execution>
</executions>
<configuration>
<jvms>
<jvm>-server</jvm>
<jvm>-Xmx512m</jvm>
<jvm>-Djava.security.egd=file:/dev/./urandom</jvm>
</jvms>
</configuration>
</plugin>
</plugins>
</build>

View File

@@ -1,14 +1,19 @@
package im.zhaojun;
import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
import com.alicp.jetcache.anno.config.EnableMethodCache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* @author zhaojun
*/
@EnableAsync
@SpringBootApplication
@EnableCaching
@EnableMethodCache(basePackages = "im.zhaojun", proxyTargetClass = true)
@EnableCreateCacheAnnotation
@EnableAspectJAutoProxy(exposeProxy = true)
public class ZfileApplication {

View File

@@ -1,121 +1,54 @@
package im.zhaojun.aliyun.service;
import cn.hutool.core.util.URLUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
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.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
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 im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.net.URL;
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 AliyunServiceImpl implements FileService {
public class AliyunServiceImpl extends AbstractS3FileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(AliyunServiceImpl.class);
@Value("${zfile.cache.timeout}")
private Long timeout;
@Resource
private StorageConfigService storageConfigService;
private static final String BUCKET_NAME_KEY = "bucket-name";
private static final String ACCESS_KEY = "accessKey";
private static final String SECRET_KEY = "secretKey";
private static final String DOMAIN_KEY = "domain";
private static final String ENDPOINT_KEY = "endPoint";
private OSS ossClient;
private String bucketName;
private String domain;
private boolean isPrivate;
private boolean isInitialized;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.ALIYUN);
String accessKey = stringStorageConfigMap.get(ACCESS_KEY).getValue();
String secretKey = stringStorageConfigMap.get(SECRET_KEY).getValue();
String endPoint = stringStorageConfigMap.get(ENDPOINT_KEY).getValue();
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.ALIYUN);
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(BUCKET_NAME_KEY).getValue();
domain = stringStorageConfigMap.get(DOMAIN_KEY).getValue();
ossClient = new OSSClientBuilder().build(endPoint, accessKey, secretKey);
super.bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
super.domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
AccessControlList bucketAcl = ossClient.getBucketAcl(bucketName);
CannedAccessControlList cannedAcl = bucketAcl.getCannedACL();
isPrivate = "Private".equals(cannedAcl.name());
isInitialized = true;
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(StorageTypeEnum.ALIYUN.getDescription() + "初始化异常, 已跳过");
}
}
@Override
public List<FileItemDTO> fileList(String path) {
path = StringUtils.removeFirstSeparator(path);
List<FileItemDTO> fileItemList = new ArrayList<>();
ObjectListing objectListing =
ossClient.listObjects(new ListObjectsRequest(bucketName).withDelimiter("/").withPrefix(path));
for (OSSObjectSummary s : objectListing.getObjectSummaries()) {
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setName(s.getKey().substring(path.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();
fileItemDTO.setName(commonPrefix.substring(path.length(), commonPrefix.length() - 1));
fileItemDTO.setType(FileTypeEnum.FOLDER);
fileItemDTO.setPath(path);
fileItemList.add(fileItemDTO);
}
return fileItemList;
}
@Override
public String getDownloadUrl(String path) {
path = StringUtils.removeFirstSeparator(path);
if (isPrivate) {
Date expirationDate = new Date(System.currentTimeMillis() + timeout * 1000);
URL url = ossClient.generatePresignedUrl(bucketName, path, expirationDate);
return URLUtil.complateUrl(domain, url.getFile());
} else {
return URLUtil.complateUrl(domain, path);
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@@ -124,8 +57,4 @@ public class AliyunServiceImpl implements FileService {
return StorageTypeEnum.ALIYUN;
}
@Override
public boolean getIsInitialized() {
return isInitialized;
}
}

View File

@@ -7,6 +7,7 @@ import java.lang.annotation.Target;
/**
* 标记注解, 用于在调用前检查是否已存储策略
* @author zhaojun
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -1,13 +1,16 @@
package im.zhaojun.common.aspect;
import im.zhaojun.common.exception.StorageStrategyUninitializedException;
import im.zhaojun.common.model.enums.StorageTypeEnum;
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 {
@@ -15,11 +18,14 @@ public class StorageStrategyInitCheckAspect {
@Before("@annotation(im.zhaojun.common.annotation.CheckStorageStrategyInit)")
public void logStart() {
SystemConfigService systemConfigService = SpringContextHolder.getBean(SystemConfigService.class);
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
if (currentStorageStrategy == null) {
throw new StorageStrategyUninitializedException("存储策略未初始化");
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
if (currentFileService == null) {
throw new StorageStrategyUninitializedException("存储策略尚未初始化, 请联系管理员!");
}
if (currentFileService.getIsUnInitialized()) {
throw new StorageStrategyUninitializedException("存储策略异常, 请联系管理员!");
}
}
}

View File

@@ -14,6 +14,7 @@ import java.io.IOException;
/**
* 开启跨域支持. 一般用于开发环境, 或前后端分离部署时开启.
* @author zhaojun
*/
public class CorsFilter extends GenericFilterBean {

View File

@@ -0,0 +1,103 @@
package im.zhaojun.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.AbstractFileService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.service.SystemConfigService;
import im.zhaojun.onedrive.china.service.OneDriveChinaService;
import im.zhaojun.onedrive.china.service.OneDriveServiceChinaImpl;
import im.zhaojun.onedrive.common.model.OneDriveToken;
import im.zhaojun.onedrive.international.service.OneDriveService;
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.Arrays;
import java.util.Objects;
/**
* @author zhaojun
*/
@Configuration
@EnableScheduling
@Slf4j
public class GlobalScheduleTask {
@Resource
private StorageConfigService storageConfigService;
@Resource
private OneDriveService oneDriveService;
@Resource
private OneDriveChinaService oneDriveChinaService;
@Resource
private SystemConfigService systemConfigService;
/**
* 项目启动 30 秒后, 每半小时执行一次刷新 OneDrive Token 的定时任务.
*/
@Scheduled(fixedRate = 1000 * 60 * 30, initialDelay = 1000 * 30)
public void autoRefreshOneDriveToken() {
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
if (!(currentFileService instanceof OneDriveServiceImpl
|| currentFileService instanceof OneDriveServiceChinaImpl)) {
log.debug("当前启用存储类型, 不是 OneDrive, 跳过自动刷新 AccessToken");
return;
}
if (currentFileService.getIsUnInitialized()) {
log.debug("当前启用 OneDrive 未初始化成功, 跳过自动刷新 AccessToken");
return;
}
try {
refreshOneDriveToken(StorageTypeEnum.ONE_DRIVE);
} catch (Exception e) {
log.debug("刷新 OneDrive Token 失败.", e);
}
try {
refreshOneDriveToken(StorageTypeEnum.ONE_DRIVE_CHINA);
} catch (Exception e) {
log.debug("刷新 OneDrive 世纪互联 Token 失败.", e);
}
}
/**
* 调用刷新 OneDrive Token
*/
public void refreshOneDriveToken(StorageTypeEnum storageType) {
OneDriveToken refreshToken;
if (Objects.equals(storageType, StorageTypeEnum.ONE_DRIVE_CHINA)) {
refreshToken = oneDriveChinaService.getRefreshToken();
} else {
refreshToken = oneDriveService.getRefreshToken();
}
if (refreshToken.getAccessToken() == null || refreshToken.getRefreshToken() == null) {
return;
}
StorageConfig accessTokenConfig =
storageConfigService.selectByTypeAndKey(storageType, StorageConfigConstant.ACCESS_TOKEN_KEY);
StorageConfig refreshTokenConfig =
storageConfigService.selectByTypeAndKey(storageType, StorageConfigConstant.REFRESH_TOKEN_KEY);
accessTokenConfig.setValue(refreshToken.getAccessToken());
refreshTokenConfig.setValue(refreshToken.getRefreshToken());
storageConfigService.updateStorageConfig(Arrays.asList(accessTokenConfig, refreshTokenConfig));
log.info("刷新 {} key 时间: {}", storageType.getDescription(), LocalDateTime.now());
}
}

View File

@@ -1,7 +1,7 @@
package im.zhaojun.common.config;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.FileService;
import im.zhaojun.common.service.AbstractFileService;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -9,10 +9,13 @@ import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author zhaojun
*/
@Component
public class StorageTypeFactory implements ApplicationContextAware {
private static Map<String, FileService> storageTypeEnumFileServiceMap;
private static Map<String, AbstractFileService> storageTypeEnumFileServiceMap;
private static ApplicationContext applicationContext;
@@ -24,15 +27,15 @@ public class StorageTypeFactory implements ApplicationContextAware {
applicationContext = act;
// 获取 Spring 容器中所有 FileService 类型的类
storageTypeEnumFileServiceMap = act.getBeansOfType(FileService.class);
storageTypeEnumFileServiceMap = act.getBeansOfType(AbstractFileService.class);
}
/**
* 获取指定存储类型 Service
*/
public static FileService getStorageTypeService(StorageTypeEnum type) {
FileService result = null;
for (FileService fileService : storageTypeEnumFileServiceMap.values()) {
public static AbstractFileService getStorageTypeService(StorageTypeEnum type) {
AbstractFileService result = null;
for (AbstractFileService fileService : storageTypeEnumFileServiceMap.values()) {
if (fileService.getStorageTypeEnum() == type) {
result = fileService;
break;

View File

@@ -5,6 +5,9 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author zhaojun
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

View File

@@ -1,75 +0,0 @@
package im.zhaojun.common.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import java.util.Collections;
/**
* 缓存配置类, 用于根据配置决定使用 redis 缓存还是 caffeine (内存).
*/
@Configuration
public class ZFileCacheConfiguration {
public static final String CACHE_NAME = "zfile";
/**
* 个性化配置缓存
*/
@Bean
@ConditionalOnProperty(value = "spring.cache.type", havingValue = "caffeine")
public CaffeineCacheManager caffeineCacheManager() {
CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
caffeineCacheManager.setCacheNames(Collections.singletonList(CACHE_NAME));
return caffeineCacheManager;
}
@Bean
@ConditionalOnProperty(value = "spring.cache.type", havingValue = "redis")
public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
GenericJackson2JsonRedisSerializer jsonRedisSerializer
= new GenericJackson2JsonRedisSerializer();
RedisSerializationContext.SerializationPair<Object> pair
= RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer);
RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
}
@Bean
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
char separator = ':';
StringBuilder strBuilder = new StringBuilder();
// 类名
strBuilder.append(target.getClass().getSimpleName());
strBuilder.append(separator);
// 方法名
strBuilder.append(method.getName());
strBuilder.append(separator);
// 参数值
for (int i = 0; i < params.length; i++) {
if (i == params.length - 1) {
strBuilder.append(params[i]);
} else {
strBuilder.append(params[i]).append(",");
}
}
return strBuilder.toString();
};
}
}

View File

@@ -7,6 +7,9 @@ import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
/**
* @author zhaojun
*/
@Configuration
public class ZFileConfiguration {

View File

@@ -1,11 +1,13 @@
package im.zhaojun.common.controller;
import im.zhaojun.common.config.StorageTypeFactory;
import im.zhaojun.common.model.StorageConfig;
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.FileService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.service.SystemConfigService;
import org.slf4j.Logger;
@@ -13,13 +15,18 @@ import org.slf4j.LoggerFactory;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 后台管理
* @author zhaojun
*/
@RestController
@RequestMapping("/admin")
@@ -48,6 +55,26 @@ public class AdminController {
/**
* 更新系统配置
*/
@PostMapping("/config")
public ResultBean updateConfig(SystemConfigDTO systemConfigDTO) throws Exception {
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
systemConfigDTO.setId(1);
systemConfigService.updateSystemConfig(systemConfigDTO);
StorageTypeEnum currentStorageStrategy = currentFileService.getStorageTypeEnum();
if (!Objects.equals(currentStorageStrategy, systemConfigDTO.getStorageStrategy())) {
log.info("已将存储策略由 {} 切换为 {}",
currentStorageStrategy.getDescription(),
systemConfigDTO.getStorageStrategy().getDescription());
refreshStorageStrategy();
}
return ResultBean.success();
}
/**
* 修改管理员登陆密码
*/
@PostMapping("/update-pwd")
public ResultBean updatePwd(String username, String password) {
systemConfigService.updateUsernameAndPwd(username, password);
@@ -55,43 +82,75 @@ public class AdminController {
}
/**
* 更新系统配
* 获取指定存储策略的设
* @param storageType 存储策略
* @return 所有设置
*/
@PostMapping("/config")
public ResultBean updateConfig(SystemConfigDTO systemConfigDTO) {
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
systemConfigDTO.setId(1);
systemConfigService.updateSystemConfig(systemConfigDTO);
if (!currentStorageStrategy.equals(systemConfigDTO.getStorageStrategy())) {
refreshStorageStrategy();
}
return ResultBean.success();
}
@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 表单解析出错异常
*/
@GetMapping("/clear-cache")
public ResultBean clearCache() {
FileService fileService = systemConfigService.getCurrentFileService();
fileService.clearCache();
return ResultBean.success();
@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() {
public void refreshStorageStrategy() throws Exception {
StorageTypeEnum storageStrategy = systemConfigService.getCurrentStorageStrategy();
refreshStorageStrategy(storageStrategy);
}
@@ -99,16 +158,16 @@ public class AdminController {
/**
* 更新存储策略
*/
public void refreshStorageStrategy(StorageTypeEnum storageStrategy) {
public void refreshStorageStrategy(StorageTypeEnum storageStrategy) throws Exception {
if (storageStrategy == null) {
log.info("尚未配置存储策略.");
} else {
FileService fileService = systemConfigService.getCurrentFileService();
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.init();
log.info("当前启用存储类型: {}", storageStrategy.getDescription());
// if 判断是否开启搜索.
fileService.clearFileCache();
log.info("切换至存储类型: {}", storageStrategy.getDescription());
fileAsyncCacheService.cacheGlobalFile();
}
}
}

View File

@@ -0,0 +1,81 @@
package im.zhaojun.common.controller;
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
* @date 2020/1/11 14:03
*/
@RestController
@RequestMapping("/admin/cache")
public class CacheController {
@Resource
private SystemConfigService systemConfigService;
@Resource
private FileAsyncCacheService fileAsyncCacheService;
@Resource
private FileCacheService fileCacheService;
@PostMapping("/enable")
public ResultBean enableCache() throws Exception {
fileCacheService.enableCache();
return ResultBean.success();
}
@PostMapping("/disable")
public ResultBean disableCache() throws Exception {
fileCacheService.disableCache();
return ResultBean.success();
}
@GetMapping("/config")
public ResultBean cacheConfig() throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
Set<String> cacheKeys = fileService.getCacheKeys();
CacheConfigDTO cacheConfigDTO = new CacheConfigDTO();
cacheConfigDTO.setEnableCache(systemConfigService.getEnableCache());
cacheConfigDTO.setCacheFinish(fileAsyncCacheService.isCacheFinish());
cacheConfigDTO.setCacheKeys(cacheKeys);
cacheConfigDTO.setCacheDirectoryCount(fileAsyncCacheService.getCacheDirectoryCount());
cacheConfigDTO.setCacheFileCount(fileAsyncCacheService.getCacheFileCount());
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) throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.clearFileCache();
return ResultBean.success();
}
@PostMapping("/all")
public ResultBean cacheAll() throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.clearFileCache();
fileAsyncCacheService.cacheGlobalFile();
return ResultBean.success();
}
}

View File

@@ -0,0 +1,66 @@
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
* @date 2020/1/13 21:40
*/
@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));
}
}

View File

@@ -1,20 +1,18 @@
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.StorageConfig;
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.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.FileService;
import im.zhaojun.common.service.StorageConfigService;
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.AudioHelper;
import im.zhaojun.common.util.FileComparator;
import im.zhaojun.common.util.HttpUtil;
import im.zhaojun.common.util.StringUtils;
@@ -29,6 +27,7 @@ import java.util.List;
/**
* 前台文件管理
* @author zhaojun
*/
@RequestMapping("/api")
@RestController
@@ -41,12 +40,12 @@ public class FileController {
private SystemConfigService systemConfigService;
@Resource
private StorageConfigService storageConfigService;
private FileAsyncCacheService fileAsyncCacheService;
/**
* 滚动加载每页条数.
*/
private static final Integer PAGE_SIZE = 20;
private static final Integer PAGE_SIZE = 30;
@CheckStorageStrategyInit
@GetMapping("/list")
@@ -55,47 +54,22 @@ public class FileController {
@RequestParam(defaultValue = "asc") String order,
@RequestParam(required = false) String password,
@RequestParam(defaultValue = "1") Integer page) throws Exception {
FileService fileService = systemConfigService.getCurrentFileService();
List<FileItemDTO> fileItemList = fileService.fileList(StringUtils.removeDuplicateSeparator("/" + URLUtil.decode(path) + "/"));
AbstractFileService fileService = systemConfigService.getCurrentFileService();
List<FileItemDTO> fileItemList = fileService.fileList(StringUtils.removeDuplicateSeparator("/" + path + "/"));
for (FileItemDTO fileItemDTO : fileItemList) {
if (ZFileConstant.PASSWORD_FILE_NAME.equals(fileItemDTO.getName())) {
if (!HttpUtil.getTextContent(fileItemDTO.getUrl()).equals(password)) {
if (password != null && !"".equals(password)) {
return ResultBean.error("密码错误.");
}
return ResultBean.error("此文件夹需要密码.", ResultBean.REQUIRED_PASSWORD);
if (ZFileConstant.PASSWORD_FILE_NAME.equals(fileItemDTO.getName())
&& !HttpUtil.getTextContent(fileItemDTO.getUrl()).equals(password)) {
if (password != null && !"".equals(password)) {
return ResultBean.error("密码错误.");
}
return ResultBean.error("此文件夹需要密码.", ResultBean.REQUIRED_PASSWORD);
}
}
// 排序, 先按照文件类型比较, 文件夹在前, 文件在后, 然后根据 sortBy 字段排序, 默认为升序;
fileItemList.sort(new FileComparator(sortBy, order));
filterFileList(fileItemList);
Integer total = fileItemList.size();
Integer totalPage = (total + PAGE_SIZE - 1) / PAGE_SIZE;
if (page > totalPage) {
return ResultBean.successData(new ArrayList<>());
}
Integer start = (page - 1) * PAGE_SIZE;
Integer end = page * PAGE_SIZE;
end = end > total ? total : end;
List<FileItemDTO> fileSubItem = fileItemList.subList(start, end);
return ResultBean.successData(fileSubItem);
List<FileItemDTO> sortedPagingData = getSortedPagingData(fileItemList, page);
return ResultBean.successData(sortedPagingData);
}
/**
* 获取文件类容, 仅限用于, txt, md, ini 等普通文本文件.
* @param url 文件路径
* @return 文件内容
*/
@CheckStorageStrategyInit
@GetMapping("/content")
public ResultBean getContent(String url) {
return ResultBean.successData(HttpUtil.getTextContent(url));
}
/**
* 获取系统配置信息和当前页的标题, 文件头, 文件尾信息
@@ -104,45 +78,31 @@ public class FileController {
@CheckStorageStrategyInit
@GetMapping("/config")
public ResultBean getConfig(String path) throws Exception {
SiteConfigDTO config = systemService.getConfig(URLUtil.decode(StringUtils.removeDuplicateSeparator("/" + path + "/")));
SiteConfigDTO config = systemService.getConfig(StringUtils.removeDuplicateSeparator("/" + path + "/"));
config.setSystemConfigDTO(systemConfigService.getSystemConfig());
return ResultBean.successData(config);
}
@CheckStorageStrategyInit
@GetMapping("/clearCache")
public ResultBean clearCache() {
FileService fileService = systemConfigService.getCurrentFileService();
if (fileService != null) {
fileService.clearCache();
}
return ResultBean.success();
}
@CheckStorageStrategyInit
@GetMapping("/audioInfo")
public ResultBean getAudioInfo(String url) throws Exception {
return ResultBean.success(AudioHelper.getAudioInfo(url));
}
@CheckStorageStrategyInit
@GetMapping("/search")
public ResultBean search(@RequestParam(value = "name", defaultValue = "/") String name) throws Exception {
FileService fileService = systemConfigService.getCurrentFileService();
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 (!systemConfigDTO.getSearchEnable()) {
if (BooleanUtil.isFalse(systemConfigDTO.getSearchEnable())) {
throw new SearchDisableException("搜索功能未开启");
}
return ResultBean.success(fileService.search(URLUtil.decode(name)));
if (!fileAsyncCacheService.isCacheFinish()) {
throw new SearchDisableException("搜索功能缓存预热中, 请稍后再试");
}
List<FileItemDTO> fileItemList = fileService.search(URLUtil.decode(name));
List<FileItemDTO> sortedPagingData = getSortedPagingData(fileItemList, page);
return ResultBean.successData(sortedPagingData);
}
@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);
}
/**
* 过滤文件列表, 不显示密码, 头部和尾部文件.
@@ -153,8 +113,38 @@ public class FileController {
}
fileItemList.removeIf(fileItem -> ZFileConstant.PASSWORD_FILE_NAME.equals(fileItem.getName())
|| ZFileConstant.FOOTER_FILE_NAME.equals(fileItem.getName())
|| ZFileConstant.HEADER_FILE_NAME.equals(fileItem.getName()));
}
private List<FileItemDTO> getSortedPagingData(List<FileItemDTO> fileItemList, Integer page) {
// 排序, 先按照文件类型比较, 文件夹在前, 文件在后, 然后根据 sortBy 字段排序, 默认为升序;
fileItemList.sort(new FileComparator());
filterFileList(fileItemList);
int total = fileItemList.size();
int totalPage = (total + PAGE_SIZE - 1) / PAGE_SIZE;
if (page > totalPage) {
return new ArrayList<>();
}
int start = (page - 1) * PAGE_SIZE;
int end = page * PAGE_SIZE;
end = Math.min(end, total);
return new ArrayList<>(fileItemList.subList(start, end));
}
/**
* 获取指定路径下的文件信息内容
* @param path 文件全路径
* @return 该文件的名称, 路径, 大小, 下载地址等信息.
*/
@CheckStorageStrategyInit
@GetMapping("/directlink")
public ResultBean directlink(String path) {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
return ResultBean.successData(fileService.getFileItem(path));
}
}

View File

@@ -6,24 +6,21 @@ 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.FileService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.service.SystemConfigService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 系统安装初始化
* @author zhaojun
*/
@Controller
@RestController
public class InstallController {
@Resource
@@ -36,7 +33,6 @@ public class InstallController {
private AdminController adminController;
@GetMapping("/is-installed")
@ResponseBody
public ResultBean isInstall() {
if (systemConfigService.getCurrentStorageStrategy() == null) {
return ResultBean.success();
@@ -46,8 +42,7 @@ public class InstallController {
@PostMapping("/install")
@ResponseBody
public ResultBean install(InstallModelDTO installModelDTO) {
public ResultBean install(InstallModelDTO installModelDTO) throws Exception {
SystemConfigDTO systemConfigDTO = systemConfigService.getSystemConfig();
if (systemConfigDTO.getStorageStrategy() != null) {
@@ -76,24 +71,13 @@ public class InstallController {
return ResultBean.success();
}
@PostMapping("/storage-strategy")
@ResponseBody
public ResultBean save(@RequestParam Map<String, String> storageStrategyConfig, StorageTypeEnum storageStrategy) {
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);
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
if (Objects.equals(storageStrategy, currentStorageStrategy)) {
FileService fileService = systemConfigService.getCurrentFileService();
fileService.clearCache();
fileService.init();
}
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);
}
}
}

View File

@@ -13,14 +13,15 @@ import org.springframework.web.bind.annotation.ResponseStatus;
/**
* 全局异常处理器
* @author zhaojun
*/
@ControllerAdvice
@ResponseBody
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()) {
@@ -31,6 +32,7 @@ public class GlobleExceptionHandler {
@ExceptionHandler
@ResponseBody
@ResponseStatus
public ResultBean searchDisableExceptionHandler(StorageStrategyUninitializedException e) {
if (log.isDebugEnabled()) {
@@ -39,6 +41,16 @@ public class GlobleExceptionHandler {
return ResultBean.error(e.getMessage());
}
/**
* 不存在的文件异常
*/
@ExceptionHandler({NotExistFileException.class})
@ResponseBody
public ResultBean notExistFile(Exception ex) {
return ResultBean.error("文件不存在");
}
/**
* 捕获 ClientAbortException 异常, 不做任何处理, 防止出现大量堆栈日志输出, 此异常不影响功能.
*/
@@ -52,6 +64,7 @@ public class GlobleExceptionHandler {
}
@ExceptionHandler
@ResponseBody
@ResponseStatus(code= HttpStatus.INTERNAL_SERVER_ERROR)
public ResultBean searchDisableExceptionHandler(Exception e) {
if (log.isDebugEnabled()) {

View File

@@ -2,6 +2,7 @@ package im.zhaojun.common.exception;
/**
* 对象存储初始化异常
* @author zhaojun
*/
public class InitializeException extends RuntimeException {

View File

@@ -0,0 +1,29 @@
package im.zhaojun.common.exception;
/**
* @author zhaojun
* @date 2020/1/20 22:15
*/
public class NotExistFileException extends RuntimeException {
public NotExistFileException() {
super();
}
public NotExistFileException(String message) {
super(message);
}
public NotExistFileException(String message, Throwable cause) {
super(message, cause);
}
public NotExistFileException(Throwable cause) {
super(cause);
}
protected NotExistFileException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -1,5 +1,8 @@
package im.zhaojun.common.exception;
/**
* @author zhaojun
*/
public class SearchDisableException extends RuntimeException {
public SearchDisableException() {

View File

@@ -2,6 +2,7 @@ package im.zhaojun.common.exception;
/**
* 存储策略未初始化异常
* @author zhaojun
*/
public class StorageStrategyUninitializedException extends RuntimeException {

View File

@@ -2,6 +2,7 @@ package im.zhaojun.common.exception;
/**
* 未知的存储类型异常
* @author zhaojun
*/
public class UnknownStorageTypeException extends RuntimeException {

View File

@@ -3,8 +3,15 @@ package im.zhaojun.common.model;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import lombok.Data;
import javax.persistence.*;
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 {
@@ -20,6 +27,7 @@ public class StorageConfig {
private String title;
@Column(length = 2048)
private String value;
public Integer getId() {

View File

@@ -2,8 +2,15 @@ package im.zhaojun.common.model;
import lombok.Data;
import javax.persistence.*;
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 {

View File

@@ -0,0 +1,37 @@
package im.zhaojun.common.model.constant;
/**
* @author zhaojun
* @date 2019/12/28 18:47
*/
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";
}

View File

@@ -1,5 +1,8 @@
package im.zhaojun.common.model.constant;
/**
* @author zhaojun
*/
public class SystemConfigConstant {
public static final String SITE_NAME = "siteName";
@@ -10,6 +13,8 @@ public class SystemConfigConstant {
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";

View File

@@ -1,5 +1,13 @@
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");
@@ -9,16 +17,21 @@ public class ZFileConstant {
/**
* 页面头部文件
*/
public static final String HEADER_FILE_NAME = "header.md";
/**
* 页面尾部文件
*/
public static final String FOOTER_FILE_NAME = "footer.md";
public static String HEADER_FILE_NAME = "header.md";
/**
* 密码文件
*/
public static final String PASSWORD_FILE_NAME = "password.txt";
public static String PASSWORD_FILE_NAME = "password.txt";
@Autowired(required = false)
public void setHeaderFileName(@Value("${zfile.constant.header}") String headerFileName) {
ZFileConstant.HEADER_FILE_NAME = headerFileName;
}
@Autowired(required = false)
public void setPasswordFileName(@Value("${zfile.constant.password}") String passwordFileName) {
ZFileConstant.PASSWORD_FILE_NAME = passwordFileName;
}
}

View File

@@ -1,5 +1,8 @@
package im.zhaojun.common.model.dto;
/**
* @author zhaojun
*/
public class AudioInfoDTO {
private String title;
private String artist;

View File

@@ -0,0 +1,18 @@
package im.zhaojun.common.model.dto;
import lombok.Data;
import java.util.Set;
/**
* @author zhaojun
* @date 2020/1/3 12:39
*/
@Data
public class CacheConfigDTO {
private Boolean enableCache;
private Boolean cacheFinish;
private Set<String> cacheKeys;
private Integer cacheDirectoryCount;
private Integer cacheFileCount;
}

View File

@@ -5,6 +5,9 @@ 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;

View File

@@ -4,6 +4,9 @@ import im.zhaojun.common.model.enums.StorageTypeEnum;
import java.util.Map;
/**
* @author zhaojun
*/
public class InstallModelDTO {
private String siteName;
private StorageTypeEnum storageStrategy;

View File

@@ -2,6 +2,9 @@ package im.zhaojun.common.model.dto;
import java.io.Serializable;
/**
* @author zhaojun
*/
public class ResultBean implements Serializable {
private static final long serialVersionUID = -8276264968757808344L;

View File

@@ -1,50 +1,23 @@
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 header;
private String footer;
@JsonProperty("viewConfig")
private SystemConfigDTO systemConfigDTO;
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
public String getFooter() {
return footer;
}
public void setFooter(String footer) {
this.footer = footer;
}
public SystemConfigDTO getSystemConfigDTO() {
return systemConfigDTO;
}
public void setSystemConfigDTO(SystemConfigDTO systemConfigDTO) {
this.systemConfigDTO = systemConfigDTO;
}
@Override
public String toString() {
return "SiteConfigDTO{" +
"header='" + header + '\'' +
", footer='" + footer + '\'' +
", systemConfig=" + systemConfigDTO +
'}';
}
}

View File

@@ -0,0 +1,21 @@
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;
}

View File

@@ -4,7 +4,15 @@ 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
@@ -28,75 +36,12 @@ public class SystemConfigDTO {
private String domain;
public Integer getId() {
return id;
}
private Boolean enableCache;
public void setId(Integer id) {
this.id = id;
}
private Boolean searchContainEncryptedFile;
public String getSiteName() {
return siteName;
}
private String customJs;
public void setSiteName(String siteName) {
this.siteName = siteName;
}
private String customCss;
public Boolean getInfoEnable() {
return infoEnable;
}
public void setInfoEnable(Boolean infoEnable) {
this.infoEnable = infoEnable;
}
public Boolean getSearchEnable() {
return searchEnable;
}
public void setSearchEnable(Boolean searchEnable) {
this.searchEnable = searchEnable;
}
public Boolean getSearchIgnoreCase() {
return searchIgnoreCase;
}
public void setSearchIgnoreCase(Boolean searchIgnoreCase) {
this.searchIgnoreCase = searchIgnoreCase;
}
public StorageTypeEnum getStorageStrategy() {
return storageStrategy;
}
public void setStorageStrategy(StorageTypeEnum storageStrategy) {
this.storageStrategy = storageStrategy;
}
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;
}
}

View File

@@ -1,5 +1,8 @@
package im.zhaojun.common.model.enums;
/**
* @author zhaojun
*/
public enum FileTypeEnum {
/**

View File

@@ -1,21 +1,33 @@
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 {
/**
* 当前系统支持的所有存储策略
*/
UPYUN("upyun", "又拍云 USS"),
QINIU("qiniu", "七牛云 KODO"),
HUAWEI("huawei", "华为云 OBS"),
ALIYUN("aliyun", "阿里云 OSS"),
FTP("ftp", "FTP"),
HUAWEI("huawei", "华为云 OBS"),
LOCAL("local", "本地存储"),
MINIO("minio", "MINIO"),
ONE_DRIVE("onedrive", "OneDrive"),
ONE_DRIVE_CHINA("onedrive-china", "OneDrive 世纪互联"),
QINIU("qiniu", "七牛云 KODO"),
TENCENT("tencent", "腾讯云 COS"),
MINIO("minio", "MINIO");
UPYUN("upyun", "又拍云 USS");
private String key;
private String description;
private static Map<String, StorageTypeEnum> enumMap = new HashMap<>();
@@ -30,9 +42,6 @@ public enum StorageTypeEnum {
this.description = description;
}
private String key;
private String description;
public String getKey() {
return key;
}
@@ -50,7 +59,7 @@ public enum StorageTypeEnum {
}
public static StorageTypeEnum getEnum(String value) {
return enumMap.get(value);
return enumMap.get(value.toLowerCase());
}
}

View File

@@ -3,6 +3,9 @@ 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> {

View File

@@ -1,11 +1,15 @@
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(String s) {
public StorageTypeEnum convert(@NonNull String s) {
return StorageTypeEnum.getEnum(s);
}
}

View File

@@ -6,6 +6,9 @@ import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
/**
* @author zhaojun
*/
public class StorageTypeEnumSerializerConvert extends JsonSerializer<StorageTypeEnum> {
@Override

View File

@@ -7,9 +7,25 @@ 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);
}

View File

@@ -4,8 +4,16 @@ 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);
}

View File

@@ -3,7 +3,10 @@ package im.zhaojun.common.security;
import cn.hutool.crypto.SecureUtil;
import org.springframework.security.crypto.password.PasswordEncoder;
public class MD5PasswordEncoder implements PasswordEncoder {
/**
* @author zhaojun
*/
public class Md5PasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {

View File

@@ -6,6 +6,9 @@ import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* @author zhaojun
*/
@Configuration
public class MyCorsFilter {

View File

@@ -12,13 +12,16 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
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配置类
* 自定义 Security 配置类
* @author zhaojun
*/
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@@ -97,6 +100,13 @@ public class MySecurityConfig extends WebSecurityConfigurerAdapter {
http.csrf().disable();
}
@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());
@@ -111,11 +121,12 @@ public class MySecurityConfig extends WebSecurityConfigurerAdapter {
public void configure(WebSecurity web) {
//对于在header里面增加token等类似情况放行所有OPTIONS请求。
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
}
@Bean
public static PasswordEncoder passwordEncoder() {
return new MD5PasswordEncoder();
return new Md5PasswordEncoder();
}
}

View File

@@ -10,6 +10,9 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
import javax.annotation.Resource;
import java.util.Collections;
/**
* @author zhaojun
*/
public class MyUserDetailsServiceImpl implements UserDetailsService {
@Resource

View File

@@ -0,0 +1,244 @@
package im.zhaojun.common.service;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.RefreshPolicy;
import com.alicp.jetcache.anno.CacheRefresh;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.Cached;
import com.alicp.jetcache.anno.CreateCache;
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.FileTypeEnum;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.util.StringUtils;
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.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* @author zhaojun
* @date 2019/12/28 19:27
*/
@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;
@Resource
private SystemConfigService systemConfigService;
@Resource
private FileAsyncCacheService fileAsyncCacheService;
@CreateCache(name = SYSTEM_CONFIG_CACHE_PREFIX, cacheType = CacheType.LOCAL)
private Cache<String, List<FileItemDTO>> cache;
/***
* 获取指定路径下的文件及文件夹, 默认缓存 60 分钟,每隔 30 分钟刷新一次.
* @param path 文件路径
* @return 文件及文件夹列表
* @throws Exception 获取文件列表中出现的异常
*/
@Override
@Cached(name = SYSTEM_CONFIG_CACHE_PREFIX,
key = "args[0]",
cacheType = CacheType.LOCAL, localLimit = 100000, condition = "mvel{bean('systemConfigService').enableCache}")
@CacheRefresh(refresh = 30, timeUnit = TimeUnit.MINUTES)
public abstract List<FileItemDTO> fileList(String path) throws Exception;
/**
* 清理当前存储策略的缓存
* 1. 删除全部缓存
* 2. 关闭自动刷新
* 3. 重置缓存个数
* 4. 标记为当前处于未完成缓存状态
*/
public void clearFileCache() throws Exception {
Set<String> cacheKeys = getCacheKeys();
cache.removeAll(cacheKeys);
closeCacheAutoRefresh();
fileAsyncCacheService.resetCacheCount();
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) {
List<FileItemDTO> result = new ArrayList<>();
boolean searchIgnoreCase = systemConfigService.getSearchIgnoreCase();
List<FileItemDTO> fileItemList = selectAllFileList();
for (FileItemDTO fileItemDTO : fileItemList) {
boolean testResult;
if (searchIgnoreCase) {
testResult = StrUtil.containsIgnoreCase(fileItemDTO.getName(), name);
} else {
testResult = fileItemDTO.getName().contains(name);
}
if (testResult) {
result.add(fileItemDTO);
}
}
return result;
}
/**
* 查询所有文件, 仅去缓存中查询.
* @return 所有文件
*/
public List<FileItemDTO> selectAllFileList() {
List<FileItemDTO> result = new ArrayList<>();
boolean enableCache = systemConfigService.getEnableCache();
if (!enableCache) {
log.debug("未开启缓存, 不支持查询所有文件.");
return null;
}
String path = "/";
List<FileItemDTO> fileItemList = cache.get(path);
fileItemList = fileItemList == null ? new ArrayList<>() : fileItemList;
ArrayDeque<FileItemDTO> queue = new ArrayDeque<>(fileItemList);
while (!queue.isEmpty()) {
FileItemDTO fileItemDTO = queue.pop();
result.add(fileItemDTO);
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
String filePath = StringUtils.removeDuplicateSeparator("/" + fileItemDTO.getPath() + "/" + fileItemDTO.getName() + "/");
List<FileItemDTO> cacheList = cache.get(filePath);
if (cacheList != null && isNotEncryptedFolder(cacheList)) {
queue.addAll(cacheList);
}
}
}
return result;
}
/**
* 不是加密文件夹
* @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;
}
/**
* 获取所有缓存的 Key, 仅当开启缓存, 且缓存完成时, 可获取.
* @return 所有缓存的 Key
*/
public Set<String> getCacheKeys() {
if (systemConfigService.getEnableCache() && fileAsyncCacheService.isCacheFinish()) {
Set<String> collect = selectAllFileList().stream().map(fileItemDTO -> {
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
return StringUtils.removeDuplicateSeparator("/" + fileItemDTO.getPath() + "/" + fileItemDTO.getName() + "/");
}
return null;
}).collect(Collectors.toSet());
collect.remove(null);
collect.add("/");
return collect;
} else {
return Collections.emptySet();
}
}
/**
* 刷新缓存
*/
public void refreshCache(String key) throws Exception {
cache.remove(key);
FileService currentFileService = (FileService) AopContext.currentProxy();
currentFileService.fileList(key);
}
public void closeCacheAutoRefresh() {
cache.config().setRefreshPolicy(null);
}
public void openCacheAutoRefresh() {
RefreshPolicy refreshPolicy = RefreshPolicy.newPolicy(30, TimeUnit.MINUTES);
cache.config().setRefreshPolicy(refreshPolicy);
}
public abstract FileItemDTO getFileItem(String path);
}

View File

@@ -0,0 +1,127 @@
package im.zhaojun.common.service;
import cn.hutool.core.util.ObjectUtil;
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
* @date 2019/12/26 22:26
*/
public abstract class AbstractS3FileService extends AbstractFileService {
@Resource
protected StorageConfigService storageConfigService;
protected String path;
protected String basePath;
protected String bucketName;
protected String domain;
protected AmazonS3 s3Client;
@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(getFullPath());
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));
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);
}
/**
* 获取 basePath + path 的全路径地址.
* @return basePath + path 的全路径地址.
*/
public String getFullPath() {
String basePath = ObjectUtil.defaultIfNull(this.basePath, "");
String path = ObjectUtil.defaultIfNull(this.path, "");
return StringUtils.removeDuplicateSeparator(basePath + "/" + path);
}
@Override
public FileItemDTO getFileItem(String path) {
List<FileItemDTO> list = fileList(path);
if (list == null || list.size() == 0) {
throw new NotExistFileException();
}
return list.get(0);
}
}

View File

@@ -1,18 +1,39 @@
package im.zhaojun.common.service;
import cn.hutool.core.util.ObjectUtil;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.CreateCache;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import im.zhaojun.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayDeque;
import java.util.List;
/**
* @author zhaojun
*/
@Slf4j
@Service
public class FileAsyncCacheService {
private static final Logger log = LoggerFactory.getLogger(FileAsyncCacheService.class);
public static final String CACHE_PROCESS_PREFIX = "zfile-process-cache:";
public static final String CACHE_FILE_COUNT_KEY = "file-count";
public static final String CACHE_DIRECTORY_COUNT_KEY = "directory-count";
@CreateCache(name = "SYSTEM_CONFIG_CACHE_PREFIX", cacheType = CacheType.LOCAL)
private Cache<String, Integer> cache;
private boolean cacheFinish;
@Resource
private SystemConfigService systemConfigService;
@@ -22,22 +43,98 @@ public class FileAsyncCacheService {
StorageTypeEnum storageStrategy = systemConfigService.getCurrentStorageStrategy();
if (storageStrategy == null) {
log.info("尚未配置存储策略. 跳过启动缓存.");
log.debug("尚未配置存储策略. 跳过启动缓存.");
return;
}
FileService fileService = StorageTypeFactory.getStorageTypeService(storageStrategy);
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 = cache.get(CACHE_DIRECTORY_COUNT_KEY);
if (cacheDirectoryCount == null) {
cacheDirectoryCount = 0;
}
Integer cacheFileCount = cache.get(CACHE_FILE_COUNT_KEY);
if (cacheFileCount == null) {
cacheFileCount = 0;
}
log.info("缓存 {} 所有文件开始", storageStrategy.getDescription());
long startTime = System.currentTimeMillis();
try {
if (fileService.getIsInitialized()) {
fileService.selectAllFileList();
FileService currentFileService = systemConfigService.getCurrentFileService();
List<FileItemDTO> rootFileItems = currentFileService.fileList("/");
ArrayDeque<FileItemDTO> queue = new ArrayDeque<>(rootFileItems);
while (!queue.isEmpty()) {
FileItemDTO fileItemDTO = queue.pop();
if (FileTypeEnum.FOLDER.equals(fileItemDTO.getType())) {
cacheDirectoryCount++;
}
if (FileTypeEnum.FILE.equals(fileItemDTO.getType())) {
cacheFileCount++;
}
log.debug("已缓存 {} 个文件夹", cacheDirectoryCount);
cache.put(CACHE_DIRECTORY_COUNT_KEY, cacheDirectoryCount);
log.debug("已缓存 {} 个文件", cacheFileCount);
cache.put(CACHE_FILE_COUNT_KEY, cacheFileCount);
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
String filePath = StringUtils.removeDuplicateSeparator("/" + fileItemDTO.getPath() + "/" + fileItemDTO.getName() + "/");
List<FileItemDTO> fileItems = currentFileService.fileList(filePath);
queue.addAll(fileItems);
}
}
cache.put(CACHE_DIRECTORY_COUNT_KEY, cacheDirectoryCount);
cache.put(CACHE_FILE_COUNT_KEY, cacheFileCount);
} catch (Exception e) {
log.error("缓存所有文件失败", e);
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
log.info("缓存 {} 所有文件结束, 用时: {} 秒", storageStrategy.getDescription(), ( (endTime - startTime) / 1000 ));
log.info("缓存 {} 所有文件结束, 用时: {} 秒, 文件夹共 {} 个, 文件共 {} 个",
storageStrategy.getDescription(),
( (endTime - startTime) / 1000 ), cacheDirectoryCount, cacheFileCount);
cacheFinish = true;
}
/**
* 清理缓存的文件/文件夹数量统计
*/
public void resetCacheCount() {
cache.remove(CACHE_DIRECTORY_COUNT_KEY);
cache.remove(CACHE_FILE_COUNT_KEY);
}
public Integer getCacheDirectoryCount() {
return ObjectUtil.defaultIfNull(cache.get(CACHE_DIRECTORY_COUNT_KEY), 0);
}
public Integer getCacheFileCount() {
return ObjectUtil.defaultIfNull(cache.get(CACHE_FILE_COUNT_KEY), 0);
}
public boolean isCacheFinish() {
return cacheFinish;
}
public void setCacheFinish(boolean cacheFinish) {
this.cacheFinish = cacheFinish;
}
}

View File

@@ -0,0 +1,39 @@
package im.zhaojun.common.service;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @author zhaojun
* @date 2020/1/11 14:08
*/
@Service
public class FileCacheService {
@Resource
private SystemConfigService systemConfigService;
@Resource
@Lazy
private FileAsyncCacheService fileAsyncCacheService;
public void enableCache() {
systemConfigService.updateCacheEnableConfig(true);
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
currentFileService.openCacheAutoRefresh();
fileAsyncCacheService.cacheGlobalFile();
}
public void disableCache() throws Exception {
systemConfigService.updateCacheEnableConfig(false);
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
currentFileService.clearFileCache();
fileAsyncCacheService.resetCacheCount();
}
}

View File

@@ -1,73 +1,27 @@
package im.zhaojun.common.service;
import im.zhaojun.common.config.ZFileCacheConfiguration;
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 org.springframework.aop.framework.AopContext;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import javax.annotation.PostConstruct;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
@CacheConfig(cacheNames = ZFileCacheConfiguration.CACHE_NAME, keyGenerator = "keyGenerator")
/**
* @author zhaojun
*/
public interface FileService {
@Cacheable
/***
* 获取指定路径下的文件及文件夹
* @param path 文件路径
* @return 文件及文件夹列表
* @throws Exception 获取文件列表中出现的异常
*/
List<FileItemDTO> fileList(String path) throws Exception;
@Cacheable
String getDownloadUrl(String path) throws Exception;
@PostConstruct
default void init() {}
/**
* 清除缓存.
* 获取文件下载地址
* @param path 文件路径
* @return 文件下载地址
*/
@CacheEvict(allEntries = true)
default void clearCache() {}
String getDownloadUrl(String path);
default List<FileItemDTO> search(String name) throws Exception {
List<FileItemDTO> result = new ArrayList<>();
List<FileItemDTO> fileItemList = selectAllFileList();
for (FileItemDTO fileItemDTO : fileItemList) {
if (fileItemDTO.getName().contains(name)) {
result.add(fileItemDTO);
}
}
return result;
}
default List<FileItemDTO> selectAllFileList() throws Exception {
List<FileItemDTO> result = new ArrayList<>();
String path = "/";
FileService currentFileService = (FileService) AopContext.currentProxy();
List<FileItemDTO> fileItemList = currentFileService.fileList(path);
ArrayDeque<FileItemDTO> queue = new ArrayDeque<>(fileItemList);
while (!queue.isEmpty()) {
FileItemDTO fileItemDTO = queue.pop();
result.add(fileItemDTO);
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
String filePath = StringUtils.removeDuplicateSeparator("/" + fileItemDTO.getPath() + "/" + fileItemDTO.getName() + "/");
queue.addAll(currentFileService.fileList(filePath));
}
}
return result;
}
StorageTypeEnum getStorageTypeEnum();
boolean getIsInitialized();
}

View File

@@ -10,6 +10,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author zhaojun
*/
@Service
public class StorageConfigService {
@@ -20,6 +23,12 @@ public class StorageConfigService {
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)) {
@@ -28,6 +37,7 @@ public class StorageConfigService {
return map;
}
public void updateStorageConfig(List<StorageConfig> storageConfigList) {
storageConfigRepository.saveAll(storageConfigList);
}

View File

@@ -1,126 +1,158 @@
package im.zhaojun.common.service;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.crypto.SecureUtil;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.CreateCache;
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 im.zhaojun.common.util.StringUtils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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";
@CreateCache(name = SYSTEM_CONFIG_CACHE_PREFIX, cacheType = CacheType.LOCAL)
private Cache<String, Object> configCache;
@Resource
private SystemConfigRepository systemConfigRepository;
@Resource
private FileCacheService fileCacheService;
private Class<SystemConfigDTO> systemConfigDTOClass = SystemConfigDTO.class;
public SystemConfigDTO getSystemConfig() {
Object cache = configCache.get(SYSTEM_CONFIG_CACHE_KEY);
if (configCache.get(SYSTEM_CONFIG_CACHE_KEY) != null) {
return (SystemConfigDTO) cache;
}
SystemConfigDTO systemConfigDTO = new SystemConfigDTO();
List<SystemConfig> systemConfigList = systemConfigRepository.findAll();
for (SystemConfig systemConfig : systemConfigList) {
switch (systemConfig.getKey()) {
case SystemConfigConstant.SITE_NAME:
systemConfigDTO.setSiteName(systemConfig.getValue());
break;
case SystemConfigConstant.INFO_ENABLE:
systemConfigDTO.setInfoEnable("true".equals(systemConfig.getValue()));
break;
case SystemConfigConstant.SEARCH_ENABLE:
systemConfigDTO.setSearchEnable("true".equals(systemConfig.getValue()));
break;
case SystemConfigConstant.SEARCH_IGNORE_CASE:
systemConfigDTO.setSearchIgnoreCase("true".equals(systemConfig.getValue()));
break;
case SystemConfigConstant.STORAGE_STRATEGY:
String value = systemConfig.getValue();
systemConfigDTO.setStorageStrategy(StorageTypeEnum.getEnum(value));
break;
case SystemConfigConstant.USERNAME:
systemConfigDTO.setUsername(systemConfig.getValue());
break;
case SystemConfigConstant.PASSWORD:
systemConfigDTO.setPassword(systemConfig.getValue());
break;
case SystemConfigConstant.DOMAIN:
systemConfigDTO.setDomain(systemConfig.getValue());
break;
default:break;
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);
}
}
}
configCache.put(SYSTEM_CONFIG_CACHE_KEY, systemConfigDTO);
return systemConfigDTO;
}
public void updateSystemConfig(SystemConfigDTO systemConfigDTO) {
public void updateSystemConfig(SystemConfigDTO systemConfigDTO) throws Exception {
List<SystemConfig> systemConfigList = new ArrayList<>();
SystemConfig systemConfig = systemConfigRepository.findByKey(SystemConfigConstant.SITE_NAME);
systemConfig.setValue(systemConfigDTO.getSiteName());
systemConfigList.add(systemConfig);
SystemConfig domainConfig = systemConfigRepository.findByKey(SystemConfigConstant.DOMAIN);
domainConfig.setValue(systemConfigDTO.getDomain());
systemConfigList.add(domainConfig);
SystemConfig infoEnableSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.INFO_ENABLE);
infoEnableSystemConfig.setValue(systemConfigDTO.getInfoEnable() ? "true" : "false");
systemConfigList.add(infoEnableSystemConfig);
SystemConfig searchEnableSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.SEARCH_ENABLE);
searchEnableSystemConfig.setValue(systemConfigDTO.getSearchEnable() ? "true" : "false");
systemConfigList.add(searchEnableSystemConfig);
SystemConfig searchIgnoreCaseSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.SEARCH_IGNORE_CASE);
searchIgnoreCaseSystemConfig.setValue(systemConfigDTO.getSearchIgnoreCase() ? "true" : "false");
systemConfigList.add(searchIgnoreCaseSystemConfig);
SystemConfig storageStrategySystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.STORAGE_STRATEGY);
storageStrategySystemConfig.setValue(systemConfigDTO.getStorageStrategy().getKey());
systemConfigList.add(storageStrategySystemConfig);
if (!StringUtils.isNullOrEmpty(systemConfigDTO.getUsername())) {
SystemConfig usernameSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.USERNAME);
usernameSystemConfig.setValue(systemConfigDTO.getUsername());
systemConfigList.add(usernameSystemConfig);
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);
}
}
}
if (!StringUtils.isNullOrEmpty(systemConfigDTO.getPassword())) {
SystemConfig passwordSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.PASSWORD);
passwordSystemConfig.setValue(systemConfigDTO.getPassword());
systemConfigList.add(passwordSystemConfig);
}
boolean oldEnableCache = getEnableCache();
boolean curEnableCache = BooleanUtil.isTrue(systemConfigDTO.getEnableCache());
configCache.remove(SYSTEM_CONFIG_CACHE_KEY);
systemConfigRepository.saveAll(systemConfigList);
if (!oldEnableCache && curEnableCache) {
log.debug("检测到开启了缓存, 开启预热缓存");
fileCacheService.enableCache();
}
if (oldEnableCache && !curEnableCache) {
log.debug("检测到关闭了缓存, 正在清理缓存数据及关闭自动刷新");
fileCacheService.disableCache();
}
}
public void updateUsernameAndPwd(String username, String password) {
SystemConfig usernameConfig = systemConfigRepository.findByKey(SystemConfigConstant.USERNAME);
usernameConfig.setValue(username);
systemConfigRepository.save(usernameConfig);
password = SecureUtil.md5(password);;
String encryptionPassword = SecureUtil.md5(password);
SystemConfig systemConfig = systemConfigRepository.findByKey(SystemConfigConstant.PASSWORD);
systemConfig.setValue(password);
systemConfig.setValue(encryptionPassword);
configCache.remove(SYSTEM_CONFIG_CACHE_KEY);
systemConfigRepository.save(systemConfig);
}
public FileService getCurrentFileService() {
public void updateCacheEnableConfig(Boolean isEnable) {
SystemConfig enableConfig = systemConfigRepository.findByKey(SystemConfigConstant.ENABLE_CACHE);
enableConfig.setValue(isEnable.toString());
systemConfigRepository.save(enableConfig);
configCache.remove(SYSTEM_CONFIG_CACHE_KEY);
}
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());
}
}

View File

@@ -7,8 +7,12 @@ import im.zhaojun.common.util.HttpUtil;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* @author zhaojun
*/
@Service
public class SystemService {
@@ -22,13 +26,11 @@ public class SystemService {
public synchronized SiteConfigDTO getConfig(String path) throws Exception {
SiteConfigDTO siteConfigDTO = new SiteConfigDTO();
FileService fileService = systemConfigService.getCurrentFileService();
AbstractFileService fileService = systemConfigService.getCurrentFileService();
List<FileItemDTO> fileItemList = fileService.fileList(path);
List<FileItemDTO> fileItemList = new ArrayList<>(fileService.fileList(path));
for (FileItemDTO fileItemDTO : fileItemList) {
if (ZFileConstant.FOOTER_FILE_NAME.equalsIgnoreCase(fileItemDTO.getName())) {
siteConfigDTO.setFooter(HttpUtil.getTextContent(fileItemDTO.getUrl()));
} else if (ZFileConstant.HEADER_FILE_NAME.equalsIgnoreCase(fileItemDTO.getName())) {
if (ZFileConstant.HEADER_FILE_NAME.equalsIgnoreCase(fileItemDTO.getName())) {
siteConfigDTO.setHeader(HttpUtil.getTextContent(fileItemDTO.getUrl()));
}
}

View File

@@ -5,7 +5,11 @@ 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.*;
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;
@@ -18,6 +22,7 @@ import java.net.URL;
/**
* 音频解析工具类
* @author zhaojun
*/
public class AudioHelper {
@@ -43,7 +48,7 @@ public class AudioHelper {
AudioInfoDTO audioInfoDTO = new AudioInfoDTO();
audioInfoDTO.setTitle("未知歌曲");
audioInfoDTO.setArtist("未知");
audioInfoDTO.setCover("/shikwasa/audio.png");
audioInfoDTO.setCover("http://c.jun6.net/audio.png");
Mp3File mp3File = null;
try {

View File

@@ -12,12 +12,17 @@ import java.util.Comparator;
* - 默认按照名称排序
* - 默认排序为升序
* - 按名称排序不区分大小写
*
* @author zhaojun
*/
public class FileComparator implements Comparator<FileItemDTO> {
private String sortBy;
private String order;
public FileComparator() {
}
public FileComparator(String sortBy, String order) {
this.sortBy = sortBy;
this.order = order;
@@ -25,6 +30,14 @@ public class FileComparator implements Comparator<FileItemDTO> {
@Override
public int compare(FileItemDTO o1, FileItemDTO o2) {
if (sortBy == null) {
sortBy = "name";
}
if (order == null) {
order = "asc";
}
FileTypeEnum o1Type = o1.getType();
FileTypeEnum o2Type = o2.getType();
@@ -33,7 +46,7 @@ public class FileComparator implements Comparator<FileItemDTO> {
switch (sortBy) {
case "time": result = o1.getTime().compareTo(o2.getTime()); break;
case "size": result = o1.getSize().compareTo(o2.getSize()); break;
default: result = o1.getName().compareToIgnoreCase(o2.getName());
default: result = o1.getName().compareToIgnoreCase(o2.getName()); break;
}
return "asc".equals(order) ? result : -result;
}

View File

@@ -1,14 +1,29 @@
package im.zhaojun.common.util;
import cn.hutool.core.util.URLUtil;
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.class);
String result = restTemplate.getForObject(URLUtil.decode(url), String.class);
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;
}
}

View File

@@ -7,8 +7,12 @@ 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 {
@@ -59,7 +63,7 @@ public class SpringContextHolder implements ApplicationContextAware, DisposableB
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
SpringContextHolder.applicationContext = applicationContext;
}

View File

@@ -1,32 +1,58 @@
package im.zhaojun.common.util;
import cn.hutool.core.net.NetUtil;
import im.zhaojun.common.exception.InitializeException;
import im.zhaojun.common.service.FileAsyncCacheService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.env.Environment;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.LinkedHashSet;
/**
* 项目启动监听器, 当项目启动时, 遍历当前对象存储的所有内容, 添加到缓存中.
* @author zhaojun
*/
@Component
public class StartupListener implements ApplicationListener<ContextRefreshedEvent> {
private static final Logger log = LoggerFactory.getLogger(StartupListener.class);
@Slf4j
public class StartupListener implements ApplicationListener<ApplicationStartedEvent> {
@Resource
private FileAsyncCacheService fileAsyncCacheService;
@Resource
private Environment environment;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
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 RuntimeException("缓存异常.", e);
throw new InitializeException("初始化缓存异常.", e);
}
}
}

View File

@@ -1,13 +1,23 @@
package im.zhaojun.common.util;
/**
* @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) == '/') {
if (!"".equals(path) && path.charAt(0) == DELIMITER) {
path = path.substring(1);
}
return path;
@@ -18,14 +28,14 @@ public class StringUtils {
* @return 如 path = '/folder1/file1/', 返回 '/folder1/file1'
*/
public static String removeLastSeparator(String path) {
if (!"".equals(path) && path.charAt(path.length() - 1) == '/') {
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("/" + path + "/" + name);
return removeDuplicateSeparator(DELIMITER + path + DELIMITER + name);
}
@@ -36,11 +46,11 @@ public class StringUtils {
* @return URL
*/
public static String concatPath(String domain, String path) {
if (path != null && path.length() > 1 && path.charAt(0) != '/') {
path = '/' + path;
if (path != null && path.length() > 1 && path.charAt(0) != DELIMITER) {
path = DELIMITER + path;
}
if (domain.charAt(domain.length() - 1) == '/') {
if (domain != null && domain.charAt(domain.length() - 1) == DELIMITER) {
domain = domain.substring(0, domain.length() - 2);
}
@@ -54,16 +64,16 @@ public class StringUtils {
StringBuilder sb = new StringBuilder();
if (path.indexOf("http://") == 0) {
sb.append("http://");
} else if (path.indexOf("https://") == 0) {
sb.append("http://");
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 == '/' && next == '/')) {
if (!(current == DELIMITER && next == DELIMITER)) {
sb.append(current);
}
}
@@ -75,5 +85,7 @@ public class StringUtils {
return s == null || "".equals(s);
}
public static boolean isNotNullOrEmpty(String s) {
return !isNullOrEmpty(s);
}
}

View File

@@ -3,9 +3,11 @@ 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;
@@ -18,47 +20,44 @@ import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @author zhaojun
*/
@Service
public class FtpServiceImpl implements FileService {
public class FtpServiceImpl extends AbstractFileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(FtpServiceImpl.class);
@Resource
private StorageConfigService storageConfigService;
private static final String HOST_KEY = "host";
private static final String PORT_KEY = "port";
private static final String USERNAME_KEY = "username";
private static final String PASSWORD_KEY = "password";
private static final String DOMAIN_KEY = "domain";
private Ftp ftp;
private String domain;
private boolean isInitialized;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.FTP);
String host = stringStorageConfigMap.get(HOST_KEY).getValue();
String port = stringStorageConfigMap.get(PORT_KEY).getValue();
String username = stringStorageConfigMap.get(USERNAME_KEY).getValue();
String password = stringStorageConfigMap.get(PASSWORD_KEY).getValue();
domain = stringStorageConfigMap.get(DOMAIN_KEY).getValue();
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.FTP);
String host = stringStorageConfigMap.get(StorageConfigConstant.HOST_KEY).getValue();
String port = stringStorageConfigMap.get(StorageConfigConstant.PORT_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();
ftp = new Ftp(host, Integer.parseInt(port), username, password);
isInitialized = true;
} catch (Exception e) {
log.debug(StorageTypeEnum.FTP.getDescription() + "初始化异常, 已跳过");
}
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);
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@Override
@@ -92,9 +91,11 @@ public class FtpServiceImpl implements FileService {
return StorageTypeEnum.FTP;
}
@Override
public boolean getIsInitialized() {
return isInitialized;
public FileItemDTO getFileItem(String path) {
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setUrl(getDownloadUrl(path));
return fileItemDTO;
}
}

View File

@@ -1,132 +1,60 @@
package im.zhaojun.huawei.service;
import cn.hutool.core.util.URLUtil;
import com.obs.services.ObsClient;
import com.obs.services.model.*;
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.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
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 im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @author zhaojun
*/
@Service
public class HuaweiServiceImpl implements FileService {
public class HuaweiServiceImpl extends AbstractS3FileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(HuaweiServiceImpl.class);
private String bucketName;
private String domain;
@Value("${zfile.cache.timeout}")
private Long timeout;
private static final String BUCKET_NAME_KEY = "bucket-name";
private static final String ACCESS_KEY = "accessKey";
private static final String SECRET_KEY = "secretKey";
private static final String DOMAIN_KEY = "domain";
private static final String ENDPOINT_KEY = "endPoint";
@Resource
private StorageConfigService storageConfigService;
private ObsClient obsClient;
private boolean isInitialized;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.HUAWEI);
String accessKey = stringStorageConfigMap.get(ACCESS_KEY).getValue();
String secretKey = stringStorageConfigMap.get(SECRET_KEY).getValue();
String endPoint = stringStorageConfigMap.get(ENDPOINT_KEY).getValue();
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.HUAWEI);
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(BUCKET_NAME_KEY).getValue();
domain = stringStorageConfigMap.get(DOMAIN_KEY).getValue();
obsClient = new ObsClient(accessKey, secretKey, endPoint);
isInitialized = true;
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(StorageTypeEnum.HUAWEI.getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@Override
public List<FileItemDTO> fileList(String path) throws Exception {
path = StringUtils.removeFirstSeparator(path);
List<FileItemDTO> fileItemList = new ArrayList<>();
ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
listObjectsRequest.setBucketName(bucketName);
listObjectsRequest.setDelimiter("/");
listObjectsRequest.setPrefix(path);
ObjectListing objectListing = obsClient.listObjects(listObjectsRequest);
List<ObsObject> objects = objectListing.getObjects();
for (ObsObject object : objects) {
String fileName = object.getObjectKey();
ObjectMetadata metadata = object.getMetadata();
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setName(fileName.substring(path.length()));
fileItemDTO.setSize(metadata.getContentLength());
fileItemDTO.setTime(metadata.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();
fileItemDTO.setName(commonPrefix.substring(0, commonPrefix.length() - 1));
fileItemDTO.setType(FileTypeEnum.FOLDER);
fileItemDTO.setPath(path);
fileItemList.add(fileItemDTO);
}
return fileItemList;
}
@Override
public String getDownloadUrl(String path) throws Exception {
path = StringUtils.removeFirstSeparator(path);
TemporarySignatureRequest req = new TemporarySignatureRequest(HttpMethodEnum.GET, timeout);
req.setBucketName(bucketName);
req.setObjectKey(path);
TemporarySignatureResponse res = obsClient.createTemporarySignature(req);
URL url = new URL(res.getSignedUrl());
return URLUtil.complateUrl(domain, url.getFile());
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.HUAWEI;
}
@Override
public boolean getIsInitialized() {
return isInitialized;
}
}

View File

@@ -1,6 +1,7 @@
package im.zhaojun.local.controller;
import cn.hutool.core.util.URLUtil;
import im.zhaojun.common.exception.NotExistFileException;
import im.zhaojun.common.util.StringUtils;
import im.zhaojun.local.service.LocalServiceImpl;
import org.springframework.core.io.FileSystemResource;
@@ -16,9 +17,11 @@ import org.springframework.web.servlet.HandlerMapping;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.Date;
/**
* @author zhaojun
*/
@Controller
public class LocalController {
@@ -27,17 +30,22 @@ public class LocalController {
@GetMapping("/file/**")
@ResponseBody
public ResponseEntity<FileSystemResource> downAttachment(final HttpServletRequest request) throws IOException {
public ResponseEntity<FileSystemResource> 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);
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
AntPathMatcher apm = new AntPathMatcher();
String filePath = apm.extractPathWithinPattern(bestMatchPattern, path);
return export(new File(StringUtils.concatPath(localServiceImpl.getFilePath(), URLUtil.decode(filePath))));
}
private ResponseEntity<FileSystemResource> export(File file) throws IOException {
private ResponseEntity<FileSystemResource> export(File file) {
if (!file.exists()) {
throw new NotExistFileException();
}
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;

View File

@@ -1,36 +1,39 @@
package im.zhaojun.local.service;
import cn.hutool.core.util.URLUtil;
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.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.File;
import java.net.Inet4Address;
import java.net.InetAddress;
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 implements FileService {
public class LocalServiceImpl extends AbstractFileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(LocalServiceImpl.class);
private static final String FILE_PATH_KEY = "filePath";
@Resource
private StorageConfigService storageConfigService;
@@ -39,22 +42,25 @@ public class LocalServiceImpl implements FileService {
private String filePath;
private boolean isInitialized;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.LOCAL);
filePath = stringStorageConfigMap.get(FILE_PATH_KEY).getValue();
isInitialized = true;
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(StorageTypeEnum.LOCAL.getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@Override
public List<FileItemDTO> fileList(String path) throws Exception {
public List<FileItemDTO> fileList(String path) {
List<FileItemDTO> fileItemList = new ArrayList<>();
String fullPath = StringUtils.concatPath(filePath, path);
@@ -82,9 +88,9 @@ public class LocalServiceImpl implements FileService {
}
@Override
public String getDownloadUrl(String path) throws Exception {
public String getDownloadUrl(String path) {
SystemConfig usernameConfig = systemConfigRepository.findByKey(SystemConfigConstant.DOMAIN);
return StringUtils.removeDuplicateSeparator(usernameConfig.getValue() + "/file/" + path);
return URLUtil.encode(StringUtils.removeDuplicateSeparator(usernameConfig.getValue() + "/file/" + path));
}
public String getFilePath() {
@@ -100,10 +106,26 @@ public class LocalServiceImpl implements FileService {
return StorageTypeEnum.LOCAL;
}
@Override
public boolean getIsInitialized() {
return isInitialized;
}
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;
}
}

View File

@@ -1,109 +1,61 @@
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.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
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 im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.util.StringUtils;
import io.minio.MinioClient;
import io.minio.Result;
import io.minio.messages.Item;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @author zhaojun
*/
@Service
public class MinIOServiceImpl implements FileService {
public class MinIOServiceImpl extends AbstractS3FileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(MinIOServiceImpl.class);
private String bucketName;
@Value("${zfile.cache.timeout}")
private Long timeout;
private static final String BUCKET_NAME_KEY = "bucket-name";
private static final String ACCESS_KEY = "accessKey";
private static final String SECRET_KEY = "secretKey";
private static final String ENDPOINT_KEY = "endPoint";
@Resource
private StorageConfigService storageConfigService;
private MinioClient minioClient;
private boolean isInitialized;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.MINIO);
String accessKey = stringStorageConfigMap.get(ACCESS_KEY).getValue();
String secretKey = stringStorageConfigMap.get(SECRET_KEY).getValue();
String endPoint = stringStorageConfigMap.get(ENDPOINT_KEY).getValue();
bucketName = stringStorageConfigMap.get(BUCKET_NAME_KEY).getValue();
minioClient = new MinioClient(endPoint, accessKey, secretKey);
isInitialized = true;
} catch (Exception e) {
log.debug(StorageTypeEnum.MINIO.getDescription() + "初始化异常, 已跳过");
}
}
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.MINIO);
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();
@Override
public List<FileItemDTO> fileList(String path) throws Exception {
path = StringUtils.removeFirstSeparator(path);
List<FileItemDTO> fileItemList = new ArrayList<>();
Iterable<Result<Item>> iterable = minioClient.listObjects(bucketName, path, false);
for (Result<Item> itemResult : iterable) {
Item item = itemResult.get();
FileItemDTO fileItemDTO = new FileItemDTO();
if (item.isDir()) {
fileItemDTO.setName(StringUtils.removeLastSeparator(item.objectName().replaceFirst(path, "")));
fileItemDTO.setType(FileTypeEnum.FOLDER);
fileItemDTO.setPath(path);
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
isInitialized = false;
} else {
fileItemDTO.setName(item.objectName().replaceFirst(path, ""));
fileItemDTO.setSize(item.objectSize());
fileItemDTO.setTime(item.lastModified());
fileItemDTO.setType(FileTypeEnum.FILE);
fileItemDTO.setPath(path);
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(path, fileItemDTO.getName())));
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
s3Client = AmazonS3ClientBuilder.standard()
.withPathStyleAccessEnabled(true)
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "minio")).build();
isInitialized = testConnection();
}
fileItemList.add(fileItemDTO);
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
return fileItemList;
}
@Override
public String getDownloadUrl(String path) throws Exception {
return minioClient.presignedGetObject(bucketName, path, timeout.intValue());
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.MINIO;
}
@Override
public boolean getIsInitialized() {
return isInitialized;
}
}

View File

@@ -0,0 +1,54 @@
package im.zhaojun.onedrive.china.service;
import im.zhaojun.onedrive.common.service.AbstractOneDriveService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
/**
* @author zhaojun
*/
@Service
public class OneDriveChinaService extends AbstractOneDriveService {
@Value("${zfile.onedirve-china.clientId}")
private String clientId;
@Value("${zfile.onedirve-china.redirectUri}")
private String redirectUri;
@Value("${zfile.onedirve-china.clientSecret}")
private String clientSecret;
@Value("${zfile.onedirve-china.scope}")
private String scope;
@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;
}
}

View File

@@ -0,0 +1,83 @@
package im.zhaojun.onedrive.china.service;
import im.zhaojun.common.config.GlobalScheduleTask;
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.StorageTypeEnum;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.FileService;
import im.zhaojun.common.service.StorageConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
/**
* @author zhaojun
* @date 2020/1/12 13:53
*/
@Service
@Slf4j
public class OneDriveServiceChinaImpl extends AbstractFileService implements FileService {
@Resource
private GlobalScheduleTask globalScheduleTask;
@Resource
private StorageConfigService storageConfigService;
@Resource
private OneDriveChinaService oneDriveChinaService;
@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();
if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
isInitialized = false;
} else {
globalScheduleTask.refreshOneDriveToken(getStorageTypeEnum());
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@Override
public List<FileItemDTO> fileList(String path) {
return oneDriveChinaService.list(path);
}
@Override
public String getDownloadUrl(String path) {
return null;
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.ONE_DRIVE_CHINA;
}
@Override
public FileItemDTO getFileItem(String path) {
FileItemDTO fileItemDTO ;
try {
fileItemDTO = oneDriveChinaService.getItem(path);
} catch (Exception e) {
throw new NotExistFileException();
}
return fileItemDTO;
}
}

View File

@@ -0,0 +1,62 @@
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.OneDriveChinaService;
import im.zhaojun.onedrive.international.service.OneDriveService;
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
* @date 2020/1/18 17:13
*/
@Configuration
public class OneDriveConfig {
@Resource
private StorageConfigService storageConfigService;
@Resource
@Lazy
private OneDriveService oneDriveService;
@Resource
@Lazy
private OneDriveChinaService oneDriveChinaService;
@Bean
public RestTemplate oneDriveRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
ClientHttpRequestInterceptor interceptor = (httpRequest, bytes, clientHttpRequestExecution) -> {
String host = httpRequest.getURI().getHost();
StorageTypeEnum type;
if (oneDriveChinaService.getGraphEndPoint().contains(host)) {
type = StorageTypeEnum.ONE_DRIVE_CHINA;
} else if (oneDriveService.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;
}
}

View File

@@ -0,0 +1,43 @@
package im.zhaojun.onedrive.common.controller;
import im.zhaojun.onedrive.china.service.OneDriveChinaService;
import im.zhaojun.onedrive.common.model.OneDriveToken;
import im.zhaojun.onedrive.international.service.OneDriveService;
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("/onedirve")
public class OneDriveController {
@Resource
private OneDriveService oneDriveService;
@Resource
private OneDriveChinaService oneDriveChinaService;
@GetMapping("/callback")
public String onedriveCallback(String code, Model model) {
OneDriveToken oneDriveToken = oneDriveService.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 = oneDriveChinaService.getToken(code);
model.addAttribute("accessToken", oneDriveToken.getAccessToken());
model.addAttribute("refreshToken", oneDriveToken.getRefreshToken());
return "callback";
}
}

View File

@@ -0,0 +1,18 @@
package im.zhaojun.onedrive.common.model;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
/**
* @author zhaojun
* @date 2020/1/18 17:28
*/
@Data
public class OneDriveToken {
@JSONField(name = "access_token")
private String accessToken;
@JSONField(name = "refresh_token")
private String refreshToken;
}

View File

@@ -0,0 +1,172 @@
package im.zhaojun.onedrive.common.service;
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.model.enums.StorageTypeEnum;
import im.zhaojun.common.repository.StorageConfigRepository;
import im.zhaojun.common.util.StringUtils;
import im.zhaojun.onedrive.common.model.OneDriveToken;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* @author Zhao Jun
* 2020/1/29 11:54
*/
public abstract class AbstractOneDriveService {
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;
public OneDriveToken getRefreshToken() {
StorageConfig refreshStorageConfig =
storageConfigRepository.findByTypeAndKey(StorageTypeEnum.ONE_DRIVE, 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);
}
public List<FileItemDTO> list(String path) {
List<FileItemDTO> result = new ArrayList<>();
String nextLink = null;
do {
String requestUrl;
if (nextLink != null) {
requestUrl = nextLink;
}else if ("/".equalsIgnoreCase(path)) {
requestUrl = DRIVER_ROOT_URL;
} else {
requestUrl = DRIVER_ITEMS_URL;
}
path = StringUtils.removeLastSeparator(path);
ResponseEntity<String> responseEntity = oneDriveRestTemplate.getForEntity(requestUrl, String.class, getGraphEndPoint(), path);
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;
}
public FileItemDTO getItem(String path) {
String requestUrl;
ResponseEntity<String> responseEntity = oneDriveRestTemplate.getForEntity(DRIVER_ITEM_URL, String.class, path);
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();
}

View File

@@ -1,35 +0,0 @@
// package im.zhaojun.onedrive.config;
//
// import org.nuxeo.onedrive.client.*;
// import org.springframework.context.annotation.Configuration;
//
// @Configuration
// public class OneDriveConfig {
//
//
// public void a () {
// OneDriveAPI api = new OneDriveBasicAPI("YOUR_ACCESS_TOKEN");
//
// OneDriveFolder folder = new OneDriveFolder(api, "FOLDER_ID");
// OneDriveFile file = new OneDriveFile(api, "FILE_ID");
// }
//
// public static void main(String[] args) throws OneDriveAPIException {
// OneDriveBasicAPI api = new OneDriveBasicAPI("EwAgA61DBAAUcSSzoTJJsy+XrnQXgAKO5cj4yc8AAQ0ZknDUY8YnwB9aJv7vA9YjiRAVMnKc+rG11fSXLZRAA8Q/CgJaz+OkRN60vaLDfp6KxbmVlob6kxeD/peOUI2eHtk0055Q2+n057tlyVAvGIFl9dvqkItoAthjmybcSkKBZS5h1meWxQ5IOvzSVrdgCKL0NOtTxfh33ZUDsYjvSid6NOX4Bs+pRjvZhQkvqEfGt8KlOL+JoIowmv2I+u09iDmS60BMwSoeK2K3CCLIXxLaiiPYUMsrNk65j4PWEBwBEfyHb6j3lrM/YvwFLq7Y8KJVjrXjFENC7ruja6Ko/cfTMX90yLkUEckpsZ30E6RJHWEHt7jXtNwndDZVknYDZgAACL5pnk17FJfb8AGGxJL1Y0CnAzgkTM2gw+WkFRRDDNzujuW1LQofwZ119HdeANhPrBZ14x32VaPGL1l0RvtR9LCeAN+EogcV5xhVpmCExitaXQB6OkZ6BnXaxLj5TNvFRNeZq0ZfJ3T08clLA1vXHkZhNKgiFDI8xUbahy4r6QpzgoF+0+dz+MA1NzQCQCsRGieS63OD1BKrzRsNxzls5Z9rKzBT6CpWpiaiOg4mmW0yeino/L9zz9Gf5kAJr813bpNr+rH/E8MPd0pZf+6hv37FaVCM7RN1V7CkkCDnRAxwxEK8pDgZhRjZOw7gKutPOiOoTO9ptjh2Jcrds714HitX2HI3RsRY+yyAOcb8XI27m4daSEGCJCuu/TJwXTE4ul54MWsi8MrcDlZN9DOjckiJIqVI8IbvhM+OUAP4FUIfZJJrIVa8WFwxcsMmjlLTxp/I7+JfdvZjJSk3j1yYvbWFviyoSkpQgw2hIDhZxCg083Z6qS467g5H9Uz3fQc+Ss0K0Mud6RcZTU9RqCcp+h92tUc8+gDxQ2NwJsG5vcmSRwf5KHKvsWjt6yK4OHxCpkLYi31eJZtv2EjQGXX1gYyhc/2wQ+cHPvbgBzIfhXetbZKpSxoowAQO/J1i5oRs90h24kjTd4qJd3qspxk1lhZcEC8IkfZXjNgjEQI=");
//
// OneDriveFolder folder = new OneDriveFolder(api, "PDF");
//
// for (OneDriveItem.Metadata metadata : OneDriveFolder.getRoot(api)) {
// System.out.println(metadata.getName());
// if (metadata.isFile()) {
// }
// }
//
// // for (OneDriveItem.Metadata search : OneDriveFolder.getRoot(api).search("index.html")) {
// // System.out.println(search.getName());
// // }
//
// // OneDriveFile file = new OneDriveFile(api, "key.txt");
// // System.out.println(file);
// }
// }

View File

@@ -1,42 +0,0 @@
package im.zhaojun.onedrive.controller;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@Controller
public class OneDriveController {
@Resource
private RestTemplate restTemplate;
@GetMapping("/onedirve/callback")
@ResponseBody
public String onedriveCallback(String code, HttpServletRequest request) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
headers.setContentType(type);
headers.add("Accept", MediaType.APPLICATION_JSON.toString());
String json = "client_id=04a73532-6c16-4fe4-92e5-f2cd125ed553&redirect_uri=http://localhost:8080/onedirve/callback&client_secret=2gY/t?*Eff6i36TgKTtiG*08/k]@.I4[&code=" + code + "&grant_type=authorization_code";
HttpRequest post = HttpUtil.createPost("https://login.microsoftonline.com/common/oauth2/v2.0/token");
post.body(json, "application/x-www-form-urlencoded");
HttpResponse response = post.execute();
System.out.println(response.body());
return response.body();
}
}

View File

@@ -0,0 +1,54 @@
package im.zhaojun.onedrive.international.service;
import im.zhaojun.onedrive.common.service.AbstractOneDriveService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
/**
* @author zhaojun
*/
@Service
public class OneDriveService extends AbstractOneDriveService {
@Value("${zfile.onedirve.clientId}")
protected String clientId;
@Value("${zfile.onedirve.redirectUri}")
protected String redirectUri;
@Value("${zfile.onedirve.clientSecret}")
protected String clientSecret;
@Value("${zfile.onedirve.scope}")
protected String scope;
@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;
}
}

View File

@@ -0,0 +1,83 @@
package im.zhaojun.onedrive.international.service;
import im.zhaojun.common.config.GlobalScheduleTask;
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.StorageTypeEnum;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.FileService;
import im.zhaojun.common.service.StorageConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
/**
* @author zhaojun
* @date 2020/1/12 13:53
*/
@Service
@Slf4j
public class OneDriveServiceImpl extends AbstractFileService implements FileService {
@Resource
private GlobalScheduleTask globalScheduleTask;
@Resource
private StorageConfigService storageConfigService;
@Resource
private OneDriveService oneDriveService;
@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();
if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
isInitialized = false;
} else {
globalScheduleTask.refreshOneDriveToken(getStorageTypeEnum());
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@Override
public List<FileItemDTO> fileList(String path) {
return oneDriveService.list(path);
}
@Override
public String getDownloadUrl(String path) {
return null;
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.ONE_DRIVE;
}
@Override
public FileItemDTO getFileItem(String path) {
FileItemDTO fileItemDTO ;
try {
fileItemDTO = oneDriveService.getItem(path);
} catch (Exception e) {
throw new NotExistFileException();
}
return fileItemDTO;
}
}

View File

@@ -1,130 +1,55 @@
package im.zhaojun.qiniu.service;
import cn.hutool.core.util.URLUtil;
import com.qiniu.common.Zone;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.model.FileInfo;
import com.qiniu.storage.model.FileListing;
import com.qiniu.util.Auth;
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.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
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 im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
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 QiniuServiceImpl implements FileService {
public class QiniuServiceImpl extends AbstractS3FileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(QiniuServiceImpl.class);
@Value("${zfile.cache.timeout}")
private Long timeout;
@Resource
private StorageConfigService storageConfigService;
private static final String BUCKET_NAME_KEY = "bucket-name";
private static final String ACCESS_KEY = "accessKey";
private static final String SECRET_KEY = "secretKey";
private static final String DOMAIN_KEY = "domain";
private BucketManager bucketManager;
private Auth auth;
private String bucketName;
private String domain;
private boolean isPrivate;
private boolean isInitialized;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.QINIU);
String accessKey = stringStorageConfigMap.get(ACCESS_KEY).getValue();
String secretKey = stringStorageConfigMap.get(SECRET_KEY).getValue();
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.QINIU);
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
Configuration cfg = new Configuration(Zone.autoZone());
auth = Auth.create(accessKey, secretKey);
bucketManager = new BucketManager(auth, cfg);
bucketName = stringStorageConfigMap.get(BUCKET_NAME_KEY).getValue();
domain = stringStorageConfigMap.get(DOMAIN_KEY).getValue();
bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
isPrivate = bucketManager.getBucketInfo(bucketName).getPrivate() == 1;
isInitialized = true;
} catch (Exception e) {
log.debug(StorageTypeEnum.QINIU.getDescription() + "初始化异常, 已跳过");
}
}
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();
@Override
public List<FileItemDTO> fileList(String path) throws Exception {
path = StringUtils.removeFirstSeparator(path);
List<FileItemDTO> fileItemList = new ArrayList<>();
// 每次迭代的长度限制, 最大1000, 推荐值 1000
int limit = 1000;
// 指定目录分隔符, 列出所有公共前缀(模拟列出目录效果). 缺省值为空字符串
String delimiter = "/";
// 列举空间文件列表
FileListing fileListing = bucketManager.listFilesV2(bucketName, path, "", limit, delimiter);
for (FileInfo item : fileListing.items) {
String fileKey = item.key;
String fileName = fileKey.substring(path.length());
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setName(fileName);
fileItemDTO.setSize(item.fsize);
fileItemDTO.setTime(new Date(item.putTime / 1000));
fileItemDTO.setType(FileTypeEnum.FILE);
fileItemDTO.setPath(path);
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(path, fileItemDTO.getName())));
fileItemList.add(fileItemDTO);
}
String[] commonPrefixes = fileListing.commonPrefixes;
for (String commonPrefix : commonPrefixes) {
if ("/".equals(commonPrefix)) {
continue;
isInitialized = testConnection();
}
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setName(commonPrefix.substring(0, commonPrefix.length() - 1));
fileItemDTO.setType(FileTypeEnum.FOLDER);
fileItemDTO.setPath(path);
fileItemList.add(fileItemDTO);
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
return fileItemList;
}
@Override
public String getDownloadUrl(String path) {
String url = URLUtil.complateUrl(domain, path);
if (isPrivate) {
url = auth.privateDownloadUrl(url, timeout);
}
return url;
}
@Override
@@ -132,10 +57,4 @@ public class QiniuServiceImpl implements FileService {
return StorageTypeEnum.QINIU;
}
@Override
public boolean getIsInitialized() {
return isInitialized;
}
}

View File

@@ -1,128 +1,61 @@
package im.zhaojun.tencent;
import cn.hutool.core.util.URLUtil;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.model.COSObjectSummary;
import com.qcloud.cos.model.ListObjectsRequest;
import com.qcloud.cos.model.ObjectListing;
import com.qcloud.cos.region.Region;
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.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
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 im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.net.URL;
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 TencentServiceImpl implements FileService {
public class TencentServiceImpl extends AbstractS3FileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(TencentServiceImpl.class);
@Resource
private StorageConfigService storageConfigService;
private static final String BUCKET_NAME_KEY = "bucket-name";
private static final String SECRET_ID_KEY = "secretId";
private static final String SECRET_KEY = "secretKey";
private static final String DOMAIN_KEY = "domain";
private static final String ENDPOINT_KEY = "endPoint";
@Value("${zfile.cache.timeout}")
private Long timeout;
private String bucketName;
private String domain;
private COSClient cosClient;
private boolean isInitialized;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.TENCENT);
String secretId = stringStorageConfigMap.get(SECRET_ID_KEY).getValue();
String secretKey = stringStorageConfigMap.get(SECRET_KEY).getValue();
String regionName = stringStorageConfigMap.get(ENDPOINT_KEY).getValue();
bucketName = stringStorageConfigMap.get(BUCKET_NAME_KEY).getValue();
domain = stringStorageConfigMap.get(DOMAIN_KEY).getValue();
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.TENCENT);
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();
COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
Region region = new Region(regionName);
ClientConfig clientConfig = new ClientConfig(region);
cosClient = new COSClient(cred, clientConfig);
isInitialized = true;
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(StorageTypeEnum.TENCENT.getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@Override
public List<FileItemDTO> fileList(String path) {
path = StringUtils.removeFirstSeparator(path);
List<FileItemDTO> fileItemList = new ArrayList<>();
ObjectListing objectListing = cosClient.listObjects(new ListObjectsRequest().withBucketName(bucketName).withDelimiter("/").withPrefix(path));
for (COSObjectSummary s : objectListing.getObjectSummaries()) {
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setName(s.getKey().substring(path.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();
fileItemDTO.setName(commonPrefix.substring(path.length(), commonPrefix.length() - 1));
fileItemDTO.setType(FileTypeEnum.FOLDER);
fileItemDTO.setPath(path);
fileItemList.add(fileItemDTO);
}
return fileItemList;
}
@Override
public String getDownloadUrl(String path) {
Date expirationDate = new Date(System.currentTimeMillis() + timeout * 1000);
URL url = cosClient.generatePresignedUrl(bucketName, path, expirationDate);
return URLUtil.complateUrl(domain, url.getFile());
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.TENCENT;
}
@Override
public boolean getIsInitialized() {
return isInitialized;
}
}

View File

@@ -1,16 +1,19 @@
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@@ -18,42 +21,47 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @author zhaojun
*/
@Service
public class UpYunServiceImpl implements FileService {
@Slf4j
public class UpYunServiceImpl extends AbstractFileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(UpYunServiceImpl.class);
private static final String END_MARK = "g2gCZAAEbmV4dGQAA2VvZg";
@Resource
private StorageConfigService storageConfigService;
private static final String BUCKET_NAME_KEY = "bucket-name";
private static final String USERNAME_KEY = "username";
private static final String PASSWORD_KEY = "password";
private static final String DOMAIN_KEY = "domain";
private String domain;
private UpYun upYun;
private boolean isInitialized;
private String basePath;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.UPYUN);
String bucketName = stringStorageConfigMap.get(BUCKET_NAME_KEY).getValue();
String username = stringStorageConfigMap.get(USERNAME_KEY).getValue();
String password = stringStorageConfigMap.get(PASSWORD_KEY).getValue();
domain = stringStorageConfigMap.get(DOMAIN_KEY).getValue();
upYun = new UpYun(bucketName, username, password);
isInitialized = true;
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(StorageTypeEnum.UPYUN.getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@@ -66,7 +74,7 @@ public class UpYunServiceImpl implements FileService {
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(path), hashMap);
UpYun.FolderItemIter folderItemIter = upYun.readDirIter(URLUtil.encode(basePath + path), hashMap);
nextMark = folderItemIter.iter;
ArrayList<UpYun.FolderItem> folderItems = folderItemIter.files;
if (folderItems != null) {
@@ -81,12 +89,12 @@ public class UpYunServiceImpl implements FileService {
fileItemDTO.setType(FileTypeEnum.FOLDER);
} else {
fileItemDTO.setType(FileTypeEnum.FILE);
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(path, fileItemDTO.getName())));
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(basePath + path, fileItemDTO.getName())));
}
fileItemList.add(fileItemDTO);
}
}
} while (!"g2gCZAAEbmV4dGQAA2VvZg".equals(nextMark));
} while (!END_MARK.equals(nextMark));
return fileItemList;
}
@@ -101,11 +109,23 @@ public class UpYunServiceImpl implements FileService {
return StorageTypeEnum.UPYUN;
}
@Override
public boolean getIsInitialized() {
return isInitialized;
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();
}
}

View File

@@ -4,5 +4,38 @@
"name": "zfile.cache.timeout",
"type": "java.lang.Long",
"description": "目录缓存过期时间 和 下载地址过期时间. 单位为秒."
},
{
"name": "zfile.constant.header",
"type": "java.lang.String",
"defaultValue": "header.md",
"description": "头部文件 文件名."
},
{
"name": "zfile.constant.password",
"type": "java.lang.String",
"defaultValue": "password.txt",
"description": "密码文件 文件名."
},
{
"name": "zfile.onedirve.clientId",
"type": "java.lang.String",
"description": "OneDrive ClientId."
},
{
"name": "zfile.onedirve.clientSecret",
"type": "java.lang.String",
"description": "OneDrive ClientSecret."
},
{
"name": "zfile.onedirve.redirectUri",
"type": "java.lang.String",
"description": "OneDrive 认证重定向地址."
},
{
"name": "zfile.onedirve.scope",
"type": "java.lang.String",
"description": "OneDrive 认证权限."
}
] }
]
}

View File

@@ -1,20 +1,13 @@
logging:
level:
im:
zhaojun:
common: debug
path: ${user.home}/.zfile/logs
server:
port: 8080
servlet:
context-path: ''
tomcat:
max-threads: 20
compression:
enabled: true
spring:
cache:
type: caffeine
# type: redis
datasource:
# 初始化数据导入
data: classpath*:db/data.sql
@@ -23,13 +16,13 @@ spring:
# h2 内存数据库 配置
driver-class-name: org.h2.Driver
url: jdbc:h2:~/.zfile/db/zfile-demo
username: zfile-demo
url: jdbc:h2:~/.zfile/db/zfile
username: zfile
password: 123456
# MySQL 配置
# driver-class-name: com.mysql.jdbc.Driver
# url: jdbc:mysql://127.0.0.1:3306/zfile-demo?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
# url: jdbc:mysql://127.0.0.1:3306/zfile?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
# username: root
# password: 123456
jackson:
@@ -41,13 +34,33 @@ spring:
properties:
hibernate:
format_sql: false
show-sql: true
show-sql: false
resources:
chain:
gzipped: true
redis:
host: 127.0.0.1
password: 12345
profiles:
active: prod
zfile:
cache:
timeout: 300
timeout: 300
constant:
header: header.md
password: password.txt
onedirve:
clientId: 09939809-c617-43c8-a220-a93c1513c5d4
clientSecret: _l:zI-_yrW75lV8M61K@z.I2K@B/On6Q
redirectUri: https://zfile.jun6.net/onedirve/callback
scope: offline_access User.Read Files.ReadWrite.All
onedirve-china:
clientId: 4a72d927-1907-488d-9eb2-1b465c53c1c5
clientSecret: Y9CEA=82da5n-y_]KAWAgLH3?R9xf7Uw
redirectUri: https://zfile.jun6.net/onedirve/china-callback
scope: offline_access User.Read Files.ReadWrite.All
jetcache:
statIntervalMinutes: 0
areaInCacheName: false
local:
default:
type: caffeine
keyConvertor: fastjson

View File

@@ -1,43 +1,58 @@
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `VALUE`) VALUES (1, 'siteName', '站点名称', null);
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `VALUE`) VALUES (2, 'infoEnable', '是否开启信息框', null);
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `VALUE`) VALUES (3, 'searchEnable', '是否开启搜索', null);
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `VALUE`) VALUES (4, 'searchIgnoreCase', '是否搜索时忽略大小写', null);
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `VALUE`) VALUES (5, 'storageStrategy', '当前启用存储引擎', null);
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `VALUE`) VALUES (6, 'username', '管理员账号', null);
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `VALUE`) VALUES (7, 'password', '管理员密码', null);
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `VALUE`) VALUES (8, 'domain', '站点域名', null);
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (1, 'siteName', '站点名称');
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (2, 'infoEnable', '是否开启信息框');
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (3, 'searchEnable', '是否开启搜索');
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (4, 'searchIgnoreCase', '是否搜索时忽略大小写');
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (5, 'storageStrategy', '当前启用存储引擎');
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (6, 'username', '管理员账号');
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (7, 'password', '管理员密码');
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (8, 'domain', '站点域名');
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (9, 'enableCache', '是否开启缓存');
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (10, 'searchContainEncryptedFile', '搜索包含加密文件');
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (11, 'customCss', '自定义 CSS');
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (12, 'customJs', '自定义 JS (可用于统计代码)');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (1, 'bucket-name', '云存储服务名称', 'upyun', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (2, 'username', '操作员名称', 'upyun', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (3, 'password', '操作员密码', 'upyun', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (4, 'domain', '加速域名', 'upyun', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (5, 'accessKey', 'AccessKey', 'qiniu', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (6, 'secretKey', 'SecretKey', 'qiniu', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (7, 'bucket-name', '存储空间名称', 'qiniu', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (8, 'domain', '加速域名', 'qiniu', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (9, 'accessKey', 'AccessKey', 'huawei', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (10, 'secretKey', 'SecretKey', 'huawei', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (11, 'bucket-name', '云存储服务名称', 'huawei', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (12, 'domain', '加速域名', 'huawei', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (13, 'endPoint', '区域', 'huawei', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (14, 'accessKey', 'AccessKey', 'aliyun', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (15, 'secretKey', 'SecretKey', 'aliyun', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (16, 'bucket-name', 'Bucket 名', 'aliyun', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (17, 'domain', 'Bucket 域名 / CDN 加速域名', 'aliyun', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (18, 'endPoint', '区域', 'aliyun', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (19, 'filePath', '文件路径', 'local', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (20, 'host', '域名或IP', 'ftp', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (21, 'port', '端口', 'ftp', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (22, 'username', '用户名', 'ftp', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (23, 'password', '密码', 'ftp', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (24, 'domain', '域名', 'ftp', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (25, 'secretId', 'SecretId', 'tencent', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (26, 'secretKey', 'SecretKey', 'tencent', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (27, 'bucket-name', '云存储服务名称', 'tencent', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (28, 'domain', '加速域名', 'tencent', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (29, 'endPoint', '区域', 'tencent', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (30, 'accessKey', 'SecretId', 'minio', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (31, 'secretKey', 'SecretKey', 'minio', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (32, 'endPoint', '服务地址', 'minio', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (33, 'bucket-name', '存储空间名称', 'minio', null);
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (1, 'bucket-name', '云存储服务名称', 'upyun');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (2, 'username', '操作员名称', 'upyun');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (3, 'password', '操作员密码', 'upyun');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (4, 'domain', '加速域名', 'upyun');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (5, 'accessKey', 'AccessKey', 'qiniu');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (6, 'secretKey', 'SecretKey', 'qiniu');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (7, 'bucket-name', '存储空间名称', 'qiniu');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (8, 'domain', '加速域名', 'qiniu');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (9, 'accessKey', 'AccessKey', 'huawei');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (10, 'secretKey', 'SecretKey', 'huawei');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (11, 'bucket-name', '云存储服务名称', 'huawei');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (12, 'domain', '加速域名', 'huawei');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (13, 'endPoint', '区域', 'huawei');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (14, 'accessKey', 'AccessKey', 'aliyun');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (15, 'secretKey', 'SecretKey', 'aliyun');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (16, 'bucket-name', 'Bucket 名称', 'aliyun');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (17, 'domain', 'Bucket 域名 / CDN 加速域', 'aliyun');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (18, 'endPoint', '区域', 'aliyun');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (19, 'filePath', '文件路径', 'local');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (20, 'host', '域名或IP', 'ftp');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (21, 'port', '端口', 'ftp');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (22, 'username', '用户名', 'ftp');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (23, 'password', '密码', 'ftp');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (24, 'domain', '域名', 'ftp');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (25, 'secretId', 'SecretId', 'tencent');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (26, 'secretKey', 'SecretKey', 'tencent');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (27, 'bucket-name', '云存储服务名称', 'tencent');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (28, 'domain', '加速域名', 'tencent');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (29, 'endPoint', '区域', 'tencent');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (30, 'accessKey', 'AccessKey', 'minio');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (31, 'secretKey', 'SecretKey', 'minio');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (32, 'endPoint', '服务地址', 'minio');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (33, 'bucket-name', '存储空间名称', 'minio');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (34, 'base-path', '基路径', 'upyun');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (35, 'base-path', '基路径', 'minio');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (36, 'base-path', '基路径', 'tencent');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (37, 'endPoint', '区域', 'qiniu');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (38, 'base-path', '基路径', 'qiniu');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (39, 'base-path', '基路径', 'aliyun');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (40, 'base-path', '基路径', 'huawei');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (41, 'ftp', '基路径', 'ftp');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (42, 'accessToken', '访问令牌', 'onedrive');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (43, 'refreshToken', '刷新令牌', 'onedrive');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (44, 'accessToken', '访问令牌', 'onedrive-china');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (45, 'refreshToken', '刷新令牌', 'onedrive-china');

View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
scan当此属性设置为true时配置文件如果发生改变将会被重新加载默认值为true。
scanPeriod设置监测配置文件是否有修改的时间间隔如果没有给出时间单位默认单位是毫秒当scan为true时此属性生效。默认的时间间隔为1分钟。
debug当此属性设置为true时将打印出logback内部日志信息实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 定义日志的根目录 -->
<property name="LOG_HOME" value="${user.home}/.zfile/logs"/>
<!-- 定义日志文件名称 -->
<property name="appName" value="zfile"/>
<!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<springProfile name="prod">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</springProfile>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}</pattern>
</layout>
</appender>
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
<appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 指定日志文件的名称 -->
<file>${LOG_HOME}/${appName}.log</file>
<!--
当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
TimeBasedRollingPolicy 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--
滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动
%i当文件大小超过maxFileSize时按照i进行文件滚动
-->
<fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<!--
可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
且maxHistory是365则只保存最近365天的文件删除之前的旧文件。注意删除旧文件是
那些为了归档而创建的目录也会被删除。
-->
<MaxHistory>365</MaxHistory>
<!--
当日志文件超过maxFileSize指定的大小是根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的必须配置timeBasedFileNamingAndTriggeringPolicy
-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>5MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 日志输出格式: -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}</pattern>
</layout>
</appender>
<!--
logger主要用于存放日志对象也可以定义日志类型、级别
name表示匹配的logger类型前缀也就是包的前半部分
level要记录的日志级别包括 TRACE < DEBUG < INFO < WARN < ERROR
additivity作用在于children-logger是否使用 rootLogger配置的appender进行输出
false表示只用当前logger的appender-ref
true 表示当前logger的appender-ref和rootLogger的appender-ref都有效
-->
<!-- jetCache logger -->
<logger name="com.alicp" additivity="false" level="debug"/>
<!-- hibernate logger -->
<logger name="org.hibernate.SQL" additivity="false" level="debug"/>
<springProfile name="dev">
<logger name="im.zhaojun" additivity="true" level="debug"/>
</springProfile>
<springProfile name="prod">
<logger name="im.zhaojun" additivity="true" level="debug"/>
</springProfile>
<!--
root与logger是父子关系没有特别定义则默认为root任何一个类只会和一个logger对应
要么是定义的logger要么是root判断的关键在于找到这个logger然后判断这个logger的appender和level。
-->
<root level="info">
<appender-ref ref="stdout"/>
<appender-ref ref="appLogAppender"/>
</root>
</configuration>

View File

@@ -0,0 +1 @@
.el-menu[data-v-0d38e212],.el-row[data-v-0d38e212]{height:100vh}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
.box-card[data-v-7e472c9a]{padding-top:30px;padding-right:30px;margin:15vh auto;height:65vh;overflow-y:auto}.el-select[data-v-7e472c9a]{width:100%}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -1 +1 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><script src=//at.alicdn.com/t/font_1428963_kk67lnmooll.js></script><title></title><link href=/css/app.16ea04c8.css rel=preload as=style><link href=/css/chunk-vendors.7d475efd.css rel=preload as=style><link href=/js/app.fa7d2b78.js rel=preload as=script><link href=/js/chunk-vendors.e68c4b36.js rel=preload as=script><link href=/css/chunk-vendors.7d475efd.css rel=stylesheet><link href=/css/app.16ea04c8.css rel=stylesheet></head><body><noscript><strong>We're sorry but zfile doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.e68c4b36.js></script><script src=/js/app.fa7d2b78.js></script></body></html>
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title></title><link href=/css/adminIndex.25eab1c6.css rel=prefetch><link href=/css/install.b4e8b552.css rel=prefetch><link href=/js/adminIndex.456ac23a.js rel=prefetch><link href=/js/dplayer.acc587f7.js rel=prefetch><link href=/js/install.f6845f54.js rel=prefetch><link href=/css/app.22664175.css rel=preload as=style><link href=/css/chunk-vendors.39edcc5c.css rel=preload as=style><link href=/js/app.ce8716ec.js rel=preload as=script><link href=/js/chunk-vendors.065da641.js rel=preload as=script><link href=/css/chunk-vendors.39edcc5c.css rel=stylesheet><link href=/css/app.22664175.css rel=stylesheet></head><body><noscript><strong>We're sorry but zfile doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.065da641.js></script><script src=/js/app.ce8716ec.js></script></body></html>

View File

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["adminIndex"],{7869:function(t,e,a){},adf4:function(t,e,a){"use strict";a.r(e);var i=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("el-row",[a("el-col",{attrs:{span:3}},[a("el-menu",{staticClass:"el-menu-vertical-demo",attrs:{"default-active":"/admin"!==this.$route.path?this.$route.path:"/admin/site",router:!0}},[a("el-menu-item",{attrs:{index:"/admin/site"}},[a("i",{staticClass:"el-icon-setting"}),a("span",{attrs:{slot:"title"},slot:"title"},[t._v("站点设置")])]),a("el-menu-item",{attrs:{index:"/admin/storage"}},[a("i",{staticClass:"el-icon-s-operation"}),a("span",{attrs:{slot:"title"},slot:"title"},[t._v("存储策略")])]),a("el-menu-item",{attrs:{index:"/admin/password"}},[a("i",{staticClass:"el-icon-key"}),a("span",{attrs:{slot:"title"},slot:"title"},[t._v("修改密码")])]),a("el-menu-item",{attrs:{index:"/admin/cache"}},[a("i",{staticClass:"el-icon-collection"}),a("span",{attrs:{slot:"title"},slot:"title"},[t._v("缓存管理")])])],1)],1),a("el-col",{attrs:{span:16}},[a("keep-alive",{attrs:{exclude:"CacheManager,SiteSetting"}},[a("router-view")],1)],1)],1)},s=[],n={name:"Index",data:function(){return{active:"/admin/storage"}}},l=n,o=(a("f2cb"),a("2877")),r=Object(o["a"])(l,i,s,!1,null,"0d38e212",null);e["default"]=r.exports},f2cb:function(t,e,a){"use strict";var i=a("7869"),s=a.n(i);s.a}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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