mirror of
https://github.com/zfile-dev/zfile.git
synced 2025-04-19 05:34:52 +00:00
Compare commits
86 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78f795e1cb | ||
|
|
c69ee0f356 | ||
|
|
39475de789 | ||
|
|
bd71712765 | ||
|
|
8698686a47 | ||
|
|
762c67ee37 | ||
|
|
7bf3a29c17 | ||
|
|
6ee5002f0c | ||
|
|
fadc64add4 | ||
|
|
234f43846f | ||
|
|
67e42d9753 | ||
|
|
04f94b4bf5 | ||
|
|
d29c498457 | ||
|
|
5aa45b44b2 | ||
|
|
8273a645f2 | ||
|
|
46f292cc9b | ||
|
|
261d48059e | ||
|
|
79f931c51b | ||
|
|
399e961a65 | ||
|
|
3e61d7d146 | ||
|
|
ace95d9071 | ||
|
|
60d2a2b986 | ||
|
|
69d5661e06 | ||
|
|
01d11dfc23 | ||
|
|
d35e3ecd93 | ||
|
|
9e5a3e5385 | ||
|
|
b62163b4e8 | ||
|
|
595a00f067 | ||
|
|
a759d9fe44 | ||
|
|
7a24fd10e0 | ||
|
|
7e04a817d7 | ||
|
|
791967f45e | ||
|
|
d789436a16 | ||
|
|
96ab8ff7dd | ||
|
|
8809aca170 | ||
|
|
97106383b6 | ||
|
|
208da95234 | ||
|
|
d4c843f5f5 | ||
|
|
d273fc9f12 | ||
|
|
0b4a38218c | ||
|
|
2f57c5b5cc | ||
|
|
610f68295f | ||
|
|
39ced8eb84 | ||
|
|
368b7a1df2 | ||
|
|
8e2107cd46 | ||
|
|
fa32a33371 | ||
|
|
59116a9414 | ||
|
|
b2c732a389 | ||
|
|
0e1ffef92b | ||
|
|
a5b19d3577 | ||
|
|
185c84dd79 | ||
|
|
d554dd298c | ||
|
|
aa6ecf0aaa | ||
|
|
83692718e3 | ||
|
|
030bd95941 | ||
|
|
8722d11ac3 | ||
|
|
0131ff02c0 | ||
|
|
2d115bf1c6 | ||
|
|
946113216d | ||
|
|
77b05c6dac | ||
|
|
07c9fca210 | ||
|
|
27cf61693a | ||
|
|
37e1aa1fec | ||
|
|
31b54a3c05 | ||
|
|
589f07c103 | ||
|
|
fe8b60d873 | ||
|
|
1734619eac | ||
|
|
f5724dc9ab | ||
|
|
f7bb147b71 | ||
|
|
47fc1bc2df | ||
|
|
45172f69ba | ||
|
|
9566b138ff | ||
|
|
e15b6c2242 | ||
|
|
acc41511e0 | ||
|
|
b882b87405 | ||
|
|
518b5170ae | ||
|
|
8bfac6d9ac | ||
|
|
3ffdb4f1b2 | ||
|
|
47509450a0 | ||
|
|
812fd18aac | ||
|
|
77a13cf8ad | ||
|
|
4c914793b0 | ||
|
|
5698cfb2d3 | ||
|
|
3cd5f8f9a7 | ||
|
|
76747771de | ||
|
|
cfacd39210 |
48
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
48
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal 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 细节,确认出现此问题的复现步骤,例如点击了哪里,发生了什么情况?
|
||||
|
||||
你可以粘贴截图或附件。
|
||||
-->
|
||||
34
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
34
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal 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
14
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: Question
|
||||
about: 对 ZFile 有任何问题吗?
|
||||
title: ''
|
||||
labels: 'question'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
如果你有任何问题也可以通过此渠道来向我们反馈。
|
||||
|
||||
谢谢!
|
||||
-->
|
||||
164
API.md
Normal file
164
API.md
Normal 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 片段
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
97
README.md
97
README.md
@@ -11,51 +11,64 @@
|
||||
|
||||
预览地址: [https://zfile.jun6.net](https://zfile.jun6.net)
|
||||
|
||||
文档地址: [http://docs.zhaojun.im/zfile](http://docs.zhaojun.im/zfile)
|
||||
|
||||
## 系统特色
|
||||
|
||||
* 内存缓存 (免安装)
|
||||
* 内存数据库 (免安装)
|
||||
* 个性化配置
|
||||
* 自定义目录的 header 说明文件
|
||||
* 自定义目录的 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.8/zfile-0.8.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.8.jar
|
||||
|
||||
## 高级启动
|
||||
java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.8.jar --server.port=18777
|
||||
|
||||
## 后台运行
|
||||
nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.8.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)
|
||||
|
||||
访问地址:
|
||||
|
||||
@@ -68,7 +81,18 @@ nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.8.jar &
|
||||
|
||||
## OneDrive 使用教程.
|
||||
|
||||
访问地址进行授权, 获取 accessToken 和 refreshToken: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=09939809-c617-43c8-a220-a93c1513c5d4&response_type=code&redirect_uri=https://zfile.jun6.net/onedirve/callback&scope=offline_access%20User.Read%20Files.ReadWrite.All
|
||||
访问地址进行授权, 获取 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
|
||||
|
||||
|
||||
然后分别填写至访问令牌和刷新令牌即可:
|
||||
|
||||
@@ -77,9 +101,15 @@ nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.8.jar &
|
||||
## 运行环境
|
||||
|
||||
* JDK: `1.8`
|
||||
* 缓存: `caffeine`
|
||||
* 数据库: `h2/mysql`
|
||||
|
||||
## 预览
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## 常见问题
|
||||
|
||||
### 数据库
|
||||
@@ -90,23 +120,32 @@ nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.8.jar &
|
||||
|
||||
默认 H2 数据库文件地址: `~/.zfile/db/`, `~` 表示用户目录, windows 为 `C:/Users/用户名/`, linux 为 `/home/用户名/`, root 用户为 `/root/`
|
||||
|
||||
### 头尾文件和加密文件
|
||||
### 文档文件和加密文件
|
||||
|
||||
- 目录头部显示文件名为 `header.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="150" height="234">
|
||||
<img src="http://cdn.jun6.net/alipay.png" width="200" height="312">
|
||||
<img src="http://cdn.jun6.net/wechat.png" width="222" height="300">
|
||||
|
||||
48
pom.xml
48
pom.xml
@@ -12,8 +12,9 @@
|
||||
|
||||
<groupId>im.zhaojun</groupId>
|
||||
<artifactId>zfile</artifactId>
|
||||
<version>0.8</version>
|
||||
<version>2.1</version>
|
||||
<name>zfile</name>
|
||||
<packaging>war</packaging>
|
||||
<description>一个在线的文件浏览系统</description>
|
||||
|
||||
<properties>
|
||||
@@ -44,6 +45,10 @@
|
||||
<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>
|
||||
@@ -66,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-->
|
||||
@@ -105,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>
|
||||
@@ -132,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>
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package im.zhaojun;
|
||||
|
||||
import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
|
||||
import com.alicp.jetcache.anno.config.EnableMethodCache;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.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 {
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package im.zhaojun.aliyun.service;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
@@ -27,14 +28,16 @@ public class AliyunServiceImpl extends AbstractS3FileService implements FileServ
|
||||
@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());
|
||||
|
||||
55
src/main/java/im/zhaojun/common/aop/FileListCacheAspect.java
Normal file
55
src/main/java/im/zhaojun/common/aop/FileListCacheAspect.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package im.zhaojun.common.aop;
|
||||
|
||||
import im.zhaojun.common.cache.ZFileCache;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 操作日志切面.
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class FileListCacheAspect {
|
||||
|
||||
@Resource
|
||||
private ZFileCache zFileCache;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Pointcut("execution(public * im.zhaojun.common.service.AbstractFileService.fileList(..))")
|
||||
public void pointcut() {
|
||||
}
|
||||
|
||||
@Around(value = "pointcut()")
|
||||
public Object around(ProceedingJoinPoint point) throws Throwable {
|
||||
List<FileItemDTO> result;
|
||||
|
||||
Object[] args = point.getArgs();
|
||||
String path = String.valueOf(args[0]);
|
||||
|
||||
boolean enableCache = systemConfigService.getEnableCache();
|
||||
|
||||
if (enableCache) {
|
||||
List<FileItemDTO> cacheFileList = zFileCache.get(path);
|
||||
if (CollectionUtils.isEmpty(cacheFileList)) {
|
||||
result = (List<FileItemDTO>) point.proceed();
|
||||
zFileCache.put(path, result);
|
||||
} else {
|
||||
result = cacheFileList;
|
||||
}
|
||||
} else {
|
||||
result = (List<FileItemDTO>) point.proceed();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
94
src/main/java/im/zhaojun/common/cache/ZFileCache.java
vendored
Normal file
94
src/main/java/im/zhaojun/common/cache/ZFileCache.java
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
package im.zhaojun.common.cache;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Component
|
||||
public class ZFileCache {
|
||||
|
||||
private ConcurrentMap<String, List<FileItemDTO>> fileCache = new ConcurrentHashMap<>();
|
||||
|
||||
private SystemConfigDTO systemConfigCache;
|
||||
|
||||
public Date lastCacheAutoRefreshDate;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
public synchronized void put(String key, List<FileItemDTO> value) {
|
||||
fileCache.put(key, value);
|
||||
}
|
||||
|
||||
public List<FileItemDTO> get(String key) {
|
||||
return fileCache.get(key);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
fileCache.clear();
|
||||
}
|
||||
|
||||
public int cacheCount() {
|
||||
return fileCache.size();
|
||||
}
|
||||
|
||||
public List<FileItemDTO> find(String key, boolean ignoreCase) {
|
||||
List<FileItemDTO> result = new ArrayList<>();
|
||||
|
||||
Collection<List<FileItemDTO>> values = fileCache.values();
|
||||
for (List<FileItemDTO> fileItemList : values) {
|
||||
for (FileItemDTO fileItemDTO : fileItemList) {
|
||||
boolean testResult;
|
||||
|
||||
if (ignoreCase) {
|
||||
testResult = StrUtil.containsIgnoreCase(fileItemDTO.getName(), key);
|
||||
} else {
|
||||
testResult = fileItemDTO.getName().contains(key);
|
||||
}
|
||||
|
||||
if (testResult) {
|
||||
result.add(fileItemDTO);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Set<String> keySet() {
|
||||
return fileCache.keySet();
|
||||
}
|
||||
|
||||
public void remove(String key) {
|
||||
fileCache.remove(key);
|
||||
}
|
||||
|
||||
public void updateConfig(SystemConfigDTO systemConfigCache) {
|
||||
this.systemConfigCache = systemConfigCache;
|
||||
}
|
||||
|
||||
public SystemConfigDTO getConfig() {
|
||||
return this.systemConfigCache;
|
||||
}
|
||||
|
||||
public void removeConfig() {
|
||||
this.systemConfigCache = null;
|
||||
}
|
||||
|
||||
public Date getLastCacheAutoRefreshDate() {
|
||||
return lastCacheAutoRefreshDate;
|
||||
}
|
||||
|
||||
public void setLastCacheAutoRefreshDate(Date lastCacheAutoRefreshDate) {
|
||||
this.lastCacheAutoRefreshDate = lastCacheAutoRefreshDate;
|
||||
}
|
||||
}
|
||||
21
src/main/java/im/zhaojun/common/config/CacheConfig.java
Normal file
21
src/main/java/im/zhaojun/common/config/CacheConfig.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package im.zhaojun.common.config;
|
||||
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Configuration
|
||||
public class CacheConfig {
|
||||
|
||||
@Bean
|
||||
public ConcurrentMap<String, List<FileItemDTO>> concurrentMapCache() {
|
||||
return new ConcurrentHashMap<>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package im.zhaojun.common.config;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
|
||||
public class ContentTypeTextToTextJson implements ClientHttpRequestInterceptor {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ContentTypeTextToTextJson.class);
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
|
||||
throws IOException {
|
||||
URI uri = request.getURI();
|
||||
ClientHttpResponse response = execution.execute(request, body);
|
||||
HttpHeaders headers = response.getHeaders();
|
||||
headers.put("Content-Type", Collections.singletonList("application/text"));
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
package im.zhaojun.common.config;
|
||||
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.cache.ZFileCache;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import im.zhaojun.onedrive.service.OneDriveService;
|
||||
import im.zhaojun.onedrive.service.OneDriveServiceImpl;
|
||||
import im.zhaojun.onedrive.service.OneDriveToken;
|
||||
import im.zhaojun.onedrive.china.service.OneDriveChinaServiceImpl;
|
||||
import im.zhaojun.onedrive.international.service.OneDriveServiceImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
@@ -16,7 +14,7 @@ import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
@@ -26,50 +24,66 @@ import java.util.Arrays;
|
||||
@Slf4j
|
||||
public class GlobalScheduleTask {
|
||||
|
||||
@Resource
|
||||
private ZFileCache zFileCache;
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Resource
|
||||
private OneDriveService oneDriveService;
|
||||
private OneDriveServiceImpl oneDriveServiceImpl;
|
||||
|
||||
@Resource
|
||||
private OneDriveChinaServiceImpl oneDriveChinaServiceImpl;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
/**
|
||||
* 项目启动 30 秒后, 每半小时执行一次刷新 OneDrive Token 的定时任务.
|
||||
* 项目启动 30 秒后, 每 15 分钟执行一次刷新 OneDrive Token 的定时任务.
|
||||
*/
|
||||
@Scheduled(fixedRate = 1000 * 60 * 30, initialDelay = 1000 * 30)
|
||||
@Scheduled(fixedRate = 1000 * 60 * 10, initialDelay = 1000 * 30)
|
||||
public void autoRefreshOneDriveToken() {
|
||||
|
||||
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
|
||||
try {
|
||||
log.debug("尝试调用 OneDrive 自动刷新 AccessToken 定时任务");
|
||||
|
||||
if (!(currentFileService instanceof OneDriveServiceImpl)) {
|
||||
log.debug("当前启用存储类型, 不是 OneDrive, 跳过自动刷新 AccessToken");
|
||||
return;
|
||||
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
|
||||
|
||||
if (!(currentFileService instanceof OneDriveServiceImpl
|
||||
|| currentFileService instanceof OneDriveChinaServiceImpl)) {
|
||||
log.debug("当前启用存储类型, 不是 OneDrive, 跳过自动刷新 AccessToken");
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentFileService.getIsUnInitialized()) {
|
||||
log.debug("当前启用 OneDrive 未初始化成功, 跳过自动刷新 AccessToken");
|
||||
return;
|
||||
}
|
||||
|
||||
StorageTypeEnum currentStorageTypeEnum = currentFileService.getStorageTypeEnum();
|
||||
|
||||
try {
|
||||
refreshOneDriveToken(currentStorageTypeEnum);
|
||||
} catch (Exception e) {
|
||||
log.debug("刷新 " + currentStorageTypeEnum.getDescription() + " Token 失败.", e);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.debug("尝试调用 OneDrive 自动刷新 AccessToken 定时任务出现未知异常", e);
|
||||
}
|
||||
|
||||
if (currentFileService.getIsUnInitialized()) {
|
||||
log.debug("当前启用 OneDrive 未初始化成功, 跳过自动刷新 AccessToken");
|
||||
return;
|
||||
}
|
||||
|
||||
refreshOneDriveToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用刷新 OneDrive Token
|
||||
*/
|
||||
public void refreshOneDriveToken() {
|
||||
OneDriveToken refreshToken = oneDriveService.getRefreshToken();
|
||||
|
||||
StorageConfig accessTokenConfig =
|
||||
storageConfigService.selectByTypeAndKey(StorageTypeEnum.ONE_DRIVE, StorageConfigConstant.ACCESS_TOKEN_KEY);
|
||||
StorageConfig refreshTokenConfig =
|
||||
storageConfigService.selectByTypeAndKey(StorageTypeEnum.ONE_DRIVE, StorageConfigConstant.REFRESH_TOKEN_KEY);
|
||||
accessTokenConfig.setValue(refreshToken.getAccessToken());
|
||||
refreshTokenConfig.setValue(refreshToken.getRefreshToken());
|
||||
|
||||
storageConfigService.updateStorageConfig(Arrays.asList(accessTokenConfig, refreshTokenConfig));
|
||||
log.info("刷新 OneDrive key 时间: " + LocalDateTime.now());
|
||||
public void refreshOneDriveToken(StorageTypeEnum storageType) {
|
||||
if (Objects.equals(storageType, StorageTypeEnum.ONE_DRIVE_CHINA)) {
|
||||
oneDriveChinaServiceImpl.refreshOneDriveToken();
|
||||
} else {
|
||||
oneDriveServiceImpl.refreshOneDriveToken();
|
||||
}
|
||||
log.info("刷新 {} key 时间: {}", storageType.getDescription(), LocalDateTime.now());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package im.zhaojun.common.config;
|
||||
|
||||
import im.zhaojun.common.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;
|
||||
@@ -15,4 +18,14 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package im.zhaojun.common.controller;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.ZipUtil;
|
||||
import im.zhaojun.common.config.StorageTypeFactory;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.SystemMonitorInfo;
|
||||
import im.zhaojun.common.model.dto.ResultBean;
|
||||
import im.zhaojun.common.model.dto.StorageStrategyDTO;
|
||||
import im.zhaojun.common.model.dto.SystemConfigDTO;
|
||||
@@ -10,19 +13,17 @@ import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.FileAsyncCacheService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import im.zhaojun.common.util.FileUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* 后台管理
|
||||
@@ -43,6 +44,8 @@ public class AdminController {
|
||||
@Resource
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
private ScheduledExecutorService scheduledExecutorService;
|
||||
|
||||
/**
|
||||
* 获取系统配置
|
||||
*/
|
||||
@@ -57,16 +60,20 @@ public class AdminController {
|
||||
*/
|
||||
@PostMapping("/config")
|
||||
public ResultBean updateConfig(SystemConfigDTO systemConfigDTO) throws Exception {
|
||||
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
|
||||
systemConfigDTO.setId(1);
|
||||
systemConfigService.updateSystemConfig(systemConfigDTO);
|
||||
|
||||
StorageTypeEnum currentStorageStrategy = currentFileService.getStorageTypeEnum();
|
||||
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
|
||||
if (!Objects.equals(currentStorageStrategy, systemConfigDTO.getStorageStrategy())) {
|
||||
log.info("已将存储策略由 {} 切换为 {}", currentStorageStrategy, systemConfigDTO.getStorageStrategy());
|
||||
if (systemConfigService.getEnableCache()) {
|
||||
return ResultBean.error("不支持缓存开启状态下, 切换存储策略, 请先手动关闭缓存");
|
||||
}
|
||||
log.info("已将存储策略由 {} 切换为 {}",
|
||||
currentStorageStrategy.getDescription(),
|
||||
systemConfigDTO.getStorageStrategy().getDescription());
|
||||
refreshStorageStrategy();
|
||||
}
|
||||
|
||||
systemConfigDTO.setId(1);
|
||||
systemConfigService.updateSystemConfig(systemConfigDTO);
|
||||
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
@@ -90,6 +97,9 @@ public class AdminController {
|
||||
return ResultBean.success(storageConfigList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回支持的存储引擎.
|
||||
*/
|
||||
@GetMapping("/support-strategy")
|
||||
public ResultBean supportStrategy() {
|
||||
List<StorageStrategyDTO> result = new ArrayList<>();
|
||||
@@ -148,7 +158,7 @@ public class AdminController {
|
||||
/**
|
||||
* 更新存储策略
|
||||
*/
|
||||
public void refreshStorageStrategy() throws Exception {
|
||||
public void refreshStorageStrategy() {
|
||||
StorageTypeEnum storageStrategy = systemConfigService.getCurrentStorageStrategy();
|
||||
refreshStorageStrategy(storageStrategy);
|
||||
}
|
||||
@@ -156,7 +166,7 @@ public class AdminController {
|
||||
/**
|
||||
* 更新存储策略
|
||||
*/
|
||||
public void refreshStorageStrategy(StorageTypeEnum storageStrategy) throws Exception {
|
||||
private void refreshStorageStrategy(StorageTypeEnum storageStrategy) {
|
||||
if (storageStrategy == null) {
|
||||
log.info("尚未配置存储策略.");
|
||||
} else {
|
||||
@@ -168,4 +178,23 @@ public class AdminController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统日志下载
|
||||
*/
|
||||
@GetMapping("/log")
|
||||
public ResponseEntity<Object> downloadLog(HttpServletResponse response) {
|
||||
String userHome = System.getProperty("user.home");
|
||||
File fileZip = ZipUtil.zip(userHome + "/.zfile/logs");
|
||||
String currentDate = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
|
||||
return FileUtil.export(fileZip, "ZFile 诊断日志 - " + currentDate + ".zip");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统监控信息
|
||||
*/
|
||||
@GetMapping("monitor")
|
||||
public ResultBean monitor() {
|
||||
return ResultBean.success(new SystemMonitorInfo());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package im.zhaojun.common.controller;
|
||||
|
||||
import im.zhaojun.common.cache.ZFileCache;
|
||||
import im.zhaojun.common.model.dto.CacheConfigDTO;
|
||||
import im.zhaojun.common.model.dto.ResultBean;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
@@ -16,7 +17,6 @@ import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2020/1/11 14:03
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin/cache")
|
||||
@@ -31,32 +31,34 @@ 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 {
|
||||
public ResultBean cacheConfig() {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
Set<String> cacheKeys = fileService.getCacheKeys();
|
||||
|
||||
CacheConfigDTO cacheConfigDTO = new CacheConfigDTO();
|
||||
cacheConfigDTO.setEnableCache(systemConfigService.getEnableCache());
|
||||
cacheConfigDTO.setCacheFinish(fileAsyncCacheService.isCacheFinish());
|
||||
cacheConfigDTO.setCacheKeys(cacheKeys);
|
||||
cacheConfigDTO.setCacheDirectoryCount(fileAsyncCacheService.getCacheDirectoryCount());
|
||||
cacheConfigDTO.setCacheFileCount(fileAsyncCacheService.getCacheFileCount());
|
||||
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();
|
||||
@@ -64,18 +66,21 @@ public class CacheController {
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
/*
|
||||
@PostMapping("/clear")
|
||||
public ResultBean clearCache(String key) throws Exception {
|
||||
public ResultBean clearCache(String key) {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
fileService.clearFileCache();
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/all")
|
||||
public ResultBean cacheAll() throws Exception {
|
||||
public ResultBean cacheAll() {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
fileService.clearFileCache();
|
||||
fileAsyncCacheService.cacheGlobalFile();
|
||||
return ResultBean.success();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2020/1/13 21:40
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/common")
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package im.zhaojun.common.controller;
|
||||
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import im.zhaojun.common.annotation.CheckStorageStrategyInit;
|
||||
import im.zhaojun.common.exception.SearchDisableException;
|
||||
import im.zhaojun.common.model.FilePageModel;
|
||||
import im.zhaojun.common.model.constant.ZFileConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.dto.ResultBean;
|
||||
@@ -15,19 +17,21 @@ import im.zhaojun.common.service.SystemService;
|
||||
import im.zhaojun.common.util.FileComparator;
|
||||
import im.zhaojun.common.util.HttpUtil;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 前台文件管理
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Slf4j
|
||||
@RequestMapping("/api")
|
||||
@RestController
|
||||
public class FileController {
|
||||
@@ -56,22 +60,39 @@ public class FileController {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
List<FileItemDTO> fileItemList = fileService.fileList(StringUtils.removeDuplicateSeparator("/" + path + "/"));
|
||||
for (FileItemDTO fileItemDTO : fileItemList) {
|
||||
if (ZFileConstant.PASSWORD_FILE_NAME.equals(fileItemDTO.getName())
|
||||
&& !HttpUtil.getTextContent(fileItemDTO.getUrl()).equals(password)) {
|
||||
if (ZFileConstant.PASSWORD_FILE_NAME.equals(fileItemDTO.getName())) {
|
||||
String expectedPasswordContent = null;
|
||||
try {
|
||||
expectedPasswordContent = HttpUtil.getTextContent(fileItemDTO.getUrl() + '1');
|
||||
} catch (HttpClientErrorException httpClientErrorException) {
|
||||
log.debug("尝试重新获取密码文件缓存中链接后仍失败", httpClientErrorException);
|
||||
try {
|
||||
String fullPath = StringUtils.removeDuplicateSeparator(fileItemDTO.getPath() + "/" + fileItemDTO.getName());
|
||||
FileItemDTO fileItem = fileService.getFileItem(fullPath);
|
||||
expectedPasswordContent = HttpUtil.getTextContent(fileItem.getUrl());
|
||||
} catch (Exception e) {
|
||||
log.debug("尝试重新获取密码文件链接后仍失败, 已暂时取消密码", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Objects.equals(expectedPasswordContent, password)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (password != null && !"".equals(password)) {
|
||||
return ResultBean.error("密码错误.");
|
||||
return ResultBean.error("密码错误.", ResultBean.INVALID_PASSWORD);
|
||||
}
|
||||
return ResultBean.error("此文件夹需要密码.", ResultBean.REQUIRED_PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
List<FileItemDTO> sortedPagingData = getSortedPagingData(fileItemList, page);
|
||||
return ResultBean.successData(sortedPagingData);
|
||||
return ResultBean.successData(getSortedPagingData(fileItemList, page));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取系统配置信息和当前页的标题, 文件头, 文件尾信息
|
||||
* 获取系统配置信息和当前页的标题, 页面文档信息
|
||||
* @param path 路径
|
||||
*/
|
||||
@CheckStorageStrategyInit
|
||||
@@ -88,23 +109,22 @@ public class FileController {
|
||||
public ResultBean search(@RequestParam(value = "name", defaultValue = "/") String name,
|
||||
@RequestParam(defaultValue = "name") String sortBy,
|
||||
@RequestParam(defaultValue = "asc") String order,
|
||||
@RequestParam(defaultValue = "1") Integer page) throws Exception {
|
||||
@RequestParam(defaultValue = "1") Integer page) {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
SystemConfigDTO systemConfigDTO = systemConfigService.getSystemConfig();
|
||||
if (!systemConfigDTO.getSearchEnable()) {
|
||||
if (BooleanUtil.isFalse(systemConfigDTO.getSearchEnable())) {
|
||||
throw new SearchDisableException("搜索功能未开启");
|
||||
}
|
||||
if (!fileAsyncCacheService.isCacheFinish()) {
|
||||
throw new SearchDisableException("搜索功能缓存预热中, 请稍后再试");
|
||||
}
|
||||
List<FileItemDTO> fileItemList = fileService.search(URLUtil.decode(name));
|
||||
List<FileItemDTO> sortedPagingData = getSortedPagingData(fileItemList, page);
|
||||
return ResultBean.successData(sortedPagingData);
|
||||
return ResultBean.successData(getSortedPagingData(fileItemList, page));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 过滤文件列表, 不显示密码, 头部和尾部文件.
|
||||
* 过滤文件列表, 不显示密码, 文档文件.
|
||||
*/
|
||||
private void filterFileList(List<FileItemDTO> fileItemList) {
|
||||
if (fileItemList == null) {
|
||||
@@ -112,26 +132,29 @@ public class FileController {
|
||||
}
|
||||
|
||||
fileItemList.removeIf(fileItem -> ZFileConstant.PASSWORD_FILE_NAME.equals(fileItem.getName())
|
||||
|| ZFileConstant.HEADER_FILE_NAME.equals(fileItem.getName()));
|
||||
|| ZFileConstant.README_FILE_NAME.equals(fileItem.getName()));
|
||||
}
|
||||
|
||||
|
||||
private List<FileItemDTO> getSortedPagingData(List<FileItemDTO> fileItemList, Integer page) {
|
||||
// 排序, 先按照文件类型比较, 文件夹在前, 文件在后, 然后根据 sortBy 字段排序, 默认为升序;
|
||||
fileItemList.sort(new FileComparator());
|
||||
filterFileList(fileItemList);
|
||||
private FilePageModel getSortedPagingData(List<FileItemDTO> fileItemList, Integer page) {
|
||||
ArrayList<FileItemDTO> copy = new ArrayList<>(Arrays.asList(new FileItemDTO[fileItemList.size()]));
|
||||
Collections.copy(copy, fileItemList);
|
||||
|
||||
int total = fileItemList.size();
|
||||
// 排序, 先按照文件类型比较, 文件夹在前, 文件在后, 然后根据 sortBy 字段排序, 默认为升序;
|
||||
copy.sort(new FileComparator());
|
||||
filterFileList(copy);
|
||||
|
||||
int total = copy.size();
|
||||
int totalPage = (total + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
|
||||
if (page > totalPage) {
|
||||
return new ArrayList<>();
|
||||
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 ArrayList<>(fileItemList.subList(start, end));
|
||||
return new FilePageModel(total, totalPage, copy.subList(start, end));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package im.zhaojun.common.controller;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Zhao Jun
|
||||
* 2020/2/9 11:17
|
||||
*/
|
||||
@Controller
|
||||
public class PageController {
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@GetMapping("/directlink/**")
|
||||
public String directlink(final HttpServletRequest request) throws MalformedURLException {
|
||||
String path = (String) request.getAttribute(
|
||||
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
|
||||
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
|
||||
AntPathMatcher apm = new AntPathMatcher();
|
||||
String filePath = apm.extractPathWithinPattern(bestMatchPattern, path);
|
||||
|
||||
if (filePath.length() > 0 && filePath.charAt(0) != '/') {
|
||||
filePath = "/" + filePath;
|
||||
}
|
||||
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
FileItemDTO fileItem = fileService.getFileItem(filePath);
|
||||
|
||||
String url = fileItem.getUrl();
|
||||
|
||||
int queryIndex = url.indexOf('?');
|
||||
|
||||
if (queryIndex != -1) {
|
||||
String origin = url.substring(0, queryIndex);
|
||||
String queryString = url.substring(queryIndex + 1);
|
||||
|
||||
url = URLUtil.encode(origin) + "?" + URLUtil.encode(queryString);
|
||||
} else {
|
||||
url = URLUtil.encode(url);
|
||||
}
|
||||
|
||||
|
||||
if (Objects.equals(fileItem.getType(), FileTypeEnum.FOLDER)) {
|
||||
return "redirect:" + fileItem.getUrl();
|
||||
} else {
|
||||
return "redirect:" + url;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package im.zhaojun.common.exception;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2020/1/20 22:15
|
||||
*/
|
||||
public class NotExistFileException extends RuntimeException {
|
||||
|
||||
|
||||
22
src/main/java/im/zhaojun/common/model/FilePageModel.java
Normal file
22
src/main/java/im/zhaojun/common/model/FilePageModel.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package im.zhaojun.common.model;
|
||||
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class FilePageModel {
|
||||
|
||||
private int total;
|
||||
|
||||
private int totalPage;
|
||||
|
||||
private List<FileItemDTO> fileList;
|
||||
|
||||
}
|
||||
39
src/main/java/im/zhaojun/common/model/Jvm.java
Normal file
39
src/main/java/im/zhaojun/common/model/Jvm.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package im.zhaojun.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
public class Jvm {
|
||||
|
||||
/**
|
||||
* 当前 JVM 占用的内存总数 (M)
|
||||
*/
|
||||
private double total;
|
||||
|
||||
/**
|
||||
* JVM 最大可用内存总数 (M)
|
||||
*/
|
||||
private double max;
|
||||
|
||||
/**
|
||||
* JVM 空闲内存 (M)
|
||||
*/
|
||||
private double free;
|
||||
|
||||
/**
|
||||
* JDK 版本
|
||||
*/
|
||||
private String version;
|
||||
|
||||
public Jvm() {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
this.total = runtime.totalMemory();
|
||||
this.free = runtime.freeMemory();
|
||||
this.max = runtime.maxMemory();
|
||||
this.version = System.getProperty("java.version");
|
||||
}
|
||||
|
||||
}
|
||||
41
src/main/java/im/zhaojun/common/model/Mem.java
Normal file
41
src/main/java/im/zhaojun/common/model/Mem.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package im.zhaojun.common.model;
|
||||
|
||||
import com.sun.management.OperatingSystemMXBean;
|
||||
import lombok.Data;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
public class Mem {
|
||||
|
||||
/**
|
||||
* 内存总量
|
||||
*/
|
||||
private double total;
|
||||
|
||||
/**
|
||||
* 已用内存
|
||||
*/
|
||||
private double used;
|
||||
|
||||
/**
|
||||
* 剩余内存
|
||||
*/
|
||||
private double free;
|
||||
|
||||
public Mem() {
|
||||
OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
|
||||
// 总的物理内存+虚拟内存
|
||||
long totalVirtualMemory = operatingSystemMXBean.getTotalSwapSpaceSize();
|
||||
// 剩余的物理内存
|
||||
long freePhysicalMemorySize = operatingSystemMXBean.getFreePhysicalMemorySize();
|
||||
this.total = totalVirtualMemory;
|
||||
this.free = freePhysicalMemorySize;
|
||||
this.used = totalVirtualMemory - freePhysicalMemorySize;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -27,7 +27,7 @@ public class StorageConfig {
|
||||
|
||||
private String title;
|
||||
|
||||
@Column(length = 2048)
|
||||
@Column(length = 4000)
|
||||
private String value;
|
||||
|
||||
public Integer getId() {
|
||||
|
||||
58
src/main/java/im/zhaojun/common/model/Sys.java
Normal file
58
src/main/java/im/zhaojun/common/model/Sys.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package im.zhaojun.common.model;
|
||||
|
||||
import cn.hutool.core.date.BetweenFormater;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import lombok.Data;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
public class Sys {
|
||||
|
||||
/**
|
||||
* 项目路径
|
||||
*/
|
||||
private String projectDir;
|
||||
|
||||
/**
|
||||
* 操作系统
|
||||
*/
|
||||
private String osName;
|
||||
|
||||
/**
|
||||
* 系统架构
|
||||
*/
|
||||
private String osArch;
|
||||
|
||||
/**
|
||||
* 系统版本
|
||||
*/
|
||||
private String osVersion;
|
||||
|
||||
/**
|
||||
* 启动时间
|
||||
*/
|
||||
private String upTime;
|
||||
|
||||
public Sys() {
|
||||
this.osName = System.getProperty("os.name");
|
||||
this.osArch = System.getProperty("os.arch");
|
||||
this.osVersion = System.getProperty("os.version");
|
||||
Resource resource = new ClassPathResource("");
|
||||
try {
|
||||
this.projectDir = resource.getFile().getAbsolutePath();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
long uptime = ManagementFactory.getRuntimeMXBean().getUptime();
|
||||
this.upTime = DateUtil.formatBetween(uptime, BetweenFormater.Level.SECOND);
|
||||
}
|
||||
|
||||
}
|
||||
34
src/main/java/im/zhaojun/common/model/SystemMonitorInfo.java
Normal file
34
src/main/java/im/zhaojun/common/model/SystemMonitorInfo.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package im.zhaojun.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
public class SystemMonitorInfo implements Serializable {
|
||||
|
||||
/**
|
||||
* 服务器基本信息
|
||||
*/
|
||||
private Sys sys;
|
||||
|
||||
/**
|
||||
* JVM 信息
|
||||
*/
|
||||
private Jvm jvm;
|
||||
|
||||
/**
|
||||
* 系统内存
|
||||
*/
|
||||
private Mem mem;
|
||||
|
||||
public SystemMonitorInfo() {
|
||||
this.jvm = new Jvm();
|
||||
this.sys = new Sys();
|
||||
this.mem = new Mem();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package im.zhaojun.common.model.constant;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2019/12/28 18:47
|
||||
*/
|
||||
public class StorageConfigConstant {
|
||||
|
||||
@@ -34,4 +33,8 @@ public class StorageConfigConstant {
|
||||
|
||||
public static final String REFRESH_TOKEN_KEY = "refreshToken";
|
||||
|
||||
public static final String PATH_STYLE = "pathStyle";
|
||||
|
||||
public static final String IS_PRIVATE = "isPrivate";
|
||||
|
||||
}
|
||||
@@ -15,9 +15,9 @@ public class ZFileConstant {
|
||||
public static final String AUDIO_TMP_PATH = "/.zfile/tmp/audio/";
|
||||
|
||||
/**
|
||||
* 页面头部文件
|
||||
* 页面文档文件
|
||||
*/
|
||||
public static String HEADER_FILE_NAME = "header.md";
|
||||
public static String README_FILE_NAME = "readme.md";
|
||||
|
||||
/**
|
||||
* 密码文件
|
||||
@@ -25,8 +25,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;
|
||||
public void setHeaderFileName(@Value("${zfile.constant.readme}") String headerFileName) {
|
||||
ZFileConstant.README_FILE_NAME = headerFileName;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
|
||||
@@ -2,11 +2,11 @@ package im.zhaojun.common.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 {
|
||||
@@ -15,4 +15,5 @@ public class CacheConfigDTO {
|
||||
private Set<String> cacheKeys;
|
||||
private Integer cacheDirectoryCount;
|
||||
private Integer cacheFileCount;
|
||||
private Date lastCacheAutoRefreshDate;
|
||||
}
|
||||
|
||||
@@ -15,12 +15,16 @@ 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;
|
||||
|
||||
private Object data;
|
||||
|
||||
private int total;
|
||||
|
||||
private ResultBean() {
|
||||
super();
|
||||
}
|
||||
@@ -43,6 +47,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);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public class SiteConfigDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8811196207046121740L;
|
||||
|
||||
private String header;
|
||||
private String readme;
|
||||
|
||||
@JsonProperty("viewConfig")
|
||||
private SystemConfigDTO systemConfigDTO;
|
||||
|
||||
@@ -44,4 +44,15 @@ public class SystemConfigDTO {
|
||||
|
||||
private String customCss;
|
||||
|
||||
private String tableSize;
|
||||
|
||||
private Boolean showOperator;
|
||||
|
||||
private Boolean showDocument;
|
||||
|
||||
private String announcement;
|
||||
|
||||
private Boolean showAnnouncement;
|
||||
|
||||
private String layout;
|
||||
}
|
||||
@@ -14,15 +14,18 @@ public enum StorageTypeEnum {
|
||||
/**
|
||||
* 当前系统支持的所有存储策略
|
||||
*/
|
||||
ALIYUN("aliyun", "阿里云 OSS"),
|
||||
FTP("ftp", "FTP"),
|
||||
HUAWEI("huawei", "华为云 OBS"),
|
||||
LOCAL("local", "本地存储"),
|
||||
MINIO("minio", "MINIO"),
|
||||
ONE_DRIVE("onedrive", "OneDrive"),
|
||||
QINIU("qiniu", "七牛云 KODO"),
|
||||
ALIYUN("aliyun", "阿里云 OSS"),
|
||||
TENCENT("tencent", "腾讯云 COS"),
|
||||
UPYUN("upyun", "又拍云 USS");
|
||||
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;
|
||||
|
||||
@@ -98,6 +98,7 @@ public class MySecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
http.cors();
|
||||
http.csrf().disable();
|
||||
http.headers().frameOptions().sameOrigin();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,23 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alicp.jetcache.Cache;
|
||||
import com.alicp.jetcache.RefreshPolicy;
|
||||
import com.alicp.jetcache.anno.CacheRefresh;
|
||||
import com.alicp.jetcache.anno.CacheType;
|
||||
import com.alicp.jetcache.anno.Cached;
|
||||
import com.alicp.jetcache.anno.CreateCache;
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import im.zhaojun.common.cache.ZFileCache;
|
||||
import im.zhaojun.common.model.constant.ZFileConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.aop.framework.AopContext;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2019/12/28 19:27
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractFileService extends FileCacheService implements FileService {
|
||||
@@ -42,14 +29,16 @@ public abstract class AbstractFileService extends FileCacheService implements Fi
|
||||
|
||||
protected boolean isInitialized = false;
|
||||
|
||||
protected String basePath;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Resource
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
@CreateCache(name = SYSTEM_CONFIG_CACHE_PREFIX, cacheType = CacheType.LOCAL)
|
||||
private Cache<String, List<FileItemDTO>> cache;
|
||||
@Resource
|
||||
private ZFileCache zFileCache;
|
||||
|
||||
/***
|
||||
* 获取指定路径下的文件及文件夹, 默认缓存 60 分钟,每隔 30 分钟刷新一次.
|
||||
@@ -58,24 +47,15 @@ public abstract class AbstractFileService extends FileCacheService implements Fi
|
||||
* @throws Exception 获取文件列表中出现的异常
|
||||
*/
|
||||
@Override
|
||||
@Cached(name = SYSTEM_CONFIG_CACHE_PREFIX,
|
||||
key = "args[0]",
|
||||
cacheType = CacheType.LOCAL, localLimit = 100000, condition = "mvel{bean('systemConfigService').enableCache}")
|
||||
@CacheRefresh(refresh = 30, timeUnit = TimeUnit.MINUTES)
|
||||
public abstract List<FileItemDTO> fileList(String path) throws Exception;
|
||||
|
||||
/**
|
||||
* 清理当前存储策略的缓存
|
||||
* 1. 删除全部缓存
|
||||
* 2. 关闭自动刷新
|
||||
* 3. 重置缓存个数
|
||||
* 4. 标记为当前处于未完成缓存状态
|
||||
* 2. 标记为当前处于未完成缓存状态
|
||||
*/
|
||||
public void clearFileCache() throws Exception {
|
||||
Set<String> cacheKeys = getCacheKeys();
|
||||
cache.removeAll(cacheKeys);
|
||||
closeCacheAutoRefresh();
|
||||
fileAsyncCacheService.resetCacheCount();
|
||||
public void clearFileCache() {
|
||||
zFileCache.clear();
|
||||
fileAsyncCacheService.setCacheFinish(false);
|
||||
}
|
||||
|
||||
@@ -124,59 +104,8 @@ public abstract class AbstractFileService extends FileCacheService implements Fi
|
||||
* @return 包含该文件名的所有文件或文件夹
|
||||
*/
|
||||
public List<FileItemDTO> search(String name) {
|
||||
List<FileItemDTO> result = new ArrayList<>();
|
||||
|
||||
boolean searchIgnoreCase = systemConfigService.getSearchIgnoreCase();
|
||||
|
||||
List<FileItemDTO> fileItemList = selectAllFileList();
|
||||
for (FileItemDTO fileItemDTO : fileItemList) {
|
||||
|
||||
boolean testResult;
|
||||
|
||||
if (searchIgnoreCase) {
|
||||
testResult = StrUtil.containsIgnoreCase(fileItemDTO.getName(), name);
|
||||
} else {
|
||||
testResult = fileItemDTO.getName().contains(name);
|
||||
}
|
||||
|
||||
if (testResult) {
|
||||
result.add(fileItemDTO);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有文件, 仅去缓存中查询.
|
||||
* @return 所有文件
|
||||
*/
|
||||
public List<FileItemDTO> selectAllFileList() {
|
||||
List<FileItemDTO> result = new ArrayList<>();
|
||||
boolean enableCache = systemConfigService.getEnableCache();
|
||||
if (!enableCache) {
|
||||
log.debug("未开启缓存, 不支持查询所有文件.");
|
||||
return null;
|
||||
}
|
||||
String path = "/";
|
||||
|
||||
List<FileItemDTO> fileItemList = cache.get(path);
|
||||
fileItemList = fileItemList == null ? new ArrayList<>() : fileItemList;
|
||||
ArrayDeque<FileItemDTO> queue = new ArrayDeque<>(fileItemList);
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
FileItemDTO fileItemDTO = queue.pop();
|
||||
result.add(fileItemDTO);
|
||||
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
|
||||
String filePath = StringUtils.removeDuplicateSeparator("/" + fileItemDTO.getPath() + "/" + fileItemDTO.getName() + "/");
|
||||
List<FileItemDTO> cacheList = cache.get(filePath);
|
||||
if (cacheList != null && isNotEncryptedFolder(cacheList)) {
|
||||
queue.addAll(cacheList);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
return zFileCache.find(name, searchIgnoreCase);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,7 +116,7 @@ public abstract class AbstractFileService extends FileCacheService implements Fi
|
||||
private boolean isNotEncryptedFolder(List<FileItemDTO> list) {
|
||||
// 如果开启了 "搜索包含加密文件" 选项, 则直接返回 true.
|
||||
SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();
|
||||
if (systemConfig.getSearchContainEncryptedFile()) {
|
||||
if (BooleanUtil.isFalse(systemConfig.getSearchContainEncryptedFile())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -200,44 +129,15 @@ public abstract class AbstractFileService extends FileCacheService implements Fi
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有缓存的 Key, 仅当开启缓存, 且缓存完成时, 可获取.
|
||||
* @return 所有缓存的 Key
|
||||
*/
|
||||
public Set<String> getCacheKeys() {
|
||||
if (systemConfigService.getEnableCache() && fileAsyncCacheService.isCacheFinish()) {
|
||||
Set<String> collect = selectAllFileList().stream().map(fileItemDTO -> {
|
||||
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
|
||||
return StringUtils.removeDuplicateSeparator("/" + fileItemDTO.getPath() + "/" + fileItemDTO.getName() + "/");
|
||||
}
|
||||
return null;
|
||||
}).collect(Collectors.toSet());
|
||||
collect.remove(null);
|
||||
collect.add("/");
|
||||
return collect;
|
||||
} else {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新缓存
|
||||
*/
|
||||
public void refreshCache(String key) throws Exception {
|
||||
cache.remove(key);
|
||||
zFileCache.remove(key);
|
||||
FileService currentFileService = (FileService) AopContext.currentProxy();
|
||||
currentFileService.fileList(key);
|
||||
}
|
||||
|
||||
public void closeCacheAutoRefresh() {
|
||||
cache.config().setRefreshPolicy(null);
|
||||
}
|
||||
|
||||
public void openCacheAutoRefresh() {
|
||||
RefreshPolicy refreshPolicy = RefreshPolicy.newPolicy(30, TimeUnit.MINUTES);
|
||||
cache.config().setRefreshPolicy(refreshPolicy);
|
||||
}
|
||||
|
||||
public abstract FileItemDTO getFileItem(String path);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
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;
|
||||
@@ -20,7 +20,6 @@ import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2019/12/26 22:26
|
||||
*/
|
||||
public abstract class AbstractS3FileService extends AbstractFileService {
|
||||
|
||||
@@ -29,14 +28,14 @@ public abstract class AbstractS3FileService extends AbstractFileService {
|
||||
|
||||
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;
|
||||
@@ -56,7 +55,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));
|
||||
|
||||
@@ -95,6 +94,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);
|
||||
|
||||
@@ -105,23 +109,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 = fileList(path);
|
||||
|
||||
if (list == null || list.size() == 0) {
|
||||
List<FileItemDTO> list;
|
||||
try {
|
||||
int end = path.lastIndexOf("/");
|
||||
list = fileList(path.substring(0, end + 1));
|
||||
} catch (Exception e) {
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
return list.get(0);
|
||||
|
||||
for (FileItemDTO fileItemDTO : list) {
|
||||
String fullPath = StringUtils.concatUrl(fileItemDTO.getPath(), fileItemDTO.getName());
|
||||
if (Objects.equals(fullPath, path)) {
|
||||
return fileItemDTO;
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
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.cache.ZFileCache;
|
||||
import im.zhaojun.common.config.StorageTypeFactory;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
@@ -26,20 +26,28 @@ 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;
|
||||
|
||||
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) {
|
||||
@@ -60,15 +68,7 @@ public class FileAsyncCacheService {
|
||||
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;
|
||||
}
|
||||
Integer cacheDirectoryCount = 0;
|
||||
|
||||
log.info("缓存 {} 所有文件开始", storageStrategy.getDescription());
|
||||
long startTime = System.currentTimeMillis();
|
||||
@@ -80,18 +80,10 @@ public class FileAsyncCacheService {
|
||||
while (!queue.isEmpty()) {
|
||||
FileItemDTO fileItemDTO = queue.pop();
|
||||
|
||||
if (FileTypeEnum.FOLDER.equals(fileItemDTO.getType())) {
|
||||
cacheDirectoryCount++;
|
||||
if (stopFlag) {
|
||||
zFileCache.clear();
|
||||
break;
|
||||
}
|
||||
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() + "/");
|
||||
@@ -100,34 +92,86 @@ public class FileAsyncCacheService {
|
||||
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;
|
||||
|
||||
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();
|
||||
|
||||
/**
|
||||
* 清理缓存的文件/文件夹数量统计
|
||||
*/
|
||||
public void resetCacheCount() {
|
||||
cache.remove(CACHE_DIRECTORY_COUNT_KEY);
|
||||
cache.remove(CACHE_FILE_COUNT_KEY);
|
||||
if (enableAutoRefreshCache) {
|
||||
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
||||
scheduledExecutorService.scheduleWithFixedDelay(() -> {
|
||||
zFileCache.setLastCacheAutoRefreshDate(new Date());
|
||||
|
||||
boolean enableCache = systemConfigService.getEnableCache();
|
||||
|
||||
if (!enableCache) {
|
||||
log.debug("当前存储引擎未开启缓存, 跳过自动刷新缓存");
|
||||
zFileCache.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("开始调用自动刷新缓存");
|
||||
|
||||
Set<String> keySet = zFileCache.keySet();
|
||||
|
||||
ArrayList<String> keys = new ArrayList<>(keySet);
|
||||
|
||||
|
||||
for (String key : keys) {
|
||||
try {
|
||||
Thread.sleep(300);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (stopFlag) {
|
||||
break;
|
||||
}
|
||||
|
||||
zFileCache.remove(key);
|
||||
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
|
||||
try {
|
||||
if (Objects.equals(currentStorageStrategy, systemConfigService.getCurrentStorageStrategy())) {
|
||||
currentFileService.fileList(key);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("刷新过程中出错 : [" + key + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (stopFlag) {
|
||||
log.debug("检测到停止 [{}] 缓存指令, 已停止自动刷新任务", currentStorageStrategy);
|
||||
scheduledExecutorService.shutdownNow();
|
||||
stopFlag = false;
|
||||
} else {
|
||||
log.debug("自动刷新缓存完成");
|
||||
}
|
||||
}, delay, interval, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getCacheDirectoryCount() {
|
||||
return ObjectUtil.defaultIfNull(cache.get(CACHE_DIRECTORY_COUNT_KEY), 0);
|
||||
public void stopScheduled() {
|
||||
this.stopFlag = true;
|
||||
}
|
||||
|
||||
public Integer getCacheFileCount() {
|
||||
return ObjectUtil.defaultIfNull(cache.get(CACHE_FILE_COUNT_KEY), 0);
|
||||
public void enableScheduled() {
|
||||
this.stopFlag = false;
|
||||
}
|
||||
|
||||
public boolean isCacheFinish() {
|
||||
@@ -137,4 +181,5 @@ public class FileAsyncCacheService {
|
||||
public void setCacheFinish(boolean cacheFinish) {
|
||||
this.cacheFinish = cacheFinish;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import im.zhaojun.common.cache.ZFileCache;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -7,7 +8,6 @@ import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2020/1/11 14:08
|
||||
*/
|
||||
@Service
|
||||
public class FileCacheService {
|
||||
@@ -19,21 +19,19 @@ public class FileCacheService {
|
||||
@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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package im.zhaojun.common.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.cache.ZFileCache;
|
||||
import im.zhaojun.common.config.StorageTypeFactory;
|
||||
import im.zhaojun.common.model.SystemConfig;
|
||||
import im.zhaojun.common.model.constant.SystemConfigConstant;
|
||||
@@ -31,8 +29,8 @@ public class SystemConfigService {
|
||||
|
||||
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;
|
||||
@@ -43,9 +41,9 @@ public class SystemConfigService {
|
||||
private Class<SystemConfigDTO> systemConfigDTOClass = SystemConfigDTO.class;
|
||||
|
||||
public SystemConfigDTO getSystemConfig() {
|
||||
Object cache = configCache.get(SYSTEM_CONFIG_CACHE_KEY);
|
||||
if (configCache.get(SYSTEM_CONFIG_CACHE_KEY) != null) {
|
||||
return (SystemConfigDTO) cache;
|
||||
SystemConfigDTO cacheConfig = zFileCache.getConfig();
|
||||
if (cacheConfig != null) {
|
||||
return cacheConfig;
|
||||
}
|
||||
|
||||
SystemConfigDTO systemConfigDTO = new SystemConfigDTO();
|
||||
@@ -69,7 +67,7 @@ public class SystemConfigService {
|
||||
}
|
||||
}
|
||||
|
||||
configCache.put(SYSTEM_CONFIG_CACHE_KEY, systemConfigDTO);
|
||||
zFileCache.updateConfig(systemConfigDTO);
|
||||
return systemConfigDTO;
|
||||
}
|
||||
|
||||
@@ -91,22 +89,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);
|
||||
|
||||
if (!oldEnableCache && curEnableCache) {
|
||||
log.debug("检测到开启了缓存, 开启预热缓存");
|
||||
fileCacheService.enableCache();
|
||||
}
|
||||
|
||||
if (oldEnableCache && !curEnableCache) {
|
||||
log.debug("检测到关闭了缓存, 正在清理缓存数据及关闭自动刷新");
|
||||
fileCacheService.disableCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +103,7 @@ public class SystemConfigService {
|
||||
SystemConfig systemConfig = systemConfigRepository.findByKey(SystemConfigConstant.PASSWORD);
|
||||
systemConfig.setValue(encryptionPassword);
|
||||
|
||||
configCache.remove(SYSTEM_CONFIG_CACHE_KEY);
|
||||
zFileCache.removeConfig();
|
||||
|
||||
systemConfigRepository.save(systemConfig);
|
||||
}
|
||||
@@ -129,7 +113,7 @@ 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -146,12 +130,12 @@ public class SystemConfigService {
|
||||
|
||||
public boolean getEnableCache() {
|
||||
SystemConfigDTO systemConfigDTO = getSystemConfig();
|
||||
return ObjectUtil.defaultIfNull(systemConfigDTO.getEnableCache(), false);
|
||||
return BooleanUtil.isTrue(systemConfigDTO.getEnableCache());
|
||||
}
|
||||
|
||||
public boolean getSearchIgnoreCase() {
|
||||
SystemConfigDTO systemConfigDTO = getSystemConfig();
|
||||
return ObjectUtil.defaultIfNull(systemConfigDTO.getSearchIgnoreCase(), false);
|
||||
return BooleanUtil.isTrue(systemConfigDTO.getSearchIgnoreCase());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import im.zhaojun.common.model.SystemMonitorInfo;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class SystemMonitorService {
|
||||
|
||||
public SystemMonitorInfo systemMonitorInfo() {
|
||||
return new SystemMonitorInfo();
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,22 @@ package im.zhaojun.common.service;
|
||||
import im.zhaojun.common.model.constant.ZFileConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.dto.SiteConfigDTO;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.util.HttpUtil;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SystemService {
|
||||
|
||||
@@ -20,18 +26,38 @@ public class SystemService {
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
/**
|
||||
* 构建指定路径下标题, 页头, 页尾
|
||||
* 构建指定路径下标题, 页面文档信息
|
||||
* @param path 路径
|
||||
*/
|
||||
public synchronized SiteConfigDTO getConfig(String path) throws Exception {
|
||||
public SiteConfigDTO getConfig(String path) throws Exception {
|
||||
|
||||
SiteConfigDTO siteConfigDTO = new SiteConfigDTO();
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
|
||||
List<FileItemDTO> fileItemList = new ArrayList<>(fileService.fileList(path));
|
||||
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.HEADER_FILE_NAME.equalsIgnoreCase(fileItemDTO.getName())) {
|
||||
siteConfigDTO.setHeader(HttpUtil.getTextContent(fileItemDTO.getUrl()));
|
||||
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;
|
||||
|
||||
@@ -37,16 +37,15 @@ public class FileComparator implements Comparator<FileItemDTO> {
|
||||
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;
|
||||
}
|
||||
@@ -57,4 +56,4 @@ public class FileComparator implements Comparator<FileItemDTO> {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
src/main/java/im/zhaojun/common/util/FileUtil.java
Normal file
68
src/main/java/im/zhaojun/common/util/FileUtil.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package im.zhaojun.common.util;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class FileUtil {
|
||||
|
||||
public static ResponseEntity<Object> export(File file, String fileName) {
|
||||
if (!file.exists()) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("404 FILE NOT FOUND");
|
||||
}
|
||||
|
||||
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
|
||||
if (StringUtils.isNullOrEmpty(fileName)) {
|
||||
fileName = file.getName();
|
||||
}
|
||||
|
||||
headers.setContentDispositionFormData("attachment", URLUtil.encode(fileName));
|
||||
|
||||
headers.add("Pragma", "no-cache");
|
||||
headers.add("Expires", "0");
|
||||
headers.add("Last-Modified", new Date().toString());
|
||||
headers.add("ETag", String.valueOf(System.currentTimeMillis()));
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.headers(headers)
|
||||
.contentLength(file.length())
|
||||
.contentType(mediaType)
|
||||
.body(new FileSystemResource(file));
|
||||
}
|
||||
|
||||
public static ResponseEntity<Object> export(File file) {
|
||||
if (!file.exists()) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("404 FILE NOT FOUND");
|
||||
}
|
||||
|
||||
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
headers.setContentDispositionFormData("attachment", URLUtil.encode(file.getName()));
|
||||
|
||||
headers.add("Pragma", "no-cache");
|
||||
headers.add("Expires", "0");
|
||||
headers.add("Last-Modified", new Date().toString());
|
||||
headers.add("ETag", String.valueOf(System.currentTimeMillis()));
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.headers(headers)
|
||||
.contentLength(file.length())
|
||||
.contentType(mediaType)
|
||||
.body(new FileSystemResource(file));
|
||||
}
|
||||
}
|
||||
142
src/main/java/im/zhaojun/common/util/NaturalOrderComparator.java
Normal file
142
src/main/java/im/zhaojun/common/util/NaturalOrderComparator.java
Normal file
@@ -0,0 +1,142 @@
|
||||
package im.zhaojun.common.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;
|
||||
|
||||
public class NaturalOrderComparator implements Comparator<String> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int compare(String a, String b) {
|
||||
int ia = 0, ib = 0;
|
||||
int nza = 0, nzb = 0;
|
||||
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 == '0') {
|
||||
if (ca == '0') {
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
package im.zhaojun.common.util;
|
||||
|
||||
import cn.hutool.core.net.NetUtil;
|
||||
import im.zhaojun.common.cache.ZFileCache;
|
||||
import im.zhaojun.common.exception.InitializeException;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.FileAsyncCacheService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.env.Environment;
|
||||
@@ -11,7 +16,7 @@ import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 项目启动监听器, 当项目启动时, 遍历当前对象存储的所有内容, 添加到缓存中.
|
||||
@@ -27,12 +32,16 @@ public class StartupListener implements ApplicationListener<ApplicationStartedEv
|
||||
@Resource
|
||||
private Environment environment;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(@NonNull ApplicationStartedEvent event) {
|
||||
printStartInfo();
|
||||
cacheAllFile();
|
||||
}
|
||||
|
||||
|
||||
private void printStartInfo() {
|
||||
String serverPort = environment.getProperty("server.port", "8080");
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package im.zhaojun.common.util;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@@ -88,4 +90,14 @@ public class StringUtils {
|
||||
public static boolean isNotNullOrEmpty(String s) {
|
||||
return !isNullOrEmpty(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 basePath + path 的全路径地址.
|
||||
* @return basePath + path 的全路径地址.
|
||||
*/
|
||||
public static String getFullPath(String basePath, String path) {
|
||||
basePath = ObjectUtil.defaultIfNull(basePath, "");
|
||||
path = ObjectUtil.defaultIfNull(path, "");
|
||||
return StringUtils.removeDuplicateSeparator(basePath + "/" + path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,13 @@ 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.apache.commons.net.ftp.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -37,24 +38,33 @@ public class FtpServiceImpl extends AbstractFileService implements FileService {
|
||||
|
||||
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(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();
|
||||
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);
|
||||
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() + " 初始化异常, 已跳过");
|
||||
}
|
||||
@@ -62,7 +72,17 @@ public class FtpServiceImpl extends AbstractFileService implements FileService {
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) {
|
||||
FTPFile[] ftpFiles = ftp.lsFiles(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<>();
|
||||
|
||||
@@ -83,7 +103,17 @@ public class FtpServiceImpl extends AbstractFileService implements FileService {
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
return URLUtil.complateUrl(domain, 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
|
||||
@@ -98,4 +128,33 @@ public class FtpServiceImpl extends AbstractFileService implements FileService {
|
||||
return fileItemDTO;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean testConnection() {
|
||||
// FTPClient ftpClient = ftp.getClient();
|
||||
// try {
|
||||
// ftpClient.connect(host, Integer.parseInt(port));
|
||||
// return ftpClient.login(username, password);
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// return false;
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ifDisConnectionReConnection() {
|
||||
FTPClient ftpClient = ftp.getClient();
|
||||
try {
|
||||
// 验证FTP服务器是否登录成功
|
||||
int replyCode = ftpClient.getReplyCode();
|
||||
|
||||
// ftpClient.reinitialize();
|
||||
if (!FTPReply.isPositiveCompletion(replyCode)) {
|
||||
System.out.println("断开了连接, 已尝试重新连接.");
|
||||
ftpClient.connect(host, Integer.parseInt(port));
|
||||
ftpClient.login(username, password);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package im.zhaojun.huawei.service;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
@@ -27,7 +28,8 @@ public class HuaweiServiceImpl extends AbstractS3FileService implements FileServ
|
||||
@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());
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
package im.zhaojun.local.controller;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import im.zhaojun.common.exception.NotExistFileException;
|
||||
import im.zhaojun.common.util.FileUtil;
|
||||
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;
|
||||
@@ -17,7 +13,6 @@ 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
|
||||
@@ -30,39 +25,13 @@ public class LocalController {
|
||||
|
||||
@GetMapping("/file/**")
|
||||
@ResponseBody
|
||||
public ResponseEntity<FileSystemResource> downAttachment(final HttpServletRequest request) {
|
||||
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 export(new File(StringUtils.concatPath(localServiceImpl.getFilePath(), URLUtil.decode(filePath))));
|
||||
return FileUtil.export(new File(StringUtils.concatPath(localServiceImpl.getFilePath(), filePath)));
|
||||
}
|
||||
|
||||
private ResponseEntity<FileSystemResource> export(File file) {
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
|
||||
|
||||
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
headers.setContentDispositionFormData("attachment", URLUtil.encode(file.getName()));
|
||||
|
||||
headers.add("Pragma", "no-cache");
|
||||
headers.add("Expires", "0");
|
||||
headers.add("Last-Modified", new Date().toString());
|
||||
headers.add("ETag", String.valueOf(System.currentTimeMillis()));
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.headers(headers)
|
||||
.contentLength(file.length())
|
||||
.contentType(mediaType)
|
||||
.body(new FileSystemResource(file));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package im.zhaojun.local.service;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import im.zhaojun.common.exception.NotExistFileException;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.SystemConfig;
|
||||
@@ -46,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());
|
||||
@@ -90,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() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package im.zhaojun.minio;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
@@ -27,12 +28,14 @@ public class MinIOServiceImpl extends AbstractS3FileService implements FileServi
|
||||
@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());
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package im.zhaojun.onedrive.china.service;
|
||||
|
||||
import im.zhaojun.common.config.GlobalScheduleTask;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.onedrive.common.service.AbstractOneDriveService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OneDriveChinaServiceImpl extends AbstractOneDriveService implements FileService {
|
||||
|
||||
@Resource
|
||||
private GlobalScheduleTask globalScheduleTask;
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Value("${zfile.onedrive-china.clientId}")
|
||||
private String clientId;
|
||||
|
||||
@Value("${zfile.onedrive-china.redirectUri}")
|
||||
private String redirectUri;
|
||||
|
||||
@Value("${zfile.onedrive-china.clientSecret}")
|
||||
private String clientSecret;
|
||||
|
||||
@Value("${zfile.onedrive-china.scope}")
|
||||
private String scope;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
String accessToken = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_TOKEN_KEY).getValue();
|
||||
String refreshToken = stringStorageConfigMap.get(StorageConfigConstant.REFRESH_TOKEN_KEY).getValue();
|
||||
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
|
||||
if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
refreshOneDriveToken();
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.ONE_DRIVE_CHINA;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGraphEndPoint() {
|
||||
return "microsoftgraph.chinacloudapi.cn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticateEndPoint() {
|
||||
return "login.partner.microsoftonline.cn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
package im.zhaojun.onedrive.config;
|
||||
package im.zhaojun.onedrive.common.config;
|
||||
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.onedrive.china.service.OneDriveChinaServiceImpl;
|
||||
import im.zhaojun.onedrive.international.service.OneDriveServiceImpl;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@@ -14,7 +17,6 @@ import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2020/1/18 17:13
|
||||
*/
|
||||
@Configuration
|
||||
public class OneDriveConfig {
|
||||
@@ -22,13 +24,32 @@ 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(StorageTypeEnum.ONE_DRIVE, StorageConfigConstant.ACCESS_TOKEN_KEY);
|
||||
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);
|
||||
@@ -37,5 +58,4 @@ public class OneDriveConfig {
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package im.zhaojun.onedrive.common.controller;
|
||||
|
||||
import im.zhaojun.onedrive.china.service.OneDriveChinaServiceImpl;
|
||||
import im.zhaojun.onedrive.common.model.OneDriveToken;
|
||||
import im.zhaojun.onedrive.international.service.OneDriveServiceImpl;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/onedrive")
|
||||
public class OneDriveController {
|
||||
|
||||
@Resource
|
||||
private OneDriveServiceImpl oneDriveServiceImpl;
|
||||
|
||||
@Resource
|
||||
private OneDriveChinaServiceImpl oneDriveChinaServiceImpl;
|
||||
|
||||
@GetMapping("/callback")
|
||||
public String onedriveCallback(String code, Model model) {
|
||||
OneDriveToken oneDriveToken = oneDriveServiceImpl.getToken(code);
|
||||
model.addAttribute("accessToken", oneDriveToken.getAccessToken());
|
||||
model.addAttribute("refreshToken", oneDriveToken.getRefreshToken());
|
||||
return "callback";
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/china-callback")
|
||||
public String onedriveChinaCallback(String code, Model model) {
|
||||
OneDriveToken oneDriveToken = oneDriveChinaServiceImpl.getToken(code);
|
||||
model.addAttribute("accessToken", oneDriveToken.getAccessToken());
|
||||
model.addAttribute("refreshToken", oneDriveToken.getRefreshToken());
|
||||
return "callback";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
package im.zhaojun.onedrive.service;
|
||||
package im.zhaojun.onedrive.common.model;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2020/1/18 17:28
|
||||
*/
|
||||
@Data
|
||||
public class OneDriveToken {
|
||||
@@ -0,0 +1,212 @@
|
||||
package im.zhaojun.onedrive.common.service;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.repository.StorageConfigRepository;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import im.zhaojun.onedrive.common.model.OneDriveToken;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Zhao Jun
|
||||
* 2020/1/29 11:54
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractOneDriveService extends AbstractFileService {
|
||||
|
||||
protected static final String DRIVER_INFO_URL = "https://{graphEndPoint}/v1.0/me/drives";
|
||||
|
||||
protected static final String DRIVER_ROOT_URL = "https://{graphEndPoint}/v1.0/me/drive/root/children";
|
||||
|
||||
protected static final String DRIVER_ITEMS_URL = "https://{graphEndPoint}/v1.0/me/drive/root:{path}:/children";
|
||||
|
||||
protected static final String DRIVER_ITEM_URL = "https://{graphEndPoint}/v1.0/me/drive/root:{path}";
|
||||
|
||||
protected static final String AUTHENTICATE_URL = "https://{authenticateEndPoint}/common/oauth2/v2.0/token";
|
||||
|
||||
@Resource
|
||||
private RestTemplate oneDriveRestTemplate;
|
||||
|
||||
@Resource
|
||||
private StorageConfigRepository storageConfigRepository;
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
public OneDriveToken getRefreshToken() {
|
||||
StorageConfig refreshStorageConfig =
|
||||
storageConfigRepository.findByTypeAndKey(this.getStorageTypeEnum(), StorageConfigConstant.REFRESH_TOKEN_KEY);
|
||||
|
||||
String param = "client_id=" + getClientId() +
|
||||
"&redirect_uri=" + getRedirectUri() +
|
||||
"&client_secret=" + getClientSecret() +
|
||||
"&refresh_token=" + refreshStorageConfig.getValue() +
|
||||
"&grant_type=refresh_token";
|
||||
|
||||
String fullAuthenticateUrl = AUTHENTICATE_URL.replace("{authenticateEndPoint}", getAuthenticateEndPoint());
|
||||
HttpRequest post = HttpUtil.createPost(fullAuthenticateUrl);
|
||||
|
||||
post.body(param, "application/x-www-form-urlencoded");
|
||||
HttpResponse response = post.execute();
|
||||
return JSONObject.parseObject(response.body(), OneDriveToken.class);
|
||||
}
|
||||
|
||||
public OneDriveToken getToken(String code) {
|
||||
String param = "client_id=" + getClientId() +
|
||||
"&redirect_uri=" + getRedirectUri() +
|
||||
"&client_secret=" + getClientSecret() +
|
||||
"&code=" + code +
|
||||
"&scope=" + getScope() +
|
||||
"&grant_type=authorization_code";
|
||||
|
||||
String fullAuthenticateUrl = AUTHENTICATE_URL.replace("{authenticateEndPoint}", getAuthenticateEndPoint());
|
||||
HttpRequest post = HttpUtil.createPost(fullAuthenticateUrl);
|
||||
|
||||
post.body(param, "application/x-www-form-urlencoded");
|
||||
HttpResponse response = post.execute();
|
||||
return JSONObject.parseObject(response.body(), OneDriveToken.class);
|
||||
}
|
||||
|
||||
public String getUserInfo() {
|
||||
return oneDriveRestTemplate.getForObject(DRIVER_INFO_URL, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) {
|
||||
path = StringUtils.removeFirstSeparator(path);
|
||||
String fullPath = StringUtils.getFullPath(basePath, path);
|
||||
|
||||
List<FileItemDTO> result = new ArrayList<>();
|
||||
String nextLink = null;
|
||||
|
||||
do {
|
||||
|
||||
String requestUrl;
|
||||
|
||||
if (nextLink != null) {
|
||||
nextLink = nextLink.replace("+", "%2B");
|
||||
requestUrl = URLUtil.decode(nextLink);
|
||||
}else if ("/".equalsIgnoreCase(fullPath) || "".equalsIgnoreCase(fullPath)) {
|
||||
requestUrl = DRIVER_ROOT_URL;
|
||||
} else {
|
||||
requestUrl = DRIVER_ITEMS_URL;
|
||||
}
|
||||
fullPath = StringUtils.removeLastSeparator(fullPath);
|
||||
|
||||
ResponseEntity<String> responseEntity;
|
||||
try {
|
||||
responseEntity = oneDriveRestTemplate.getForEntity(requestUrl, String.class, getGraphEndPoint(), fullPath);
|
||||
} catch (HttpClientErrorException e) {
|
||||
log.debug("调用 OneDrive 时出现了网络异常: {} , 已尝试重新刷新 token 后再试.", e.getMessage());
|
||||
refreshOneDriveToken();
|
||||
responseEntity = oneDriveRestTemplate.getForEntity(requestUrl, String.class, getGraphEndPoint(), fullPath);
|
||||
}
|
||||
|
||||
String body = responseEntity.getBody();
|
||||
|
||||
JSONObject root = JSON.parseObject(body);
|
||||
|
||||
nextLink = root.getString("@odata.nextLink");
|
||||
|
||||
JSONArray fileList = root.getJSONArray("value");
|
||||
|
||||
for (int i = 0; i < fileList.size(); i++) {
|
||||
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
JSONObject fileItem = fileList.getJSONObject(i);
|
||||
fileItemDTO.setName(fileItem.getString("name"));
|
||||
fileItemDTO.setSize(fileItem.getLong("size"));
|
||||
fileItemDTO.setTime(fileItem.getDate("lastModifiedDateTime"));
|
||||
|
||||
if (fileItem.containsKey("file")) {
|
||||
fileItemDTO.setUrl(fileItem.getString("@microsoft.graph.downloadUrl"));
|
||||
fileItemDTO.setType(FileTypeEnum.FILE);
|
||||
} else {
|
||||
fileItemDTO.setType(FileTypeEnum.FOLDER);
|
||||
}
|
||||
|
||||
fileItemDTO.setPath(path);
|
||||
result.add(fileItemDTO);
|
||||
}
|
||||
} while (nextLink != null);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileItemDTO getFileItem(String path) {
|
||||
|
||||
String fullPath = StringUtils.getFullPath(basePath, path);
|
||||
|
||||
String requestUrl;
|
||||
|
||||
ResponseEntity<String> responseEntity = oneDriveRestTemplate.getForEntity(DRIVER_ITEM_URL, String.class, getGraphEndPoint(), fullPath);
|
||||
String body = responseEntity.getBody();
|
||||
|
||||
JSONObject fileItem = JSON.parseObject(body);
|
||||
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
fileItemDTO.setName(fileItem.getString("name"));
|
||||
fileItemDTO.setSize(fileItem.getLong("size"));
|
||||
fileItemDTO.setTime(fileItem.getDate("lastModifiedDateTime"));
|
||||
|
||||
if (fileItem.containsKey("file")) {
|
||||
fileItemDTO.setUrl(fileItem.getString("@microsoft.graph.downloadUrl"));
|
||||
fileItemDTO.setType(FileTypeEnum.FILE);
|
||||
} else {
|
||||
fileItemDTO.setType(FileTypeEnum.FOLDER);
|
||||
}
|
||||
|
||||
fileItemDTO.setPath(path);
|
||||
return fileItemDTO;
|
||||
}
|
||||
|
||||
|
||||
public abstract String getGraphEndPoint();
|
||||
|
||||
public abstract String getAuthenticateEndPoint();
|
||||
|
||||
public abstract String getClientId();
|
||||
|
||||
public abstract String getRedirectUri();
|
||||
|
||||
public abstract String getClientSecret();
|
||||
|
||||
public abstract String getScope();
|
||||
|
||||
public void refreshOneDriveToken() {
|
||||
OneDriveToken refreshToken = getRefreshToken();
|
||||
|
||||
if (refreshToken.getAccessToken() == null || refreshToken.getRefreshToken() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
StorageConfig accessTokenConfig =
|
||||
storageConfigService.selectByTypeAndKey(this.getStorageTypeEnum(), StorageConfigConstant.ACCESS_TOKEN_KEY);
|
||||
StorageConfig refreshTokenConfig =
|
||||
storageConfigService.selectByTypeAndKey(this.getStorageTypeEnum(), StorageConfigConstant.REFRESH_TOKEN_KEY);
|
||||
accessTokenConfig.setValue(refreshToken.getAccessToken());
|
||||
refreshTokenConfig.setValue(refreshToken.getRefreshToken());
|
||||
|
||||
storageConfigService.updateStorageConfig(Arrays.asList(accessTokenConfig, refreshTokenConfig));
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package im.zhaojun.onedrive.controller;
|
||||
|
||||
import im.zhaojun.onedrive.service.OneDriveService;
|
||||
import im.zhaojun.onedrive.service.OneDriveToken;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/onedirve")
|
||||
public class OneDriveController {
|
||||
|
||||
@Resource
|
||||
private OneDriveService oneDriveService;
|
||||
|
||||
@GetMapping("/callback")
|
||||
public String onedriveCallback(String code, Model model) {
|
||||
OneDriveToken oneDriveToken = oneDriveService.getToken(code);
|
||||
model.addAttribute("accessToken", oneDriveToken.getAccessToken());
|
||||
model.addAttribute("refreshToken", oneDriveToken.getRefreshToken());
|
||||
return "callback";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +1,26 @@
|
||||
package im.zhaojun.onedrive.service;
|
||||
package im.zhaojun.onedrive.international.service;
|
||||
|
||||
import im.zhaojun.common.config.GlobalScheduleTask;
|
||||
import im.zhaojun.common.exception.NotExistFileException;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.onedrive.common.service.AbstractOneDriveService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2020/1/12 13:53
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OneDriveServiceImpl extends AbstractFileService implements FileService {
|
||||
|
||||
public static final String SYSTEM_ONEDRIVE_CACHE_PREFIX = "zfile-onedrive-cache:";
|
||||
|
||||
public static final String SYSTEM_ONEDRIVE_CACHE_ASSESS_TOKEN_KEY = "accessToken";
|
||||
|
||||
public static final String SYSTEM_ONEDRIVE_CACHE_REFRESH_TOKEN_KEY = "refreshToken";
|
||||
public class OneDriveServiceImpl extends AbstractOneDriveService implements FileService {
|
||||
|
||||
@Resource
|
||||
private GlobalScheduleTask globalScheduleTask;
|
||||
@@ -37,22 +28,32 @@ public class OneDriveServiceImpl extends AbstractFileService implements FileServ
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Resource
|
||||
private OneDriveService oneDriveService;
|
||||
@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(StorageTypeEnum.ONE_DRIVE);
|
||||
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 {
|
||||
globalScheduleTask.refreshOneDriveToken();
|
||||
refreshOneDriveToken();
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -60,11 +61,6 @@ public class OneDriveServiceImpl extends AbstractFileService implements FileServ
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) {
|
||||
return oneDriveService.list(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
return null;
|
||||
@@ -76,12 +72,32 @@ public class OneDriveServiceImpl extends AbstractFileService implements FileServ
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileItemDTO getFileItem(String path) {
|
||||
List<FileItemDTO> list = fileList(path);
|
||||
|
||||
if (list == null || list.size() == 0) {
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
return list.get(0);
|
||||
public String getGraphEndPoint() {
|
||||
return "graph.microsoft.com";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticateEndPoint() {
|
||||
return "login.microsoftonline.com";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package im.zhaojun.onedrive.service;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.repository.StorageConfigRepository;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2020/1/12 12:17
|
||||
*/
|
||||
@Service
|
||||
public class OneDriveService {
|
||||
|
||||
private static final String DRIVER_INFO_URL = "https://graph.microsoft.com/v1.0/me/drives";
|
||||
|
||||
private static final String DRIVER_ROOT_URL = "https://graph.microsoft.com/v1.0/me/drive/root/children";
|
||||
|
||||
private static final String DRIVER_ITEMS_URL = "https://graph.microsoft.com/v1.0/me/drive/root:{path}:/children";
|
||||
|
||||
private static final String AUTHENTICATE_URL = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
|
||||
|
||||
@Value("${zfile.onedirve.clientId}")
|
||||
private String clientId;
|
||||
|
||||
@Value("${zfile.onedirve.redirectUri}")
|
||||
private String redirectUri;
|
||||
|
||||
@Value("${zfile.onedirve.clientSecret}")
|
||||
private String clientSecret;
|
||||
|
||||
@Value("${zfile.onedirve.scope}")
|
||||
private String scope;
|
||||
|
||||
@Resource
|
||||
private RestTemplate oneDriveRestTemplate;
|
||||
|
||||
@Resource
|
||||
private StorageConfigRepository storageConfigRepository;
|
||||
|
||||
public OneDriveToken getToken(String code) {
|
||||
String param = "client_id=" + clientId +
|
||||
"&redirect_uri=" + redirectUri +
|
||||
"&client_secret=" + clientSecret +
|
||||
"&code=" + code +
|
||||
"&scope=" + scope +
|
||||
"&grant_type=authorization_code";
|
||||
|
||||
HttpRequest post = HttpUtil.createPost(AUTHENTICATE_URL);
|
||||
|
||||
post.body(param, "application/x-www-form-urlencoded");
|
||||
HttpResponse response = post.execute();
|
||||
return JSONObject.parseObject(response.body(), OneDriveToken.class);
|
||||
}
|
||||
|
||||
public OneDriveToken getRefreshToken() {
|
||||
StorageConfig refreshStorageConfig =
|
||||
storageConfigRepository.findByTypeAndKey(StorageTypeEnum.ONE_DRIVE, StorageConfigConstant.REFRESH_TOKEN_KEY);
|
||||
|
||||
String param = "client_id=" + clientId +
|
||||
"&redirect_uri=" + redirectUri +
|
||||
"&client_secret=" + clientSecret +
|
||||
"&refresh_token=" + refreshStorageConfig.getValue() +
|
||||
"&grant_type=refresh_token";
|
||||
|
||||
HttpRequest post = HttpUtil.createPost(AUTHENTICATE_URL);
|
||||
|
||||
post.body(param, "application/x-www-form-urlencoded");
|
||||
HttpResponse response = post.execute();
|
||||
return JSONObject.parseObject(response.body(), OneDriveToken.class);
|
||||
}
|
||||
|
||||
public String getUserInfo() {
|
||||
return oneDriveRestTemplate.getForObject(DRIVER_INFO_URL, String.class);
|
||||
}
|
||||
|
||||
public List<FileItemDTO> list(String path) {
|
||||
List<FileItemDTO> result = new ArrayList<>();
|
||||
String nextLink = null;
|
||||
|
||||
do {
|
||||
|
||||
String requestUrl;
|
||||
|
||||
if (nextLink != null) {
|
||||
requestUrl = nextLink;
|
||||
}else if ("/".equalsIgnoreCase(path)) {
|
||||
requestUrl = DRIVER_ROOT_URL;
|
||||
} else {
|
||||
requestUrl = DRIVER_ITEMS_URL;
|
||||
}
|
||||
path = StringUtils.removeLastSeparator(path);
|
||||
|
||||
ResponseEntity<String> responseEntity = oneDriveRestTemplate.getForEntity(requestUrl, String.class, path);
|
||||
String body = responseEntity.getBody();
|
||||
|
||||
JSONObject root = JSON.parseObject(body);
|
||||
|
||||
nextLink = root.getString("@odata.nextLink");
|
||||
|
||||
JSONArray fileList = root.getJSONArray("value");
|
||||
|
||||
for (int i = 0; i < fileList.size(); i++) {
|
||||
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
JSONObject fileItem = fileList.getJSONObject(i);
|
||||
fileItemDTO.setName(fileItem.getString("name"));
|
||||
fileItemDTO.setSize(fileItem.getLong("size"));
|
||||
fileItemDTO.setTime(fileItem.getDate("lastModifiedDateTime"));
|
||||
|
||||
if (fileItem.containsKey("file")) {
|
||||
fileItemDTO.setUrl(fileItem.getString("@microsoft.graph.downloadUrl"));
|
||||
fileItemDTO.setType(FileTypeEnum.FILE);
|
||||
} else {
|
||||
fileItemDTO.setType(FileTypeEnum.FOLDER);
|
||||
}
|
||||
|
||||
fileItemDTO.setPath(path);
|
||||
result.add(fileItemDTO);
|
||||
}
|
||||
} while (nextLink != null);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package im.zhaojun.qiniu.service;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
@@ -27,7 +28,8 @@ public class QiniuServiceImpl extends AbstractS3FileService implements FileServi
|
||||
@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());
|
||||
|
||||
68
src/main/java/im/zhaojun/s3/S3ServiceImpl.java
Normal file
68
src/main/java/im/zhaojun/s3/S3ServiceImpl.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package im.zhaojun.s3;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractS3FileService;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class S3ServiceImpl extends AbstractS3FileService implements FileService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(S3ServiceImpl.class);
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
|
||||
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
|
||||
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
|
||||
|
||||
super.domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
|
||||
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
super.bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
|
||||
super.isPrivate = Convert.toBool(stringStorageConfigMap.get(StorageConfigConstant.IS_PRIVATE).getValue(), true);
|
||||
|
||||
String pathStyle = stringStorageConfigMap.get(StorageConfigConstant.PATH_STYLE).getValue();
|
||||
|
||||
boolean isPathStyle = "path-style".equals(pathStyle);
|
||||
|
||||
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
|
||||
s3Client = AmazonS3ClientBuilder.standard()
|
||||
.withPathStyleAccessEnabled(isPathStyle)
|
||||
.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "")).build();
|
||||
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.S3;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package im.zhaojun.tencent;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
@@ -27,13 +28,15 @@ public class TencentServiceImpl extends AbstractS3FileService implements FileSer
|
||||
@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());
|
||||
|
||||
18
src/main/java/im/zhaojun/ufile/service/UFileServiceImpl.java
Normal file
18
src/main/java/im/zhaojun/ufile/service/UFileServiceImpl.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package im.zhaojun.ufile.service;
|
||||
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.upyun.service.UpYunServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class UFileServiceImpl extends UpYunServiceImpl {
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.UFILE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -45,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();
|
||||
|
||||
@@ -6,10 +6,25 @@
|
||||
"description": "目录缓存过期时间 和 下载地址过期时间. 单位为秒."
|
||||
},
|
||||
{
|
||||
"name": "zfile.constant.header",
|
||||
"name": "zfile.cache.auto-refresh.enable",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "是否开启自动刷新缓存."
|
||||
},
|
||||
{
|
||||
"name": "zfile.cache.auto-refresh.delay",
|
||||
"type": "java.lang.Long",
|
||||
"description": "启动项目后多久开始自动刷新缓存, 推荐与 interval 一致, 因为项目启动时会缓存所有文件. 单位为秒.."
|
||||
},
|
||||
{
|
||||
"name": "zfile.cache.auto-refresh.interval",
|
||||
"type": "java.lang.Long",
|
||||
"description": "任务间隔时间, 也就是每多长时间会自动刷新缓存一次.."
|
||||
},
|
||||
{
|
||||
"name": "zfile.constant.readme",
|
||||
"type": "java.lang.String",
|
||||
"defaultValue": "header.md",
|
||||
"description": "头部文件 文件名."
|
||||
"defaultValue": "readme.md",
|
||||
"description": "文档文件 文件名."
|
||||
},
|
||||
{
|
||||
"name": "zfile.constant.password",
|
||||
@@ -18,24 +33,44 @@
|
||||
"description": "密码文件 文件名."
|
||||
},
|
||||
{
|
||||
"name": "zfile.onedirve.clientId",
|
||||
"name": "zfile.onedrive.clientId",
|
||||
"type": "java.lang.String",
|
||||
"description": "OneDrive ClientId."
|
||||
},
|
||||
{
|
||||
"name": "zfile.onedirve.clientSecret",
|
||||
"name": "zfile.onedrive.clientSecret",
|
||||
"type": "java.lang.String",
|
||||
"description": "OneDrive ClientSecret."
|
||||
},
|
||||
{
|
||||
"name": "zfile.onedirve.redirectUri",
|
||||
"name": "zfile.onedrive.redirectUri",
|
||||
"type": "java.lang.String",
|
||||
"description": "OneDrive 认证重定向地址."
|
||||
},
|
||||
{
|
||||
"name": "zfile.onedirve.scope",
|
||||
"name": "zfile.onedrive.scope",
|
||||
"type": "java.lang.String",
|
||||
"description": "OneDrive 认证权限."
|
||||
},
|
||||
{
|
||||
"name": "zfile.onedrive-china.clientId",
|
||||
"type": "java.lang.String",
|
||||
"description": "OneDrive China ClientId."
|
||||
},
|
||||
{
|
||||
"name": "zfile.onedrive-china.clientSecret",
|
||||
"type": "java.lang.String",
|
||||
"description": "OneDrive China ClientSecret."
|
||||
},
|
||||
{
|
||||
"name": "zfile.onedrive-china.redirectUri",
|
||||
"type": "java.lang.String",
|
||||
"description": "OneDrive China 认证重定向地址."
|
||||
},
|
||||
{
|
||||
"name": "zfile.onedrive-china.scope",
|
||||
"type": "java.lang.String",
|
||||
"description": "OneDrive China 认证权限."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -8,9 +8,17 @@ server:
|
||||
enabled: true
|
||||
|
||||
spring:
|
||||
h2:
|
||||
console:
|
||||
settings:
|
||||
web-allow-others: true
|
||||
path: /h2-console
|
||||
enabled: false
|
||||
datasource:
|
||||
# 初始化数据导入
|
||||
data: classpath*:db/data.sql
|
||||
sql-script-encoding: utf-8
|
||||
|
||||
initialization-mode: always
|
||||
continue-on-error: true
|
||||
|
||||
@@ -21,10 +29,10 @@ spring:
|
||||
password: 123456
|
||||
|
||||
# MySQL 配置
|
||||
# driver-class-name: com.mysql.jdbc.Driver
|
||||
# url: jdbc:mysql://127.0.0.1:3306/zfile?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
|
||||
# username: root
|
||||
# password: 123456
|
||||
# driver-class-name: com.mysql.jdbc.Driver
|
||||
# url: jdbc:mysql://127.0.0.1:3306/zfile?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
|
||||
# username: root
|
||||
# password: 123456
|
||||
jackson:
|
||||
date-format: yyyy-MM-dd HH:mm
|
||||
time-zone: GMT+8
|
||||
@@ -40,22 +48,24 @@ spring:
|
||||
gzipped: true
|
||||
profiles:
|
||||
active: prod
|
||||
|
||||
zfile:
|
||||
cache:
|
||||
timeout: 300
|
||||
auto-refresh:
|
||||
enable: true # 是否开启自动刷新缓存.
|
||||
delay: 1800 # 启动项目后多久开始自动刷新缓存, 推荐与 interval 一致, 因为项目启动时会缓存所有文件.
|
||||
interval: 1800 # 任务间隔时间, 也就是每多长时间会自动刷新缓存一次.
|
||||
timeout: 1800
|
||||
constant:
|
||||
header: header.md
|
||||
readme: readme.md
|
||||
password: password.txt
|
||||
onedirve:
|
||||
onedrive:
|
||||
clientId: 09939809-c617-43c8-a220-a93c1513c5d4
|
||||
clientSecret: _l:zI-_yrW75lV8M61K@z.I2K@B/On6Q
|
||||
redirectUri: https://zfile.jun6.net/onedirve/callback
|
||||
redirectUri: https://zfile.jun6.net/onedrive/callback
|
||||
scope: offline_access User.Read Files.ReadWrite.All
|
||||
onedrive-china:
|
||||
clientId: 4a72d927-1907-488d-9eb2-1b465c53c1c5
|
||||
clientSecret: Y9CEA=82da5n-y_]KAWAgLH3?R9xf7Uw
|
||||
redirectUri: https://zfile.jun6.net/onedrive/china-callback
|
||||
scope: offline_access User.Read Files.ReadWrite.All
|
||||
|
||||
jetcache:
|
||||
statIntervalMinutes: 0
|
||||
areaInCacheName: false
|
||||
local:
|
||||
default:
|
||||
type: caffeine
|
||||
keyConvertor: fastjson
|
||||
@@ -10,6 +10,12 @@ INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (9, 'enableCache', '是
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (10, 'searchContainEncryptedFile', '搜索包含加密文件');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (11, 'customCss', '自定义 CSS');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (12, 'customJs', '自定义 JS (可用于统计代码)');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (13, 'tableSize', '表格大小');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (14, 'showOperator', '是否显示操作按钮');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (15, 'showDocument', '是否显示文档');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (16, 'announcement', '网站公告');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (17, 'showAnnouncement', '是否显示网站公告');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (18, 'layout', '页面布局');
|
||||
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (1, 'bucket-name', '云存储服务名称', 'upyun');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (2, 'username', '操作员名称', 'upyun');
|
||||
@@ -34,7 +40,7 @@ INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (20, 'host', '域
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (21, 'port', '端口', 'ftp');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (22, 'username', '用户名', 'ftp');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (23, 'password', '密码', 'ftp');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (24, 'domain', '域名', 'ftp');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (24, 'domain', '加速域名', 'ftp');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (25, 'secretId', 'SecretId', 'tencent');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (26, 'secretKey', 'SecretKey', 'tencent');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (27, 'bucket-name', '云存储服务名称', 'tencent');
|
||||
@@ -51,6 +57,30 @@ INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (37, 'endPoint',
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (38, 'base-path', '基路径', 'qiniu');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (39, 'base-path', '基路径', 'aliyun');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (40, 'base-path', '基路径', 'huawei');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (41, 'ftp', '基路径', 'ftp');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (41, 'base-path', '基路径', 'ftp');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (42, 'accessToken', '访问令牌', 'onedrive');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (43, 'refreshToken', '刷新令牌', 'onedrive');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (43, 'refreshToken', '刷新令牌', 'onedrive');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (44, 'accessToken', '访问令牌', 'onedrive-china');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (45, 'refreshToken', '刷新令牌', 'onedrive-china');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (46, 'accessKey', 'AccessKey', 's3');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (47, 'secretKey', 'SecretKey', 's3');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (48, 'endPoint', '服务地址(EndPoint)', 's3');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (49, 'bucket-name', '存储空间名称', 's3');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (50, 'base-path', '基路径', 's3');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (51, 'domain', '加速域名', 's3');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (52, 'pathStyle', '域名风格', 's3');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (53, 'isPrivate', '是否是私有空间', 's3');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (54, 'base-path', '基路径', 'onedrive');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (55, 'base-path', '基路径', 'onedrive-china');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (56, 'bucket-name', '云存储服务名称', 'ufile');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (57, 'username', '操作员名称', 'ufile');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (58, 'password', '操作员密码', 'ufile');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (59, 'domain', '加速域名', 'ufile');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (60, 'base-path', '基路径', 'ufile');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (61, 'isPrivate', '是否是私有空间', 'tencent');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (62, 'isPrivate', '是否是私有空间', 'aliyun');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (63, 'isPrivate', '是否是私有空间', 'huawei');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (64, 'isPrivate', '是否是私有空间', 'minio');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (65, 'isPrivate', '是否是私有空间', 'qiniu');
|
||||
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
.el-menu[data-v-0d38e212],.el-row[data-v-0d38e212]{height:100vh}
|
||||
1
src/main/resources/static/css/app.34f4cf05.css
Normal file
1
src/main/resources/static/css/app.34f4cf05.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
.el-row[data-v-333298fb]{overflow-y:auto}#siteForm[data-v-333298fb]{margin-top:20px;margin-left:20px}.zfile-word-aux[data-v-333298fb]{margin-left:20px;color:#aaa}
|
||||
@@ -0,0 +1 @@
|
||||
.login-container[data-v-ec97d092]{width:100%;height:100%}.login-page[data-v-ec97d092]{border-radius:5px;margin:180px auto;width:350px;padding:35px 35px 15px;background:#fff;border:1px solid #eaeaea;-webkit-box-shadow:0 0 25px #cac6c6;box-shadow:0 0 25px #cac6c6}label.el-checkbox.rememberme[data-v-ec97d092]{margin:0 0 15px;text-align:left}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
.el-row[data-v-5289a8fc]{padding:20px}.el-form-item[data-v-5289a8fc]{margin-right:50px}.card-title[data-v-5289a8fc]{color:rgba(0,0,0,.45);font-size:14px}.card-content[data-v-5289a8fc]{color:rgba(0,0,0,.85);font-size:25px;line-height:30px}.card-title-button[data-v-5289a8fc]{float:right;padding:3px 0}.table-search-input[data-v-5289a8fc]{width:300px;float:right}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
#aplyer[data-v-0d5a77b5] .el-icon-close{position:absolute;right:0;top:0}
|
||||
@@ -0,0 +1 @@
|
||||
#pwdForm[data-v-2af85e9a]{margin-top:20px;margin-left:20px}
|
||||
@@ -0,0 +1 @@
|
||||
#storageForm[data-v-24d679d0]{margin-left:20px}#storageForm[data-v-24d679d0] .el-select{width:100%}.zfile-word-aux[data-v-24d679d0]{margin-left:20px;color:#aaa}.el-tabs[data-v-0a248fe4]{display:block}
|
||||
@@ -0,0 +1 @@
|
||||
.box-card[data-v-4811005a]{padding-top:30px;padding-right:30px;margin:10vh auto;height:75vh;overflow-y:auto}.el-select[data-v-4811005a]{width:100%}.zfile-word-aux[data-v-4811005a]{margin-left:20px;color:#aaa}
|
||||
@@ -0,0 +1 @@
|
||||
.el-menu[data-v-e36af7ca],.el-row[data-v-e36af7ca]{height:100vh}
|
||||
@@ -0,0 +1 @@
|
||||
.el-row[data-v-30de6894]{overflow-y:auto}#siteForm[data-v-30de6894]{margin-top:20px;margin-left:20px}#siteForm[data-v-30de6894] .el-select{width:70%}.zfile-word-aux[data-v-30de6894]{margin-left:20px;color:#aaa}
|
||||
@@ -0,0 +1 @@
|
||||
.markdown-body[data-v-1d926c65]{padding:20px!important}.scroll[data-v-1d926c65]{height:100vh;overflow-y:auto}
|
||||
@@ -0,0 +1 @@
|
||||
.monitor-body[data-v-73fd5f73]{margin:20px}
|
||||
@@ -0,0 +1 @@
|
||||
.zfile-header[data-v-69b2489f]{height:48px;line-height:48px!important;background:#fafafa;border-bottom:1px solid rgba(0,0,0,.05);padding-left:30px}.zfile-header .el-breadcrumb[data-v-69b2489f],.zfile-header .el-input[data-v-69b2489f]{line-height:48px}@media only screen and (max-width:767px){.hidden-xs-only{display:none!important}}@media only screen and (min-width:768px){.hidden-sm-and-up{display:none!important}}@media only screen and (min-width:768px) and (max-width:991px){.hidden-sm-only{display:none!important}}@media only screen and (max-width:991px){.hidden-sm-and-down{display:none!important}}@media only screen and (min-width:992px){.hidden-md-and-up{display:none!important}}@media only screen and (min-width:992px) and (max-width:1199px){.hidden-md-only{display:none!important}}@media only screen and (max-width:1199px){.hidden-md-and-down{display:none!important}}@media only screen and (min-width:1200px){.hidden-lg-and-up{display:none!important}}@media only screen and (min-width:1200px) and (max-width:1919px){.hidden-lg-only{display:none!important}}@media only screen and (max-width:1919px){.hidden-lg-and-down{display:none!important}}@media only screen and (min-width:1920px){.hidden-xl-only{display:none!important}}#List[data-v-360f2068]{overflow:hidden}.el-table[data-v-360f2068]{margin:20px 0 0 20px;padding-right:30px;overflow-y:hidden}.el-table[data-v-360f2068]:before{height:0}.el-table svg[data-v-360f2068]{font-size:18px;margin-right:15px}#ListTable[data-v-360f2068] .table-header-left{margin-left:38px}#ListTable[data-v-360f2068] tr{cursor:pointer}.el-scrollbar[data-v-360f2068] .el-scrollbar__wrap{overflow-x:hidden!important}#videoDialog[data-v-360f2068] .el-dialog__body{padding:10px 0 0 0}#List[data-v-360f2068] .el-dialog__header{text-align:center;margin-bottom:-10px;padding:5px 0 5px 0}#videoDialog[data-v-360f2068] .el-dialog__headerbtn{top:10px}#textDialog[data-v-360f2068] .el-dialog{margin-bottom:0}.v-contextmenu-item[data-v-360f2068] label{margin-left:10px}@media screen and (max-device-width:1920px){#videoDialog[data-v-360f2068] .el-dialog{margin-top:5vh!important;width:70%!important}}@media screen and (max-device-width:769px){#videoDialog[data-v-360f2068] .el-dialog{margin-top:10vh!important;width:90%!important}}.operator-btn[data-v-360f2068]{color:#1e9fff;margin-right:20px;font-size:16px}#app{font-family:Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,"\5FAE\8F6F\96C5\9ED1",Arial,sans-serif;font-size:16px;line-height:1.5;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#2c3e50;overflow-x:hidden}body{margin:unset}.icon,body{overflow:hidden}.icon{width:1em;height:1em;vertical-align:-.15em;fill:currentColor}::-webkit-scrollbar{width:6px;height:8px;background:rgba(144,147,153,.3)}::-webkit-scrollbar-button:vertical{display:none}::-webkit-scrollbar-corner,::-webkit-scrollbar-track{background-color:#e2e2e2}::-webkit-scrollbar-thumb{border-radius:8px;background-color:#a6a6a6}::-webkit-scrollbar-thumb:vertical:hover{background-color:#7f7f7f}::-webkit-scrollbar-thumb:vertical:active{background-color:rgba(0,0,0,.38)}.center-box-card{width:1100px;margin:0 auto}.markdown-body{height:300px;overflow-y:auto;padding:0!important;min-width:100%!important}.alert{background-color:#f4f4f5;color:#909399;font-size:12px;margin:0 0 0;width:100%;padding:10px 16px;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;position:relative;overflow:hidden;opacity:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-transition:opacity .2s;transition:opacity .2s}
|
||||
File diff suppressed because one or more lines are too long
1
src/main/resources/static/css/chunk-vendors.4a45e43f.css
Normal file
1
src/main/resources/static/css/chunk-vendors.4a45e43f.css
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.box-card[data-v-7e472c9a]{padding-top:30px;padding-right:30px;margin:15vh auto;height:65vh;overflow-y:auto}.el-select[data-v-7e472c9a]{width:100%}
|
||||
6
src/main/resources/static/editor.worker.js
Normal file
6
src/main/resources/static/editor.worker.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title></title><link href=/css/adminIndex.25eab1c6.css rel=prefetch><link href=/css/install.b4e8b552.css rel=prefetch><link href=/js/adminIndex.456ac23a.js rel=prefetch><link href=/js/dplayer.acc587f7.js rel=prefetch><link href=/js/install.f6845f54.js rel=prefetch><link href=/css/app.c1328c72.css rel=preload as=style><link href=/css/chunk-vendors.39edcc5c.css rel=preload as=style><link href=/js/app.e67a1b5e.js rel=preload as=script><link href=/js/chunk-vendors.065da641.js rel=preload as=script><link href=/css/chunk-vendors.39edcc5c.css rel=stylesheet><link href=/css/app.c1328c72.css rel=stylesheet></head><body><noscript><strong>We're sorry but zfile doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.065da641.js></script><script src=/js/app.e67a1b5e.js></script></body></html>
|
||||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title></title><link href=/css/chunk-049ad60c.8d1c3f59.css rel=prefetch><link href=/css/chunk-06f6e882.4c106b9d.css rel=prefetch><link href=/css/chunk-09797b6e.0e88456f.css rel=prefetch><link href=/css/chunk-0b11d68a.2bc89311.css rel=prefetch><link href=/css/chunk-1f0cfb2a.773f5ef1.css rel=prefetch><link href=/css/chunk-227db9c4.091f6ac0.css rel=prefetch><link href=/css/chunk-28547ac9.d30178ad.css rel=prefetch><link href=/css/chunk-361b31cc.434c5719.css rel=prefetch><link href=/css/chunk-548ba676.5c3079db.css rel=prefetch><link href=/css/chunk-569f4781.adb5e9d9.css rel=prefetch><link href=/css/chunk-5a048f62.71936c2b.css rel=prefetch><link href=/css/chunk-6b263e10.bc5fc5af.css rel=prefetch><link href=/css/chunk-718902cb.e55a2dd9.css rel=prefetch><link href=/css/chunk-d1e104d6.ecae5695.css rel=prefetch><link href=/css/chunk-faa8fca8.bb4fd588.css rel=prefetch><link href=/js/chunk-049ad60c.0b1b3166.js rel=prefetch><link href=/js/chunk-06f6e882.fc195f68.js rel=prefetch><link href=/js/chunk-09797b6e.7b1e5e16.js rel=prefetch><link href=/js/chunk-0b11d68a.d20d6721.js rel=prefetch><link href=/js/chunk-1f0cfb2a.05b8606a.js rel=prefetch><link href=/js/chunk-227db9c4.0342e2c8.js rel=prefetch><link href=/js/chunk-28547ac9.89bfa8e0.js rel=prefetch><link href=/js/chunk-2d0a43df.0bb25464.js rel=prefetch><link href=/js/chunk-2d0e57ec.56324ec2.js rel=prefetch><link href=/js/chunk-361b31cc.ec6b72b5.js rel=prefetch><link href=/js/chunk-548ba676.7767e226.js rel=prefetch><link href=/js/chunk-569f4781.42bfec95.js rel=prefetch><link href=/js/chunk-5a048f62.c7a85d91.js rel=prefetch><link href=/js/chunk-6b263e10.fe00e76b.js rel=prefetch><link href=/js/chunk-718902cb.4d2eda07.js rel=prefetch><link href=/js/chunk-d1e104d6.5ae45d97.js rel=prefetch><link href=/js/chunk-faa8fca8.429fb078.js rel=prefetch><link href=/css/app.34f4cf05.css rel=preload as=style><link href=/css/chunk-vendors.4a45e43f.css rel=preload as=style><link href=/js/app.86017352.js rel=preload as=script><link href=/js/chunk-vendors.b5ddaa46.js rel=preload as=script><link href=/css/chunk-vendors.4a45e43f.css rel=stylesheet><link href=/css/app.34f4cf05.css rel=stylesheet></head><body><noscript><strong>We're sorry but zfile doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.b5ddaa46.js></script><script src=/js/app.86017352.js></script></body></html>
|
||||
@@ -1 +0,0 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["adminIndex"],{7869:function(t,e,a){},adf4:function(t,e,a){"use strict";a.r(e);var i=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("el-row",[a("el-col",{attrs:{span:3}},[a("el-menu",{staticClass:"el-menu-vertical-demo",attrs:{"default-active":"/admin"!==this.$route.path?this.$route.path:"/admin/site",router:!0}},[a("el-menu-item",{attrs:{index:"/admin/site"}},[a("i",{staticClass:"el-icon-setting"}),a("span",{attrs:{slot:"title"},slot:"title"},[t._v("站点设置")])]),a("el-menu-item",{attrs:{index:"/admin/storage"}},[a("i",{staticClass:"el-icon-s-operation"}),a("span",{attrs:{slot:"title"},slot:"title"},[t._v("存储策略")])]),a("el-menu-item",{attrs:{index:"/admin/password"}},[a("i",{staticClass:"el-icon-key"}),a("span",{attrs:{slot:"title"},slot:"title"},[t._v("修改密码")])]),a("el-menu-item",{attrs:{index:"/admin/cache"}},[a("i",{staticClass:"el-icon-collection"}),a("span",{attrs:{slot:"title"},slot:"title"},[t._v("缓存管理")])])],1)],1),a("el-col",{attrs:{span:16}},[a("keep-alive",{attrs:{exclude:"CacheManager,SiteSetting"}},[a("router-view")],1)],1)],1)},s=[],n={name:"Index",data:function(){return{active:"/admin/storage"}}},l=n,o=(a("f2cb"),a("2877")),r=Object(o["a"])(l,i,s,!1,null,"0d38e212",null);e["default"]=r.exports},f2cb:function(t,e,a){"use strict";var i=a("7869"),s=a.n(i);s.a}}]);
|
||||
1
src/main/resources/static/js/app.86017352.js
Normal file
1
src/main/resources/static/js/app.86017352.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
src/main/resources/static/js/chunk-049ad60c.0b1b3166.js
Normal file
1
src/main/resources/static/js/chunk-049ad60c.0b1b3166.js
Normal file
File diff suppressed because one or more lines are too long
1
src/main/resources/static/js/chunk-06f6e882.fc195f68.js
Normal file
1
src/main/resources/static/js/chunk-06f6e882.fc195f68.js
Normal file
File diff suppressed because one or more lines are too long
92
src/main/resources/static/js/chunk-09797b6e.7b1e5e16.js
Normal file
92
src/main/resources/static/js/chunk-09797b6e.7b1e5e16.js
Normal file
File diff suppressed because one or more lines are too long
10
src/main/resources/static/js/chunk-0b11d68a.d20d6721.js
Normal file
10
src/main/resources/static/js/chunk-0b11d68a.d20d6721.js
Normal file
File diff suppressed because one or more lines are too long
6
src/main/resources/static/js/chunk-1f0cfb2a.05b8606a.js
Normal file
6
src/main/resources/static/js/chunk-1f0cfb2a.05b8606a.js
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user