mirror of
https://github.com/zfile-dev/zfile.git
synced 2025-04-19 05:34:52 +00:00
Compare commits
102 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
90cd13f2c3 | ||
|
|
018a68246e | ||
|
|
b6a2e3ccb8 | ||
|
|
38b811f8e6 | ||
|
|
6922fa2195 | ||
|
|
4bca6cf7a5 | ||
|
|
c3484426ab | ||
|
|
0455bd366c | ||
|
|
bbe3c053f8 | ||
|
|
f47708f45d | ||
|
|
2e7a7b8cec | ||
|
|
9e067dbce9 | ||
|
|
a4a236e488 | ||
|
|
7d5b0431f5 | ||
|
|
a758c8cc6d | ||
|
|
21a64ec0f3 | ||
|
|
3f241d129a | ||
|
|
fa5f16c61f | ||
|
|
492b22506d | ||
|
|
a12f685340 | ||
|
|
2ee3f3dd66 | ||
|
|
245937e773 | ||
|
|
aef34facbd | ||
|
|
14bb5e15e3 | ||
|
|
12371f06dd | ||
|
|
28e43e968f | ||
|
|
669b413ff0 | ||
|
|
f32e5e8f9e | ||
|
|
3719378614 | ||
|
|
40c759078e | ||
|
|
e37e778e1a | ||
|
|
031607402a | ||
|
|
6c9150466c | ||
|
|
be633ebe1a | ||
|
|
9715cf922a | ||
|
|
f6163c7e19 | ||
|
|
dcc4cb19ad | ||
|
|
ad0ad12c08 | ||
|
|
74c935cdf0 | ||
|
|
1876e692f2 |
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 片段
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
107
README.md
107
README.md
@@ -10,52 +10,64 @@
|
||||
前端基于 [h5ai](https://larsjung.de/h5ai/) 的原有功能使用 Vue 重新开发了一遍. 后端采用 SpringBoot, 数据库采用内嵌数据库.
|
||||
|
||||
预览地址: [https://zfile.jun6.net](https://zfile.jun6.net)
|
||||
文档地址: [http://docs.zhaojun.im/zfile](http://docs.zhaojun.im/zfile)
|
||||
|
||||
## 系统特色
|
||||
|
||||
* 内存缓存 (免安装)
|
||||
* 内存数据库 (免安装)
|
||||
* 个性化配置
|
||||
* 自定义目录的 header 和 footer 说明文件
|
||||
* 自定义目录的 readme 说明文件
|
||||
* 自定义 JS, CSS
|
||||
* 文件夹密码
|
||||
* 支持在线浏览文本文件, 视频, 图片, 音乐. (支持 FLV 和 HLS)
|
||||
* 文件/目录二维码
|
||||
* 缓存动态开启, 缓存自动刷新
|
||||
* 全局搜索
|
||||
* 支持 阿里云 OSS, FTP, 华为云 OBS, 本地存储, MINIO, OneDrive 国际/家庭/个人版, OneDrive 世纪互联版, 七牛云 KODO, 腾讯云 COS, 又拍云 USS.
|
||||
|
||||
## 快速开始
|
||||
|
||||
安装 JDK 1.8 :
|
||||
安装依赖环境:
|
||||
|
||||
```bash
|
||||
yum install -y java # 适用于 Centos 7.x
|
||||
# CentOS系统
|
||||
yum install -y java-1.8.0-openjdk unzip
|
||||
|
||||
# Debian/Ubuntu系统
|
||||
apt update
|
||||
apt install -y openjdk-8-jre-headless unzip
|
||||
```
|
||||
|
||||
> 如为更新程序, 则请先执行 `rm -rf ~/zfile` 清理旧程序. 首次安装请忽略此选项.
|
||||
|
||||
下载项目:
|
||||
|
||||
```bash
|
||||
wget https://github.com/zhaojun1998/zfile/releases/download/0.6/zfile-0.6.jar
|
||||
wget -P ~ https://c.jun6.net/ZFILE/zfile-release.war
|
||||
cd ~
|
||||
mkdir zfile && unzip zfile-release.war -d zfile && rm -rf zfile-release.war
|
||||
chmod +x ~/zfile/bin/*.sh
|
||||
```
|
||||
|
||||
程序的目录结构为:
|
||||
```
|
||||
├── zfile
|
||||
├── META-INF
|
||||
├── WEB-INF
|
||||
└── bin
|
||||
├── start.sh # 启动脚本
|
||||
└── stop.sh # 停止脚本
|
||||
├── restart.sh # 重启脚本
|
||||
```
|
||||
|
||||
启动项目:
|
||||
|
||||
```bash
|
||||
java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.6.jar
|
||||
|
||||
## 高级启动
|
||||
java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.6.jar --server.port=18777
|
||||
|
||||
## 后台运行
|
||||
nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.6.jar &
|
||||
~/zfile/bin/start.sh
|
||||
```
|
||||
|
||||
> 系统使用的是内置配置文件, 默认配置请参考: [application.yml](https://github.com/zhaojun1998/zfile/blob/master/src/main/resources/application.yml)
|
||||
|
||||
> **可下载此文件放置与 jar 包同目录, 此时会以外部配置文件为准, 推荐适用此方式!.**
|
||||
|
||||
> 所有参数都可在命令行启动时, 以类似 `--server.port=18777` 的方式强制执行, 此方式的优先级最高.
|
||||
|
||||
> *指定 `-Djava.security.egd=file:/dev/./urandom` 是为了防止在 Linux 环境中, 生成首次登陆生成 sessionId 取系统随机数过慢的问题.*
|
||||
篇幅有限, 更详细的安装教程请参考: [安装文档](http://zhaojun.im/zfile-install)
|
||||
|
||||
访问地址:
|
||||
|
||||
@@ -66,12 +78,38 @@ nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.6.jar &
|
||||
管理后台: http://127.0.0.1:8080/#/admin
|
||||
|
||||
|
||||
## OneDrive 使用教程.
|
||||
|
||||
访问地址进行授权, 获取 accessToken 和 refreshToken:
|
||||
|
||||
|
||||
国际/家庭/个人版:
|
||||
|
||||
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=09939809-c617-43c8-a220-a93c1513c5d4&response_type=code&redirect_uri=https://zfile.jun6.net/onedrive/callback&scope=offline_access%20User.Read%20Files.ReadWrite.All
|
||||
|
||||
|
||||
世纪互联版:
|
||||
|
||||
https://login.chinacloudapi.cn/common/oauth2/v2.0/authorize?client_id=4a72d927-1907-488d-9eb2-1b465c53c1c5&response_type=code&redirect_uri=https://zfile.jun6.net/onedrive/china-callback&scope=offline_access%20User.Read%20Files.ReadWrite.All
|
||||
|
||||
|
||||
然后分别填写至访问令牌和刷新令牌即可:
|
||||
|
||||

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

|
||||

|
||||

|
||||

|
||||
|
||||
## 常见问题
|
||||
|
||||
### 数据库
|
||||
@@ -82,15 +120,32 @@ nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.6.jar &
|
||||
|
||||
默认 H2 数据库文件地址: `~/.zfile/db/`, `~` 表示用户目录, windows 为 `C:/Users/用户名/`, linux 为 `/home/用户名/`, root 用户为 `/root/`
|
||||
|
||||
### 头尾文件和加密文件
|
||||
### 文档文件和加密文件
|
||||
|
||||
- 目录头部显示文件名为 `header.md`
|
||||
- 目录底部显示文件名为 `footer.md`
|
||||
- 目录文档显示文件名为 `readme.md`
|
||||
- 目录需要密码访问, 添加文件 `password.txt` (无法拦截此文件被下载, 但可以改名文件)
|
||||
|
||||
## TODO
|
||||
## 开发计划
|
||||
|
||||
- 文本预览更换更好用的编辑器
|
||||
- 后台支持上传、编辑、删除等操作
|
||||
- API 支持
|
||||
- 更方便的部署方式
|
||||
- [x] API 支持 [点击查看文档](https://github.com/zhaojun1998/zfile/blob/master/API.md)
|
||||
- [x] 更方便的部署方式
|
||||
- [x] 布局优化 - 自定义操作按钮 (现为右键实现)
|
||||
- [x] 后台优化 - 设置按照其功能进行分离
|
||||
- [x] 体验优化 - 支持前后端分离部署
|
||||
- [ ] 新功能 - 后台支持上传、编辑、删除等操作
|
||||
- [ ] 新功能 - WebDav 支持
|
||||
- [ ] 新功能 - Docker 支持
|
||||
- [ ] 新功能 - 离线下载 (aria2)
|
||||
- [ ] 体验优化 - 文本预览更换 vscode 同款编辑器 monaco editor
|
||||
- [ ] 体验优化 - 忽略文件列表 (正则表达式)
|
||||
- [ ] 体验优化 - 自定义支持预览的文件后缀 (正则表达式)
|
||||
- [ ] 架构调整 - 支持多存储策略
|
||||
- [ ] 体验优化 - 一键安装脚本
|
||||
|
||||
## 支持作者
|
||||
|
||||
如果本项目对你有帮助,请作者喝杯咖啡吧。
|
||||
|
||||
|
||||
<img src="http://cdn.jun6.net/alipay.png" width="200" height="312">
|
||||
<img src="http://cdn.jun6.net/wechat.png" width="222" height="300">
|
||||
|
||||
28
pom.xml
28
pom.xml
@@ -12,8 +12,9 @@
|
||||
|
||||
<groupId>im.zhaojun</groupId>
|
||||
<artifactId>zfile</artifactId>
|
||||
<version>0.6</version>
|
||||
<version>1.8</version>
|
||||
<name>zfile</name>
|
||||
<packaging>war</packaging>
|
||||
<description>一个在线的文件浏览系统</description>
|
||||
|
||||
<properties>
|
||||
@@ -45,6 +46,10 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据库驱动-->
|
||||
<dependency>
|
||||
@@ -62,7 +67,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-->
|
||||
@@ -128,6 +133,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>
|
||||
|
||||
|
||||
@@ -27,14 +27,15 @@ 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();
|
||||
|
||||
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
@@ -47,10 +48,8 @@ public class AliyunServiceImpl extends AbstractS3FileService implements FileServ
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "oss")).build();
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package im.zhaojun.common.config;
|
||||
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import im.zhaojun.onedrive.china.service.OneDriveChinaServiceImpl;
|
||||
import im.zhaojun.onedrive.international.service.OneDriveServiceImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
@Slf4j
|
||||
public class GlobalScheduleTask {
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Resource
|
||||
private OneDriveServiceImpl oneDriveServiceImpl;
|
||||
|
||||
@Resource
|
||||
private OneDriveChinaServiceImpl oneDriveChinaServiceImpl;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
/**
|
||||
* 项目启动 30 秒后, 每 15 分钟执行一次刷新 OneDrive Token 的定时任务.
|
||||
*/
|
||||
@Scheduled(fixedRate = 1000 * 60 * 15, initialDelay = 1000 * 30)
|
||||
public void autoRefreshOneDriveToken() {
|
||||
|
||||
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
|
||||
|
||||
if (!(currentFileService instanceof OneDriveServiceImpl
|
||||
|| currentFileService instanceof OneDriveChinaServiceImpl)) {
|
||||
log.debug("当前启用存储类型, 不是 OneDrive, 跳过自动刷新 AccessToken");
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentFileService.getIsUnInitialized()) {
|
||||
log.debug("当前启用 OneDrive 未初始化成功, 跳过自动刷新 AccessToken");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
refreshOneDriveToken(StorageTypeEnum.ONE_DRIVE);
|
||||
} catch (Exception e) {
|
||||
log.debug("刷新 OneDrive Token 失败.", e);
|
||||
}
|
||||
|
||||
try {
|
||||
refreshOneDriveToken(StorageTypeEnum.ONE_DRIVE_CHINA);
|
||||
} catch (Exception e) {
|
||||
log.debug("刷新 OneDrive 世纪互联 Token 失败.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用刷新 OneDrive Token
|
||||
*/
|
||||
public void refreshOneDriveToken(StorageTypeEnum storageType) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,22 @@
|
||||
package im.zhaojun.common.controller;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.ZipUtil;
|
||||
import im.zhaojun.common.config.StorageTypeFactory;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.SystemMonitorInfo;
|
||||
import im.zhaojun.common.model.dto.ResultBean;
|
||||
import im.zhaojun.common.model.dto.StorageStrategyDTO;
|
||||
import im.zhaojun.common.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.FileAsyncCacheService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import im.zhaojun.common.util.FileUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -17,6 +24,10 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -54,13 +65,15 @@ public class AdminController {
|
||||
*/
|
||||
@PostMapping("/config")
|
||||
public ResultBean updateConfig(SystemConfigDTO systemConfigDTO) throws Exception {
|
||||
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
|
||||
|
||||
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
|
||||
systemConfigDTO.setId(1);
|
||||
systemConfigService.updateSystemConfig(systemConfigDTO);
|
||||
|
||||
StorageTypeEnum currentStorageStrategy = currentFileService.getStorageTypeEnum();
|
||||
if (!Objects.equals(currentStorageStrategy, systemConfigDTO.getStorageStrategy())) {
|
||||
log.info("已将存储策略由 {} 切换为 {}", currentStorageStrategy, systemConfigDTO.getStorageStrategy());
|
||||
log.info("已将存储策略由 {} 切换为 {}",
|
||||
currentStorageStrategy.getDescription(),
|
||||
systemConfigDTO.getStorageStrategy().getDescription());
|
||||
refreshStorageStrategy();
|
||||
}
|
||||
|
||||
@@ -87,6 +100,21 @@ public class AdminController {
|
||||
return ResultBean.success(storageConfigList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回支持的存储引擎.
|
||||
*/
|
||||
@GetMapping("/support-strategy")
|
||||
public ResultBean supportStrategy() {
|
||||
List<StorageStrategyDTO> result = new ArrayList<>();
|
||||
StorageTypeEnum[] values = StorageTypeEnum.values();
|
||||
for (StorageTypeEnum value : values) {
|
||||
AbstractFileService storageTypeService = StorageTypeFactory.getStorageTypeService(value);
|
||||
result.add(new StorageStrategyDTO(value.getKey(),
|
||||
value.getDescription(),
|
||||
storageTypeService.getIsInitialized()));
|
||||
}
|
||||
return ResultBean.successData(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存存储策略
|
||||
@@ -97,6 +125,7 @@ public class AdminController {
|
||||
*/
|
||||
@PostMapping("/storage-strategy")
|
||||
public ResultBean save(@RequestParam Map<String, String> storageStrategyConfig, StorageTypeEnum storageStrategy) throws Exception {
|
||||
// 保存设置.
|
||||
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageStrategy);
|
||||
for (StorageConfig storageConfig : storageConfigList) {
|
||||
String key = storageConfig.getKey();
|
||||
@@ -105,6 +134,11 @@ public class AdminController {
|
||||
}
|
||||
storageConfigService.updateStorageConfig(storageConfigList);
|
||||
|
||||
// 获取当前修改的存储策略 Service, 尝试调用初始化.
|
||||
AbstractFileService updateStorageStrategyService = StorageTypeFactory.getStorageTypeService(storageStrategy);
|
||||
updateStorageStrategyService.init();
|
||||
|
||||
// 如果修改的为当前启用的缓存, 则重新进行缓存.
|
||||
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
|
||||
if (Objects.equals(storageStrategy, currentStorageStrategy)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -113,17 +147,21 @@ public class AdminController {
|
||||
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
fileService.clearFileCache();
|
||||
fileService.init();
|
||||
fileAsyncCacheService.cacheGlobalFile();
|
||||
}
|
||||
|
||||
return ResultBean.success();
|
||||
// 返回是否初始化成功.
|
||||
if (updateStorageStrategyService.getIsInitialized()) {
|
||||
return ResultBean.success();
|
||||
} else {
|
||||
return ResultBean.error("保存成功, 但尝试初始化异常, 请检查设置.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新存储策略
|
||||
*/
|
||||
public void refreshStorageStrategy() {
|
||||
public void refreshStorageStrategy() throws Exception {
|
||||
StorageTypeEnum storageStrategy = systemConfigService.getCurrentStorageStrategy();
|
||||
refreshStorageStrategy(storageStrategy);
|
||||
}
|
||||
@@ -131,16 +169,35 @@ public class AdminController {
|
||||
/**
|
||||
* 更新存储策略
|
||||
*/
|
||||
public void refreshStorageStrategy(StorageTypeEnum storageStrategy) {
|
||||
public void refreshStorageStrategy(StorageTypeEnum storageStrategy) throws Exception {
|
||||
if (storageStrategy == null) {
|
||||
log.info("尚未配置存储策略.");
|
||||
} else {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
fileService.init();
|
||||
fileService.clearFileCache();
|
||||
log.info("切换至存储类型: {}", storageStrategy.getDescription());
|
||||
|
||||
fileAsyncCacheService.cacheGlobalFile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统日志下载
|
||||
*/
|
||||
@GetMapping("/log")
|
||||
public ResponseEntity<Object> downloadLog(HttpServletResponse response) {
|
||||
String userHome = System.getProperty("user.home");
|
||||
File fileZip = ZipUtil.zip(userHome + "/.zfile/logs");
|
||||
String currentDate = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
|
||||
return FileUtil.export(fileZip, "ZFile 诊断日志 - " + currentDate + ".zip");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统监控信息
|
||||
*/
|
||||
@GetMapping("monitor")
|
||||
public ResultBean monitor() {
|
||||
return ResultBean.success(new SystemMonitorInfo());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2020/1/11 14:03
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin/cache")
|
||||
@@ -68,7 +67,6 @@ public class CacheController {
|
||||
public ResultBean clearCache(String key) throws Exception {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
fileService.clearFileCache();
|
||||
fileAsyncCacheService.resetCacheCount();
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package im.zhaojun.common.controller;
|
||||
|
||||
import im.zhaojun.common.model.dto.ResultBean;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.util.AudioHelper;
|
||||
import im.zhaojun.common.util.HttpUtil;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/common")
|
||||
public class CommonController {
|
||||
|
||||
@GetMapping("/support-strategy")
|
||||
public ResultBean supportStrategy() {
|
||||
return ResultBean.successData(StorageTypeEnum.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件内容, 仅限用于, txt, md, ini 等普通文本文件.
|
||||
* @param url 文件路径
|
||||
* @return 文件内容
|
||||
*/
|
||||
@GetMapping("/content")
|
||||
public ResultBean getContent(String url) {
|
||||
return ResultBean.successData(HttpUtil.getTextContent(url));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取文件内容, 仅限用于, txt, md, ini 等普通文本文件.
|
||||
* @param url 文件路径
|
||||
* @return 文件内容
|
||||
*/
|
||||
@GetMapping("/content/origin")
|
||||
public String getContentOrigin(String url) {
|
||||
return HttpUtil.getTextContent(url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检测文件是否存在
|
||||
* @param url 文件路径
|
||||
* @return 是否存在
|
||||
*/
|
||||
@GetMapping("/content/exist")
|
||||
public boolean checkFileExist(String url) {
|
||||
return HttpUtil.checkUrlExist(url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取音频文件信息
|
||||
* @param url 文件 URL
|
||||
* @return 音频信息, 标题封面等信息
|
||||
*/
|
||||
@GetMapping("/audio-info")
|
||||
public ResultBean getAudioInfo(String url) throws Exception {
|
||||
return ResultBean.success(AudioHelper.getAudioInfo(url));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
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;
|
||||
@@ -10,10 +11,8 @@ import im.zhaojun.common.model.dto.SiteConfigDTO;
|
||||
import im.zhaojun.common.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.FileAsyncCacheService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import im.zhaojun.common.service.SystemService;
|
||||
import im.zhaojun.common.util.AudioHelper;
|
||||
import im.zhaojun.common.util.FileComparator;
|
||||
import im.zhaojun.common.util.HttpUtil;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
@@ -24,6 +23,8 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -40,9 +41,6 @@ public class FileController {
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Resource
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
@@ -59,7 +57,7 @@ public class FileController {
|
||||
@RequestParam(required = false) String password,
|
||||
@RequestParam(defaultValue = "1") Integer page) throws Exception {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
List<FileItemDTO> fileItemList = fileService.fileList(StringUtils.removeDuplicateSeparator("/" + URLUtil.decode(path) + "/"));
|
||||
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)) {
|
||||
@@ -70,95 +68,46 @@ public class FileController {
|
||||
}
|
||||
}
|
||||
|
||||
// 排序, 先按照文件类型比较, 文件夹在前, 文件在后, 然后根据 sortBy 字段排序, 默认为升序;
|
||||
fileItemList.sort(new FileComparator(sortBy, order));
|
||||
filterFileList(fileItemList);
|
||||
|
||||
int total = fileItemList.size();
|
||||
int totalPage = (total + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
|
||||
if (page > totalPage) {
|
||||
return ResultBean.successData(new ArrayList<>());
|
||||
}
|
||||
|
||||
int start = (page - 1) * PAGE_SIZE;
|
||||
int end = page * PAGE_SIZE;
|
||||
end = Math.min(end, total);
|
||||
List<FileItemDTO> fileSubItem = new ArrayList<>(fileItemList.subList(start, end));
|
||||
return ResultBean.successData(fileSubItem);
|
||||
List<FileItemDTO> sortedPagingData = getSortedPagingData(fileItemList, page);
|
||||
return ResultBean.successData(sortedPagingData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取文件类容, 仅限用于, txt, md, ini 等普通文本文件.
|
||||
* @param url 文件路径
|
||||
* @return 文件内容
|
||||
*/
|
||||
@GetMapping("/content")
|
||||
public ResultBean getContent(String url) {
|
||||
return ResultBean.successData(HttpUtil.getTextContent(url));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取文件类容, 仅限用于, txt, md, ini 等普通文本文件.
|
||||
* @param url 文件路径
|
||||
* @return 文件内容
|
||||
*/
|
||||
@GetMapping("/content/origin")
|
||||
public String getContentOrigin(String url) {
|
||||
return HttpUtil.getTextContent(url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检测文件是否存在
|
||||
* @param url 文件路径
|
||||
* @return 是否存在
|
||||
*/
|
||||
@GetMapping("/content/exist")
|
||||
public boolean checkFileExist(String url) {
|
||||
return HttpUtil.checkUrlExist(url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取系统配置信息和当前页的标题, 文件头, 文件尾信息
|
||||
* 获取系统配置信息和当前页的标题, 页面文档信息
|
||||
* @param path 路径
|
||||
*/
|
||||
@CheckStorageStrategyInit
|
||||
@GetMapping("/config")
|
||||
public ResultBean getConfig(String path) throws Exception {
|
||||
SiteConfigDTO config = systemService.getConfig(URLUtil.decode(StringUtils.removeDuplicateSeparator("/" + path + "/")));
|
||||
SiteConfigDTO config = systemService.getConfig(StringUtils.removeDuplicateSeparator("/" + path + "/"));
|
||||
config.setSystemConfigDTO(systemConfigService.getSystemConfig());
|
||||
return ResultBean.successData(config);
|
||||
}
|
||||
|
||||
|
||||
@CheckStorageStrategyInit
|
||||
@GetMapping("/audioInfo")
|
||||
public ResultBean getAudioInfo(String url) throws Exception {
|
||||
return ResultBean.success(AudioHelper.getAudioInfo(url));
|
||||
}
|
||||
|
||||
|
||||
@CheckStorageStrategyInit
|
||||
@GetMapping("/search")
|
||||
public ResultBean search(@RequestParam(value = "name", defaultValue = "/") String name) throws Exception {
|
||||
public ResultBean search(@RequestParam(value = "name", defaultValue = "/") String name,
|
||||
@RequestParam(defaultValue = "name") String sortBy,
|
||||
@RequestParam(defaultValue = "asc") String order,
|
||||
@RequestParam(defaultValue = "1") Integer page) {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
SystemConfigDTO systemConfigDTO = systemConfigService.getSystemConfig();
|
||||
if (!systemConfigDTO.getSearchEnable()) {
|
||||
if (BooleanUtil.isFalse(systemConfigDTO.getSearchEnable())) {
|
||||
throw new SearchDisableException("搜索功能未开启");
|
||||
}
|
||||
if (!fileAsyncCacheService.isCacheFinish()) {
|
||||
throw new SearchDisableException("搜索功能缓存预热中, 请稍后再试");
|
||||
}
|
||||
return ResultBean.success(fileService.search(URLUtil.decode(name)));
|
||||
List<FileItemDTO> fileItemList = fileService.search(URLUtil.decode(name));
|
||||
List<FileItemDTO> sortedPagingData = getSortedPagingData(fileItemList, page);
|
||||
return ResultBean.successData(sortedPagingData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 过滤文件列表, 不显示密码, 头部和尾部文件.
|
||||
* 过滤文件列表, 不显示密码, 文档文件.
|
||||
*/
|
||||
private void filterFileList(List<FileItemDTO> fileItemList) {
|
||||
if (fileItemList == null) {
|
||||
@@ -166,8 +115,41 @@ public class FileController {
|
||||
}
|
||||
|
||||
fileItemList.removeIf(fileItem -> ZFileConstant.PASSWORD_FILE_NAME.equals(fileItem.getName())
|
||||
|| ZFileConstant.FOOTER_FILE_NAME.equals(fileItem.getName())
|
||||
|| ZFileConstant.HEADER_FILE_NAME.equals(fileItem.getName()));
|
||||
|| ZFileConstant.README_FILE_NAME.equals(fileItem.getName()));
|
||||
}
|
||||
|
||||
|
||||
private List<FileItemDTO> getSortedPagingData(List<FileItemDTO> fileItemList, Integer page) {
|
||||
ArrayList<FileItemDTO> copy = new ArrayList<>(Arrays.asList(new FileItemDTO[fileItemList.size()]));
|
||||
Collections.copy(copy, fileItemList);
|
||||
|
||||
// 排序, 先按照文件类型比较, 文件夹在前, 文件在后, 然后根据 sortBy 字段排序, 默认为升序;
|
||||
copy.sort(new FileComparator());
|
||||
filterFileList(copy);
|
||||
|
||||
int total = copy.size();
|
||||
int totalPage = (total + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
|
||||
if (page > totalPage) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
int start = (page - 1) * PAGE_SIZE;
|
||||
int end = page * PAGE_SIZE;
|
||||
end = Math.min(end, total);
|
||||
return new ArrayList<>(copy.subList(start, end));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定路径下的文件信息内容
|
||||
* @param path 文件全路径
|
||||
* @return 该文件的名称, 路径, 大小, 下载地址等信息.
|
||||
*/
|
||||
@CheckStorageStrategyInit
|
||||
@GetMapping("/directlink")
|
||||
public ResultBean directlink(String path) {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
return ResultBean.successData(fileService.getFileItem(path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import im.zhaojun.common.model.dto.InstallModelDTO;
|
||||
import im.zhaojun.common.model.dto.ResultBean;
|
||||
import im.zhaojun.common.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.FileAsyncCacheService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -33,9 +32,6 @@ public class InstallController {
|
||||
@Resource
|
||||
private AdminController adminController;
|
||||
|
||||
@Resource
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
@GetMapping("/is-installed")
|
||||
public ResultBean isInstall() {
|
||||
if (systemConfigService.getCurrentStorageStrategy() == null) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,12 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
* @author zhaojun
|
||||
*/
|
||||
@ControllerAdvice
|
||||
@ResponseBody
|
||||
public class GlobleExceptionHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(GlobleExceptionHandler.class);
|
||||
|
||||
@ExceptionHandler(SearchDisableException.class)
|
||||
@ResponseBody
|
||||
@ResponseStatus(code= HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public ResultBean searchDisableExceptionHandler(SearchDisableException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -32,6 +32,7 @@ public class GlobleExceptionHandler {
|
||||
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
@ResponseStatus
|
||||
public ResultBean searchDisableExceptionHandler(StorageStrategyUninitializedException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -40,6 +41,16 @@ public class GlobleExceptionHandler {
|
||||
return ResultBean.error(e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 不存在的文件异常
|
||||
*/
|
||||
@ExceptionHandler({NotExistFileException.class})
|
||||
@ResponseBody
|
||||
public ResultBean notExistFile(Exception ex) {
|
||||
return ResultBean.error("文件不存在");
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获 ClientAbortException 异常, 不做任何处理, 防止出现大量堆栈日志输出, 此异常不影响功能.
|
||||
*/
|
||||
@@ -53,6 +64,7 @@ public class GlobleExceptionHandler {
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
@ResponseStatus(code= HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public ResultBean searchDisableExceptionHandler(Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package im.zhaojun.common.exception;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class NotExistFileException extends RuntimeException {
|
||||
|
||||
public NotExistFileException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NotExistFileException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public NotExistFileException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public NotExistFileException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
protected NotExistFileException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
}
|
||||
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,6 +27,7 @@ public class StorageConfig {
|
||||
|
||||
private String title;
|
||||
|
||||
@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 {
|
||||
|
||||
@@ -30,4 +29,12 @@ public class StorageConfigConstant {
|
||||
|
||||
public static final String FILE_PATH_KEY = "filePath";
|
||||
|
||||
}
|
||||
public static final String ACCESS_TOKEN_KEY = "accessToken";
|
||||
|
||||
public static final String REFRESH_TOKEN_KEY = "refreshToken";
|
||||
|
||||
public static final String PATH_STYLE = "pathStyle";
|
||||
|
||||
public static final String IS_PRIVATE = "isPrivate";
|
||||
|
||||
}
|
||||
@@ -15,14 +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 FOOTER_FILE_NAME = "footer.md";
|
||||
public static String README_FILE_NAME = "readme.md";
|
||||
|
||||
/**
|
||||
* 密码文件
|
||||
@@ -30,13 +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;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setFooterFileName(@Value("${zfile.constant.footer}") String footerFileName) {
|
||||
ZFileConstant.FOOTER_FILE_NAME = footerFileName;
|
||||
public void setHeaderFileName(@Value("${zfile.constant.readme}") String headerFileName) {
|
||||
ZFileConstant.README_FILE_NAME = headerFileName;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
|
||||
@@ -6,12 +6,11 @@ import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2020/1/3 12:39
|
||||
*/
|
||||
@Data
|
||||
public class CacheConfigDTO {
|
||||
private boolean enableCache;
|
||||
private boolean cacheFinish;
|
||||
private Boolean enableCache;
|
||||
private Boolean cacheFinish;
|
||||
private Set<String> cacheKeys;
|
||||
private Integer cacheDirectoryCount;
|
||||
private Integer cacheFileCount;
|
||||
|
||||
@@ -1,53 +1,23 @@
|
||||
package im.zhaojun.common.model.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
public class SiteConfigDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8811196207046121740L;
|
||||
|
||||
private String header;
|
||||
|
||||
private String footer;
|
||||
private String readme;
|
||||
|
||||
@JsonProperty("viewConfig")
|
||||
private SystemConfigDTO systemConfigDTO;
|
||||
|
||||
public String getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public void setHeader(String header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
public String getFooter() {
|
||||
return footer;
|
||||
}
|
||||
|
||||
public void setFooter(String footer) {
|
||||
this.footer = footer;
|
||||
}
|
||||
|
||||
public SystemConfigDTO getSystemConfigDTO() {
|
||||
return systemConfigDTO;
|
||||
}
|
||||
|
||||
public void setSystemConfigDTO(SystemConfigDTO systemConfigDTO) {
|
||||
this.systemConfigDTO = systemConfigDTO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SiteConfigDTO{" +
|
||||
"header='" + header + '\'' +
|
||||
", footer='" + footer + '\'' +
|
||||
", systemConfig=" + systemConfigDTO +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package im.zhaojun.common.model.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Zhao Jun
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class StorageStrategyDTO {
|
||||
|
||||
private String key;
|
||||
|
||||
private String description;
|
||||
|
||||
private boolean available;
|
||||
|
||||
}
|
||||
@@ -37,4 +37,22 @@ public class SystemConfigDTO {
|
||||
private String domain;
|
||||
|
||||
private Boolean enableCache;
|
||||
|
||||
private Boolean searchContainEncryptedFile;
|
||||
|
||||
private String customJs;
|
||||
|
||||
private String customCss;
|
||||
|
||||
private String tableSize;
|
||||
|
||||
private Boolean showOperator;
|
||||
|
||||
private Boolean showDocument;
|
||||
|
||||
private String announcement;
|
||||
|
||||
private Boolean showAnnouncement;
|
||||
|
||||
private String layout;
|
||||
}
|
||||
@@ -1,24 +1,31 @@
|
||||
package im.zhaojun.common.model.enums;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
|
||||
public enum StorageTypeEnum {
|
||||
|
||||
/**
|
||||
* 当前系统支持的所有存储策略
|
||||
*/
|
||||
UPYUN("upyun", "又拍云 USS"),
|
||||
QINIU("qiniu", "七牛云 KODO"),
|
||||
HUAWEI("huawei", "华为云 OBS"),
|
||||
ALIYUN("aliyun", "阿里云 OSS"),
|
||||
FTP("ftp", "FTP"),
|
||||
HUAWEI("huawei", "华为云 OBS"),
|
||||
LOCAL("local", "本地存储"),
|
||||
MINIO("minio", "MINIO"),
|
||||
S3("s3", "S3通用协议"),
|
||||
ONE_DRIVE("onedrive", "OneDrive"),
|
||||
ONE_DRIVE_CHINA("onedrive-china", "OneDrive 世纪互联"),
|
||||
QINIU("qiniu", "七牛云 KODO"),
|
||||
TENCENT("tencent", "腾讯云 COS"),
|
||||
MINIO("minio", "MINIO");
|
||||
UPYUN("upyun", "又拍云 USS");
|
||||
|
||||
|
||||
private String key;
|
||||
private String description;
|
||||
|
||||
@@ -20,4 +20,12 @@ public interface StorageConfigRepository extends JpaRepository<StorageConfig, In
|
||||
*/
|
||||
List<StorageConfig> findByTypeOrderById(StorageTypeEnum type);
|
||||
|
||||
/**
|
||||
* 根据存储类型找到某个 KEY 的值
|
||||
* @param type 存储类型
|
||||
* @param key KEY
|
||||
* @return KEY 对应的对象
|
||||
*/
|
||||
StorageConfig findByTypeAndKey(StorageTypeEnum type, String key);
|
||||
|
||||
}
|
||||
@@ -98,6 +98,7 @@ public class MySecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
http.cors();
|
||||
http.csrf().disable();
|
||||
http.headers().frameOptions().sameOrigin();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alicp.jetcache.Cache;
|
||||
import com.alicp.jetcache.RefreshPolicy;
|
||||
import com.alicp.jetcache.anno.CacheRefresh;
|
||||
import com.alicp.jetcache.anno.CacheType;
|
||||
import com.alicp.jetcache.anno.Cached;
|
||||
import com.alicp.jetcache.anno.CreateCache;
|
||||
import im.zhaojun.common.model.constant.ZFileConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
@@ -20,23 +24,25 @@ 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 {
|
||||
|
||||
public static final String SYSTEM_CONFIG_CACHE_PREFIX = "zfile-cache:";
|
||||
private static final String SYSTEM_CONFIG_CACHE_PREFIX = "zfile-cache:";
|
||||
|
||||
@Value("${zfile.cache.timeout}")
|
||||
protected Long timeout;
|
||||
|
||||
protected boolean isInitialized;
|
||||
protected boolean isInitialized = false;
|
||||
|
||||
protected String basePath;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
@@ -62,11 +68,16 @@ public abstract class AbstractFileService extends FileCacheService implements Fi
|
||||
|
||||
/**
|
||||
* 清理当前存储策略的缓存
|
||||
* 1. 删除全部缓存
|
||||
* 2. 关闭自动刷新
|
||||
* 3. 重置缓存个数
|
||||
* 4. 标记为当前处于未完成缓存状态
|
||||
*/
|
||||
public void clearFileCache() throws Exception {
|
||||
Set<String> cacheKeys = getCacheKeys();
|
||||
cache.removeAll(cacheKeys);
|
||||
closeCacheAutoRefresh();
|
||||
fileAsyncCacheService.resetCacheCount();
|
||||
fileAsyncCacheService.setCacheFinish(false);
|
||||
}
|
||||
|
||||
@@ -81,7 +92,7 @@ public abstract class AbstractFileService extends FileCacheService implements Fi
|
||||
try {
|
||||
fileList("/");
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + "初始化异常", e);
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常", e);
|
||||
flag = false;
|
||||
}
|
||||
return flag;
|
||||
@@ -95,6 +106,14 @@ public abstract class AbstractFileService extends FileCacheService implements Fi
|
||||
return !isInitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取是否初始化成功
|
||||
* @return 初始化成功与否
|
||||
*/
|
||||
public boolean getIsInitialized() {
|
||||
return isInitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储策略类型
|
||||
* @return 存储策略类型枚举
|
||||
@@ -105,14 +124,24 @@ public abstract class AbstractFileService extends FileCacheService implements Fi
|
||||
* 搜索文件
|
||||
* @param name 文件名
|
||||
* @return 包含该文件名的所有文件或文件夹
|
||||
* @throws Exception 搜索过程出现的异常
|
||||
*/
|
||||
public List<FileItemDTO> search(String name) throws Exception {
|
||||
public List<FileItemDTO> search(String name) {
|
||||
List<FileItemDTO> result = new ArrayList<>();
|
||||
|
||||
boolean searchIgnoreCase = systemConfigService.getSearchIgnoreCase();
|
||||
|
||||
List<FileItemDTO> fileItemList = selectAllFileList();
|
||||
for (FileItemDTO fileItemDTO : fileItemList) {
|
||||
if (fileItemDTO.getName().contains(name)) {
|
||||
|
||||
boolean testResult;
|
||||
|
||||
if (searchIgnoreCase) {
|
||||
testResult = StrUtil.containsIgnoreCase(fileItemDTO.getName(), name);
|
||||
} else {
|
||||
testResult = fileItemDTO.getName().contains(name);
|
||||
}
|
||||
|
||||
if (testResult) {
|
||||
result.add(fileItemDTO);
|
||||
}
|
||||
}
|
||||
@@ -124,7 +153,7 @@ public abstract class AbstractFileService extends FileCacheService implements Fi
|
||||
* 查询所有文件, 仅去缓存中查询.
|
||||
* @return 所有文件
|
||||
*/
|
||||
public List<FileItemDTO> selectAllFileList() throws Exception {
|
||||
public List<FileItemDTO> selectAllFileList() {
|
||||
List<FileItemDTO> result = new ArrayList<>();
|
||||
boolean enableCache = systemConfigService.getEnableCache();
|
||||
if (!enableCache) {
|
||||
@@ -143,7 +172,7 @@ public abstract class AbstractFileService extends FileCacheService implements Fi
|
||||
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
|
||||
String filePath = StringUtils.removeDuplicateSeparator("/" + fileItemDTO.getPath() + "/" + fileItemDTO.getName() + "/");
|
||||
List<FileItemDTO> cacheList = cache.get(filePath);
|
||||
if (cacheList != null) {
|
||||
if (cacheList != null && isNotEncryptedFolder(cacheList)) {
|
||||
queue.addAll(cacheList);
|
||||
}
|
||||
}
|
||||
@@ -152,12 +181,32 @@ public abstract class AbstractFileService extends FileCacheService implements Fi
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 不是加密文件夹
|
||||
* @param list 文件夹中的内容
|
||||
* @return 返回此文件夹是否加密.
|
||||
*/
|
||||
private boolean isNotEncryptedFolder(List<FileItemDTO> list) {
|
||||
// 如果开启了 "搜索包含加密文件" 选项, 则直接返回 true.
|
||||
SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();
|
||||
if (BooleanUtil.isFalse(systemConfig.getSearchContainEncryptedFile())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 遍历文件判断是否包含
|
||||
for (FileItemDTO fileItemDTO : list) {
|
||||
if (Objects.equals(ZFileConstant.PASSWORD_FILE_NAME, fileItemDTO.getName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有缓存的 Key, 仅当开启缓存, 且缓存完成时, 可获取.
|
||||
* @return 所有缓存的 Key
|
||||
* @throws Exception 可能出现的异常
|
||||
*/
|
||||
public Set<String> getCacheKeys() throws Exception {
|
||||
public Set<String> getCacheKeys() {
|
||||
if (systemConfigService.getEnableCache() && fileAsyncCacheService.isCacheFinish()) {
|
||||
Set<String> collect = selectAllFileList().stream().map(fileItemDTO -> {
|
||||
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
|
||||
@@ -191,4 +240,6 @@ public abstract class AbstractFileService extends FileCacheService implements Fi
|
||||
cache.config().setRefreshPolicy(refreshPolicy);
|
||||
}
|
||||
|
||||
public abstract FileItemDTO getFileItem(String path);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
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;
|
||||
import com.amazonaws.services.s3.model.ObjectListing;
|
||||
import com.amazonaws.services.s3.model.S3ObjectSummary;
|
||||
import im.zhaojun.common.exception.NotExistFileException;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
@@ -15,10 +16,10 @@ import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2019/12/26 22:26
|
||||
*/
|
||||
public abstract class AbstractS3FileService extends AbstractFileService {
|
||||
|
||||
@@ -27,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;
|
||||
@@ -54,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));
|
||||
|
||||
@@ -74,6 +75,9 @@ public abstract class AbstractS3FileService extends AbstractFileService {
|
||||
|
||||
for (String commonPrefix : objectListing.getCommonPrefixes()) {
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
if (Objects.equals(commonPrefix, "/")) {
|
||||
continue;
|
||||
}
|
||||
fileItemDTO.setName(commonPrefix.substring(fullPath.length(), commonPrefix.length() - 1));
|
||||
fileItemDTO.setType(FileTypeEnum.FOLDER);
|
||||
fileItemDTO.setPath(path);
|
||||
@@ -90,6 +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);
|
||||
|
||||
@@ -100,13 +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;
|
||||
try {
|
||||
int end = path.lastIndexOf("/");
|
||||
list = fileList(path.substring(0, end + 1));
|
||||
} catch (Exception e) {
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
|
||||
for (FileItemDTO fileItemDTO : list) {
|
||||
String fullPath = StringUtils.concatUrl(fileItemDTO.getPath(), fileItemDTO.getName());
|
||||
if (Objects.equals(fullPath, path)) {
|
||||
return fileItemDTO;
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
}
|
||||
@@ -73,8 +73,6 @@ public class FileAsyncCacheService {
|
||||
log.info("缓存 {} 所有文件开始", storageStrategy.getDescription());
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
String path = "/";
|
||||
|
||||
FileService currentFileService = systemConfigService.getCurrentFileService();
|
||||
List<FileItemDTO> rootFileItems = currentFileService.fileList("/");
|
||||
ArrayDeque<FileItemDTO> queue = new ArrayDeque<>(rootFileItems);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2020/1/11 14:08
|
||||
*/
|
||||
@Service
|
||||
public class FileCacheService {
|
||||
@@ -15,6 +15,7 @@ public class FileCacheService {
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
public void enableCache() {
|
||||
|
||||
@@ -23,6 +23,12 @@ public class StorageConfigService {
|
||||
return storageConfigRepository.findByTypeOrderById(storageTypeEnum);
|
||||
}
|
||||
|
||||
|
||||
public StorageConfig selectByTypeAndKey(StorageTypeEnum storageType, String key) {
|
||||
return storageConfigRepository.findByTypeAndKey(storageType, key);
|
||||
}
|
||||
|
||||
|
||||
public Map<String, StorageConfig> selectStorageConfigMapByKey(StorageTypeEnum storageTypeEnum) {
|
||||
Map<String, StorageConfig> map = new HashMap<>(24);
|
||||
for (StorageConfig storageConfig : selectStorageConfigByType(storageTypeEnum)) {
|
||||
@@ -31,6 +37,7 @@ public class StorageConfigService {
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
public void updateStorageConfig(List<StorageConfig> storageConfigList) {
|
||||
storageConfigRepository.saveAll(storageConfigList);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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;
|
||||
@@ -37,9 +37,6 @@ public class SystemConfigService {
|
||||
@Resource
|
||||
private SystemConfigRepository systemConfigRepository;
|
||||
|
||||
@Resource
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
@Resource
|
||||
private FileCacheService fileCacheService;
|
||||
|
||||
@@ -95,14 +92,12 @@ public class SystemConfigService {
|
||||
}
|
||||
|
||||
boolean oldEnableCache = getEnableCache();
|
||||
boolean curEnableCache = ObjectUtil.defaultIfNull(systemConfigDTO.getEnableCache(), false);
|
||||
boolean curEnableCache = BooleanUtil.isTrue(systemConfigDTO.getEnableCache());
|
||||
|
||||
configCache.remove(SYSTEM_CONFIG_CACHE_KEY);
|
||||
|
||||
systemConfigRepository.saveAll(systemConfigList);
|
||||
|
||||
AbstractFileService currentFileService = getCurrentFileService();
|
||||
|
||||
if (!oldEnableCache && curEnableCache) {
|
||||
log.debug("检测到开启了缓存, 开启预热缓存");
|
||||
fileCacheService.enableCache();
|
||||
@@ -149,10 +144,15 @@ public class SystemConfigService {
|
||||
return systemConfigDTO.getStorageStrategy();
|
||||
}
|
||||
|
||||
|
||||
public boolean getEnableCache() {
|
||||
SystemConfigDTO systemConfigDTO = getSystemConfig();
|
||||
return ObjectUtil.defaultIfNull(systemConfigDTO.getEnableCache(), false);
|
||||
return BooleanUtil.isTrue(systemConfigDTO.getEnableCache());
|
||||
}
|
||||
|
||||
public boolean getSearchIgnoreCase() {
|
||||
SystemConfigDTO systemConfigDTO = getSystemConfig();
|
||||
return BooleanUtil.isTrue(systemConfigDTO.getSearchIgnoreCase());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ public class SystemService {
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
/**
|
||||
* 构建指定路径下标题, 页头, 页尾
|
||||
* 构建指定路径下标题, 页面文档信息
|
||||
* @param path 路径
|
||||
*/
|
||||
public synchronized SiteConfigDTO getConfig(String path) throws Exception {
|
||||
@@ -30,10 +30,8 @@ public class SystemService {
|
||||
|
||||
List<FileItemDTO> fileItemList = new ArrayList<>(fileService.fileList(path));
|
||||
for (FileItemDTO fileItemDTO : fileItemList) {
|
||||
if (ZFileConstant.FOOTER_FILE_NAME.equalsIgnoreCase(fileItemDTO.getName())) {
|
||||
siteConfigDTO.setFooter(HttpUtil.getTextContent(fileItemDTO.getUrl()));
|
||||
} else if (ZFileConstant.HEADER_FILE_NAME.equalsIgnoreCase(fileItemDTO.getName())) {
|
||||
siteConfigDTO.setHeader(HttpUtil.getTextContent(fileItemDTO.getUrl()));
|
||||
if (ZFileConstant.README_FILE_NAME.equalsIgnoreCase(fileItemDTO.getName())) {
|
||||
siteConfigDTO.setReadme(HttpUtil.getTextContent(fileItemDTO.getUrl()));
|
||||
}
|
||||
}
|
||||
return siteConfigDTO;
|
||||
|
||||
@@ -20,6 +20,9 @@ public class FileComparator implements Comparator<FileItemDTO> {
|
||||
private String sortBy;
|
||||
private String order;
|
||||
|
||||
public FileComparator() {
|
||||
}
|
||||
|
||||
public FileComparator(String sortBy, String order) {
|
||||
this.sortBy = sortBy;
|
||||
this.order = order;
|
||||
@@ -27,15 +30,22 @@ public class FileComparator implements Comparator<FileItemDTO> {
|
||||
|
||||
@Override
|
||||
public int compare(FileItemDTO o1, FileItemDTO o2) {
|
||||
if (sortBy == null) {
|
||||
sortBy = "name";
|
||||
}
|
||||
|
||||
if (order == null) {
|
||||
order = "asc";
|
||||
}
|
||||
FileTypeEnum o1Type = o1.getType();
|
||||
FileTypeEnum o2Type = o2.getType();
|
||||
|
||||
NaturalOrderComparator naturalOrderComparator = new NaturalOrderComparator();
|
||||
if (o1Type.equals(o2Type)) {
|
||||
int result;
|
||||
switch (sortBy) {
|
||||
case "time": result = o1.getTime().compareTo(o2.getTime()); break;
|
||||
case "size": result = o1.getSize().compareTo(o2.getSize()); break;
|
||||
default: result = o1.getName().compareToIgnoreCase(o2.getName()); break;
|
||||
default: result = naturalOrderComparator.compare(o1.getName(), o2.getName()); break;
|
||||
}
|
||||
return "asc".equals(order) ? result : -result;
|
||||
}
|
||||
@@ -46,4 +56,4 @@ public class FileComparator implements Comparator<FileItemDTO> {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -11,13 +11,13 @@ import org.springframework.web.client.RestTemplate;
|
||||
public class HttpUtil {
|
||||
|
||||
public static String getTextContent(String url) {
|
||||
RestTemplate restTemplate = SpringContextHolder.getBean(RestTemplate.class);
|
||||
RestTemplate restTemplate = SpringContextHolder.getBean("restTemplate");
|
||||
String result = restTemplate.getForObject(url, String.class);
|
||||
return result == null ? "" : result;
|
||||
}
|
||||
|
||||
public static boolean checkUrlExist(String url) {
|
||||
RestTemplate restTemplate = SpringContextHolder.getBean(RestTemplate.class);
|
||||
RestTemplate restTemplate = SpringContextHolder.getBean("restTemplate");
|
||||
try {
|
||||
restTemplate.headForHeaders(url);
|
||||
return true;
|
||||
|
||||
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,5 +1,7 @@
|
||||
package im.zhaojun.common.util;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@@ -50,7 +52,7 @@ public class StringUtils {
|
||||
path = DELIMITER + path;
|
||||
}
|
||||
|
||||
if (domain.charAt(domain.length() - 1) == DELIMITER) {
|
||||
if (domain != null && domain.charAt(domain.length() - 1) == DELIMITER) {
|
||||
domain = domain.substring(0, domain.length() - 2);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -37,32 +38,41 @@ 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 = true;
|
||||
isInitialized = false;
|
||||
} else {
|
||||
ftp = new Ftp(host, Integer.parseInt(port), username, password);
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) {
|
||||
FTPFile[] ftpFiles = ftp.lsFiles(path);
|
||||
public synchronized List<FileItemDTO> fileList(String path) throws IOException {
|
||||
String fullPath = StringUtils.getFullPath(basePath, path);
|
||||
FTPFile[] ftpFiles = ftp.lsFiles(fullPath);
|
||||
|
||||
List<FileItemDTO> fileItemList = new ArrayList<>();
|
||||
|
||||
@@ -83,11 +93,29 @@ 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
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.FTP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileItemDTO getFileItem(String path) {
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
fileItemDTO.setUrl(getDownloadUrl(path));
|
||||
return fileItemDTO;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,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();
|
||||
@@ -48,7 +49,7 @@ public class HuaweiServiceImpl extends AbstractS3FileService implements FileServ
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package im.zhaojun.local.controller;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
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;
|
||||
@@ -16,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
|
||||
@@ -29,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()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
|
||||
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
headers.setContentDispositionFormData("attachment", URLUtil.encode(file.getName()));
|
||||
|
||||
headers.add("Pragma", "no-cache");
|
||||
headers.add("Expires", "0");
|
||||
headers.add("Last-Modified", new Date().toString());
|
||||
headers.add("ETag", String.valueOf(System.currentTimeMillis()));
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.headers(headers)
|
||||
.contentLength(file.length())
|
||||
.contentType(mediaType)
|
||||
.body(new FileSystemResource(file));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package im.zhaojun.local.service;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import im.zhaojun.common.exception.NotExistFileException;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.SystemConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
@@ -45,7 +46,7 @@ public class LocalServiceImpl extends AbstractFileService implements FileService
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.LOCAL);
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
filePath = stringStorageConfigMap.get(StorageConfigConstant.FILE_PATH_KEY).getValue();
|
||||
if (Objects.isNull(filePath)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
@@ -54,7 +55,7 @@ public class LocalServiceImpl extends AbstractFileService implements FileService
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +90,7 @@ public class LocalServiceImpl extends AbstractFileService implements FileService
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
SystemConfig usernameConfig = systemConfigRepository.findByKey(SystemConfigConstant.DOMAIN);
|
||||
return URLUtil.encode(StringUtils.removeDuplicateSeparator(usernameConfig.getValue() + "/file/" + path));
|
||||
return StringUtils.removeDuplicateSeparator(usernameConfig.getValue() + "/file/" + path);
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
@@ -105,4 +106,26 @@ public class LocalServiceImpl extends AbstractFileService implements FileService
|
||||
return StorageTypeEnum.LOCAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileItemDTO getFileItem(String path) {
|
||||
String fullPath = StringUtils.concatPath(filePath, path);
|
||||
|
||||
File file = new File(fullPath);
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
fileItemDTO.setType(file.isDirectory() ? FileTypeEnum.FOLDER : FileTypeEnum.FILE);
|
||||
fileItemDTO.setTime(new Date(file.lastModified()));
|
||||
fileItemDTO.setSize(file.length());
|
||||
fileItemDTO.setName(file.getName());
|
||||
fileItemDTO.setPath(filePath);
|
||||
if (file.isFile()) {
|
||||
fileItemDTO.setUrl(getDownloadUrl(path));
|
||||
}
|
||||
|
||||
return fileItemDTO;
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,8 @@ 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();
|
||||
@@ -40,13 +41,14 @@ public class MinIOServiceImpl extends AbstractS3FileService implements FileServi
|
||||
} else {
|
||||
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
|
||||
s3Client = AmazonS3ClientBuilder.standard()
|
||||
.withPathStyleAccessEnabled(true)
|
||||
.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "minio")).build();
|
||||
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package im.zhaojun.onedrive.common.config;
|
||||
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.onedrive.china.service.OneDriveChinaServiceImpl;
|
||||
import im.zhaojun.onedrive.international.service.OneDriveServiceImpl;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Configuration
|
||||
public class OneDriveConfig {
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private OneDriveServiceImpl oneDriveServiceImpl;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private OneDriveChinaServiceImpl oneDriveChinaServiceImpl;
|
||||
|
||||
@Bean
|
||||
public RestTemplate oneDriveRestTemplate() {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
ClientHttpRequestInterceptor interceptor = (httpRequest, bytes, clientHttpRequestExecution) -> {
|
||||
String host = httpRequest.getURI().getHost();
|
||||
StorageTypeEnum type;
|
||||
if (oneDriveChinaServiceImpl.getGraphEndPoint().contains(host)) {
|
||||
type = StorageTypeEnum.ONE_DRIVE_CHINA;
|
||||
} else if (oneDriveServiceImpl.getGraphEndPoint().contains(host)) {
|
||||
type = StorageTypeEnum.ONE_DRIVE;
|
||||
} else {
|
||||
return clientHttpRequestExecution.execute(httpRequest, bytes);
|
||||
}
|
||||
|
||||
StorageConfig accessTokenConfig =
|
||||
storageConfigService.selectByTypeAndKey(type, StorageConfigConstant.ACCESS_TOKEN_KEY);
|
||||
|
||||
String tokenValue = String.format("%s %s", "Bearer", accessTokenConfig.getValue());
|
||||
httpRequest.getHeaders().add("Authorization", tokenValue);
|
||||
return clientHttpRequestExecution.execute(httpRequest, bytes);
|
||||
};
|
||||
restTemplate.setInterceptors(Collections.singletonList(interceptor));
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package im.zhaojun.onedrive.common.model;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
public class OneDriveToken {
|
||||
|
||||
@JSONField(name = "access_token")
|
||||
private String accessToken;
|
||||
|
||||
@JSONField(name = "refresh_token")
|
||||
private String refreshToken;
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
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 org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Zhao Jun
|
||||
* 2020/1/29 11:54
|
||||
*/
|
||||
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 = 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,35 +0,0 @@
|
||||
// package im.zhaojun.onedrive.config;
|
||||
//
|
||||
// import org.nuxeo.onedrive.client.*;
|
||||
// import org.springframework.context.annotation.Configuration;
|
||||
//
|
||||
// @Configuration
|
||||
// public class OneDriveConfig {
|
||||
//
|
||||
//
|
||||
// public void a () {
|
||||
// OneDriveAPI api = new OneDriveBasicAPI("YOUR_ACCESS_TOKEN");
|
||||
//
|
||||
// OneDriveFolder folder = new OneDriveFolder(api, "FOLDER_ID");
|
||||
// OneDriveFile file = new OneDriveFile(api, "FILE_ID");
|
||||
// }
|
||||
//
|
||||
// public static void main(String[] args) throws OneDriveAPIException {
|
||||
// OneDriveBasicAPI api = new OneDriveBasicAPI("EwAgA61DBAAUcSSzoTJJsy+XrnQXgAKO5cj4yc8AAQ0ZknDUY8YnwB9aJv7vA9YjiRAVMnKc+rG11fSXLZRAA8Q/CgJaz+OkRN60vaLDfp6KxbmVlob6kxeD/peOUI2eHtk0055Q2+n057tlyVAvGIFl9dvqkItoAthjmybcSkKBZS5h1meWxQ5IOvzSVrdgCKL0NOtTxfh33ZUDsYjvSid6NOX4Bs+pRjvZhQkvqEfGt8KlOL+JoIowmv2I+u09iDmS60BMwSoeK2K3CCLIXxLaiiPYUMsrNk65j4PWEBwBEfyHb6j3lrM/YvwFLq7Y8KJVjrXjFENC7ruja6Ko/cfTMX90yLkUEckpsZ30E6RJHWEHt7jXtNwndDZVknYDZgAACL5pnk17FJfb8AGGxJL1Y0CnAzgkTM2gw+WkFRRDDNzujuW1LQofwZ119HdeANhPrBZ14x32VaPGL1l0RvtR9LCeAN+EogcV5xhVpmCExitaXQB6OkZ6BnXaxLj5TNvFRNeZq0ZfJ3T08clLA1vXHkZhNKgiFDI8xUbahy4r6QpzgoF+0+dz+MA1NzQCQCsRGieS63OD1BKrzRsNxzls5Z9rKzBT6CpWpiaiOg4mmW0yeino/L9zz9Gf5kAJr813bpNr+rH/E8MPd0pZf+6hv37FaVCM7RN1V7CkkCDnRAxwxEK8pDgZhRjZOw7gKutPOiOoTO9ptjh2Jcrds714HitX2HI3RsRY+yyAOcb8XI27m4daSEGCJCuu/TJwXTE4ul54MWsi8MrcDlZN9DOjckiJIqVI8IbvhM+OUAP4FUIfZJJrIVa8WFwxcsMmjlLTxp/I7+JfdvZjJSk3j1yYvbWFviyoSkpQgw2hIDhZxCg083Z6qS467g5H9Uz3fQc+Ss0K0Mud6RcZTU9RqCcp+h92tUc8+gDxQ2NwJsG5vcmSRwf5KHKvsWjt6yK4OHxCpkLYi31eJZtv2EjQGXX1gYyhc/2wQ+cHPvbgBzIfhXetbZKpSxoowAQO/J1i5oRs90h24kjTd4qJd3qspxk1lhZcEC8IkfZXjNgjEQI=");
|
||||
//
|
||||
// OneDriveFolder folder = new OneDriveFolder(api, "PDF");
|
||||
//
|
||||
// for (OneDriveItem.Metadata metadata : OneDriveFolder.getRoot(api)) {
|
||||
// System.out.println(metadata.getName());
|
||||
// if (metadata.isFile()) {
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // for (OneDriveItem.Metadata search : OneDriveFolder.getRoot(api).search("index.html")) {
|
||||
// // System.out.println(search.getName());
|
||||
// // }
|
||||
//
|
||||
// // OneDriveFile file = new OneDriveFile(api, "key.txt");
|
||||
// // System.out.println(file);
|
||||
// }
|
||||
// }
|
||||
@@ -1,32 +0,0 @@
|
||||
package im.zhaojun.onedrive.controller;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Controller
|
||||
public class OneDriveController {
|
||||
|
||||
@GetMapping("/onedirve/callback")
|
||||
@ResponseBody
|
||||
public String onedriveCallback(String code, HttpServletRequest request) {
|
||||
String json = "client_id=04a73532-6c16-4fe4-92e5-f2cd125ed553&redirect_uri=http://localhost:8080/onedirve/callback&client_secret=2gY/t?*Eff6i36TgKTtiG*08/k]@.I4[&code=" + code + "&grant_type=authorization_code";
|
||||
|
||||
HttpRequest post = HttpUtil.createPost("https://login.microsoftonline.com/common/oauth2/v2.0/token");
|
||||
post.body(json, "application/x-www-form-urlencoded");
|
||||
HttpResponse response = post.execute();
|
||||
|
||||
System.out.println(response.body());
|
||||
return response.body();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package im.zhaojun.onedrive.international.service;
|
||||
|
||||
import im.zhaojun.common.config.GlobalScheduleTask;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.onedrive.common.service.AbstractOneDriveService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OneDriveServiceImpl extends AbstractOneDriveService implements FileService {
|
||||
|
||||
@Resource
|
||||
private GlobalScheduleTask globalScheduleTask;
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Value("${zfile.onedrive.clientId}")
|
||||
protected String clientId;
|
||||
|
||||
@Value("${zfile.onedrive.redirectUri}")
|
||||
protected String redirectUri;
|
||||
|
||||
@Value("${zfile.onedrive.clientSecret}")
|
||||
protected String clientSecret;
|
||||
|
||||
@Value("${zfile.onedrive.scope}")
|
||||
protected String scope;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
String accessToken = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_TOKEN_KEY).getValue();
|
||||
String refreshToken = stringStorageConfigMap.get(StorageConfigConstant.REFRESH_TOKEN_KEY).getValue();
|
||||
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
|
||||
if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
refreshOneDriveToken();
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.ONE_DRIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGraphEndPoint() {
|
||||
return "graph.microsoft.com";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticateEndPoint() {
|
||||
return "login.microsoftonline.com";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,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();
|
||||
@@ -48,7 +49,7 @@ public class QiniuServiceImpl extends AbstractS3FileService implements FileServi
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,7 +27,8 @@ 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();
|
||||
@@ -47,7 +48,7 @@ public class TencentServiceImpl extends AbstractS3FileService implements FileSer
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package im.zhaojun.upyun.service;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import com.UpYun;
|
||||
import im.zhaojun.common.exception.NotExistFileException;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
@@ -12,8 +13,7 @@ import im.zhaojun.common.service.AbstractFileService;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -27,10 +27,9 @@ import java.util.Objects;
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class UpYunServiceImpl extends AbstractFileService implements FileService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(UpYunServiceImpl.class);
|
||||
|
||||
private static final String END_MARK = "g2gCZAAEbmV4dGQAA2VvZg";
|
||||
|
||||
@Resource
|
||||
@@ -46,7 +45,7 @@ public class UpYunServiceImpl extends AbstractFileService implements FileService
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.UPYUN);
|
||||
storageConfigService.selectStorageConfigMapByKey(getStorageTypeEnum());
|
||||
String bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
|
||||
String username = stringStorageConfigMap.get(StorageConfigConstant.USERNAME_KEY).getValue();
|
||||
String password = stringStorageConfigMap.get(StorageConfigConstant.PASSWORD_KEY).getValue();
|
||||
@@ -62,7 +61,7 @@ public class UpYunServiceImpl extends AbstractFileService implements FileService
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
|
||||
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,4 +109,23 @@ public class UpYunServiceImpl extends AbstractFileService implements FileService
|
||||
return StorageTypeEnum.UPYUN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileItemDTO getFileItem(String path) {
|
||||
List<FileItemDTO> list;
|
||||
try {
|
||||
int end = path.lastIndexOf("/");
|
||||
list = fileList(path.substring(0, end));
|
||||
} catch (Exception e) {
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
|
||||
for (FileItemDTO fileItemDTO : list) {
|
||||
String fullPath = StringUtils.concatUrl(fileItemDTO.getPath(), fileItemDTO.getName());
|
||||
if (Objects.equals(fullPath, path)) {
|
||||
return fileItemDTO;
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
}
|
||||
@@ -6,22 +6,36 @@
|
||||
"description": "目录缓存过期时间 和 下载地址过期时间. 单位为秒."
|
||||
},
|
||||
{
|
||||
"name": "zfile.constant.header",
|
||||
"name": "zfile.constant.readme",
|
||||
"type": "java.lang.String",
|
||||
"defaultValue": "header.md",
|
||||
"description": "头部文件 文件名."
|
||||
},
|
||||
{
|
||||
"name": "zfile.constant.footer",
|
||||
"type": "java.lang.String",
|
||||
"defaultValue": "footer.md",
|
||||
"description": "尾部文件 文件名."
|
||||
"defaultValue": "readme.md",
|
||||
"description": "文档文件 文件名."
|
||||
},
|
||||
{
|
||||
"name": "zfile.constant.password",
|
||||
"type": "java.lang.String",
|
||||
"defaultValue": "password.txt",
|
||||
"description": "密码文件 文件名."
|
||||
},
|
||||
{
|
||||
"name": "zfile.onedrive.clientId",
|
||||
"type": "java.lang.String",
|
||||
"description": "OneDrive ClientId."
|
||||
},
|
||||
{
|
||||
"name": "zfile.onedrive.clientSecret",
|
||||
"type": "java.lang.String",
|
||||
"description": "OneDrive ClientSecret."
|
||||
},
|
||||
{
|
||||
"name": "zfile.onedrive.redirectUri",
|
||||
"type": "java.lang.String",
|
||||
"description": "OneDrive 认证重定向地址."
|
||||
},
|
||||
{
|
||||
"name": "zfile.onedrive.scope",
|
||||
"type": "java.lang.String",
|
||||
"description": "OneDrive 认证权限."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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,7 +29,7 @@ spring:
|
||||
password: 123456
|
||||
|
||||
# MySQL 配置
|
||||
# driver-class-name: com.mysql.jdbc.Driver
|
||||
# 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
|
||||
@@ -40,13 +48,26 @@ spring:
|
||||
gzipped: true
|
||||
profiles:
|
||||
active: prod
|
||||
|
||||
boot:
|
||||
admin:
|
||||
context-path: /act
|
||||
zfile:
|
||||
cache:
|
||||
timeout: 300
|
||||
constant:
|
||||
header: header.md
|
||||
footer: footer.md
|
||||
readme: readme.md
|
||||
password: password.txt
|
||||
onedrive:
|
||||
clientId: 09939809-c617-43c8-a220-a93c1513c5d4
|
||||
clientSecret: _l:zI-_yrW75lV8M61K@z.I2K@B/On6Q
|
||||
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
|
||||
@@ -54,4 +75,7 @@ jetcache:
|
||||
local:
|
||||
default:
|
||||
type: caffeine
|
||||
keyConvertor: fastjson
|
||||
keyConvertor: fastjson
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,15 @@ INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (6, 'username', '管理
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (7, 'password', '管理员密码');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (8, 'domain', '站点域名');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (9, 'enableCache', '是否开启缓存');
|
||||
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (10, 'searchContainEncryptedFile', '搜索包含加密文件');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (11, 'customCss', '自定义 CSS');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (12, 'customJs', '自定义 JS (可用于统计代码)');
|
||||
INSERT INTO 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');
|
||||
@@ -32,13 +40,13 @@ 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');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (28, 'domain', '加速域名', 'tencent');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (29, 'endPoint', '区域', 'tencent');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (30, 'accessKey', 'SecretId', 'minio');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (30, 'accessKey', 'AccessKey', 'minio');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (31, 'secretKey', 'SecretKey', 'minio');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (32, 'endPoint', '服务地址', 'minio');
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (33, 'bucket-name', '存储空间名称', 'minio');
|
||||
@@ -49,4 +57,18 @@ 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 (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');
|
||||
@@ -7,20 +7,25 @@
|
||||
-->
|
||||
<configuration scan="false" scanPeriod="60 seconds" debug="false">
|
||||
|
||||
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
|
||||
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
|
||||
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
|
||||
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
|
||||
<conversionRule conversionWord="wex"
|
||||
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
|
||||
<conversionRule conversionWord="wEx"
|
||||
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
|
||||
|
||||
<!-- 定义日志的根目录 -->
|
||||
<property name="LOG_HOME" value="${user.home}/.zfile/logs" />
|
||||
<property name="LOG_HOME" value="${user.home}/.zfile/logs"/>
|
||||
<!-- 定义日志文件名称 -->
|
||||
<property name="appName" value="zfile"/>
|
||||
|
||||
<!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
|
||||
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
<springProfile name="prod">
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
</springProfile>
|
||||
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<pattern>%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}</pattern>
|
||||
</layout>
|
||||
@@ -65,24 +70,31 @@
|
||||
name:表示匹配的logger类型前缀,也就是包的前半部分
|
||||
level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
|
||||
additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
|
||||
false:表示只用当前logger的appender-ref,true:
|
||||
表示当前logger的appender-ref和rootLogger的appender-ref都有效
|
||||
false:表示只用当前logger的appender-ref,
|
||||
true: 表示当前logger的appender-ref和rootLogger的appender-ref都有效
|
||||
-->
|
||||
|
||||
<!-- jetCache logger -->
|
||||
<logger name="com.alicp" level="debug"/>
|
||||
<logger name="com.alicp" additivity="false" level="debug"/>
|
||||
|
||||
<!-- hibernate logger -->
|
||||
<logger name="org.hibernate.SQL" level="DEBUG"/>
|
||||
<logger name="org.hibernate.SQL" additivity="false" level="debug"/>
|
||||
|
||||
<springProfile name="dev">
|
||||
<logger name="im.zhaojun" additivity="true" level="debug"/>
|
||||
</springProfile>
|
||||
|
||||
<springProfile name="prod">
|
||||
<logger name="im.zhaojun" additivity="true" level="debug"/>
|
||||
</springProfile>
|
||||
|
||||
<!--
|
||||
root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
|
||||
要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。
|
||||
-->
|
||||
<root level="info">
|
||||
<appender-ref ref="stdout" />
|
||||
<appender-ref ref="appLogAppender" />
|
||||
<appender-ref ref="stdout"/>
|
||||
<appender-ref ref="appLogAppender"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -1 +0,0 @@
|
||||
.el-menu[data-v-0d38e212],.el-row[data-v-0d38e212]{height:100vh}
|
||||
1
src/main/resources/static/css/adminIndex.c226568b.css
Normal file
1
src/main/resources/static/css/adminIndex.c226568b.css
Normal file
@@ -0,0 +1 @@
|
||||
.el-menu[data-v-7e0eaa89],.el-row[data-v-7e0eaa89]{height:100vh}
|
||||
File diff suppressed because one or more lines are too long
1
src/main/resources/static/css/app.f59b1def.css
Normal file
1
src/main/resources/static/css/app.f59b1def.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
1
src/main/resources/static/css/chunk-vendors.bc21228d.css
Normal file
1
src/main/resources/static/css/chunk-vendors.bc21228d.css
Normal file
File diff suppressed because one or more lines are too long
1
src/main/resources/static/css/install.c1550f52.css
Normal file
1
src/main/resources/static/css/install.c1550f52.css
Normal file
@@ -0,0 +1 @@
|
||||
.box-card[data-v-680e1b46]{padding-top:30px;padding-right:30px;margin:15vh auto;height:65vh;overflow-y:auto}.el-select[data-v-680e1b46]{width:100%}
|
||||
@@ -1 +0,0 @@
|
||||
.box-card[data-v-23a15e8c]{padding-top:30px;padding-right:30px;margin:15vh auto;height:65vh;overflow-y:auto}.el-select[data-v-23a15e8c]{width:100%}
|
||||
@@ -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.ca398206.css rel=prefetch><link href=/js/adminIndex.456ac23a.js rel=prefetch><link href=/js/dplayer.acc587f7.js rel=prefetch><link href=/js/install.7e582096.js rel=prefetch><link href=/css/app.9a6cb5c2.css rel=preload as=style><link href=/css/chunk-vendors.b9a3d604.css rel=preload as=style><link href=/js/app.022a9bb1.js rel=preload as=script><link href=/js/chunk-vendors.3fc3f2af.js rel=preload as=script><link href=/css/chunk-vendors.b9a3d604.css rel=stylesheet><link href=/css/app.9a6cb5c2.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.3fc3f2af.js></script><script src=/js/app.022a9bb1.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/adminIndex.c226568b.css rel=prefetch><link href=/css/install.c1550f52.css rel=prefetch><link href=/js/adminIndex.e930ce30.js rel=prefetch><link href=/js/dplayer.acc587f7.js rel=prefetch><link href=/js/install.575ebeb6.js rel=prefetch><link href=/css/app.f59b1def.css rel=preload as=style><link href=/css/chunk-vendors.bc21228d.css rel=preload as=style><link href=/js/app.3d5f33b3.js rel=preload as=script><link href=/js/chunk-vendors.b8b2057a.js rel=preload as=script><link href=/css/chunk-vendors.bc21228d.css rel=stylesheet><link href=/css/app.f59b1def.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.b8b2057a.js></script><script src=/js/app.3d5f33b3.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/adminIndex.e930ce30.js
Normal file
1
src/main/resources/static/js/adminIndex.e930ce30.js
Normal file
@@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["adminIndex"],{"1f8d":function(t,e,i){"use strict";var a=i("872f"),s=i.n(a);s.a},"872f":function(t,e,i){},adf4:function(t,e,i){"use strict";i.r(e);var a=function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("el-row",[i("el-col",{attrs:{span:3}},[i("el-menu",{staticClass:"el-menu-vertical-demo",attrs:{"default-active":"/admin"!==this.$route.path?this.$route.path:"/admin/site",router:!0}},[i("el-menu-item",{attrs:{index:"/admin/site"}},[i("i",{staticClass:"el-icon-setting"}),i("span",{attrs:{slot:"title"},slot:"title"},[t._v("基本设置")])]),i("el-menu-item",{attrs:{index:"/admin/view"}},[i("i",{staticClass:"el-icon-view"}),i("span",{attrs:{slot:"title"},slot:"title"},[t._v("显示设置")])]),i("el-menu-item",{attrs:{index:"/admin/storage"}},[i("i",{staticClass:"el-icon-s-operation"}),i("span",{attrs:{slot:"title"},slot:"title"},[t._v("存储策略")])]),i("el-menu-item",{attrs:{index:"/admin/password"}},[i("i",{staticClass:"el-icon-key"}),i("span",{attrs:{slot:"title"},slot:"title"},[t._v("修改密码")])]),i("el-menu-item",{attrs:{index:"/admin/cache"}},[i("i",{staticClass:"el-icon-collection"}),i("span",{attrs:{slot:"title"},slot:"title"},[t._v("缓存管理")])]),i("el-menu-item",{attrs:{index:"/admin/api"}},[i("i",{staticClass:"el-icon-document"}),i("span",{attrs:{slot:"title"},slot:"title"},[t._v("API 文档")])]),i("el-menu-item",{attrs:{index:"/admin/monitor"}},[i("i",{staticClass:"el-icon-monitor"}),i("span",{attrs:{slot:"title"},slot:"title"},[t._v("系统监控")])])],1)],1),i("el-col",{attrs:{span:16}},[i("keep-alive",{attrs:{exclude:"CacheManager,SiteSetting"}},[i("router-view")],1)],1)],1)},s=[],n={name:"Index",data:function(){return{active:"/admin/storage"}}},l=n,o=(i("1f8d"),i("2877")),r=Object(o["a"])(l,a,s,!1,null,"7e0eaa89",null);e["default"]=r.exports}}]);
|
||||
File diff suppressed because one or more lines are too long
1
src/main/resources/static/js/app.3d5f33b3.js
Normal file
1
src/main/resources/static/js/app.3d5f33b3.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/install.575ebeb6.js
Normal file
1
src/main/resources/static/js/install.575ebeb6.js
Normal file
@@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["install"],{"06da":function(t,e,r){},"27d7":function(t,e,r){"use strict";var a=r("06da"),o=r.n(a);o.a},f8a7:function(t,e,r){"use strict";r.r(e);var a=function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("el-row",{attrs:{gutter:20}},[r("el-col",{attrs:{span:8,offset:8}},[r("el-card",{staticClass:"box-card",attrs:{"align-center":"",shadow:"always"}},[r("el-form",{directives:[{name:"loading",rawName:"v-loading",value:t.loading,expression:"loading"}],ref:"form",attrs:{rules:t.rules,model:t.form,"label-width":"auto","status-icon":!0,"element-loading-text":"保存并初始化中."}},[r("el-form-item",{attrs:{label:"站点名称",prop:"siteName"}},[r("el-input",{model:{value:t.form.siteName,callback:function(e){t.$set(t.form,"siteName",e)},expression:"form.siteName"}})],1),r("el-form-item",{attrs:{label:"管理员账号",prop:"username"}},[r("el-input",{model:{value:t.form.username,callback:function(e){t.$set(t.form,"username","string"===typeof e?e.trim():e)},expression:"form.username"}})],1),r("el-form-item",{attrs:{label:"管理员密码",prop:"password"}},[r("el-input",{model:{value:t.form.password,callback:function(e){t.$set(t.form,"password","string"===typeof e?e.trim():e)},expression:"form.password"}})],1),r("el-form-item",{attrs:{label:"站点地址/域名",prop:"domain"}},[r("el-input",{model:{value:t.form.domain,callback:function(e){t.$set(t.form,"domain","string"===typeof e?e.trim():e)},expression:"form.domain"}})],1),r("el-form-item",{attrs:{label:"存储策略"}},[r("el-select",{attrs:{placeholder:"请选择存储策略"},model:{value:t.form.storageStrategy,callback:function(e){t.$set(t.form,"storageStrategy",e)},expression:"form.storageStrategy"}},t._l(t.supportStrategy,(function(t){return r("el-option",{key:t.key,attrs:{label:t.description,value:t.key}})})),1)],1),t._l(t.storageStrategyForm,(function(e){return r("el-form-item",{key:e.title,attrs:{label:e.title}},["endPoint"===e.key&&t.region.hasOwnProperty(t.form.storageStrategy)?r("el-select",{model:{value:t.form.storageStrategyConfig.endPoint,callback:function(e){t.$set(t.form.storageStrategyConfig,"endPoint",e)},expression:"form.storageStrategyConfig.endPoint"}},t._l(t.region[t.form.storageStrategy],(function(t){return r("el-option",{key:t.name,attrs:{label:t.name,value:t.val}})})),1):r("el-input",{model:{value:t.form.storageStrategyConfig[e.key],callback:function(r){t.$set(t.form.storageStrategyConfig,e.key,"string"===typeof r?r.trim():r)},expression:"form.storageStrategyConfig[item.key]"}})],1)})),r("el-form-item",[r("el-button",{attrs:{type:"primary"},on:{click:function(e){return t.submitForm("form")}}},[t._v("确认")])],1)],2)],1)],1)],1)},o=[],n=r("4328"),s=r.n(n),i=r("245d"),l={name:"Install",data:function(){return{active:1,form:{siteName:"",storageStrategy:"",username:"",password:"",domain:window.location.protocol+"//"+window.location.host,storageStrategyConfig:{endPoint:""},loading:!0},storageStrategyForm:[],supportStrategy:[],region:i["a"],rules:{siteName:[{required:!0,message:"请输入站点名称",trigger:"change"}],username:[{required:!0,message:"请输入管理员账号",trigger:"change"}],password:[{required:!0,message:"请输入管理员密码",trigger:"change"}],domain:[{required:!0,type:"url",message:"请输入正确的域名, 需以 http:// 或 https:// 开头",trigger:"change"}]}}},watch:{"form.storageStrategy":function(t){var e=this;this.$http.get("form",{params:{storageType:t}}).then((function(t){e.form.storageStrategyConfig.endPoint=null,e.storageStrategyForm=t.data.data}))}},methods:{submitForm:function(t){var e=this;this.loading=!0,this.$refs[t].validate((function(t){if(!t)return!1;var r=e;e.$http.post("install",s.a.stringify(e.form)).then((function(t){e.loading=!1;var a=t.data;e.$message({message:a.msg,type:0===a.code?"success":"error",duration:1500,onClose:function(){r.$router.push("/main")}})}))}))}},created:function(){var t=this;this.$http.get("is-installed").then((function(e){var r=e.data;0!==r.code&&t.$router.push("/main")}))},mounted:function(){var t=this;this.$http.get("common/support-strategy").then((function(e){t.supportStrategy=e.data.data}))}},m=l,g=(r("27d7"),r("2877")),f=Object(g["a"])(m,a,o,!1,null,"680e1b46",null);e["default"]=f.exports}}]);
|
||||
@@ -1 +0,0 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["install"],{"45a3":function(e,t,a){},6796:function(e,t,a){"use strict";var r=a("45a3"),o=a.n(r);o.a},f8a7:function(e,t,a){"use strict";a.r(t);var r=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("el-row",{attrs:{gutter:20}},[a("el-col",{attrs:{span:8,offset:8}},[a("el-card",{staticClass:"box-card",attrs:{"align-center":"",shadow:"always"}},[a("el-form",{ref:"form",attrs:{rules:e.rules,model:e.form,"label-width":"auto","status-icon":!0}},[a("el-form-item",{attrs:{label:"站点名称",prop:"siteName"}},[a("el-input",{model:{value:e.form.siteName,callback:function(t){e.$set(e.form,"siteName",t)},expression:"form.siteName"}})],1),a("el-form-item",{attrs:{label:"管理员账号",prop:"username"}},[a("el-input",{model:{value:e.form.username,callback:function(t){e.$set(e.form,"username","string"===typeof t?t.trim():t)},expression:"form.username"}})],1),a("el-form-item",{attrs:{label:"管理员密码",prop:"password"}},[a("el-input",{model:{value:e.form.password,callback:function(t){e.$set(e.form,"password","string"===typeof t?t.trim():t)},expression:"form.password"}})],1),a("el-form-item",{attrs:{label:"站点地址/域名",prop:"domain"}},[a("el-input",{model:{value:e.form.domain,callback:function(t){e.$set(e.form,"domain","string"===typeof t?t.trim():t)},expression:"form.domain"}})],1),a("el-form-item",{attrs:{label:"存储策略"}},[a("el-select",{attrs:{placeholder:"请选择存储策略"},model:{value:e.form.storageStrategy,callback:function(t){e.$set(e.form,"storageStrategy",t)},expression:"form.storageStrategy"}},[a("el-option",{attrs:{label:"阿里云 OSS",value:"aliyun"}}),a("el-option",{attrs:{label:"腾讯云 COS",value:"tencent"}}),a("el-option",{attrs:{label:"华为云 OBS",value:"huawei"}}),a("el-option",{attrs:{label:"七牛云 KODO",value:"qiniu"}}),a("el-option",{attrs:{label:"又拍云 USS",value:"upyun"}}),a("el-option",{attrs:{label:"FTP",value:"ftp"}}),a("el-option",{attrs:{label:"本地存储",value:"local"}}),a("el-option",{attrs:{label:"minio",value:"minio"}})],1)],1),e._l(e.storageStrategyForm,(function(t){return a("el-form-item",{key:t.title,attrs:{label:t.title}},["endPoint"===t.key&&e.region.hasOwnProperty(e.form.storageStrategy)?a("el-select",{model:{value:e.form.storageStrategyConfig.endPoint,callback:function(t){e.$set(e.form.storageStrategyConfig,"endPoint",t)},expression:"form.storageStrategyConfig.endPoint"}},e._l(e.region[e.form.storageStrategy],(function(e){return a("el-option",{key:e.name,attrs:{label:e.name,value:e.val}})})),1):a("el-input",{model:{value:e.form.storageStrategyConfig[t.key],callback:function(a){e.$set(e.form.storageStrategyConfig,t.key,"string"===typeof a?a.trim():a)},expression:"form.storageStrategyConfig[item.key]"}})],1)})),a("el-form-item",[a("el-button",{attrs:{type:"primary"},on:{click:function(t){return e.submitForm("form")}}},[e._v("确认")])],1)],2)],1)],1)],1)},o=[],n=a("4328"),s=a.n(n),i=a("245d"),l={name:"Install",data:function(){return{active:1,form:{siteName:"",storageStrategy:"",username:"",password:"",domain:window.location.protocol+"//"+window.location.host,storageStrategyConfig:{endPoint:""}},storageStrategyForm:[],region:i["a"],rules:{siteName:[{required:!0,message:"请输入站点名称",trigger:"change"}],username:[{required:!0,message:"请输入管理员账号",trigger:"change"}],password:[{required:!0,message:"请输入管理员密码",trigger:"change"}],domain:[{required:!0,type:"url",message:"请输入正确的域名, 需以 http:// 或 https:// 开头",trigger:"change"}]}}},watch:{"form.storageStrategy":function(e){var t=this;this.$http.get("form",{params:{storageType:e}}).then((function(e){t.form.storageStrategyConfig.endPoint=null,t.storageStrategyForm=e.data.data}))}},methods:{submitForm:function(e){var t=this;this.$refs[e].validate((function(e){if(!e)return!1;var a=t;t.$http.post("install",s.a.stringify(t.form)).then((function(e){var r=e.data;t.$message({message:r.msg,type:0===r.code?"success":"error",duration:1500,onClose:function(){a.$router.push("/main")}})}))}))}},created:function(){var e=this;this.$http.get("is-installed").then((function(t){var a=t.data;0!==a.code&&e.$router.push("/main")}))}},m=l,u=(a("6796"),a("2877")),f=Object(u["a"])(m,r,o,!1,null,"23a15e8c",null);t["default"]=f.exports}}]);
|
||||
3
src/main/resources/static/zfile.config.json
Normal file
3
src/main/resources/static/zfile.config.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"baseUrl": "http://127.0.0.1:8080/"
|
||||
}
|
||||
26
src/main/resources/templates/callback.html
Normal file
26
src/main/resources/templates/callback.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>CallBack Result</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>accessToken (访问令牌)</h1>
|
||||
<div>
|
||||
<input type="text" th:value="${accessToken}">
|
||||
</div>
|
||||
|
||||
<h1>refreshToken (刷新令牌)</h1>
|
||||
<div>
|
||||
<input type="text" th:value="${refreshToken}">
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<style>
|
||||
input {
|
||||
height: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</html>
|
||||
187
src/main/resources/templates/error/404.html
Normal file
187
src/main/resources/templates/error/404.html
Normal file
@@ -0,0 +1,187 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>404 ERROR</title>
|
||||
<style type="text/css">
|
||||
body, div, h3, h4, li, ol {
|
||||
margin: 0;
|
||||
padding: 0
|
||||
}
|
||||
|
||||
body {
|
||||
font: 14px/1.5 'Microsoft YaHei', '微软雅黑', Helvetica, Sans-serif;
|
||||
min-width: 1200px;
|
||||
background: #f0f1f3;
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline: 0
|
||||
}
|
||||
|
||||
h3, h4, strong {
|
||||
font-weight: 700
|
||||
}
|
||||
|
||||
a {
|
||||
color: #428bca;
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline
|
||||
}
|
||||
|
||||
.error-page {
|
||||
background: #f0f1f3;
|
||||
padding: 80px 0 180px
|
||||
}
|
||||
|
||||
.error-page-container {
|
||||
position: relative;
|
||||
z-index: 1
|
||||
}
|
||||
|
||||
.error-page-main {
|
||||
position: relative;
|
||||
background: #f9f9f9;
|
||||
margin: 0 auto;
|
||||
width: 617px;
|
||||
-ms-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 50px 50px 70px
|
||||
}
|
||||
|
||||
.error-page-main:before {
|
||||
content: '';
|
||||
display: block;
|
||||
background: url(img/errorPageBorder.png?1427783409637);
|
||||
height: 7px;
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
width: 100%;
|
||||
left: 0
|
||||
}
|
||||
|
||||
.error-page-main h3 {
|
||||
font-size: 24px;
|
||||
font-weight: 400;
|
||||
border-bottom: 1px solid #d0d0d0
|
||||
}
|
||||
|
||||
.error-page-main h3 strong {
|
||||
font-size: 54px;
|
||||
font-weight: 400;
|
||||
margin-right: 20px
|
||||
}
|
||||
|
||||
.error-page-main h4 {
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
color: #333
|
||||
}
|
||||
|
||||
.error-page-actions {
|
||||
font-size: 0;
|
||||
z-index: 100
|
||||
}
|
||||
|
||||
.error-page-actions div {
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
padding: 30px 0 0 10px;
|
||||
width: 50%;
|
||||
-ms-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: #838383
|
||||
}
|
||||
|
||||
.error-page-actions ol {
|
||||
list-style: decimal;
|
||||
padding-left: 20px
|
||||
}
|
||||
|
||||
.error-page-actions li {
|
||||
line-height: 2.5em
|
||||
}
|
||||
|
||||
.error-page-actions:before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
bottom: 17px;
|
||||
left: 50px;
|
||||
width: 200px;
|
||||
height: 10px;
|
||||
-moz-box-shadow: 4px 5px 31px 11px #999;
|
||||
-webkit-box-shadow: 4px 5px 31px 11px #999;
|
||||
box-shadow: 4px 5px 31px 11px #999;
|
||||
-moz-transform: rotate(-4deg);
|
||||
-webkit-transform: rotate(-4deg);
|
||||
-ms-transform: rotate(-4deg);
|
||||
-o-transform: rotate(-4deg);
|
||||
transform: rotate(-4deg)
|
||||
}
|
||||
|
||||
.error-page-actions:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
bottom: 17px;
|
||||
right: 50px;
|
||||
width: 200px;
|
||||
height: 10px;
|
||||
-moz-box-shadow: 4px 5px 31px 11px #999;
|
||||
-webkit-box-shadow: 4px 5px 31px 11px #999;
|
||||
box-shadow: 4px 5px 31px 11px #999;
|
||||
-moz-transform: rotate(4deg);
|
||||
-webkit-transform: rotate(4deg);
|
||||
-ms-transform: rotate(4deg);
|
||||
-o-transform: rotate(4deg);
|
||||
transform: rotate(4deg)
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="error-page">
|
||||
<div class="error-page-container">
|
||||
<div class="error-page-main">
|
||||
<h3>
|
||||
<strong>404</strong>很抱歉,您要访问的文件/页面不存在!
|
||||
</h3>
|
||||
<div class="error-page-actions">
|
||||
<div>
|
||||
<h4>可能原因:</h4>
|
||||
<ol>
|
||||
<li>网络信号差不稳定</li>
|
||||
<li>找不到请求的页面</li>
|
||||
<li>输入的网址不正确</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div>
|
||||
<h4>可以尝试:</h4>
|
||||
<ol>
|
||||
<li><a href="#" onclick="backHomePage()">返回首页</a></li>
|
||||
<li><a href="https://github.com/zhaojun1998/zfile/issues" target="_blank">留言反馈</a></li>
|
||||
<li><a href="#">联系站长</a></li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function backHomePage() {
|
||||
window.location.href = window.location.origin;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user