Compare commits

..

131 Commits
0.6 ... 2.2

Author SHA1 Message Date
zhaojun1998
3249266cd1 🔖 发布 2.2 版 2020-03-08 11:48:48 +08:00
zhaojun1998
1a6235df73 🔨 重构代码, 去除无效引用。 2020-03-08 11:46:31 +08:00
zhaojun1998
430aee2b7f 🔨 重构代码, 去除无效引用。 2020-03-08 11:43:42 +08:00
zhaojun1998
de2f7e4b80 🔨 重构代码, 优化目录结构 2020-03-08 11:11:12 +08:00
zhaojun1998
6dfcc409ac 更新页面 2020-03-08 11:07:01 +08:00
zhaojun1998
78f795e1cb 🔖 发布 2.1 版 2020-03-07 18:39:33 +08:00
zhaojun1998
c69ee0f356 🐛 修复 S3 协议对象存储, 在私有空间下, 无法下载的 BUG. 2020-03-07 13:10:42 +08:00
zhaojun1998
39475de789 🐛 修复 S3 协议对象存储, 在私有空间下, 无法下载的 BUG. 2020-03-07 13:10:21 +08:00
zhaojun1998
bd71712765 🐛 修复无法刷新单个缓存的 BUG 2020-03-07 13:08:42 +08:00
赵俊
8698686a47 Update application.yml 2020-03-01 14:04:14 +08:00
zhaojun1998
762c67ee37 更新文档 2020-02-29 16:04:19 +08:00
zhaojun1998
7bf3a29c17 🔖 发布 2.0 版 2020-02-29 15:48:57 +08:00
zhaojun1998
6ee5002f0c 🔒 修复任意用户名均可登陆后台的安全问题. 2020-02-29 15:47:24 +08:00
zhaojun1998
fadc64add4 💄 更新页面, 修复滚动分页 BUG. 2020-02-29 15:46:56 +08:00
zhaojun1998
234f43846f 优化分页功能 2020-02-29 15:45:34 +08:00
zhaojun1998
67e42d9753 🐛 修复 OneDrive 获取文档区或文件夹密码时, 链接超时导致异常的 BUG. 2020-02-29 15:45:11 +08:00
zhaojun1998
04f94b4bf5 🏗️ 缓存架构调整, 增强稳定性 2020-02-29 15:43:56 +08:00
zhaojun1998
d29c498457 更新配置自描述文件 2020-02-27 23:01:38 +08:00
zhaojun1998
5aa45b44b2 🔖 发布 1.9 版 2020-02-26 21:18:55 +08:00
zhaojun1998
8273a645f2 📝 更新文档 2020-02-26 21:16:49 +08:00
zhaojun1998
46f292cc9b 🔧 更新配置文件, 增加是否开启缓存自动刷新控制 2020-02-26 21:15:45 +08:00
zhaojun1998
261d48059e 增强 FTP 功能, 提高稳定性 2020-02-26 21:14:26 +08:00
zhaojun1998
79f931c51b 增强 OneDrive 调用, 如调用失败, 尝试重新获取 token 后, 再次请求. 2020-02-26 21:14:07 +08:00
zhaojun1998
399e961a65 🏗️ 缓存架构调整, 改为自实现缓存, 移除第三方依赖. 2020-02-26 21:13:08 +08:00
zhaojun1998
3e61d7d146 增加 UFILE 支持 2020-02-26 21:09:42 +08:00
zhaojun1998
ace95d9071 💄 更新页面 2020-02-26 21:09:02 +08:00
zhaojun1998
60d2a2b986 添加 github issue template 2020-02-23 12:07:16 +08:00
zhaojun1998
69d5661e06 添加 github issue template 2020-02-23 12:06:28 +08:00
zhaojun1998
01d11dfc23 移除无用依赖 2020-02-22 15:23:39 +08:00
zhaojun1998
d35e3ecd93 🔖 发布 1.8 版 2020-02-22 15:21:39 +08:00
zhaojun1998
9e5a3e5385 💄 更新页面 2020-02-22 15:18:15 +08:00
zhaojun1998
b62163b4e8 ⬆️ 升级 hutool 依赖版本 2020-02-22 15:16:39 +08:00
zhaojun1998
595a00f067 🐛 修复 FTP 加载 BUG 2020-02-22 15:16:11 +08:00
zhaojun1998
a759d9fe44 增加系统监控及日志下载功能 2020-02-22 12:53:35 +08:00
zhaojun1998
7a24fd10e0 ✏️ 修复拼写错误 2020-02-22 09:35:51 +08:00
zhaojun1998
7e04a817d7 🔖 发布 1.7 版 2020-02-19 21:45:48 +08:00
zhaojun1998
791967f45e 💄 更新页面 2020-02-19 21:43:17 +08:00
zhaojun1998
d789436a16 ✏️ 修复拼写错误 2020-02-19 21:23:09 +08:00
zhaojun1998
96ab8ff7dd 🔖 发布 1.6 版 2020-02-18 21:10:25 +08:00
zhaojun1998
8809aca170 📝 更新页面 2020-02-18 21:08:53 +08:00
zhaojun1998
97106383b6 🐛 修复使用本地存储时, 文件名中包含 + 引发的无法下载的 BUG. 2020-02-18 20:10:20 +08:00
zhaojun1998
208da95234 🐛 修复不同操作系统, 可能出现的编码问题, 导致导入的数据库文件乱码. 2020-02-18 20:09:32 +08:00
zhaojun1998
d4c843f5f5 🐛 修复 OneDrive 文件夹中包含 + 号, 且文件夹中的内容大于 200 个时, 请求出错的 BUG. 2020-02-18 20:02:37 +08:00
zhaojun1998
d273fc9f12 📝 更新文档 2020-02-15 18:39:56 +08:00
zhaojun1998
0b4a38218c 🔖 发布 1.5 版 2020-02-15 18:25:41 +08:00
zhaojun1998
2f57c5b5cc 新增功能: 自定义表格大小, 显示操作按钮, 显示文档, 公告信息 2020-02-15 18:22:37 +08:00
zhaojun1998
610f68295f 📝 更新页面 2020-02-15 18:22:01 +08:00
zhaojun1998
39ced8eb84 📝 更新页面 2020-02-15 18:05:43 +08:00
zhaojun1998
368b7a1df2 优化排序方式为自然排序 2020-02-14 20:22:22 +08:00
zhaojun1998
8e2107cd46 📝 更新开发计划 2020-02-11 18:27:34 +08:00
zhaojun1998
fa32a33371 📝 更新页面 2020-02-09 21:49:27 +08:00
zhaojun1998
59116a9414 🔖 发布 1.4.1 版 2020-02-09 21:47:27 +08:00
zhaojun1998
b2c732a389 🐛 修复 URL 中包含特殊字符, 返回 400 错误的 BUG. 2020-02-09 21:35:25 +08:00
zhaojun1998
0e1ffef92b 🔖 发布 1.4 版 2020-02-09 18:06:50 +08:00
zhaojun1998
a5b19d3577 新增获取直链下载功能. 2020-02-09 18:06:42 +08:00
zhaojun1998
185c84dd79 新增获取直链下载功能. 2020-02-09 18:05:56 +08:00
zhaojun1998
d554dd298c 优化 OneDrive 相关代码结构. 2020-02-09 18:05:01 +08:00
zhaojun1998
aa6ecf0aaa 优化 OneDrive 相关代码结构. 2020-02-09 18:04:33 +08:00
zhaojun1998
83692718e3 📝 更新页面 2020-02-09 18:01:57 +08:00
zhaojun1998
030bd95941 🔖 发布 1.3 版 2020-02-08 17:47:37 +08:00
zhaojun1998
8722d11ac3 📝 更新页面 2020-02-08 17:46:33 +08:00
zhaojun1998
0131ff02c0 🐛 修复头部文件和密码文件, 在开启缓存的状态下, 仅第一次生效的 BUG. 2020-02-08 17:43:02 +08:00
zhaojun1998
2d115bf1c6 更改本地存储, 文件下载时, 文件不存在的响应状态码为 404. 2020-02-08 17:14:01 +08:00
zhaojun1998
946113216d 🔖 发布 1.2.1 版 2020-02-03 20:57:07 +08:00
zhaojun1998
77b05c6dac 🐛 修复 OneDrive 的 Token 某些情况下, 可能超出 2000 字符的 BUG 2020-02-03 20:52:49 +08:00
zhaojun1998
07c9fca210 🔖 发布 1.2 版 2020-02-01 21:54:32 +08:00
zhaojun1998
27cf61693a 更新前端页面, 修复搜索模式下无法进入文件夹和滚动加载的 BUG 2020-02-01 21:54:20 +08:00
zhaojun1998
37e1aa1fec 🐛 修复 OneDrive 文件夹包含特殊符号时, 获取下一页数据 URL 中重复进行了 URL 编码的 BUG. 2020-02-01 21:51:15 +08:00
zhaojun1998
31b54a3c05 调整 OneDrive Token 自动刷新时间为 15 分钟. 2020-02-01 21:50:08 +08:00
zhaojun1998
589f07c103 🔖 发布 1.1 版 2020-01-31 11:52:27 +08:00
zhaojun1998
fe8b60d873 调整日志级别 2020-01-31 11:52:21 +08:00
zhaojun1998
1734619eac 📝 更新页面 2020-01-31 11:50:55 +08:00
zhaojun1998
f5724dc9ab OneDrive 基础路径支持 2020-01-31 11:21:36 +08:00
zhaojun1998
f7bb147b71 📝 更新文档 2020-01-30 19:31:50 +08:00
zhaojun1998
47fc1bc2df 🔖 发布 1.0 版 2020-01-30 18:06:09 +08:00
zhaojun1998
45172f69ba 📝 更新文档 2020-01-30 18:05:56 +08:00
zhaojun1998
9566b138ff 📝 更新页面 2020-01-30 17:58:26 +08:00
zhaojun1998
e15b6c2242 更新配置文件, 增加 h2 console, 便与调试. 2020-01-30 16:52:20 +08:00
zhaojun1998
acc41511e0 S3 协议新增是否为私有空间支持 2020-01-30 16:51:05 +08:00
zhaojun1998
b882b87405 数据库文件初始化更新, 新增 S3 通用协议支持 2020-01-30 16:50:37 +08:00
zhaojun1998
518b5170ae 优化代码 2020-01-30 16:48:37 +08:00
zhaojun1998
8bfac6d9ac 新增 S3 协议通用支持 2020-01-30 16:47:58 +08:00
zhaojun1998
3ffdb4f1b2 清洁代码 2020-01-30 16:47:03 +08:00
zhaojun1998
47509450a0 📝 更新文档, 修改 Debian/Ubuntu 安装命令. 2020-01-29 18:47:38 +08:00
zhaojun1998
812fd18aac 🐛 修复 OneDrive 世纪互联版自动刷新 REFRESH_TOKEN 失败的 BUG 2020-01-29 16:24:00 +08:00
zhaojun1998
77a13cf8ad 📝 更新文档, 新增预览图片 2020-01-29 13:56:20 +08:00
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
157 changed files with 3785 additions and 1436 deletions

48
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,48 @@
---
name: BUG 反馈
about: 事情不像预期的那样工作吗?
title: ''
labels: 'bug'
assignees: ''
---
<!--
你好!感谢你正在考虑为 ZFile 提交一个 bug。请花一点点时间尽量详细地回答以下基础问题。
谢谢!
-->
<!--
请确认你已经做了下面这些事情,若 bug 还是未解决,那么请尽可详细地描述你的问题。
- 我已经安装了最新版的 ZFile
- 我已经阅读了 ZFile 的文档http://docs.zhaojun.im/zfile
- 我已经搜索了已有的 Issues 列表中有关的信息
- 我已经清理过浏览器缓存并重试
-->
## 我的环境
<!--
请登录到管理后台,点击左侧系统监控, 复制或截图此页内容.
-->
## 错误日志
<!--
请登录到管理后台,点击左侧系统监控, 点击右上角诊断日志下载, 然后上传到此 Issue 中.
-->
## 期望行为
<!--
你期望会发生什么?
-->
## 当前行为
<!--
描述 bug 细节,确认出现此问题的复现步骤,例如点击了哪里,发生了什么情况?
你可以粘贴截图或附件。
-->

View File

@@ -0,0 +1,34 @@
---
name: 功能建议
about: 想让我们为 ZFile 增加什么功能吗?
title: 'feat: '
labels: 'Feature Request'
assignees: ''
---
<!--
你好!感谢你愿意考虑希望 ZFile 增加某个新功能。请花一点点时间尽量详细地回答以下基础问题。
谢谢!
-->
## 概述
<!--
对这个新功能的一段描述
-->
## 动机
<!--
为什么你希望在 ZFile 中使用这个功能?
-->
## 详细解释
<!--
详细描述这个新功能。
如果这是一个小功能,你可以忽略这部分。
-->

14
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,14 @@
---
name: Question
about: 对 ZFile 有任何问题吗?
title: ''
labels: 'question'
assignees: ''
---
<!--
如果你有任何问题也可以通过此渠道来向我们反馈。
谢谢!
-->

164
API.md Normal file
View File

@@ -0,0 +1,164 @@
## API 标准
所有 API 均返回 `msg`, `code`, `data` 三个属性.
| code | 描述 |
| :---: | :------------: |
| 0 | 请求成功 |
| -1 | 请求失败 |
| -2 | 文件夹需要密码 |
`code == 0` 时, `data` 中为请求所需数据.
`code != 0` 时, 应当将 `msg` 中的属性作为参考值.
## 获取文件列表
### 请求 URL
`/api/list` `GET`
### 参数
| 参数名 | 描述 | 是否必填 | 参考值 |
| :------: | :--------: | :------: | :--------------------------: |
| path | 路径 | 是 | `/`, `/文件夹名称` |
| password | 文件夹密码 | 否 | 当文件夹需要密码时, |
| page | 页数 | 否 | 默认取第一页, 每页固定 30 条 |
### 响应
```json
{
"msg": "操作成功",
"code": 0,
"data": [
{
"name": "密码文件夹",
"time": "2020-01-28 13:17",
"size": 4096,
"type": "FOLDER",
"path": "/",
"url": null
},
{
"name": "新建 文本文档.txt",
"time": "2020-01-28 13:16",
"size": 3,
"type": "FILE",
"path": "/",
"url": "http://127.0.0.1:8080/file/新建 文本文档.txt"
}
]
}
```
## 搜索
### 请求 URL
`/api/search` `GET`
### 参数
| 参数名 | 描述 | 是否必填 | 参考值 |
| :----: | :----: | :------: | :--------------------------: |
| name | 搜索值 | 是 | 模糊匹配 |
| page | 页数 | 否 | 默认取第一页, 每页固定 30 条 |
### 响应
```json
{
"msg": "操作成功",
"code": 0,
"data": [
{
"name": "密码文件夹",
"time": "2020-01-28 13:17",
"size": 4096,
"type": "FOLDER",
"path": "/",
"url": null
},
{
"name": "新建 文本文档.txt",
"time": "2020-01-28 13:16",
"size": 3,
"type": "FILE",
"path": "/",
"url": "http://127.0.0.1:8080/file/新建 文本文档.txt"
}
]
}
```
## 获取单个文件信息
### 请求 URL
`/api/directlink` `GET`
### 参数
| 参数名 | 描述 | 是否必填 | 参考值 |
| :----: | :--------: | :------: | :------------------: |
| path | 文件全路径 | 是 | `/新建 文本文档.txt` |
### 响应
```json
{
"msg": "操作成功",
"code": 0,
"data": {
"name": "新建 文本文档.txt",
"time": "2020-01-28 13:16",
"size": 3,
"type": "FILE",
"path": "d:/test",
"url": "http://127.0.0.1:8080/file/新建 文本文档.txt"
}
}
```
## 获取系统配置
### 请求 URL
`/api/config` `GET`
### 参数
| 参数名 | 描述 | 是否必填 | 参考值 |
| :----: | :--------: | :------: | :-----------: |
| path | 文件夹名称 | 是 | `/文件夹名称` |
### 响应
```json
{
"msg": "操作成功",
"code": 0,
"data": {
"readme": null, # 文档文件名称
"viewConfig": {
"siteName": "站点名称", # 站点名称
"infoEnable": false, # 是否开启右侧信息框
"searchEnable": false, # 是否开启搜索
"searchIgnoreCase": true, # 搜索是否忽略大小写
"storageStrategy": "local", # 当前启用存储引擎
"username": "2", # 用户名
"domain": "http://127.0.0.1:8080", # 域名
"enableCache": false, # 是否开启缓存
"searchContainEncryptedFile": false, # 搜索是否包含加密文件夹
"customJs": "", # 自定义 js 片段
"customCss": "" # 自定义 css 片段
}
}
}
```

109
README.md
View File

@@ -11,51 +11,64 @@
预览地址: [https://zfile.jun6.net](https://zfile.jun6.net)
文档地址: [http://docs.zhaojun.im/zfile](http://docs.zhaojun.im/zfile)
## 系统特色
* 内存缓存 (免安装)
* 内存数据库 (免安装)
* 个性化配置
* 自定义目录的 header 和 footer 说明文件
* 自定义目录的 readme 说明文件
* 自定义 JS, CSS
* 文件夹密码
* 支持在线浏览文本文件, 视频, 图片, 音乐. (支持 FLV 和 HLS)
* 文件/目录二维码
* 缓存动态开启, 缓存自动刷新
* 全局搜索
* 支持 阿里云 OSS, FTP, 华为云 OBS, 本地存储, MINIO, OneDrive 国际/家庭/个人版, OneDrive 世纪互联版, 七牛云 KODO, 腾讯云 COS, 又拍云 USS.
## 快速开始
安装 JDK 1.8 :
安装依赖环境:
```bash
yum install -y java # 适用于 Centos 7.x
# CentOS系统
yum install -y java-1.8.0-openjdk unzip
# Debian/Ubuntu系统
apt update
apt install -y openjdk-8-jre-headless unzip
```
> 如为更新程序, 则请先执行 `~/zfile/bin/stop.sh && rm -rf ~/zfile` 清理旧程序. 首次安装请忽略此选项.
下载项目:
```bash
wget https://github.com/zhaojun1998/zfile/releases/download/0.6/zfile-0.6.jar
cd ~
wget https://c.jun6.net/ZFILE/zfile-release.war
mkdir zfile && unzip zfile-release.war -d zfile && rm -rf zfile-release.war
chmod +x zfile/bin/*.sh
```
程序的目录结构为:
```
├── zfile
├── META-INF
├── WEB-INF
└── bin
├── start.sh # 启动脚本
└── stop.sh # 停止脚本
├── restart.sh # 重启脚本
```
启动项目:
```bash
java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.6.jar
## 高级启动
java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.6.jar --server.port=18777
## 后台运行
nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.6.jar &
~/zfile/bin/start.sh
```
> 系统使用的是内置配置文件, 默认配置请参考: [application.yml](https://github.com/zhaojun1998/zfile/blob/master/src/main/resources/application.yml)
> **可下载此文件放置与 jar 包同目录, 此时会以外部配置文件为准, 推荐适用此方式!.**
> 所有参数都可在命令行启动时, 以类似 `--server.port=18777` 的方式强制执行, 此方式的优先级最高.
> *指定 `-Djava.security.egd=file:/dev/./urandom` 是为了防止在 Linux 环境中, 生成首次登陆生成 sessionId 取系统随机数过慢的问题.*
篇幅有限, 更详细的安装教程请参考: [安装文档](http://zhaojun.im/zfile-install)
访问地址:
@@ -66,12 +79,37 @@ nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.6.jar &
管理后台: http://127.0.0.1:8080/#/admin
## OneDrive 使用教程.
访问地址进行授权, 获取 accessToken 和 refreshToken:
国际/家庭/个人版:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=09939809-c617-43c8-a220-a93c1513c5d4&response_type=code&redirect_uri=https://zfile.jun6.net/onedrive/callback&scope=offline_access%20User.Read%20Files.ReadWrite.All
世纪互联版:
https://login.chinacloudapi.cn/common/oauth2/v2.0/authorize?client_id=4a72d927-1907-488d-9eb2-1b465c53c1c5&response_type=code&redirect_uri=https://zfile.jun6.net/onedrive/china-callback&scope=offline_access%20User.Read%20Files.ReadWrite.All
然后分别填写至访问令牌和刷新令牌即可:
![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`
* 数据库: `h2/mysql`
## 预览
![前台首页](http://cdn.jun6.net/2020/01/29/a252a5cec7134.png)
![后台设置](http://cdn.jun6.net/2020/01/29/d5c85221bcffc.png)
![存储策略](http://cdn.jun6.net/2020/01/29/4b79bfba4e003.png)
![缓存管理](http://cdn.jun6.net/2020/01/29/60b0538e50f9f.png)
## 常见问题
### 数据库
@@ -82,15 +120,32 @@ nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.6.jar &
默认 H2 数据库文件地址: `~/.zfile/db/`, `~` 表示用户目录, windows 为 `C:/Users/用户名/`, linux 为 `/home/用户名/`, root 用户为 `/root/`
### 头尾文件和加密文件
### 文档文件和加密文件
- 目录头部显示文件名为 `header.md`
- 目录底部显示文件名为 `footer.md`
- 目录文档显示文件名为 `readme.md`
- 目录需要密码访问, 添加文件 `password.txt` (无法拦截此文件被下载, 但可以改名文件)
## TODO
## 开发计划
- 文本预览更换更好用的编辑器
- 后台支持上传、编辑、删除等操作
- API 支持
- 更方便的部署方式
- [x] API 支持 [点击查看文档](https://github.com/zhaojun1998/zfile/blob/master/API.md)
- [x] 更方便的部署方式
- [x] 布局优化 - 自定义操作按钮 (现为右键实现)
- [x] 后台优化 - 设置按照其功能进行分离
- [x] 体验优化 - 支持前后端分离部署
- [x] 体验优化 - 文本预览更换 vscode 同款编辑器 monaco editor
- [ ] 新功能 - 后台支持上传、编辑、删除等操作
- [ ] 新功能 - WebDav 支持
- [ ] 新功能 - Docker 支持
- [ ] 新功能 - 离线下载 (aria2)
- [ ] 体验优化 - 忽略文件列表 (正则表达式)
- [ ] 体验优化 - 自定义支持预览的文件后缀 (正则表达式)
- [ ] 架构调整 - 支持多存储策略
- [ ] 体验优化 - 一键安装脚本
## 支持作者
如果本项目对你有帮助,请作者喝杯咖啡吧。
<img src="http://cdn.jun6.net/alipay.png" width="200" height="312">
<img src="http://cdn.jun6.net/wechat.png" width="222" height="300">

52
pom.xml
View File

@@ -12,8 +12,9 @@
<groupId>im.zhaojun</groupId>
<artifactId>zfile</artifactId>
<version>0.6</version>
<version>2.2</version>
<name>zfile</name>
<packaging>war</packaging>
<description>一个在线的文件浏览系统</description>
<properties>
@@ -44,7 +45,15 @@
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 数据库驱动-->
<dependency>
@@ -62,7 +71,7 @@
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.5.11</version>
<version>5.1.3</version>
</dependency>
<!-- 存储策略相关 API, 对象存储、FTP、 Rest API-->
@@ -101,23 +110,9 @@
</dependency>
<dependency>
<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>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.61</version>
</dependency>
</dependencies>
@@ -128,6 +123,25 @@
<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>-Djava.security.egd=file:/dev/./urandom</jvm>
<jvm>-Dfile.encoding=utf-8</jvm>
</jvms>
</configuration>
</plugin>
</plugins>
</build>

View File

@@ -1,18 +0,0 @@
package im.zhaojun.common.config;
import im.zhaojun.common.model.enums.StorageTypeEnumDeSerializerConvert;
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 {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StorageTypeEnumDeSerializerConvert());
}
}

View File

@@ -1,173 +0,0 @@
package im.zhaojun.common.controller;
import cn.hutool.core.util.URLUtil;
import im.zhaojun.common.annotation.CheckStorageStrategyInit;
import im.zhaojun.common.exception.SearchDisableException;
import im.zhaojun.common.model.constant.ZFileConstant;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.dto.ResultBean;
import im.zhaojun.common.model.dto.SiteConfigDTO;
import im.zhaojun.common.model.dto.SystemConfigDTO;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.FileAsyncCacheService;
import im.zhaojun.common.service.StorageConfigService;
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;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* 前台文件管理
* @author zhaojun
*/
@RequestMapping("/api")
@RestController
public class FileController {
@Resource
private SystemService systemService;
@Resource
private SystemConfigService systemConfigService;
@Resource
private StorageConfigService storageConfigService;
@Resource
private FileAsyncCacheService fileAsyncCacheService;
/**
* 滚动加载每页条数.
*/
private static final Integer PAGE_SIZE = 30;
@CheckStorageStrategyInit
@GetMapping("/list")
public ResultBean list(@RequestParam(defaultValue = "/") String path,
@RequestParam(defaultValue = "name") String sortBy,
@RequestParam(defaultValue = "asc") String order,
@RequestParam(required = false) String password,
@RequestParam(defaultValue = "1") Integer page) throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
List<FileItemDTO> fileItemList = fileService.fileList(StringUtils.removeDuplicateSeparator("/" + URLUtil.decode(path) + "/"));
for (FileItemDTO fileItemDTO : fileItemList) {
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);
int total = fileItemList.size();
int totalPage = (total + PAGE_SIZE - 1) / PAGE_SIZE;
if (page > totalPage) {
return ResultBean.successData(new ArrayList<>());
}
int start = (page - 1) * PAGE_SIZE;
int end = page * PAGE_SIZE;
end = Math.min(end, total);
List<FileItemDTO> fileSubItem = new ArrayList<>(fileItemList.subList(start, end));
return ResultBean.successData(fileSubItem);
}
/**
* 获取文件类容, 仅限用于, 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 path 路径
*/
@CheckStorageStrategyInit
@GetMapping("/config")
public ResultBean getConfig(String path) throws Exception {
SiteConfigDTO config = systemService.getConfig(URLUtil.decode(StringUtils.removeDuplicateSeparator("/" + path + "/")));
config.setSystemConfigDTO(systemConfigService.getSystemConfig());
return ResultBean.successData(config);
}
@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 {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
SystemConfigDTO systemConfigDTO = systemConfigService.getSystemConfig();
if (!systemConfigDTO.getSearchEnable()) {
throw new SearchDisableException("搜索功能未开启");
}
if (!fileAsyncCacheService.isCacheFinish()) {
throw new SearchDisableException("搜索功能缓存预热中, 请稍后再试");
}
return ResultBean.success(fileService.search(URLUtil.decode(name)));
}
/**
* 过滤文件列表, 不显示密码, 头部和尾部文件.
*/
private void filterFileList(List<FileItemDTO> fileItemList) {
if (fileItemList == null) {
return;
}
fileItemList.removeIf(fileItem -> ZFileConstant.PASSWORD_FILE_NAME.equals(fileItem.getName())
|| ZFileConstant.FOOTER_FILE_NAME.equals(fileItem.getName())
|| ZFileConstant.HEADER_FILE_NAME.equals(fileItem.getName()));
}
}

View File

@@ -1,53 +0,0 @@
package im.zhaojun.common.model.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
/**
* @author zhaojun
*/
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

@@ -1,194 +0,0 @@
package im.zhaojun.common.service;
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.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.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.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 {
public static final String SYSTEM_CONFIG_CACHE_PREFIX = "zfile-cache:";
@Value("${zfile.cache.timeout}")
protected Long timeout;
protected boolean isInitialized;
@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;
/**
* 清理当前存储策略的缓存
*/
public void clearFileCache() throws Exception {
Set<String> cacheKeys = getCacheKeys();
cache.removeAll(cacheKeys);
closeCacheAutoRefresh();
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 abstract StorageTypeEnum getStorageTypeEnum();
/**
* 搜索文件
* @param name 文件名
* @return 包含该文件名的所有文件或文件夹
* @throws Exception 搜索过程出现的异常
*/
public 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;
}
/**
* 查询所有文件, 仅去缓存中查询.
* @return 所有文件
*/
public List<FileItemDTO> selectAllFileList() throws Exception {
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) {
queue.addAll(cacheList);
}
}
}
return result;
}
/**
* 获取所有缓存的 Key, 仅当开启缓存, 且缓存完成时, 可获取.
* @return 所有缓存的 Key
* @throws Exception 可能出现的异常
*/
public Set<String> getCacheKeys() throws Exception {
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);
}
}

View File

@@ -1,142 +0,0 @@
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 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 {
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;
@Async
public void cacheGlobalFile() {
StorageTypeEnum storageStrategy = systemConfigService.getCurrentStorageStrategy();
if (storageStrategy == null) {
log.debug("尚未配置存储策略. 跳过启动缓存.");
return;
}
boolean enableCache = systemConfigService.getEnableCache();
if (!enableCache) {
log.debug("存储策略 {} 未启用缓存, 跳过缓存.", storageStrategy.getDescription());
return;
}
AbstractFileService fileService = StorageTypeFactory.getStorageTypeService(storageStrategy);
if (fileService.getIsUnInitialized()) {
log.debug("存储策略 {} 未初始化成功, 跳过缓存.", storageStrategy.getDescription());
return;
}
Integer cacheDirectoryCount = 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 {
String path = "/";
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 ), 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

@@ -1,42 +0,0 @@
package im.zhaojun.common.service;
import im.zhaojun.common.model.constant.ZFileConstant;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.dto.SiteConfigDTO;
import im.zhaojun.common.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 {
@Resource
private SystemConfigService systemConfigService;
/**
* 构建指定路径下标题, 页头, 页尾
* @param path 路径
*/
public synchronized SiteConfigDTO getConfig(String path) throws Exception {
SiteConfigDTO siteConfigDTO = new SiteConfigDTO();
AbstractFileService fileService = systemConfigService.getCurrentFileService();
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())) {
siteConfigDTO.setHeader(HttpUtil.getTextContent(fileItemDTO.getUrl()));
}
}
return siteConfigDTO;
}
}

View File

@@ -1,93 +0,0 @@
package im.zhaojun.ftp.service;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.ftp.Ftp;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.FileService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.util.StringUtils;
import org.apache.commons.net.ftp.FTPFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 FtpServiceImpl extends AbstractFileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(FtpServiceImpl.class);
@Resource
private StorageConfigService storageConfigService;
private Ftp ftp;
private String domain;
@Override
public void init() {
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();
if (Objects.isNull(host) || Objects.isNull(port) || Objects.isNull(username) || Objects.isNull(password)) {
isInitialized = true;
} else {
ftp = new Ftp(host, Integer.parseInt(port), username, password);
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
}
}
@Override
public List<FileItemDTO> fileList(String path) {
FTPFile[] ftpFiles = ftp.lsFiles(path);
List<FileItemDTO> fileItemList = new ArrayList<>();
for (FTPFile ftpFile : ftpFiles) {
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setName(ftpFile.getName());
fileItemDTO.setSize(ftpFile.getSize());
fileItemDTO.setTime(ftpFile.getTimestamp().getTime());
fileItemDTO.setType(ftpFile.isDirectory() ? FileTypeEnum.FOLDER : FileTypeEnum.FILE);
fileItemDTO.setPath(path);
if (ftpFile.isFile()) {
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(path, fileItemDTO.getName())));
}
fileItemList.add(fileItemDTO);
}
return fileItemList;
}
@Override
public String getDownloadUrl(String path) {
return URLUtil.complateUrl(domain, path);
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.FTP;
}
}

View File

@@ -1,67 +0,0 @@
package im.zhaojun.local.controller;
import cn.hutool.core.util.URLUtil;
import im.zhaojun.common.util.StringUtils;
import im.zhaojun.local.service.LocalServiceImpl;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.HandlerMapping;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.Date;
/**
* @author zhaojun
*/
@Controller
public class LocalController {
@Resource
private LocalServiceImpl localServiceImpl;
@GetMapping("/file/**")
@ResponseBody
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);
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) {
if (!file.exists()) {
return ResponseEntity.notFound().build();
}
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.setContentDispositionFormData("attachment", URLUtil.encode(file.getName()));
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
headers.add("Last-Modified", new Date().toString());
headers.add("ETag", String.valueOf(System.currentTimeMillis()));
return ResponseEntity
.ok()
.headers(headers)
.contentLength(file.length())
.contentType(mediaType)
.body(new FileSystemResource(file));
}
}

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,32 +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.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
/**
* @author zhaojun
*/
@Controller
public class OneDriveController {
@GetMapping("/onedirve/callback")
@ResponseBody
public String onedriveCallback(String code, HttpServletRequest request) {
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

@@ -1,7 +1,5 @@
package im.zhaojun;
package im.zhaojun.zfile;
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.context.annotation.EnableAspectJAutoProxy;
@@ -12,8 +10,6 @@ import org.springframework.scheduling.annotation.EnableAsync;
*/
@EnableAsync
@SpringBootApplication
@EnableMethodCache(basePackages = "im.zhaojun", proxyTargetClass = true)
@EnableCreateCacheAnnotation
@EnableAspectJAutoProxy(exposeProxy = true)
public class ZfileApplication {

View File

@@ -0,0 +1,112 @@
package im.zhaojun.zfile.cache;
import cn.hutool.core.util.StrUtil;
import im.zhaojun.zfile.model.constant.ZFileConstant;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import im.zhaojun.zfile.model.dto.SystemConfigDTO;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @author zhaojun
*/
@Component
public class ZFileCache {
private ConcurrentMap<String, List<FileItemDTO>> fileCache = new ConcurrentHashMap<>();
private SystemConfigDTO systemConfigCache;
public Date lastCacheAutoRefreshDate;
public synchronized void put(String key, List<FileItemDTO> value) {
fileCache.put(key, value);
}
public List<FileItemDTO> get(String key) {
return fileCache.get(key);
}
public void clear() {
fileCache.clear();
}
public int cacheCount() {
return fileCache.size();
}
public List<FileItemDTO> find(String key, boolean ignoreCase, boolean searchContainEncryptedFile) {
List<FileItemDTO> result = new ArrayList<>();
Collection<List<FileItemDTO>> values = fileCache.values();
for (List<FileItemDTO> fileItemList : values) {
// 如果开启了 "搜索包含加密文件" 选项, 则直接返回 true.
if (!searchContainEncryptedFile && isEncryptedFolder(fileItemList)) {
continue;
}
for (FileItemDTO fileItemDTO : fileItemList) {
boolean testResult;
if (ignoreCase) {
testResult = StrUtil.containsIgnoreCase(fileItemDTO.getName(), key);
} else {
testResult = fileItemDTO.getName().contains(key);
}
if (testResult) {
result.add(fileItemDTO);
}
}
}
return result;
}
public Set<String> keySet() {
return fileCache.keySet();
}
public void remove(String key) {
fileCache.remove(key);
}
public void updateConfig(SystemConfigDTO systemConfigCache) {
this.systemConfigCache = systemConfigCache;
}
public SystemConfigDTO getConfig() {
return this.systemConfigCache;
}
public void removeConfig() {
this.systemConfigCache = null;
}
public Date getLastCacheAutoRefreshDate() {
return lastCacheAutoRefreshDate;
}
public void setLastCacheAutoRefreshDate(Date lastCacheAutoRefreshDate) {
this.lastCacheAutoRefreshDate = lastCacheAutoRefreshDate;
}
/**
* 不是加密文件夹
* @param list 文件夹中的内容
* @return 返回此文件夹是否加密.
*/
private boolean isEncryptedFolder(List<FileItemDTO> list) {
// 遍历文件判断是否包含
for (FileItemDTO fileItemDTO : list) {
if (Objects.equals(ZFileConstant.PASSWORD_FILE_NAME, fileItemDTO.getName())) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,21 @@
package im.zhaojun.zfile.config;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @author zhaojun
*/
@Configuration
public class CacheConfig {
@Bean
public ConcurrentMap<String, List<FileItemDTO>> concurrentMapCache() {
return new ConcurrentHashMap<>();
}
}

View File

@@ -0,0 +1,26 @@
package im.zhaojun.zfile.config;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.IOException;
import java.util.Collections;
/**
* @author zhaojun
*/
public class ContentTypeTextToTextJson implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
ClientHttpResponse response = execution.execute(request, body);
HttpHeaders headers = response.getHeaders();
headers.put("Content-Type", Collections.singletonList("application/text"));
return response;
}
}

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.config;
package im.zhaojun.zfile.config;
import org.springframework.http.HttpHeaders;
import org.springframework.web.cors.CorsUtils;

View File

@@ -0,0 +1,81 @@
package im.zhaojun.zfile.config;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.SystemConfigService;
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
import im.zhaojun.zfile.service.impl.OneDriveChinaServiceImpl;
import im.zhaojun.zfile.service.impl.OneDriveServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* @author zhaojun
*/
@Configuration
@EnableScheduling
@Slf4j
public class GlobalScheduleTask {
@Resource
private OneDriveServiceImpl oneDriveServiceImpl;
@Resource
private OneDriveChinaServiceImpl oneDriveChinaServiceImpl;
@Resource
private SystemConfigService systemConfigService;
/**
* 项目启动 30 秒后, 每 15 分钟执行一次刷新 OneDrive Token 的定时任务.
*/
@Scheduled(fixedRate = 1000 * 60 * 10, initialDelay = 1000 * 30)
public void autoRefreshOneDriveToken() {
try {
log.debug("尝试调用 OneDrive 自动刷新 AccessToken 定时任务");
AbstractBaseFileService currentFileService = systemConfigService.getCurrentFileService();
if (!(currentFileService instanceof OneDriveServiceImpl
|| currentFileService instanceof OneDriveChinaServiceImpl)) {
log.debug("当前启用存储类型, 不是 OneDrive, 跳过自动刷新 AccessToken");
return;
}
if (currentFileService.getIsUnInitialized()) {
log.debug("当前启用 OneDrive 未初始化成功, 跳过自动刷新 AccessToken");
return;
}
StorageTypeEnum currentStorageTypeEnum = currentFileService.getStorageTypeEnum();
try {
refreshOneDriveToken(currentStorageTypeEnum);
} catch (Exception e) {
log.debug("刷新 " + currentStorageTypeEnum.getDescription() + " Token 失败.", e);
}
} catch (Throwable e) {
log.debug("尝试调用 OneDrive 自动刷新 AccessToken 定时任务出现未知异常", e);
}
}
/**
* 调用刷新 OneDrive Token
*/
public void refreshOneDriveToken(StorageTypeEnum storageType) {
if (Objects.equals(storageType, StorageTypeEnum.ONE_DRIVE_CHINA)) {
oneDriveChinaServiceImpl.refreshOneDriveToken();
} else {
oneDriveServiceImpl.refreshOneDriveToken();
}
log.info("刷新 {} key 时间: {}", storageType.getDescription(), LocalDateTime.now());
}
}

View File

@@ -0,0 +1,61 @@
package im.zhaojun.zfile.config;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.StorageConfigService;
import im.zhaojun.zfile.service.impl.OneDriveChinaServiceImpl;
import im.zhaojun.zfile.service.impl.OneDriveServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.Collections;
/**
* @author zhaojun
*/
@Configuration
public class OneDriveConfig {
@Resource
private StorageConfigService storageConfigService;
@Resource
@Lazy
private OneDriveServiceImpl oneDriveServiceImpl;
@Resource
@Lazy
private OneDriveChinaServiceImpl oneDriveChinaServiceImpl;
@Bean
public RestTemplate oneDriveRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
ClientHttpRequestInterceptor interceptor = (httpRequest, bytes, clientHttpRequestExecution) -> {
String host = httpRequest.getURI().getHost();
StorageTypeEnum type;
if (oneDriveChinaServiceImpl.getGraphEndPoint().contains(host)) {
type = StorageTypeEnum.ONE_DRIVE_CHINA;
} else if (oneDriveServiceImpl.getGraphEndPoint().contains(host)) {
type = StorageTypeEnum.ONE_DRIVE;
} else {
return clientHttpRequestExecution.execute(httpRequest, bytes);
}
StorageConfig accessTokenConfig =
storageConfigService.selectByTypeAndKey(type, StorageConfigConstant.ACCESS_TOKEN_KEY);
String tokenValue = String.format("%s %s", "Bearer", accessTokenConfig.getValue());
httpRequest.getHeaders().add("Authorization", tokenValue);
return clientHttpRequestExecution.execute(httpRequest, bytes);
};
restTemplate.setInterceptors(Collections.singletonList(interceptor));
return restTemplate;
}
}

View File

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

View File

@@ -0,0 +1,31 @@
package im.zhaojun.zfile.config;
import im.zhaojun.zfile.model.enums.StorageTypeEnumDeSerializerConvert;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
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 {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StorageTypeEnumDeSerializerConvert());
}
@Bean
public ServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory webServerFactory = new TomcatServletWebServerFactory();
webServerFactory.addConnectorCustomizers(connector -> {
connector.setAttribute("relaxedPathChars", "<>[\\]^`{|}");
connector.setAttribute("relaxedQueryChars", "<>[\\]^`{|}");
});
return webServerFactory;
}
}

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.config;
package im.zhaojun.zfile.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -6,6 +6,7 @@ import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
/**
* @author zhaojun
@@ -17,6 +18,7 @@ public class ZFileConfiguration {
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
restTemplate.setInterceptors(Collections.singletonList(new ContentTypeTextToTextJson()));
return restTemplate;
}

View File

@@ -1,15 +1,22 @@
package im.zhaojun.common.controller;
package im.zhaojun.zfile.controller;
import im.zhaojun.common.model.StorageConfig;
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.AbstractFileService;
import im.zhaojun.common.service.FileAsyncCacheService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.service.SystemConfigService;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ZipUtil;
import im.zhaojun.zfile.config.StorageTypeFactory;
import im.zhaojun.zfile.model.dto.ResultBean;
import im.zhaojun.zfile.model.dto.StorageStrategyDTO;
import im.zhaojun.zfile.model.dto.SystemConfigDTO;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.model.support.SystemMonitorInfo;
import im.zhaojun.zfile.service.StorageConfigService;
import im.zhaojun.zfile.service.SystemConfigService;
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
import im.zhaojun.zfile.service.support.FileAsyncCacheService;
import im.zhaojun.zfile.util.FileUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -17,9 +24,9 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.*;
/**
* 后台管理
@@ -55,15 +62,19 @@ public class AdminController {
@PostMapping("/config")
public ResultBean updateConfig(SystemConfigDTO systemConfigDTO) throws Exception {
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
if (!Objects.equals(currentStorageStrategy, systemConfigDTO.getStorageStrategy())) {
if (systemConfigService.getEnableCache()) {
return ResultBean.error("不支持缓存开启状态下, 切换存储策略, 请先手动关闭缓存");
}
log.info("已将存储策略由 {} 切换为 {}",
currentStorageStrategy.getDescription(),
systemConfigDTO.getStorageStrategy().getDescription());
refreshStorageStrategy();
}
systemConfigDTO.setId(1);
systemConfigService.updateSystemConfig(systemConfigDTO);
if (!Objects.equals(currentStorageStrategy, systemConfigDTO.getStorageStrategy())) {
log.info("已将存储策略由 {} 切换为 {}", currentStorageStrategy, systemConfigDTO.getStorageStrategy());
refreshStorageStrategy();
}
return ResultBean.success();
}
@@ -87,16 +98,31 @@ public class AdminController {
return ResultBean.success(storageConfigList);
}
/**
* 返回支持的存储引擎.
*/
@GetMapping("/support-strategy")
public ResultBean supportStrategy() {
List<StorageStrategyDTO> result = new ArrayList<>();
StorageTypeEnum[] values = StorageTypeEnum.values();
for (StorageTypeEnum value : values) {
AbstractBaseFileService storageTypeService = StorageTypeFactory.getStorageTypeService(value);
result.add(new StorageStrategyDTO(value.getKey(),
value.getDescription(),
storageTypeService.getIsInitialized()));
}
return ResultBean.successData(result);
}
/**
* 保存存储策略
* @param storageStrategyConfig 保存表单值
* @param storageStrategy 所属策略
* @return 操作结果
* @throws Exception 表单解析出错异常
*/
@PostMapping("/storage-strategy")
public ResultBean save(@RequestParam Map<String, String> storageStrategyConfig, StorageTypeEnum storageStrategy) throws Exception {
public ResultBean save(@RequestParam Map<String, String> storageStrategyConfig, StorageTypeEnum storageStrategy) {
// 保存设置.
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageStrategy);
for (StorageConfig storageConfig : storageConfigList) {
String key = storageConfig.getKey();
@@ -105,19 +131,28 @@ public class AdminController {
}
storageConfigService.updateStorageConfig(storageConfigList);
// 获取当前修改的存储策略 Service, 尝试调用初始化.
AbstractBaseFileService 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();
AbstractBaseFileService fileService = systemConfigService.getCurrentFileService();
fileService.clearFileCache();
fileService.init();
fileAsyncCacheService.cacheGlobalFile();
}
return ResultBean.success();
// 返回是否初始化成功.
if (updateStorageStrategyService.getIsInitialized()) {
return ResultBean.success();
} else {
return ResultBean.error("保存成功, 但尝试初始化异常, 请检查设置.");
}
}
/**
@@ -131,16 +166,35 @@ public class AdminController {
/**
* 更新存储策略
*/
public void refreshStorageStrategy(StorageTypeEnum storageStrategy) {
private void refreshStorageStrategy(StorageTypeEnum storageStrategy) {
if (storageStrategy == null) {
log.info("尚未配置存储策略.");
} else {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
AbstractBaseFileService fileService = systemConfigService.getCurrentFileService();
fileService.init();
fileService.clearFileCache();
log.info("切换至存储类型: {}", storageStrategy.getDescription());
fileAsyncCacheService.cacheGlobalFile();
}
}
/**
* 系统日志下载
*/
@GetMapping("/log")
public ResponseEntity<Object> downloadLog(HttpServletResponse response) {
String userHome = System.getProperty("user.home");
File fileZip = ZipUtil.zip(userHome + "/.zfile/logs");
String currentDate = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
return FileUtil.export(fileZip, "ZFile 诊断日志 - " + currentDate + ".zip");
}
/**
* 获取系统监控信息
*/
@GetMapping("monitor")
public ResultBean monitor() {
return ResultBean.success(new SystemMonitorInfo());
}
}

View File

@@ -1,22 +1,21 @@
package im.zhaojun.common.controller;
package im.zhaojun.zfile.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 im.zhaojun.zfile.cache.ZFileCache;
import im.zhaojun.zfile.model.dto.CacheConfigDTO;
import im.zhaojun.zfile.model.dto.ResultBean;
import im.zhaojun.zfile.service.SystemConfigService;
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
import im.zhaojun.zfile.service.support.FileAsyncCacheService;
import im.zhaojun.zfile.service.support.FileCacheService;
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")
@@ -31,52 +30,55 @@ public class CacheController {
@Resource
private FileCacheService fileCacheService;
@Resource
private ZFileCache zFileCache;
@PostMapping("/enable")
public ResultBean enableCache() throws Exception {
public ResultBean enableCache() {
fileCacheService.enableCache();
return ResultBean.success();
}
@PostMapping("/disable")
public ResultBean disableCache() throws Exception {
public ResultBean disableCache() {
fileCacheService.disableCache();
return ResultBean.success();
}
@GetMapping("/config")
public ResultBean cacheConfig() throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
Set<String> cacheKeys = fileService.getCacheKeys();
public ResultBean cacheConfig() {
CacheConfigDTO cacheConfigDTO = new CacheConfigDTO();
cacheConfigDTO.setEnableCache(systemConfigService.getEnableCache());
cacheConfigDTO.setCacheFinish(fileAsyncCacheService.isCacheFinish());
cacheConfigDTO.setCacheKeys(cacheKeys);
cacheConfigDTO.setCacheDirectoryCount(fileAsyncCacheService.getCacheDirectoryCount());
cacheConfigDTO.setCacheFileCount(fileAsyncCacheService.getCacheFileCount());
cacheConfigDTO.setCacheKeys(zFileCache.keySet());
cacheConfigDTO.setCacheDirectoryCount(zFileCache.cacheCount());
cacheConfigDTO.setLastCacheAutoRefreshDate(zFileCache.getLastCacheAutoRefreshDate());
return ResultBean.success(cacheConfigDTO);
}
@PostMapping("/refresh")
public ResultBean refreshCache(String key) throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
AbstractBaseFileService fileService = systemConfigService.getCurrentFileService();
fileService.refreshCache(key);
return ResultBean.success();
}
/*
@PostMapping("/clear")
public ResultBean clearCache(String key) throws Exception {
public ResultBean clearCache(String key) {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.clearFileCache();
fileAsyncCacheService.resetCacheCount();
return ResultBean.success();
}
@PostMapping("/all")
public ResultBean cacheAll() throws Exception {
public ResultBean cacheAll() {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.clearFileCache();
fileAsyncCacheService.cacheGlobalFile();
return ResultBean.success();
}
*/
}

View File

@@ -0,0 +1,65 @@
package im.zhaojun.zfile.controller;
import im.zhaojun.zfile.model.dto.ResultBean;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.util.AudioHelper;
import im.zhaojun.zfile.util.HttpUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author zhaojun
*/
@RestController
@RequestMapping("/common")
public class CommonController {
@GetMapping("/support-strategy")
public ResultBean supportStrategy() {
return ResultBean.successData(StorageTypeEnum.values());
}
/**
* 获取文件内容, 仅限用于, txt, md, ini 等普通文本文件.
* @param url 文件路径
* @return 文件内容
*/
@GetMapping("/content")
public ResultBean getContent(String url) {
return ResultBean.successData(HttpUtil.getTextContent(url));
}
/**
* 获取文件内容, 仅限用于, txt, md, ini 等普通文本文件.
* @param url 文件路径
* @return 文件内容
*/
@GetMapping("/content/origin")
public String getContentOrigin(String url) {
return HttpUtil.getTextContent(url);
}
/**
* 检测文件是否存在
* @param url 文件路径
* @return 是否存在
*/
@GetMapping("/content/exist")
public boolean checkFileExist(String url) {
return HttpUtil.checkUrlExist(url);
}
/**
* 获取音频文件信息
* @param url 文件 URL
* @return 音频信息, 标题封面等信息
*/
@GetMapping("/audio-info")
public ResultBean getAudioInfo(String url) throws Exception {
return ResultBean.success(AudioHelper.getAudioInfo(url));
}
}

View File

@@ -0,0 +1,172 @@
package im.zhaojun.zfile.controller;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.URLUtil;
import im.zhaojun.zfile.model.annotation.CheckStorageStrategyInit;
import im.zhaojun.zfile.exception.SearchDisableException;
import im.zhaojun.zfile.model.support.FilePageModel;
import im.zhaojun.zfile.model.constant.ZFileConstant;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import im.zhaojun.zfile.model.dto.ResultBean;
import im.zhaojun.zfile.model.dto.SiteConfigDTO;
import im.zhaojun.zfile.model.dto.SystemConfigDTO;
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
import im.zhaojun.zfile.service.support.FileAsyncCacheService;
import im.zhaojun.zfile.service.SystemConfigService;
import im.zhaojun.zfile.service.SystemService;
import im.zhaojun.zfile.util.FileComparator;
import im.zhaojun.zfile.util.HttpUtil;
import im.zhaojun.zfile.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
import javax.annotation.Resource;
import java.util.*;
/**
* 前台文件管理
* @author zhaojun
*/
@Slf4j
@RequestMapping("/api")
@RestController
public class FileController {
@Resource
private SystemService systemService;
@Resource
private SystemConfigService systemConfigService;
@Resource
private FileAsyncCacheService fileAsyncCacheService;
/**
* 滚动加载每页条数.
*/
private static final Integer PAGE_SIZE = 30;
@CheckStorageStrategyInit
@GetMapping("/list")
public ResultBean list(@RequestParam(defaultValue = "/") String path,
@RequestParam(defaultValue = "name") String sortBy,
@RequestParam(defaultValue = "asc") String order,
@RequestParam(required = false) String password,
@RequestParam(defaultValue = "1") Integer page) throws Exception {
AbstractBaseFileService fileService = systemConfigService.getCurrentFileService();
List<FileItemDTO> fileItemList = fileService.fileList(StringUtils.removeDuplicateSeparator("/" + path + "/"));
for (FileItemDTO fileItemDTO : fileItemList) {
if (ZFileConstant.PASSWORD_FILE_NAME.equals(fileItemDTO.getName())) {
String expectedPasswordContent;
try {
expectedPasswordContent = HttpUtil.getTextContent(fileItemDTO.getUrl() + '1');
} catch (HttpClientErrorException httpClientErrorException) {
log.debug("尝试重新获取密码文件缓存中链接后仍失败", httpClientErrorException);
try {
String fullPath = StringUtils.removeDuplicateSeparator(fileItemDTO.getPath() + "/" + fileItemDTO.getName());
FileItemDTO fileItem = fileService.getFileItem(fullPath);
expectedPasswordContent = HttpUtil.getTextContent(fileItem.getUrl());
} catch (Exception e) {
log.debug("尝试重新获取密码文件链接后仍失败, 已暂时取消密码", e);
break;
}
}
if (Objects.equals(expectedPasswordContent, password)) {
break;
}
if (password != null && !"".equals(password)) {
return ResultBean.error("密码错误.", ResultBean.INVALID_PASSWORD);
}
return ResultBean.error("此文件夹需要密码.", ResultBean.REQUIRED_PASSWORD);
}
}
return ResultBean.successData(getSortedPagingData(fileItemList, page));
}
/**
* 获取系统配置信息和当前页的标题, 页面文档信息
* @param path 路径
*/
@CheckStorageStrategyInit
@GetMapping("/config")
public ResultBean getConfig(String path) throws Exception {
SiteConfigDTO config = systemService.getConfig(StringUtils.removeDuplicateSeparator("/" + path + "/"));
config.setSystemConfigDTO(systemConfigService.getSystemConfig());
return ResultBean.successData(config);
}
@CheckStorageStrategyInit
@GetMapping("/search")
public ResultBean search(@RequestParam(value = "name", defaultValue = "/") String name,
@RequestParam(defaultValue = "name") String sortBy,
@RequestParam(defaultValue = "asc") String order,
@RequestParam(defaultValue = "1") Integer page) {
AbstractBaseFileService fileService = systemConfigService.getCurrentFileService();
SystemConfigDTO systemConfigDTO = systemConfigService.getSystemConfig();
if (BooleanUtil.isFalse(systemConfigDTO.getSearchEnable())) {
throw new SearchDisableException("搜索功能未开启");
}
if (!fileAsyncCacheService.isCacheFinish()) {
throw new SearchDisableException("搜索功能缓存预热中, 请稍后再试");
}
List<FileItemDTO> fileItemList = fileService.search(URLUtil.decode(name));
return ResultBean.successData(getSortedPagingData(fileItemList, page));
}
/**
* 过滤文件列表, 不显示密码, 文档文件.
*/
private void filterFileList(List<FileItemDTO> fileItemList) {
if (fileItemList == null) {
return;
}
fileItemList.removeIf(fileItem -> ZFileConstant.PASSWORD_FILE_NAME.equals(fileItem.getName())
|| ZFileConstant.README_FILE_NAME.equals(fileItem.getName()));
}
private FilePageModel getSortedPagingData(List<FileItemDTO> fileItemList, Integer page) {
ArrayList<FileItemDTO> copy = new ArrayList<>(Arrays.asList(new FileItemDTO[fileItemList.size()]));
Collections.copy(copy, fileItemList);
// 排序, 先按照文件类型比较, 文件夹在前, 文件在后, 然后根据 sortBy 字段排序, 默认为升序;
copy.sort(new FileComparator());
filterFileList(copy);
int total = copy.size();
int totalPage = (total + PAGE_SIZE - 1) / PAGE_SIZE;
if (page > totalPage) {
return new FilePageModel(total, totalPage, Collections.emptyList());
}
int start = (page - 1) * PAGE_SIZE;
int end = page * PAGE_SIZE;
end = Math.min(end, total);
return new FilePageModel(total, totalPage, copy.subList(start, end));
}
/**
* 获取指定路径下的文件信息内容
* @param path 文件全路径
* @return 该文件的名称, 路径, 大小, 下载地址等信息.
*/
@CheckStorageStrategyInit
@GetMapping("/directlink")
public ResultBean directlink(String path) {
AbstractBaseFileService fileService = systemConfigService.getCurrentFileService();
return ResultBean.successData(fileService.getFileItem(path));
}
}

View File

@@ -1,14 +1,13 @@
package im.zhaojun.common.controller;
package im.zhaojun.zfile.controller;
import cn.hutool.crypto.SecureUtil;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.dto.InstallModelDTO;
import im.zhaojun.common.model.dto.ResultBean;
import im.zhaojun.common.model.dto.SystemConfigDTO;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.FileAsyncCacheService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.service.SystemConfigService;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.dto.InstallModelDTO;
import im.zhaojun.zfile.model.dto.ResultBean;
import im.zhaojun.zfile.model.dto.SystemConfigDTO;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.StorageConfigService;
import im.zhaojun.zfile.service.SystemConfigService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -33,9 +32,6 @@ public class InstallController {
@Resource
private AdminController adminController;
@Resource
private FileAsyncCacheService fileAsyncCacheService;
@GetMapping("/is-installed")
public ResultBean isInstall() {
if (systemConfigService.getCurrentStorageStrategy() == null) {

View File

@@ -0,0 +1,37 @@
package im.zhaojun.zfile.controller;
import im.zhaojun.zfile.util.FileUtil;
import im.zhaojun.zfile.util.StringUtils;
import im.zhaojun.zfile.service.impl.LocalServiceImpl;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.HandlerMapping;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
/**
* @author zhaojun
*/
@Controller
public class LocalController {
@Resource
private LocalServiceImpl localServiceImpl;
@GetMapping("/file/**")
@ResponseBody
public ResponseEntity<Object> downAttachment(final HttpServletRequest request) {
String path = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
AntPathMatcher apm = new AntPathMatcher();
String filePath = apm.extractPathWithinPattern(bestMatchPattern, path);
return FileUtil.export(new File(StringUtils.concatPath(localServiceImpl.getFilePath(), filePath)));
}
}

View File

@@ -0,0 +1,43 @@
package im.zhaojun.zfile.controller;
import im.zhaojun.zfile.service.impl.OneDriveChinaServiceImpl;
import im.zhaojun.zfile.model.support.OneDriveToken;
import im.zhaojun.zfile.service.impl.OneDriveServiceImpl;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
/**
* @author zhaojun
*/
@Controller
@RequestMapping("/onedrive")
public class OneDriveController {
@Resource
private OneDriveServiceImpl oneDriveServiceImpl;
@Resource
private OneDriveChinaServiceImpl oneDriveChinaServiceImpl;
@GetMapping("/callback")
public String onedriveCallback(String code, Model model) {
OneDriveToken oneDriveToken = oneDriveServiceImpl.getToken(code);
model.addAttribute("accessToken", oneDriveToken.getAccessToken());
model.addAttribute("refreshToken", oneDriveToken.getRefreshToken());
return "callback";
}
@GetMapping("/china-callback")
public String onedriveChinaCallback(String code, Model model) {
OneDriveToken oneDriveToken = oneDriveChinaServiceImpl.getToken(code);
model.addAttribute("accessToken", oneDriveToken.getAccessToken());
model.addAttribute("refreshToken", oneDriveToken.getRefreshToken());
return "callback";
}
}

View File

@@ -0,0 +1,63 @@
package im.zhaojun.zfile.controller;
import cn.hutool.core.util.URLUtil;
import im.zhaojun.zfile.model.constant.ZFileConstant;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import im.zhaojun.zfile.model.enums.FileTypeEnum;
import im.zhaojun.zfile.service.SystemConfigService;
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
import org.springframework.stereotype.Controller;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.HandlerMapping;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
/**
* @author Zhao Jun
* 2020/2/9 11:17
*/
@Controller
public class PageController {
@Resource
private SystemConfigService systemConfigService;
@GetMapping("/directlink/**")
public String directlink(final HttpServletRequest request) {
String path = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
AntPathMatcher apm = new AntPathMatcher();
String filePath = apm.extractPathWithinPattern(bestMatchPattern, path);
if (filePath.length() > 0 && filePath.charAt(0) != ZFileConstant.PATH_SEPARATOR_CHAR) {
filePath = "/" + filePath;
}
AbstractBaseFileService fileService = systemConfigService.getCurrentFileService();
FileItemDTO fileItem = fileService.getFileItem(filePath);
String url = fileItem.getUrl();
int queryIndex = url.indexOf('?');
if (queryIndex != -1) {
String origin = url.substring(0, queryIndex);
String queryString = url.substring(queryIndex + 1);
url = URLUtil.encode(origin) + "?" + URLUtil.encode(queryString);
} else {
url = URLUtil.encode(url);
}
if (Objects.equals(fileItem.getType(), FileTypeEnum.FOLDER)) {
return "redirect:" + fileItem.getUrl();
} else {
return "redirect:" + url;
}
}
}

View File

@@ -0,0 +1,51 @@
package im.zhaojun.zfile.core;
import im.zhaojun.zfile.cache.ZFileCache;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import im.zhaojun.zfile.service.SystemConfigService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.List;
/**
* @author zhaojun
* 缓存切面类, 用于访问文件夹时, 缓存文件列表内容.
*/
@Aspect
@Component
public class FileListCacheAop {
@Resource
private ZFileCache zFileCache;
@Resource
private SystemConfigService systemConfigService;
@Around(value = "execution(public * im.zhaojun.zfile.service.base.AbstractBaseFileService.fileList(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
List<FileItemDTO> result;
Object[] args = point.getArgs();
String path = String.valueOf(args[0]);
boolean enableCache = systemConfigService.getEnableCache();
if (enableCache) {
List<FileItemDTO> cacheFileList = zFileCache.get(path);
if (CollectionUtils.isEmpty(cacheFileList)) {
result = (List<FileItemDTO>) point.proceed();
zFileCache.put(path, result);
} else {
result = cacheFileList;
}
} else {
result = (List<FileItemDTO>) point.proceed();
}
return result;
}
}

View File

@@ -1,9 +1,9 @@
package im.zhaojun.common.aspect;
package im.zhaojun.zfile.core;
import im.zhaojun.common.exception.StorageStrategyUninitializedException;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.SystemConfigService;
import im.zhaojun.common.util.SpringContextHolder;
import im.zhaojun.zfile.exception.StorageStrategyUninitializedException;
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
import im.zhaojun.zfile.service.SystemConfigService;
import im.zhaojun.zfile.util.SpringContextHolder;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@@ -13,12 +13,12 @@ import org.springframework.stereotype.Component;
*/
@Aspect
@Component
public class StorageStrategyInitCheckAspect {
public class StorageStrategyInitCheckAop {
@Before("@annotation(im.zhaojun.common.annotation.CheckStorageStrategyInit)")
@Before("@annotation(im.zhaojun.zfile.model.annotation.CheckStorageStrategyInit)")
public void logStart() {
SystemConfigService systemConfigService = SpringContextHolder.getBean(SystemConfigService.class);
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
AbstractBaseFileService currentFileService = systemConfigService.getCurrentFileService();
if (currentFileService == null) {
throw new StorageStrategyUninitializedException("存储策略尚未初始化, 请联系管理员!");
}

View File

@@ -1,6 +1,6 @@
package im.zhaojun.common.exception;
package im.zhaojun.zfile.exception;
import im.zhaojun.common.model.dto.ResultBean;
import im.zhaojun.zfile.model.dto.ResultBean;
import org.apache.catalina.connector.ClientAbortException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -16,12 +16,12 @@ 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()) {
@@ -32,6 +32,7 @@ public class GlobleExceptionHandler {
@ExceptionHandler
@ResponseBody
@ResponseStatus
public ResultBean searchDisableExceptionHandler(StorageStrategyUninitializedException e) {
if (log.isDebugEnabled()) {
@@ -40,6 +41,16 @@ public class GlobleExceptionHandler {
return ResultBean.error(e.getMessage());
}
/**
* 不存在的文件异常
*/
@ExceptionHandler({NotExistFileException.class})
@ResponseBody
public ResultBean notExistFile(Exception ex) {
return ResultBean.error("文件不存在");
}
/**
* 捕获 ClientAbortException 异常, 不做任何处理, 防止出现大量堆栈日志输出, 此异常不影响功能.
*/
@@ -53,6 +64,7 @@ public class GlobleExceptionHandler {
}
@ExceptionHandler
@ResponseBody
@ResponseStatus(code= HttpStatus.INTERNAL_SERVER_ERROR)
public ResultBean searchDisableExceptionHandler(Exception e) {
if (log.isDebugEnabled()) {

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.exception;
package im.zhaojun.zfile.exception;
/**
* 对象存储初始化异常

View File

@@ -0,0 +1,28 @@
package im.zhaojun.zfile.exception;
/**
* @author zhaojun
*/
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,4 +1,4 @@
package im.zhaojun.common.exception;
package im.zhaojun.zfile.exception;
/**
* @author zhaojun

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.exception;
package im.zhaojun.zfile.exception;
/**
* 存储策略未初始化异常

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.exception;
package im.zhaojun.zfile.exception;
/**
* 未知的存储类型异常

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.security;
package im.zhaojun.zfile.filter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.annotation;
package im.zhaojun.zfile.model.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,8 +1,7 @@
package im.zhaojun.common.model.constant;
package im.zhaojun.zfile.model.constant;
/**
* @author zhaojun
* @date 2019/12/28 18:47
*/
public class StorageConfigConstant {
@@ -30,4 +29,12 @@ public class StorageConfigConstant {
public static final String FILE_PATH_KEY = "filePath";
}
public static final String ACCESS_TOKEN_KEY = "accessToken";
public static final String REFRESH_TOKEN_KEY = "refreshToken";
public static final String PATH_STYLE = "pathStyle";
public static final String IS_PRIVATE = "isPrivate";
}

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.model.constant;
package im.zhaojun.zfile.model.constant;
/**
* @author zhaojun

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.model.constant;
package im.zhaojun.zfile.model.constant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -14,15 +14,14 @@ public class ZFileConstant {
public static final String AUDIO_TMP_PATH = "/.zfile/tmp/audio/";
/**
* 页面头部文件
*/
public static String HEADER_FILE_NAME = "header.md";
public static final Character PATH_SEPARATOR_CHAR = '/';
public static final String PATH_SEPARATOR = "/";
/**
* 页面尾部文件
* 页面文档文件
*/
public static String FOOTER_FILE_NAME = "footer.md";
public static String README_FILE_NAME = "readme.md";
/**
* 密码文件
@@ -30,13 +29,8 @@ public class ZFileConstant {
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 setFooterFileName(@Value("${zfile.constant.footer}") String footerFileName) {
ZFileConstant.FOOTER_FILE_NAME = footerFileName;
public void setHeaderFileName(@Value("${zfile.constant.readme}") String headerFileName) {
ZFileConstant.README_FILE_NAME = headerFileName;
}
@Autowired(required = false)

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.model.dto;
package im.zhaojun.zfile.model.dto;
/**
* @author zhaojun

View File

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

View File

@@ -1,6 +1,6 @@
package im.zhaojun.common.model.dto;
package im.zhaojun.zfile.model.dto;
import im.zhaojun.common.model.enums.FileTypeEnum;
import im.zhaojun.zfile.model.enums.FileTypeEnum;
import java.io.Serializable;
import java.util.Date;

View File

@@ -1,6 +1,6 @@
package im.zhaojun.common.model.dto;
package im.zhaojun.zfile.model.dto;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import java.util.Map;

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.model.dto;
package im.zhaojun.zfile.model.dto;
import java.io.Serializable;
@@ -15,6 +15,8 @@ public class ResultBean implements Serializable {
public static final int REQUIRED_PASSWORD = -2;
public static final int INVALID_PASSWORD = -3;
private String msg = "操作成功";
private int code = SUCCESS;
@@ -43,6 +45,10 @@ public class ResultBean implements Serializable {
return success("操作成功", data);
}
public static ResultBean successPage(Object data, Long total) {
return success("操作成功", data);
}
public static ResultBean success(Object data) {
return success("操作成功", data);
}

View File

@@ -0,0 +1,23 @@
package im.zhaojun.zfile.model.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.ToString;
import java.io.Serializable;
/**
* @author zhaojun
*/
@Data
@ToString
public class SiteConfigDTO implements Serializable {
private static final long serialVersionUID = 8811196207046121740L;
private String readme;
@JsonProperty("viewConfig")
private SystemConfigDTO systemConfigDTO;
}

View File

@@ -0,0 +1,23 @@
package im.zhaojun.zfile.model.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Zhao Jun
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StorageStrategyDTO {
private String key;
private String description;
@JsonProperty(defaultValue = "false")
private Boolean available;
}

View File

@@ -1,9 +1,9 @@
package im.zhaojun.common.model.dto;
package im.zhaojun.zfile.model.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.model.enums.StorageTypeEnumSerializerConvert;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.model.enums.StorageTypeEnumSerializerConvert;
import lombok.Data;
import lombok.ToString;
@@ -37,4 +37,22 @@ public class SystemConfigDTO {
private String domain;
private Boolean enableCache;
private Boolean searchContainEncryptedFile;
private String customJs;
private String customCss;
private String tableSize;
private Boolean showOperator;
private Boolean showDocument;
private String announcement;
private Boolean showAnnouncement;
private String layout;
}

View File

@@ -1,6 +1,6 @@
package im.zhaojun.common.model;
package im.zhaojun.zfile.model.entity;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import lombok.Data;
import javax.persistence.Column;
@@ -27,6 +27,7 @@ public class StorageConfig {
private String title;
@Column(length = 4000)
private String value;
public Integer getId() {

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.model;
package im.zhaojun.zfile.model.entity;
import lombok.Data;

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.model.enums;
package im.zhaojun.zfile.model.enums;
/**
* @author zhaojun

View File

@@ -1,4 +1,6 @@
package im.zhaojun.common.model.enums;
package im.zhaojun.zfile.model.enums;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.HashMap;
import java.util.Map;
@@ -6,19 +8,24 @@ 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"),
LOCAL("local", "本地存储"),
ALIYUN("aliyun", "阿里云 OSS"),
TENCENT("tencent", "腾讯云 COS"),
MINIO("minio", "MINIO");
UPYUN("upyun", "又拍云 USS"),
FTP("ftp", "FTP"),
UFILE("ufile", "UFile"),
HUAWEI("huawei", "华为云 OBS"),
MINIO("minio", "MINIO"),
S3("s3", "S3通用协议"),
ONE_DRIVE("onedrive", "OneDrive"),
ONE_DRIVE_CHINA("onedrive-china", "OneDrive 世纪互联"),
QINIU("qiniu", "七牛云 KODO");
private String key;
private String description;

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.model.enums;
package im.zhaojun.zfile.model.enums;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.model.enums;
package im.zhaojun.zfile.model.enums;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.NonNull;

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.model.enums;
package im.zhaojun.zfile.model.enums;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;

View File

@@ -0,0 +1,22 @@
package im.zhaojun.zfile.model.support;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
/**
* @author zhaojun
*/
@Data
@AllArgsConstructor
public class FilePageModel {
private int total;
private int totalPage;
private List<FileItemDTO> fileList;
}

View File

@@ -0,0 +1,39 @@
package im.zhaojun.zfile.model.support;
import lombok.Data;
/**
* @author zhaojun
*/
@Data
public class Jvm {
/**
* 当前 JVM 占用的内存总数 (M)
*/
private double total;
/**
* JVM 最大可用内存总数 (M)
*/
private double max;
/**
* JVM 空闲内存 (M)
*/
private double free;
/**
* JDK 版本
*/
private String version;
public Jvm() {
Runtime runtime = Runtime.getRuntime();
this.total = runtime.totalMemory();
this.free = runtime.freeMemory();
this.max = runtime.maxMemory();
this.version = System.getProperty("java.version");
}
}

View File

@@ -0,0 +1,41 @@
package im.zhaojun.zfile.model.support;
import com.sun.management.OperatingSystemMXBean;
import lombok.Data;
import java.lang.management.ManagementFactory;
/**
* @author zhaojun
*/
@Data
public class Mem {
/**
* 内存总量
*/
private double total;
/**
* 已用内存
*/
private double used;
/**
* 剩余内存
*/
private double free;
public Mem() {
OperatingSystemMXBean osb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
// 总的物理内存+虚拟内存
long totalVirtualMemory = osb.getTotalSwapSpaceSize();
// 剩余的物理内存
long freePhysicalMemorySize = osb.getFreePhysicalMemorySize();
this.total = totalVirtualMemory;
this.free = freePhysicalMemorySize;
this.used = totalVirtualMemory - freePhysicalMemorySize;
}
}

View File

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

View File

@@ -0,0 +1,58 @@
package im.zhaojun.zfile.model.support;
import cn.hutool.core.date.BetweenFormater;
import cn.hutool.core.date.DateUtil;
import lombok.Data;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.lang.management.ManagementFactory;
/**
* @author zhaojun
*/
@Data
public class Sys {
/**
* 项目路径
*/
private String projectDir;
/**
* 操作系统
*/
private String osName;
/**
* 系统架构
*/
private String osArch;
/**
* 系统版本
*/
private String osVersion;
/**
* 启动时间
*/
private String upTime;
public Sys() {
this.osName = System.getProperty("os.name");
this.osArch = System.getProperty("os.arch");
this.osVersion = System.getProperty("os.version");
Resource resource = new ClassPathResource("");
try {
this.projectDir = resource.getFile().getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
long uptime = ManagementFactory.getRuntimeMXBean().getUptime();
this.upTime = DateUtil.formatBetween(uptime, BetweenFormater.Level.SECOND);
}
}

View File

@@ -0,0 +1,37 @@
package im.zhaojun.zfile.model.support;
import im.zhaojun.zfile.model.support.Jvm;
import im.zhaojun.zfile.model.support.Mem;
import im.zhaojun.zfile.model.support.Sys;
import lombok.Data;
import java.io.Serializable;
/**
* @author zhaojun
*/
@Data
public class SystemMonitorInfo implements Serializable {
/**
* 服务器基本信息
*/
private Sys sys;
/**
* JVM 信息
*/
private Jvm jvm;
/**
* 系统内存
*/
private Mem mem;
public SystemMonitorInfo() {
this.jvm = new Jvm();
this.sys = new Sys();
this.mem = new Mem();
}
}

View File

@@ -1,7 +1,7 @@
package im.zhaojun.common.repository;
package im.zhaojun.zfile.repository;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@@ -20,4 +20,12 @@ public interface StorageConfigRepository extends JpaRepository<StorageConfig, In
*/
List<StorageConfig> findByTypeOrderById(StorageTypeEnum type);
/**
* 根据存储类型找到某个 KEY 的值
* @param type 存储类型
* @param key KEY
* @return KEY 对应的对象
*/
StorageConfig findByTypeAndKey(StorageTypeEnum type, String key);
}

View File

@@ -1,6 +1,6 @@
package im.zhaojun.common.repository;
package im.zhaojun.zfile.repository;
import im.zhaojun.common.model.SystemConfig;
import im.zhaojun.zfile.model.entity.SystemConfig;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.security;
package im.zhaojun.zfile.security;
import cn.hutool.crypto.SecureUtil;
import org.springframework.security.crypto.password.PasswordEncoder;

View File

@@ -1,7 +1,7 @@
package im.zhaojun.common.security;
package im.zhaojun.zfile.security;
import com.fasterxml.jackson.databind.ObjectMapper;
import im.zhaojun.common.model.dto.ResultBean;
import im.zhaojun.zfile.model.dto.ResultBean;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.BadCredentialsException;
@@ -21,6 +21,7 @@ import java.io.PrintWriter;
/**
* 自定义 Security 配置类
*
* @author zhaojun
*/
@EnableWebSecurity
@@ -34,7 +35,7 @@ public class MySecurityConfig extends WebSecurityConfigurerAdapter {
http
// .authenticationProvider(authenticationProvider())
.exceptionHandling()
//未登录时进行json格式的提示很喜欢这种写法不用单独写一个又一个的类
// 未登录时进行 json 格式的提示.
.authenticationEntryPoint((request, response, authException) -> {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
@@ -48,8 +49,8 @@ public class MySecurityConfig extends WebSecurityConfigurerAdapter {
.antMatchers("/").permitAll()
.antMatchers("/admin/**").authenticated()
.and()
.formLogin() //使用自带的登录
//登录失败返回json
.formLogin() // 使用自带的登录
// 登录失败返回json
.failureHandler((request, response, ex) -> {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
@@ -64,7 +65,7 @@ public class MySecurityConfig extends WebSecurityConfigurerAdapter {
out.flush();
out.close();
})
//登录成功返回json
// 登录成功返回json
.successHandler((request, response, authentication) -> {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
@@ -74,7 +75,7 @@ public class MySecurityConfig extends WebSecurityConfigurerAdapter {
})
.and()
.exceptionHandling()
//没有权限返回json
// 没有权限返回json
.accessDeniedHandler((request, response, ex) -> {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
@@ -85,7 +86,7 @@ public class MySecurityConfig extends WebSecurityConfigurerAdapter {
})
.and()
.logout()
//退出成功返回json
// 退出成功返回 json
.logoutSuccessHandler((request, response, authentication) -> {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
@@ -98,6 +99,7 @@ public class MySecurityConfig extends WebSecurityConfigurerAdapter {
http.cors();
http.csrf().disable();
http.headers().frameOptions().sameOrigin();
}
@Bean
@@ -119,7 +121,7 @@ public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
//对于在header里面增加token等类似情况放行所有OPTIONS请求
// 对于在 header 里面增加 token 等类似情况放行所有 OPTIONS 请求
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
}

View File

@@ -1,7 +1,7 @@
package im.zhaojun.common.security;
package im.zhaojun.zfile.security;
import im.zhaojun.common.model.dto.SystemConfigDTO;
import im.zhaojun.common.service.SystemConfigService;
import im.zhaojun.zfile.model.dto.SystemConfigDTO;
import im.zhaojun.zfile.service.SystemConfigService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -9,6 +9,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.Objects;
/**
* @author zhaojun
@@ -24,6 +25,9 @@ public class MyUserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();
if (!Objects.equals(systemConfig.getUsername(), username)) {
throw new UsernameNotFoundException("用户名不存在");
}
return new User(systemConfig.getUsername(), systemConfig.getPassword(), Collections.emptyList());
}
}

View File

@@ -1,8 +1,8 @@
package im.zhaojun.common.service;
package im.zhaojun.zfile.service;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.repository.StorageConfigRepository;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.repository.StorageConfigRepository;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@@ -23,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)) {
@@ -31,6 +37,7 @@ public class StorageConfigService {
return map;
}
public void updateStorageConfig(List<StorageConfig> storageConfigList) {
storageConfigRepository.saveAll(storageConfigList);
}

View File

@@ -1,17 +1,16 @@
package im.zhaojun.common.service;
package im.zhaojun.zfile.service;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
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.zfile.cache.ZFileCache;
import im.zhaojun.zfile.config.StorageTypeFactory;
import im.zhaojun.zfile.model.constant.SystemConfigConstant;
import im.zhaojun.zfile.model.dto.SystemConfigDTO;
import im.zhaojun.zfile.model.entity.SystemConfig;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.repository.SystemConfigRepository;
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -27,28 +26,18 @@ import java.util.List;
@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 ZFileCache zFileCache;
@Resource
private SystemConfigRepository systemConfigRepository;
@Resource
private FileAsyncCacheService fileAsyncCacheService;
@Resource
private FileCacheService fileCacheService;
private Class<SystemConfigDTO> systemConfigDTOClass = SystemConfigDTO.class;
private Class<SystemConfigDTO> systemConfigClazz = 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 cacheConfig = zFileCache.getConfig();
if (cacheConfig != null) {
return cacheConfig;
}
SystemConfigDTO systemConfigDTO = new SystemConfigDTO();
@@ -58,7 +47,7 @@ public class SystemConfigService {
String key = systemConfig.getKey();
try {
Field field = systemConfigDTOClass.getDeclaredField(key);
Field field = systemConfigClazz.getDeclaredField(key);
if (field != null) {
field.setAccessible(true);
String strVal = systemConfig.getValue();
@@ -72,7 +61,7 @@ public class SystemConfigService {
}
}
configCache.put(SYSTEM_CONFIG_CACHE_KEY, systemConfigDTO);
zFileCache.updateConfig(systemConfigDTO);
return systemConfigDTO;
}
@@ -80,7 +69,7 @@ public class SystemConfigService {
public void updateSystemConfig(SystemConfigDTO systemConfigDTO) throws Exception {
List<SystemConfig> systemConfigList = new ArrayList<>();
Field[] fields = systemConfigDTOClass.getDeclaredFields();
Field[] fields = systemConfigClazz.getDeclaredFields();
for (Field field : fields) {
String key = field.getName();
SystemConfig systemConfig = systemConfigRepository.findByKey(key);
@@ -94,24 +83,8 @@ public class SystemConfigService {
}
}
boolean oldEnableCache = getEnableCache();
boolean curEnableCache = ObjectUtil.defaultIfNull(systemConfigDTO.getEnableCache(), false);
configCache.remove(SYSTEM_CONFIG_CACHE_KEY);
zFileCache.removeConfig();
systemConfigRepository.saveAll(systemConfigList);
AbstractFileService currentFileService = getCurrentFileService();
if (!oldEnableCache && curEnableCache) {
log.debug("检测到开启了缓存, 开启预热缓存");
fileCacheService.enableCache();
}
if (oldEnableCache && !curEnableCache) {
log.debug("检测到关闭了缓存, 正在清理缓存数据及关闭自动刷新");
fileCacheService.disableCache();
}
}
@@ -124,7 +97,7 @@ public class SystemConfigService {
SystemConfig systemConfig = systemConfigRepository.findByKey(SystemConfigConstant.PASSWORD);
systemConfig.setValue(encryptionPassword);
configCache.remove(SYSTEM_CONFIG_CACHE_KEY);
zFileCache.removeConfig();
systemConfigRepository.save(systemConfig);
}
@@ -134,11 +107,11 @@ public class SystemConfigService {
SystemConfig enableConfig = systemConfigRepository.findByKey(SystemConfigConstant.ENABLE_CACHE);
enableConfig.setValue(isEnable.toString());
systemConfigRepository.save(enableConfig);
configCache.remove(SYSTEM_CONFIG_CACHE_KEY);
zFileCache.removeConfig();
}
public AbstractFileService getCurrentFileService() {
public AbstractBaseFileService getCurrentFileService() {
StorageTypeEnum storageStrategy = getCurrentStorageStrategy();
return StorageTypeFactory.getStorageTypeService(storageStrategy);
}
@@ -149,10 +122,15 @@ public class SystemConfigService {
return systemConfigDTO.getStorageStrategy();
}
public boolean getEnableCache() {
SystemConfigDTO systemConfigDTO = getSystemConfig();
return ObjectUtil.defaultIfNull(systemConfigDTO.getEnableCache(), false);
return BooleanUtil.isTrue(systemConfigDTO.getEnableCache());
}
public boolean getSearchIgnoreCase() {
SystemConfigDTO systemConfigDTO = getSystemConfig();
return BooleanUtil.isTrue(systemConfigDTO.getSearchIgnoreCase());
}
}

View File

@@ -0,0 +1,67 @@
package im.zhaojun.zfile.service;
import im.zhaojun.zfile.model.constant.ZFileConstant;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import im.zhaojun.zfile.model.dto.SiteConfigDTO;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
import im.zhaojun.zfile.util.HttpUtil;
import im.zhaojun.zfile.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* @author zhaojun
*/
@Slf4j
@Service
public class SystemService {
@Resource
private SystemConfigService systemConfigService;
/**
* 构建指定路径下标题, 页面文档信息
* @param path 路径
*/
public SiteConfigDTO getConfig(String path) throws Exception {
SiteConfigDTO siteConfigDTO = new SiteConfigDTO();
AbstractBaseFileService fileService = systemConfigService.getCurrentFileService();
List<FileItemDTO> fileItemList;
if (Objects.equals(systemConfigService.getSystemConfig().getStorageStrategy(), StorageTypeEnum.FTP)) {
fileItemList = new ArrayList<>();
} else {
fileItemList = fileService.fileList(path);
}
for (FileItemDTO fileItemDTO : fileItemList) {
if (ZFileConstant.README_FILE_NAME.equalsIgnoreCase(fileItemDTO.getName())) {
String textContent = null;
try {
textContent = HttpUtil.getTextContent(fileItemDTO.getUrl());
} catch (HttpClientErrorException httpClientErrorException) {
log.debug("尝试重新获取文档区缓存中链接后仍失败", httpClientErrorException);
try {
String fullPath = StringUtils.removeDuplicateSeparator(fileItemDTO.getPath() + "/" + fileItemDTO.getName());
FileItemDTO fileItem = fileService.getFileItem(fullPath);
textContent = HttpUtil.getTextContent(fileItem.getUrl());
} catch (Exception e) {
log.debug("尝试重新获取文档区链接后仍失败, 已置为空", e);
}
}
siteConfigDTO.setReadme(textContent);
}
}
return siteConfigDTO;
}
}

View File

@@ -0,0 +1,128 @@
package im.zhaojun.zfile.service.base;
import cn.hutool.core.util.BooleanUtil;
import im.zhaojun.zfile.cache.ZFileCache;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import im.zhaojun.zfile.model.dto.SystemConfigDTO;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.SystemConfigService;
import im.zhaojun.zfile.service.support.FileAsyncCacheService;
import im.zhaojun.zfile.service.support.FileCacheService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Value;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
/**
* @author zhaojun
*/
@Slf4j
public abstract class AbstractBaseFileService extends FileCacheService implements BaseFileService {
@Value("${zfile.cache.timeout}")
protected Long timeout;
protected boolean isInitialized = false;
protected String basePath;
@Resource
private SystemConfigService systemConfigService;
@Resource
private FileAsyncCacheService fileAsyncCacheService;
@Resource
private ZFileCache zFileCache;
/***
* 获取指定路径下的文件及文件夹, 默认缓存 60 分钟,每隔 30 分钟刷新一次.
* @param path 文件路径
* @return 文件及文件夹列表
* @throws Exception 获取文件列表中出现的异常
*/
@Override
public abstract List<FileItemDTO> fileList(String path) throws Exception;
/**
* 清理当前存储策略的缓存
* 1. 删除全部缓存
* 2. 标记为当前处于未完成缓存状态
*/
public void clearFileCache() {
zFileCache.clear();
fileAsyncCacheService.setCacheFinish(false);
}
/**
* 初始化方法, 启动时自动调用实现类的此方法进行初始化.
*/
@PostConstruct
public abstract void init();
protected boolean testConnection() {
boolean flag = true;
try {
fileList("/");
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常", e);
flag = false;
}
return flag;
}
/**
* 获取是否初始化成功
* @return 初始化成功与否
*/
public boolean getIsUnInitialized() {
return !isInitialized;
}
/**
* 获取是否初始化成功
* @return 初始化成功与否
*/
public boolean getIsInitialized() {
return isInitialized;
}
/**
* 获取存储策略类型
* @return 存储策略类型枚举
*/
public abstract StorageTypeEnum getStorageTypeEnum();
/**
* 搜索文件
* @param name 文件名
* @return 包含该文件名的所有文件或文件夹
*/
public List<FileItemDTO> search(String name) {
SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();
boolean searchIgnoreCase = BooleanUtil.isTrue(systemConfig.getSearchIgnoreCase());
boolean searchContainEncryptedFile = BooleanUtil.isTrue(systemConfig.getSearchContainEncryptedFile());
return zFileCache.find(name, searchIgnoreCase, searchContainEncryptedFile);
}
/**
* 刷新缓存
*/
public void refreshCache(String key) throws Exception {
zFileCache.remove(key);
BaseFileService currentFileService = (BaseFileService) AopContext.currentProxy();
currentFileService.fileList(key);
}
/**
* 获取单个文件信息
* @param path 文件路径
* @return 单个文件的内容.
*/
public abstract FileItemDTO getFileItem(String path);
}

View File

@@ -0,0 +1,214 @@
package im.zhaojun.zfile.service.base;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import im.zhaojun.zfile.model.constant.ZFileConstant;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import im.zhaojun.zfile.model.enums.FileTypeEnum;
import im.zhaojun.zfile.repository.StorageConfigRepository;
import im.zhaojun.zfile.service.StorageConfigService;
import im.zhaojun.zfile.util.StringUtils;
import im.zhaojun.zfile.model.support.OneDriveToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Zhao Jun
* 2020/1/29 11:54
*/
@Slf4j
public abstract class AbstractOneDriveServiceBase extends AbstractBaseFileService {
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";
private static final String ONE_DRIVE_FILE_FLAG = "file";
@Resource
private RestTemplate oneDriveRestTemplate;
@Resource
private StorageConfigRepository storageConfigRepository;
@Resource
private StorageConfigService storageConfigService;
public OneDriveToken getRefreshToken() {
StorageConfig refreshStorageConfig =
storageConfigRepository.findByTypeAndKey(this.getStorageTypeEnum(), StorageConfigConstant.REFRESH_TOKEN_KEY);
String param = "client_id=" + getClientId() +
"&redirect_uri=" + getRedirectUri() +
"&client_secret=" + getClientSecret() +
"&refresh_token=" + refreshStorageConfig.getValue() +
"&grant_type=refresh_token";
String fullAuthenticateUrl = AUTHENTICATE_URL.replace("{authenticateEndPoint}", getAuthenticateEndPoint());
HttpRequest post = HttpUtil.createPost(fullAuthenticateUrl);
post.body(param, "application/x-www-form-urlencoded");
HttpResponse response = post.execute();
return JSONObject.parseObject(response.body(), OneDriveToken.class);
}
public OneDriveToken getToken(String code) {
String param = "client_id=" + getClientId() +
"&redirect_uri=" + getRedirectUri() +
"&client_secret=" + getClientSecret() +
"&code=" + code +
"&scope=" + getScope() +
"&grant_type=authorization_code";
String fullAuthenticateUrl = AUTHENTICATE_URL.replace("{authenticateEndPoint}", getAuthenticateEndPoint());
HttpRequest post = HttpUtil.createPost(fullAuthenticateUrl);
post.body(param, "application/x-www-form-urlencoded");
HttpResponse response = post.execute();
return JSONObject.parseObject(response.body(), OneDriveToken.class);
}
public String getUserInfo() {
return oneDriveRestTemplate.getForObject(DRIVER_INFO_URL, String.class);
}
@Override
public List<FileItemDTO> fileList(String path) {
path = StringUtils.removeFirstSeparator(path);
String fullPath = StringUtils.getFullPath(basePath, path);
List<FileItemDTO> result = new ArrayList<>();
String nextLink = null;
do {
String requestUrl;
if (nextLink != null) {
nextLink = nextLink.replace("+", "%2B");
requestUrl = URLUtil.decode(nextLink);
}else if (ZFileConstant.PATH_SEPARATOR.equalsIgnoreCase(fullPath) || "".equalsIgnoreCase(fullPath)) {
requestUrl = DRIVER_ROOT_URL;
} else {
requestUrl = DRIVER_ITEMS_URL;
}
fullPath = StringUtils.removeLastSeparator(fullPath);
ResponseEntity<String> responseEntity;
try {
responseEntity = oneDriveRestTemplate.getForEntity(requestUrl, String.class, getGraphEndPoint(), fullPath);
} catch (HttpClientErrorException e) {
log.debug("调用 OneDrive 时出现了网络异常: {} , 已尝试重新刷新 token 后再试.", e.getMessage());
refreshOneDriveToken();
responseEntity = oneDriveRestTemplate.getForEntity(requestUrl, String.class, getGraphEndPoint(), fullPath);
}
String body = responseEntity.getBody();
JSONObject root = JSON.parseObject(body);
nextLink = root.getString("@odata.nextLink");
JSONArray fileList = root.getJSONArray("value");
for (int i = 0; i < fileList.size(); i++) {
FileItemDTO fileItemDTO = new FileItemDTO();
JSONObject fileItem = fileList.getJSONObject(i);
fileItemDTO.setName(fileItem.getString("name"));
fileItemDTO.setSize(fileItem.getLong("size"));
fileItemDTO.setTime(fileItem.getDate("lastModifiedDateTime"));
if (fileItem.containsKey("file")) {
fileItemDTO.setUrl(fileItem.getString("@microsoft.graph.downloadUrl"));
fileItemDTO.setType(FileTypeEnum.FILE);
} else {
fileItemDTO.setType(FileTypeEnum.FOLDER);
}
fileItemDTO.setPath(path);
result.add(fileItemDTO);
}
} while (nextLink != null);
return result;
}
@Override
public FileItemDTO getFileItem(String path) {
String fullPath = StringUtils.getFullPath(basePath, path);
String requestUrl;
ResponseEntity<String> responseEntity = oneDriveRestTemplate.getForEntity(DRIVER_ITEM_URL, String.class, getGraphEndPoint(), fullPath);
String body = responseEntity.getBody();
JSONObject fileItem = JSON.parseObject(body);
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setName(fileItem.getString("name"));
fileItemDTO.setSize(fileItem.getLong("size"));
fileItemDTO.setTime(fileItem.getDate("lastModifiedDateTime"));
if (fileItem.containsKey(ONE_DRIVE_FILE_FLAG)) {
fileItemDTO.setUrl(fileItem.getString("@microsoft.graph.downloadUrl"));
fileItemDTO.setType(FileTypeEnum.FILE);
} else {
fileItemDTO.setType(FileTypeEnum.FOLDER);
}
fileItemDTO.setPath(path);
return fileItemDTO;
}
public abstract String getGraphEndPoint();
public abstract String getAuthenticateEndPoint();
public abstract String getClientId();
public abstract String getRedirectUri();
public abstract String getClientSecret();
public abstract String getScope();
public void refreshOneDriveToken() {
OneDriveToken refreshToken = getRefreshToken();
if (refreshToken.getAccessToken() == null || refreshToken.getRefreshToken() == null) {
return;
}
StorageConfig accessTokenConfig =
storageConfigService.selectByTypeAndKey(this.getStorageTypeEnum(), StorageConfigConstant.ACCESS_TOKEN_KEY);
StorageConfig refreshTokenConfig =
storageConfigService.selectByTypeAndKey(this.getStorageTypeEnum(), StorageConfigConstant.REFRESH_TOKEN_KEY);
accessTokenConfig.setValue(refreshToken.getAccessToken());
refreshTokenConfig.setValue(refreshToken.getRefreshToken());
storageConfigService.updateStorageConfig(Arrays.asList(accessTokenConfig, refreshTokenConfig));
}
}

View File

@@ -1,40 +1,42 @@
package im.zhaojun.common.service;
package im.zhaojun.zfile.service.base;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.URLUtil;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.ListObjectsRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
import im.zhaojun.common.util.StringUtils;
import im.zhaojun.zfile.service.StorageConfigService;
import im.zhaojun.zfile.exception.NotExistFileException;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import im.zhaojun.zfile.model.enums.FileTypeEnum;
import im.zhaojun.zfile.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 {
public abstract class AbstractS3BaseFileService extends AbstractBaseFileService {
@Resource
protected StorageConfigService storageConfigService;
protected String path;
protected String basePath;
protected String bucketName;
protected String domain;
protected AmazonS3 s3Client;
protected boolean isPrivate;
@Override
public List<FileItemDTO> fileList(String path) {
this.path = path;
@@ -54,7 +56,7 @@ public abstract class AbstractS3FileService extends AbstractFileService {
*/
public List<FileItemDTO> s3FileList(String path) {
path = StringUtils.removeFirstSeparator(path);
String fullPath = StringUtils.removeFirstSeparator(getFullPath());
String fullPath = StringUtils.removeFirstSeparator(StringUtils.getFullPath(basePath, path));
List<FileItemDTO> fileItemList = new ArrayList<>();
ObjectListing objectListing = s3Client.listObjects(new ListObjectsRequest(bucketName, fullPath, "", "/", 1000));
@@ -74,6 +76,9 @@ public abstract class AbstractS3FileService extends AbstractFileService {
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);
@@ -90,6 +95,11 @@ public abstract class AbstractS3FileService extends AbstractFileService {
public String s3ObjectUrl(String path) {
String fullPath = StringUtils.removeFirstSeparator(StringUtils.removeDuplicateSeparator(basePath + "/" + path));
// 如果不是私有空间, 且指定了加速域名, 则直接返回下载地址.
if (BooleanUtil.isFalse(isPrivate) && StringUtils.isNotNullOrEmpty(domain)) {
return StringUtils.concatPath(domain, fullPath);
}
Date expirationDate = new Date(System.currentTimeMillis() + timeout * 1000);
URL url = s3Client.generatePresignedUrl(bucketName, fullPath, expirationDate);
@@ -100,13 +110,23 @@ public abstract class AbstractS3FileService extends AbstractFileService {
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;
try {
int end = path.lastIndexOf("/");
list = fileList(path.substring(0, end + 1));
} catch (Exception e) {
throw new NotExistFileException();
}
for (FileItemDTO fileItemDTO : list) {
String fullPath = StringUtils.concatUrl(fileItemDTO.getPath(), fileItemDTO.getName());
if (Objects.equals(fullPath, path)) {
return fileItemDTO;
}
}
throw new NotExistFileException();
}
}

View File

@@ -1,13 +1,13 @@
package im.zhaojun.common.service;
package im.zhaojun.zfile.service.base;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import java.util.List;
/**
* @author zhaojun
*/
public interface FileService {
public interface BaseFileService {
/***
* 获取指定路径下的文件及文件夹

View File

@@ -1,14 +1,15 @@
package im.zhaojun.aliyun.service;
package im.zhaojun.zfile.service.impl;
import cn.hutool.core.convert.Convert;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractS3FileService;
import im.zhaojun.common.service.FileService;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.base.AbstractS3BaseFileService;
import im.zhaojun.zfile.service.base.BaseFileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@@ -20,21 +21,23 @@ import java.util.Objects;
* @author zhaojun
*/
@Service
public class AliyunServiceImpl extends AbstractS3FileService implements FileService {
public class AliyunServiceImpl extends AbstractS3BaseFileService implements BaseFileService {
private static final Logger log = LoggerFactory.getLogger(AliyunServiceImpl.class);
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.ALIYUN);
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
super.bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
super.domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
super.bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
super.isPrivate = Convert.toBool(stringStorageConfigMap.get(StorageConfigConstant.IS_PRIVATE).getValue(), true);
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
@@ -47,10 +50,8 @@ public class AliyunServiceImpl extends AbstractS3FileService implements FileServ
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "oss")).build();
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}

View File

@@ -0,0 +1,132 @@
package im.zhaojun.zfile.service.impl;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.ftp.Ftp;
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.enums.FileTypeEnum;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.StorageConfigService;
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
import im.zhaojun.zfile.service.base.BaseFileService;
import im.zhaojun.zfile.util.StringUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @author zhaojun
*/
@Service
public class FtpServiceImpl extends AbstractBaseFileService implements BaseFileService {
private static final Logger log = LoggerFactory.getLogger(FtpServiceImpl.class);
@Resource
private StorageConfigService storageConfigService;
private Ftp ftp;
private String domain;
private String host;
private String port;
private String username;
private String password;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
host = stringStorageConfigMap.get(StorageConfigConstant.HOST_KEY).getValue();
port = stringStorageConfigMap.get(StorageConfigConstant.PORT_KEY).getValue();
username = stringStorageConfigMap.get(StorageConfigConstant.USERNAME_KEY).getValue();
password = stringStorageConfigMap.get(StorageConfigConstant.PASSWORD_KEY).getValue();
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
if (Objects.isNull(host) || Objects.isNull(port) || Objects.isNull(username) || Objects.isNull(password)) {
isInitialized = false;
} else {
ftp = new Ftp(host, Integer.parseInt(port), username, password, StandardCharsets.UTF_8);
ftp.getClient().configure(new FTPClientConfig(FTPClientConfig.SYST_UNIX));
ftp.getClient().type(FTP.BINARY_FILE_TYPE);
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@Override
public List<FileItemDTO> fileList(String path) {
ftp.reconnectIfTimeout();
String fullPath = StringUtils.getFullPath(basePath, path);
ftp.cd(fullPath);
FTPFile[] ftpFiles = new FTPFile[]{};
try {
ftp.getClient().changeWorkingDirectory("/");
ftpFiles = ftp.getClient().listFiles(fullPath);
} catch (Exception e) {
e.printStackTrace();
// ignore
}
List<FileItemDTO> fileItemList = new ArrayList<>();
for (FTPFile ftpFile : ftpFiles) {
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setName(ftpFile.getName());
fileItemDTO.setSize(ftpFile.getSize());
fileItemDTO.setTime(ftpFile.getTimestamp().getTime());
fileItemDTO.setType(ftpFile.isDirectory() ? FileTypeEnum.FOLDER : FileTypeEnum.FILE);
fileItemDTO.setPath(path);
if (ftpFile.isFile()) {
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(path, fileItemDTO.getName())));
}
fileItemList.add(fileItemDTO);
}
return fileItemList;
}
@Override
public String getDownloadUrl(String path) {
String fullPath = StringUtils.getFullPath(basePath, path);
if (StringUtils.isNullOrEmpty(domain)) {
return "ftp://"
+ URLUtil.encodeQuery(username)
+ ":"
+ URLUtil.encodeQuery(password)
+ "@"
+ host + ":" + port + fullPath;
}
return URLUtil.complateUrl(domain, fullPath);
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.FTP;
}
@Override
public FileItemDTO getFileItem(String path) {
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setUrl(getDownloadUrl(path));
return fileItemDTO;
}
}

View File

@@ -1,14 +1,15 @@
package im.zhaojun.huawei.service;
package im.zhaojun.zfile.service.impl;
import cn.hutool.core.convert.Convert;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractS3FileService;
import im.zhaojun.common.service.FileService;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.base.AbstractS3BaseFileService;
import im.zhaojun.zfile.service.base.BaseFileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@@ -20,14 +21,15 @@ import java.util.Objects;
* @author zhaojun
*/
@Service
public class HuaweiServiceImpl extends AbstractS3FileService implements FileService {
public class HuaweiServiceImpl extends AbstractS3BaseFileService implements BaseFileService {
private static final Logger log = LoggerFactory.getLogger(HuaweiServiceImpl.class);
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.HUAWEI);
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
@@ -35,6 +37,7 @@ public class HuaweiServiceImpl extends AbstractS3FileService implements FileServ
bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
isPrivate = Convert.toBool(stringStorageConfigMap.get(StorageConfigConstant.IS_PRIVATE).getValue(), true);
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
@@ -48,7 +51,7 @@ public class HuaweiServiceImpl extends AbstractS3FileService implements FileServ
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}

View File

@@ -1,18 +1,18 @@
package im.zhaojun.local.service;
package im.zhaojun.zfile.service.impl;
import cn.hutool.core.util.URLUtil;
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 im.zhaojun.zfile.exception.NotExistFileException;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.entity.SystemConfig;
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
import im.zhaojun.zfile.model.constant.SystemConfigConstant;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import im.zhaojun.zfile.model.enums.FileTypeEnum;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.repository.SystemConfigRepository;
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
import im.zhaojun.zfile.service.base.BaseFileService;
import im.zhaojun.zfile.service.StorageConfigService;
import im.zhaojun.zfile.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@@ -29,7 +29,7 @@ import java.util.Objects;
* @author zhaojun
*/
@Service
public class LocalServiceImpl extends AbstractFileService implements FileService {
public class LocalServiceImpl extends AbstractBaseFileService implements BaseFileService {
private static final Logger log = LoggerFactory.getLogger(LocalServiceImpl.class);
@@ -45,7 +45,7 @@ public class LocalServiceImpl extends AbstractFileService implements FileService
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.LOCAL);
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
filePath = stringStorageConfigMap.get(StorageConfigConstant.FILE_PATH_KEY).getValue();
if (Objects.isNull(filePath)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
@@ -54,7 +54,7 @@ public class LocalServiceImpl extends AbstractFileService implements FileService
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@@ -89,7 +89,7 @@ public class LocalServiceImpl extends AbstractFileService implements FileService
@Override
public String getDownloadUrl(String path) {
SystemConfig usernameConfig = systemConfigRepository.findByKey(SystemConfigConstant.DOMAIN);
return URLUtil.encode(StringUtils.removeDuplicateSeparator(usernameConfig.getValue() + "/file/" + path));
return StringUtils.removeDuplicateSeparator(usernameConfig.getValue() + "/file/" + path);
}
public String getFilePath() {
@@ -105,4 +105,26 @@ public class LocalServiceImpl extends AbstractFileService implements FileService
return StorageTypeEnum.LOCAL;
}
@Override
public FileItemDTO getFileItem(String path) {
String fullPath = StringUtils.concatPath(filePath, path);
File file = new File(fullPath);
if (!file.exists()) {
throw new NotExistFileException();
}
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setType(file.isDirectory() ? FileTypeEnum.FOLDER : FileTypeEnum.FILE);
fileItemDTO.setTime(new Date(file.lastModified()));
fileItemDTO.setSize(file.length());
fileItemDTO.setName(file.getName());
fileItemDTO.setPath(filePath);
if (file.isFile()) {
fileItemDTO.setUrl(getDownloadUrl(path));
}
return fileItemDTO;
}
}

View File

@@ -1,14 +1,15 @@
package im.zhaojun.minio;
package im.zhaojun.zfile.service.impl;
import cn.hutool.core.convert.Convert;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractS3FileService;
import im.zhaojun.common.service.FileService;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.base.AbstractS3BaseFileService;
import im.zhaojun.zfile.service.base.BaseFileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@@ -20,19 +21,21 @@ import java.util.Objects;
* @author zhaojun
*/
@Service
public class MinIOServiceImpl extends AbstractS3FileService implements FileService {
public class MinIOServiceImpl extends AbstractS3BaseFileService implements BaseFileService {
private static final Logger log = LoggerFactory.getLogger(MinIOServiceImpl.class);
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.MINIO);
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
isPrivate = Convert.toBool(stringStorageConfigMap.get(StorageConfigConstant.IS_PRIVATE).getValue(), true);
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
@@ -40,13 +43,14 @@ public class MinIOServiceImpl extends AbstractS3FileService implements FileServi
} else {
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
s3Client = AmazonS3ClientBuilder.standard()
.withPathStyleAccessEnabled(true)
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "minio")).build();
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}

View File

@@ -0,0 +1,99 @@
package im.zhaojun.zfile.service.impl;
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.StorageConfigService;
import im.zhaojun.zfile.service.base.AbstractOneDriveServiceBase;
import im.zhaojun.zfile.service.base.BaseFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.Map;
/**
* @author zhaojun
*/
@Service
@Slf4j
public class OneDriveChinaServiceImpl extends AbstractOneDriveServiceBase implements BaseFileService {
@Resource
private StorageConfigService storageConfigService;
@Value("${zfile.onedrive-china.clientId}")
private String clientId;
@Value("${zfile.onedrive-china.redirectUri}")
private String redirectUri;
@Value("${zfile.onedrive-china.clientSecret}")
private String clientSecret;
@Value("${zfile.onedrive-china.scope}")
private String scope;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
String accessToken = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_TOKEN_KEY).getValue();
String refreshToken = stringStorageConfigMap.get(StorageConfigConstant.REFRESH_TOKEN_KEY).getValue();
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
isInitialized = false;
} else {
refreshOneDriveToken();
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@Override
public String getDownloadUrl(String path) {
return null;
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.ONE_DRIVE_CHINA;
}
@Override
public String getGraphEndPoint() {
return "microsoftgraph.chinacloudapi.cn";
}
@Override
public String getAuthenticateEndPoint() {
return "login.partner.microsoftonline.cn";
}
@Override
public String getClientId() {
return clientId;
}
@Override
public String getRedirectUri() {
return redirectUri;
}
@Override
public String getClientSecret() {
return clientSecret;
}
@Override
public String getScope() {
return scope;
}
}

View File

@@ -0,0 +1,99 @@
package im.zhaojun.zfile.service.impl;
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.StorageConfigService;
import im.zhaojun.zfile.service.base.AbstractOneDriveServiceBase;
import im.zhaojun.zfile.service.base.BaseFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.Map;
/**
* @author zhaojun
*/
@Service
@Slf4j
public class OneDriveServiceImpl extends AbstractOneDriveServiceBase implements BaseFileService {
@Resource
private StorageConfigService storageConfigService;
@Value("${zfile.onedrive.clientId}")
protected String clientId;
@Value("${zfile.onedrive.redirectUri}")
protected String redirectUri;
@Value("${zfile.onedrive.clientSecret}")
protected String clientSecret;
@Value("${zfile.onedrive.scope}")
protected String scope;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
String accessToken = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_TOKEN_KEY).getValue();
String refreshToken = stringStorageConfigMap.get(StorageConfigConstant.REFRESH_TOKEN_KEY).getValue();
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
isInitialized = false;
} else {
refreshOneDriveToken();
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@Override
public String getDownloadUrl(String path) {
return null;
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.ONE_DRIVE;
}
@Override
public String getGraphEndPoint() {
return "graph.microsoft.com";
}
@Override
public String getAuthenticateEndPoint() {
return "login.microsoftonline.com";
}
@Override
public String getClientId() {
return clientId;
}
@Override
public String getRedirectUri() {
return redirectUri;
}
@Override
public String getClientSecret() {
return clientSecret;
}
@Override
public String getScope() {
return scope;
}
}

View File

@@ -1,14 +1,15 @@
package im.zhaojun.qiniu.service;
package im.zhaojun.zfile.service.impl;
import cn.hutool.core.convert.Convert;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractS3FileService;
import im.zhaojun.common.service.FileService;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.base.AbstractS3BaseFileService;
import im.zhaojun.zfile.service.base.BaseFileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@@ -20,14 +21,15 @@ import java.util.Objects;
* @author zhaojun
*/
@Service
public class QiniuServiceImpl extends AbstractS3FileService implements FileService {
public class QiniuServiceImpl extends AbstractS3BaseFileService implements BaseFileService {
private static final Logger log = LoggerFactory.getLogger(QiniuServiceImpl.class);
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.QINIU);
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
@@ -35,6 +37,7 @@ public class QiniuServiceImpl extends AbstractS3FileService implements FileServi
bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
isPrivate = Convert.toBool(stringStorageConfigMap.get(StorageConfigConstant.IS_PRIVATE).getValue(), true);
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
@@ -48,7 +51,7 @@ public class QiniuServiceImpl extends AbstractS3FileService implements FileServi
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}

View File

@@ -0,0 +1,68 @@
package im.zhaojun.zfile.service.impl;
import cn.hutool.core.convert.Convert;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.base.AbstractS3BaseFileService;
import im.zhaojun.zfile.service.base.BaseFileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.Objects;
/**
* @author zhaojun
*/
@Service
public class S3ServiceImpl extends AbstractS3BaseFileService implements BaseFileService {
private static final Logger log = LoggerFactory.getLogger(S3ServiceImpl.class);
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
super.domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
super.bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
super.isPrivate = Convert.toBool(stringStorageConfigMap.get(StorageConfigConstant.IS_PRIVATE).getValue(), true);
String pathStyle = stringStorageConfigMap.get(StorageConfigConstant.PATH_STYLE).getValue();
boolean isPathStyle = "path-style".equals(pathStyle);
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
isInitialized = false;
} else {
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
s3Client = AmazonS3ClientBuilder.standard()
.withPathStyleAccessEnabled(isPathStyle)
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "")).build();
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.S3;
}
}

View File

@@ -1,14 +1,15 @@
package im.zhaojun.tencent;
package im.zhaojun.zfile.service.impl;
import cn.hutool.core.convert.Convert;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractS3FileService;
import im.zhaojun.common.service.FileService;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.base.AbstractS3BaseFileService;
import im.zhaojun.zfile.service.base.BaseFileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@@ -20,20 +21,22 @@ import java.util.Objects;
* @author zhaojun
*/
@Service
public class TencentServiceImpl extends AbstractS3FileService implements FileService {
public class TencentServiceImpl extends AbstractS3BaseFileService implements BaseFileService {
private static final Logger log = LoggerFactory.getLogger(TencentServiceImpl.class);
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.TENCENT);
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
String secretId = stringStorageConfigMap.get(StorageConfigConstant.SECRET_ID_KEY).getValue();
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
isPrivate = Convert.toBool(stringStorageConfigMap.get(StorageConfigConstant.IS_PRIVATE).getValue(), true);
if (Objects.isNull(secretId) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
@@ -47,7 +50,7 @@ public class TencentServiceImpl extends AbstractS3FileService implements FileSer
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}

View File

@@ -0,0 +1,17 @@
package im.zhaojun.zfile.service.impl;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import org.springframework.stereotype.Service;
/**
* @author zhaojun
*/
@Service
public class UFileServiceImpl extends UpYunServiceImpl {
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.UFILE;
}
}

View File

@@ -1,19 +1,19 @@
package im.zhaojun.upyun.service;
package im.zhaojun.zfile.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.URLUtil;
import com.UpYun;
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 im.zhaojun.zfile.exception.NotExistFileException;
import im.zhaojun.zfile.model.entity.StorageConfig;
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import im.zhaojun.zfile.model.enums.FileTypeEnum;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
import im.zhaojun.zfile.service.base.BaseFileService;
import im.zhaojun.zfile.service.StorageConfigService;
import im.zhaojun.zfile.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@@ -27,9 +27,8 @@ import java.util.Objects;
* @author zhaojun
*/
@Service
public class UpYunServiceImpl extends AbstractFileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(UpYunServiceImpl.class);
@Slf4j
public class UpYunServiceImpl extends AbstractBaseFileService implements BaseFileService {
private static final String END_MARK = "g2gCZAAEbmV4dGQAA2VvZg";
@@ -46,7 +45,7 @@ public class UpYunServiceImpl extends AbstractFileService implements FileService
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.UPYUN);
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
String bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
String username = stringStorageConfigMap.get(StorageConfigConstant.USERNAME_KEY).getValue();
String password = stringStorageConfigMap.get(StorageConfigConstant.PASSWORD_KEY).getValue();
@@ -62,7 +61,7 @@ public class UpYunServiceImpl extends AbstractFileService implements FileService
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@@ -110,4 +109,23 @@ public class UpYunServiceImpl extends AbstractFileService implements FileService
return StorageTypeEnum.UPYUN;
}
@Override
public FileItemDTO getFileItem(String path) {
List<FileItemDTO> list;
try {
int end = path.lastIndexOf("/");
list = fileList(path.substring(0, end));
} catch (Exception e) {
throw new NotExistFileException();
}
for (FileItemDTO fileItemDTO : list) {
String fullPath = StringUtils.concatUrl(fileItemDTO.getPath(), fileItemDTO.getName());
if (Objects.equals(fullPath, path)) {
return fileItemDTO;
}
}
throw new NotExistFileException();
}
}

View File

@@ -0,0 +1,191 @@
package im.zhaojun.zfile.service.support;
import im.zhaojun.zfile.cache.ZFileCache;
import im.zhaojun.zfile.config.StorageTypeFactory;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import im.zhaojun.zfile.model.enums.FileTypeEnum;
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.service.SystemConfigService;
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
import im.zhaojun.zfile.service.base.BaseFileService;
import im.zhaojun.zfile.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author zhaojun
*/
@Slf4j
@Service
public class FileAsyncCacheService {
public static final String CACHE_PROCESS_PREFIX = "zfile-process-cache:";
private boolean cacheFinish;
@Resource
private SystemConfigService systemConfigService;
private volatile boolean stopFlag = false;
@Resource
private ZFileCache zFileCache;
@Value("${zfile.cache.auto-refresh.enable}")
protected boolean enableAutoRefreshCache;
@Value("${zfile.cache.auto-refresh.delay}")
protected Long delay;
@Value("${zfile.cache.auto-refresh.interval}")
protected Long interval;
@Async
public void cacheGlobalFile() {
stopFlag = false;
StorageTypeEnum storageStrategy = systemConfigService.getCurrentStorageStrategy();
if (storageStrategy == null) {
log.debug("尚未配置存储策略. 跳过启动缓存.");
return;
}
boolean enableCache = systemConfigService.getEnableCache();
if (!enableCache) {
log.debug("存储策略 {} 未启用缓存, 跳过缓存.", storageStrategy.getDescription());
return;
}
AbstractBaseFileService fileService = StorageTypeFactory.getStorageTypeService(storageStrategy);
if (fileService.getIsUnInitialized()) {
log.debug("存储策略 {} 未初始化成功, 跳过缓存.", storageStrategy.getDescription());
return;
}
log.info("缓存 {} 所有文件开始", storageStrategy.getDescription());
long startTime = System.currentTimeMillis();
try {
BaseFileService currentFileService = systemConfigService.getCurrentFileService();
List<FileItemDTO> rootFileItems = currentFileService.fileList("/");
ArrayDeque<FileItemDTO> queue = new ArrayDeque<>(rootFileItems);
while (!queue.isEmpty()) {
FileItemDTO fileItemDTO = queue.pop();
if (stopFlag) {
zFileCache.clear();
break;
}
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
String filePath = StringUtils.removeDuplicateSeparator("/" + fileItemDTO.getPath() + "/" + fileItemDTO.getName() + "/");
List<FileItemDTO> fileItems = currentFileService.fileList(filePath);
queue.addAll(fileItems);
}
}
} catch (Exception e) {
log.error("缓存所有文件失败", e);
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
if (stopFlag) {
log.info("缓存 {} 所有文件被强制结束, 用时: {} 秒", storageStrategy.getDescription(), ((endTime - startTime) / 1000));
cacheFinish = false;
stopFlag = false;
} else {
log.info("缓存 {} 所有文件结束, 用时: {} 秒", storageStrategy.getDescription(), ((endTime - startTime) / 1000));
enableCacheAutoRefreshTask();
cacheFinish = true;
stopFlag = false;
}
}
private void enableCacheAutoRefreshTask() {
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
if (enableAutoRefreshCache) {
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleWithFixedDelay(() -> {
zFileCache.setLastCacheAutoRefreshDate(new Date());
boolean enableCache = systemConfigService.getEnableCache();
if (!enableCache) {
log.debug("当前存储引擎未开启缓存, 跳过自动刷新缓存");
zFileCache.clear();
return;
}
log.debug("开始调用自动刷新缓存");
Set<String> keySet = zFileCache.keySet();
ArrayList<String> keys = new ArrayList<>(keySet);
for (String key : keys) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (stopFlag) {
break;
}
zFileCache.remove(key);
AbstractBaseFileService currentFileService = systemConfigService.getCurrentFileService();
try {
if (Objects.equals(currentStorageStrategy, systemConfigService.getCurrentStorageStrategy())) {
currentFileService.fileList(key);
}
} catch (Exception e) {
log.error("刷新过程中出错 : [" + key + "]", e);
}
}
if (stopFlag) {
log.debug("检测到停止 [{}] 缓存指令, 已停止自动刷新任务", currentStorageStrategy);
scheduledExecutorService.shutdownNow();
stopFlag = false;
} else {
log.debug("自动刷新缓存完成");
}
}, delay, interval, TimeUnit.SECONDS);
}
}
public void stopScheduled() {
this.stopFlag = true;
}
public void enableScheduled() {
this.stopFlag = false;
}
public boolean isCacheFinish() {
return cacheFinish;
}
public void setCacheFinish(boolean cacheFinish) {
this.cacheFinish = cacheFinish;
}
}

View File

@@ -1,12 +1,14 @@
package im.zhaojun.common.service;
package im.zhaojun.zfile.service.support;
import im.zhaojun.zfile.cache.ZFileCache;
import im.zhaojun.zfile.service.SystemConfigService;
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 {
@@ -15,23 +17,22 @@ public class FileCacheService {
private SystemConfigService systemConfigService;
@Resource
@Lazy
private FileAsyncCacheService fileAsyncCacheService;
@Resource
private ZFileCache zFileCache;
public void enableCache() {
systemConfigService.updateCacheEnableConfig(true);
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
currentFileService.openCacheAutoRefresh();
fileAsyncCacheService.cacheGlobalFile();
}
public void disableCache() throws Exception {
public void disableCache() {
systemConfigService.updateCacheEnableConfig(false);
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
currentFileService.clearFileCache();
fileAsyncCacheService.resetCacheCount();
zFileCache.clear();
fileAsyncCacheService.setCacheFinish(false);
fileAsyncCacheService.stopScheduled();
}
}

View File

@@ -0,0 +1,16 @@
package im.zhaojun.zfile.service.support;
import im.zhaojun.zfile.model.support.SystemMonitorInfo;
import org.springframework.stereotype.Service;
/**
* @author zhaojun
*/
@Service
public class SystemMonitorService {
public SystemMonitorInfo systemMonitorInfo() {
return new SystemMonitorInfo();
}
}

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.util;
package im.zhaojun.zfile.util;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
@@ -10,8 +10,8 @@ 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 im.zhaojun.zfile.model.constant.ZFileConstant;
import im.zhaojun.zfile.model.dto.AudioInfoDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -1,7 +1,7 @@
package im.zhaojun.common.util;
package im.zhaojun.zfile.util;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
import im.zhaojun.zfile.model.dto.FileItemDTO;
import im.zhaojun.zfile.model.enums.FileTypeEnum;
import java.util.Comparator;
@@ -20,6 +20,9 @@ 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;
@@ -27,15 +30,22 @@ 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();
NaturalOrderComparator naturalOrderComparator = new NaturalOrderComparator();
if (o1Type.equals(o2Type)) {
int result;
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()); break;
default: result = naturalOrderComparator.compare(o1.getName(), o2.getName()); break;
}
return "asc".equals(order) ? result : -result;
}
@@ -46,4 +56,4 @@ public class FileComparator implements Comparator<FileItemDTO> {
return 1;
}
}
}
}

View File

@@ -0,0 +1,68 @@
package im.zhaojun.zfile.util;
import cn.hutool.core.util.URLUtil;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import java.io.File;
import java.util.Date;
/**
* @author zhaojun
*/
public class FileUtil {
public static ResponseEntity<Object> export(File file, String fileName) {
if (!file.exists()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("404 FILE NOT FOUND");
}
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
if (StringUtils.isNullOrEmpty(fileName)) {
fileName = file.getName();
}
headers.setContentDispositionFormData("attachment", URLUtil.encode(fileName));
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
headers.add("Last-Modified", new Date().toString());
headers.add("ETag", String.valueOf(System.currentTimeMillis()));
return ResponseEntity
.ok()
.headers(headers)
.contentLength(file.length())
.contentType(mediaType)
.body(new FileSystemResource(file));
}
public static ResponseEntity<Object> export(File file) {
if (!file.exists()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("404 FILE NOT FOUND");
}
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.setContentDispositionFormData("attachment", URLUtil.encode(file.getName()));
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
headers.add("Last-Modified", new Date().toString());
headers.add("ETag", String.valueOf(System.currentTimeMillis()));
return ResponseEntity
.ok()
.headers(headers)
.contentLength(file.length())
.contentType(mediaType)
.body(new FileSystemResource(file));
}
}

View File

@@ -1,4 +1,4 @@
package im.zhaojun.common.util;
package im.zhaojun.zfile.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.client.RestClientException;
@@ -11,13 +11,13 @@ import org.springframework.web.client.RestTemplate;
public class HttpUtil {
public static String getTextContent(String url) {
RestTemplate restTemplate = SpringContextHolder.getBean(RestTemplate.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.class);
RestTemplate restTemplate = SpringContextHolder.getBean("restTemplate");
try {
restTemplate.headForHeaders(url);
return true;

View File

@@ -0,0 +1,150 @@
package im.zhaojun.zfile.util;
/*
NaturalOrderComparator.java -- Perform 'natural order' comparisons of strings in Java.
Copyright (C) 2003 by Pierre-Luc Paour <natorder@paour.com>
Based on the C version by Martin Pool, of which this is more or less a straight conversion.
Copyright (C) 2000 by Martin Pool <mbp@humbug.org.au>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
import java.util.Comparator;
/**
* @author zhaojun
*/
public class NaturalOrderComparator implements Comparator<String> {
private static final char ZERO_CHAR = '0';
private int compareRight(String a, String b) {
int bias = 0, ia = 0, ib = 0;
// The longest run of digits wins. That aside, the greatest
// value wins, but we can't know that it will until we've scanned
// both numbers to know that they have the same magnitude, so we
// remember it in BIAS.
for (; ; ia++, ib++) {
char ca = charAt(a, ia);
char cb = charAt(b, ib);
if (!isDigit(ca) && !isDigit(cb)) {
return bias;
}
if (!isDigit(ca)) {
return -1;
}
if (!isDigit(cb)) {
return +1;
}
if (ca == 0 && cb == 0) {
return bias;
}
if (bias == 0) {
if (ca < cb) {
bias = -1;
} else if (ca > cb) {
bias = +1;
}
}
}
}
@Override
public int compare(String a, String b) {
int ia = 0, ib = 0;
int nza, nzb;
char ca, cb;
while (true) {
// Only count the number of zeroes leading the last number compared
nza = nzb = 0;
ca = charAt(a, ia);
cb = charAt(b, ib);
// skip over leading spaces or zeros
while (Character.isSpaceChar(ca) || ca == ZERO_CHAR) {
if (ca == ZERO_CHAR) {
nza++;
} else {
// Only count consecutive zeroes
nza = 0;
}
ca = charAt(a, ++ia);
}
while (Character.isSpaceChar(cb) || cb == '0') {
if (cb == '0') {
nzb++;
} else {
// Only count consecutive zeroes
nzb = 0;
}
cb = charAt(b, ++ib);
}
// Process run of digits
if (Character.isDigit(ca) && Character.isDigit(cb)) {
int bias = compareRight(a.substring(ia), b.substring(ib));
if (bias != 0) {
return bias;
}
}
if (ca == 0 && cb == 0) {
// The strings compare the same. Perhaps the caller
// will want to call strcmp to break the tie.
return compareEqual(a, b, nza, nzb);
}
if (ca < cb) {
return -1;
}
if (ca > cb) {
return +1;
}
++ia;
++ib;
}
}
private static boolean isDigit(char c) {
return Character.isDigit(c) || c == '.' || c == ',';
}
private static char charAt(String s, int i) {
return i >= s.length() ? 0 : s.charAt(i);
}
private static int compareEqual(String a, String b, int nza, int nzb) {
if (nza - nzb != 0) {
return nza - nzb;
}
if (a.length() == b.length()) {
return a.compareTo(b);
}
return a.length() - b.length();
}
}

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