Compare commits

...

42 Commits
0.5 ... 0.7

Author SHA1 Message Date
zhaojun1998
fa5f16c61f 🔖 发布 0.7 版 2020-01-24 19:00:56 +08:00
zhaojun1998
492b22506d 🐛 修复切换存储策略时没有重新缓存的 BUG 2020-01-24 18:51:20 +08:00
zhaojun1998
a12f685340 🐛 修复 FTP 初始化显示错误 2020-01-24 18:44:59 +08:00
zhaojun1998
2ee3f3dd66 💄 更新页面 2020-01-24 18:39:20 +08:00
zhaojun1998
245937e773 搜索忽略大小写支持 2020-01-24 18:37:58 +08:00
zhaojun1998
aef34facbd 🐛 修复更改系统设置时, 误清理缓存的 BUG 2020-01-24 18:37:32 +08:00
zhaojun1998
14bb5e15e3 替换 onedrive 授权重定向地址, 配置文件添加元数据描述 2020-01-24 17:40:01 +08:00
zhaojun1998
12371f06dd 🐛 修复 S3 协议中, 某些情况下出现了空文件名的 BUG 2020-01-24 17:28:48 +08:00
zhaojun1998
28e43e968f 添加分页, 修复本地存储下载错误 2020-01-24 17:27:12 +08:00
zhaojun1998
669b413ff0 优化存储策略功能, 添加是否初始化成功标识 2020-01-24 17:13:49 +08:00
zhaojun1998
f32e5e8f9e 🐛 修复切换存储引擎时, 没有清空原引擎缓存的 BUG. 2020-01-24 10:46:00 +08:00
zhaojun1998
3719378614 ✏️ 修复拼写错误 2020-01-24 10:28:21 +08:00
zhaojun1998
40c759078e 🐛 MINIO 修改 URL 路径风格指定为 path-style, 防止配置域名情况下, 找不到域名的 BUG. 2020-01-24 10:27:53 +08:00
zhaojun1998
e37e778e1a 优化日志输出 2020-01-23 10:53:16 +08:00
zhaojun1998
031607402a 本地存储, 文件不存在时, 给与友好提示 2020-01-20 22:48:04 +08:00
zhaojun1998
6c9150466c 优化日志配置 2020-01-20 21:36:58 +08:00
zhaojun1998
be633ebe1a OneDrive 支持 2020-01-20 21:36:13 +08:00
zhaojun1998
9715cf922a 优化日志输出 2020-01-20 21:35:41 +08:00
zhaojun1998
f6163c7e19 优化代码结构 2020-01-20 21:35:20 +08:00
zhaojun1998
dcc4cb19ad OneDrive 支持 2020-01-19 21:58:02 +08:00
zhaojun1998
ad0ad12c08 搜索功能支持分页 2020-01-18 22:57:29 +08:00
zhaojun1998
74c935cdf0 抽取通用代码 2020-01-17 23:28:23 +08:00
zhaojun1998
1876e692f2 💄 更改默认排序器 2020-01-16 22:24:46 +08:00
zhaojun1998
f198b34324 🔖 升级版本为 0.6 2020-01-12 11:18:50 +08:00
zhaojun1998
3095e0c8d9 💄 更新页面, 优化缓存页面展示 2020-01-12 11:15:01 +08:00
zhaojun1998
594246127d 🐛 修复更改策略时, 未正确启用和关闭缓存的 BUG 2020-01-12 11:14:20 +08:00
zhaojun1998
f6c5f7a91b 🔧 修改配置配置文件, 将日志改为单独配置 2020-01-12 11:13:02 +08:00
zhaojun1998
2a765fff7e 🐛 获取缓存是否开启时的 NPE 问题 2020-01-11 23:20:12 +08:00
zhaojun1998
28f958878b 🔊 更新日志级别 2020-01-11 23:02:19 +08:00
zhaojun1998
368f3a90eb 移除无用依赖 2020-01-11 22:53:19 +08:00
zhaojun1998
98b14abbfc 🔧 修改配置文件, 去除默认缓存时间. 2020-01-11 22:49:55 +08:00
zhaojun1998
7c04c3d6b8 优化代码结构 2020-01-11 22:48:28 +08:00
zhaojun1998
921cb1a115 通过反射优化代码 2020-01-08 22:34:15 +08:00
zhaojun1998
9371968c3b 增强根据 value 获取枚举的功能 2020-01-08 22:33:30 +08:00
zhaojun1998
47e88849ac 🐛 修复当前存储引擎为空时, 与新设置的存储引擎比较出现的 NPE 2020-01-08 22:32:47 +08:00
zhaojun1998
2f0f41f413 去除无效输出 2020-01-08 21:31:24 +08:00
zhaojun1998
7667765abc 🐛 修复本地存储的文件名, 包含 &?=[] 等特殊字符时出现的问题 2020-01-08 21:28:05 +08:00
zhaojun1998
b2a2e69af5 🔒 关闭 URL 部分校验, 允许中文文件名 2020-01-08 21:22:03 +08:00
zhaojun1998
7c729a72e2 🐛 本地存储, 获取文件不存在返回状态码 404 2020-01-07 22:57:59 +08:00
zhaojun1998
5495abc881 更新未知歌曲默认封面 2020-01-06 23:00:07 +08:00
zhaojun1998
797cd4fc06 💄 更新页面 2020-01-05 16:11:02 +08:00
zhaojun1998
8148d182cf 更新缓存策略刷新时间 2020-01-05 16:01:50 +08:00
64 changed files with 1356 additions and 382 deletions

View File

@@ -34,19 +34,19 @@ yum install -y java # 适用于 Centos 7.x
下载项目:
```bash
wget https://github.com/zhaojun1998/zfile/releases/download/0.5/zfile-0.5.jar
wget https://github.com/zhaojun1998/zfile/releases/download/0.7/zfile-0.7.jar
```
启动项目:
```bash
java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.5.jar
java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.7.jar
## 高级启动
java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.5.jar --server.port=18777
java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.7.jar --server.port=18777
## 后台运行
nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.5.jar &
nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.7.jar &
```
> 系统使用的是内置配置文件, 默认配置请参考: [application.yml](https://github.com/zhaojun1998/zfile/blob/master/src/main/resources/application.yml)
@@ -57,13 +57,6 @@ nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.5.jar &
> *指定 `-Djava.security.egd=file:/dev/./urandom` 是为了防止在 Linux 环境中, 生成首次登陆生成 sessionId 取系统随机数过慢的问题.*
重要参数:
- `server.port` 为指定端口, 默认为 `8080`
- `logging.path` 为日志文件存放路径, 默认为 `${user.home}/.zfile/logs`
- `spring.datasource` 下配置了 `h2``mysql` 两种数据库的支持, 默认采用 `h2`.
- `spring.cache.type` 为指定缓存方式, 默认为 `caffeine`, 即内存缓存, 无需安装, 支持切换为 `redis`, 但需配置 `spring.redis.host``spring.redis.password` 参数后才可使用.
访问地址:
用户前台: http://127.0.0.1:8080/#/main
@@ -73,6 +66,14 @@ nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.5.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/onedirve/callback&scope=offline_access%20User.Read%20Files.ReadWrite.All
然后分别填写至访问令牌和刷新令牌即可:
![http://cdn.jun6.net/2020-01-24_18-57-06.png](http://cdn.jun6.net/2020-01-24_18-57-06.png)
## 运行环境
* JDK: `1.8`
@@ -85,12 +86,10 @@ nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.5.jar &
缓存默认支持 `h2``mysql`, 前者为嵌入式数据库, 无需安装, 但后者相对性能更好.
### 默认路径
默认 H2 数据库文件地址: `~/.zfile/db/`, `~` 表示用户目录, windows 为 `C:/Users/用户名/`, linux 为 `/home/用户名/`, root 用户为 `/root/`
### 头尾文件和加密文件
- 目录头部显示文件名为 `header.md`

22
pom.xml
View File

@@ -12,7 +12,7 @@
<groupId>im.zhaojun</groupId>
<artifactId>zfile</artifactId>
<version>0.5</version>
<version>0.7</version>
<name>zfile</name>
<description>一个在线的文件浏览系统</description>
@@ -45,6 +45,10 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 数据库驱动-->
<dependency>
@@ -65,7 +69,7 @@
<version>4.5.11</version>
</dependency>
<!-- 存储引擎相关 API, 对象存储、FTP、 Rest API-->
<!-- 存储策略相关 API, 对象存储、FTP、 Rest API-->
<dependency>
<groupId>com.upyun</groupId>
<artifactId>java-sdk</artifactId>
@@ -104,6 +108,20 @@
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.5.14</version>
<exclusions>
<exclusion>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-redis</artifactId>
</exclusion>
<exclusion>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</exclusion>
<exclusion>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

View File

@@ -37,6 +37,7 @@ public class AliyunServiceImpl extends AbstractS3FileService implements FileServ
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
isInitialized = false;
} else {
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
@@ -46,10 +47,8 @@ public class AliyunServiceImpl extends AbstractS3FileService implements FileServ
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "oss")).build();
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}

View File

@@ -0,0 +1,75 @@
package im.zhaojun.common.config;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.service.SystemConfigService;
import im.zhaojun.onedrive.service.OneDriveService;
import im.zhaojun.onedrive.service.OneDriveServiceImpl;
import im.zhaojun.onedrive.service.OneDriveToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Arrays;
/**
* @author zhaojun
*/
@Configuration
@EnableScheduling
@Slf4j
public class GlobalScheduleTask {
@Resource
private StorageConfigService storageConfigService;
@Resource
private OneDriveService oneDriveService;
@Resource
private SystemConfigService systemConfigService;
/**
* 项目启动 30 秒后, 每半小时执行一次刷新 OneDrive Token 的定时任务.
*/
@Scheduled(fixedRate = 1000 * 60 * 30, initialDelay = 1000 * 30)
public void autoRefreshOneDriveToken() {
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
if (!(currentFileService instanceof OneDriveServiceImpl)) {
log.debug("当前启用存储类型, 不是 OneDrive, 跳过自动刷新 AccessToken");
return;
}
if (currentFileService.getIsUnInitialized()) {
log.debug("当前启用 OneDrive 未初始化成功, 跳过自动刷新 AccessToken");
return;
}
refreshOneDriveToken();
}
/**
* 调用刷新 OneDrive Token
*/
public void refreshOneDriveToken() {
OneDriveToken refreshToken = oneDriveService.getRefreshToken();
StorageConfig accessTokenConfig =
storageConfigService.selectByTypeAndKey(StorageTypeEnum.ONE_DRIVE, StorageConfigConstant.ACCESS_TOKEN_KEY);
StorageConfig refreshTokenConfig =
storageConfigService.selectByTypeAndKey(StorageTypeEnum.ONE_DRIVE, StorageConfigConstant.REFRESH_TOKEN_KEY);
accessTokenConfig.setValue(refreshToken.getAccessToken());
refreshTokenConfig.setValue(refreshToken.getRefreshToken());
storageConfigService.updateStorageConfig(Arrays.asList(accessTokenConfig, refreshTokenConfig));
log.info("刷新 OneDrive key 时间: " + LocalDateTime.now());
}
}

View File

@@ -1,8 +1,9 @@
package im.zhaojun.common.controller;
import im.zhaojun.common.config.StorageTypeFactory;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.dto.CacheConfigDTO;
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;
@@ -14,11 +15,14 @@ import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.Objects;
/**
* 后台管理
@@ -53,12 +57,13 @@ 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);
if (!currentStorageStrategy.equals(systemConfigDTO.getStorageStrategy())) {
StorageTypeEnum currentStorageStrategy = currentFileService.getStorageTypeEnum();
if (!Objects.equals(currentStorageStrategy, systemConfigDTO.getStorageStrategy())) {
log.info("已将存储策略由 {} 切换为 {}", currentStorageStrategy, systemConfigDTO.getStorageStrategy());
refreshStorageStrategy();
}
@@ -75,8 +80,8 @@ public class AdminController {
}
/**
* 获取指定存储引擎的设置
* @param storageType 存储引擎
* 获取指定存储策略的设置
* @param storageType 存储策略
* @return 所有设置
*/
@GetMapping("/strategy-form")
@@ -85,45 +90,65 @@ public class AdminController {
return ResultBean.success(storageConfigList);
}
@GetMapping("/cache/config")
public ResultBean cacheConfig() throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
Set<String> cacheKeys = fileService.getCacheKeys();
CacheConfigDTO cacheConfigDTO = new CacheConfigDTO();
cacheConfigDTO.setEnableCache(systemConfigService.getEnableCache());
cacheConfigDTO.setCacheFinish(fileAsyncCacheService.isCacheFinish());
cacheConfigDTO.setCacheKeys(cacheKeys);
return ResultBean.success(cacheConfigDTO);
@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);
}
@PostMapping("/cache/refresh")
public ResultBean refreshCache(String key) throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.refreshCache(key);
return ResultBean.success();
}
/**
* 保存存储策略
* @param storageStrategyConfig 保存表单值
* @param storageStrategy 所属策略
* @return 操作结果
* @throws Exception 表单解析出错异常
*/
@PostMapping("/storage-strategy")
public ResultBean save(@RequestParam Map<String, String> storageStrategyConfig, StorageTypeEnum storageStrategy) throws Exception {
// 保存设置.
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageStrategy);
for (StorageConfig storageConfig : storageConfigList) {
String key = storageConfig.getKey();
String value = storageStrategyConfig.get(key);
storageConfig.setValue(value);
}
storageConfigService.updateStorageConfig(storageConfigList);
@PostMapping("/cache/clear")
public ResultBean clearCache(String key) throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.clearCache();
return ResultBean.success();
}
// 获取当前修改的存储策略 Service, 尝试调用初始化.
AbstractFileService updateStorageStrategyService = StorageTypeFactory.getStorageTypeService(storageStrategy);
updateStorageStrategyService.init();
@PostMapping("/cache/all")
public ResultBean cacheAll() throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.clearCache();
fileAsyncCacheService.cacheGlobalFile();
return ResultBean.success();
// 如果修改的为当前启用的缓存, 则重新进行缓存.
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
if (Objects.equals(storageStrategy, currentStorageStrategy)) {
if (log.isDebugEnabled()) {
log.debug("检测到更新了当前启用的存储策略 {}, 已清理缓存.", currentStorageStrategy);
}
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.clearFileCache();
fileAsyncCacheService.cacheGlobalFile();
}
// 返回是否初始化成功.
if (updateStorageStrategyService.getIsInitialized()) {
return ResultBean.success();
} else {
return ResultBean.error("保存成功, 但尝试初始化异常, 请检查设置.");
}
}
/**
* 更新存储策略
*/
public void refreshStorageStrategy() {
public void refreshStorageStrategy() throws Exception {
StorageTypeEnum storageStrategy = systemConfigService.getCurrentStorageStrategy();
refreshStorageStrategy(storageStrategy);
}
@@ -131,15 +156,14 @@ 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();
log.info("当前启用存储类型: {}", storageStrategy.getDescription());
// if 判断是否开启搜索.
fileService.clearFileCache();
log.info("切换至存储类型: {}", storageStrategy.getDescription());
fileAsyncCacheService.cacheGlobalFile();
}
}

View File

@@ -0,0 +1,81 @@
package im.zhaojun.common.controller;
import im.zhaojun.common.model.dto.CacheConfigDTO;
import im.zhaojun.common.model.dto.ResultBean;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.FileAsyncCacheService;
import im.zhaojun.common.service.FileCacheService;
import im.zhaojun.common.service.SystemConfigService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Set;
/**
* @author zhaojun
* @date 2020/1/11 14:03
*/
@RestController
@RequestMapping("/admin/cache")
public class CacheController {
@Resource
private SystemConfigService systemConfigService;
@Resource
private FileAsyncCacheService fileAsyncCacheService;
@Resource
private FileCacheService fileCacheService;
@PostMapping("/enable")
public ResultBean enableCache() throws Exception {
fileCacheService.enableCache();
return ResultBean.success();
}
@PostMapping("/disable")
public ResultBean disableCache() throws Exception {
fileCacheService.disableCache();
return ResultBean.success();
}
@GetMapping("/config")
public ResultBean cacheConfig() throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
Set<String> cacheKeys = fileService.getCacheKeys();
CacheConfigDTO cacheConfigDTO = new CacheConfigDTO();
cacheConfigDTO.setEnableCache(systemConfigService.getEnableCache());
cacheConfigDTO.setCacheFinish(fileAsyncCacheService.isCacheFinish());
cacheConfigDTO.setCacheKeys(cacheKeys);
cacheConfigDTO.setCacheDirectoryCount(fileAsyncCacheService.getCacheDirectoryCount());
cacheConfigDTO.setCacheFileCount(fileAsyncCacheService.getCacheFileCount());
return ResultBean.success(cacheConfigDTO);
}
@PostMapping("/refresh")
public ResultBean refreshCache(String key) throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.refreshCache(key);
return ResultBean.success();
}
@PostMapping("/clear")
public ResultBean clearCache(String key) throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.clearFileCache();
return ResultBean.success();
}
@PostMapping("/all")
public ResultBean cacheAll() throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.clearFileCache();
fileAsyncCacheService.cacheGlobalFile();
return ResultBean.success();
}
}

View File

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

View File

@@ -3,19 +3,15 @@ package im.zhaojun.common.controller;
import cn.hutool.core.util.URLUtil;
import im.zhaojun.common.annotation.CheckStorageStrategyInit;
import im.zhaojun.common.exception.SearchDisableException;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.ZFileConstant;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.dto.ResultBean;
import im.zhaojun.common.model.dto.SiteConfigDTO;
import im.zhaojun.common.model.dto.SystemConfigDTO;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.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;
@@ -42,9 +38,6 @@ public class FileController {
@Resource
private SystemConfigService systemConfigService;
@Resource
private StorageConfigService storageConfigService;
@Resource
private FileAsyncCacheService fileAsyncCacheService;
@@ -72,54 +65,10 @@ 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);
}
/**
* 获取系统配置信息和当前页的标题, 文件头, 文件尾信息
@@ -133,25 +82,13 @@ public class FileController {
return ResultBean.successData(config);
}
@CheckStorageStrategyInit
@GetMapping("/clearCache")
public ResultBean clearCache() throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
if (fileService != null) {
fileService.clearCache();
}
return ResultBean.success();
}
@CheckStorageStrategyInit
@GetMapping("/audioInfo")
public ResultBean getAudioInfo(String url) throws Exception {
return ResultBean.success(AudioHelper.getAudioInfo(url));
}
@CheckStorageStrategyInit
@GetMapping("/search")
public ResultBean search(@RequestParam(value = "name", defaultValue = "/") String name) throws Exception {
public ResultBean search(@RequestParam(value = "name", defaultValue = "/") String name,
@RequestParam(defaultValue = "name") String sortBy,
@RequestParam(defaultValue = "asc") String order,
@RequestParam(defaultValue = "1") Integer page) throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
SystemConfigDTO systemConfigDTO = systemConfigService.getSystemConfig();
if (!systemConfigDTO.getSearchEnable()) {
@@ -160,16 +97,11 @@ public class FileController {
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);
}
@GetMapping("/form")
public ResultBean getFormByStorageType(String storageType) {
StorageTypeEnum storageTypeEnum = StorageTypeEnum.getEnum(storageType);
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageTypeEnum);
storageConfigList.forEach(storageConfig -> storageConfig.setValue(null));
return ResultBean.success(storageConfigList);
}
/**
* 过滤文件列表, 不显示密码, 头部和尾部文件.
@@ -184,4 +116,22 @@ public class FileController {
|| ZFileConstant.HEADER_FILE_NAME.equals(fileItem.getName()));
}
private List<FileItemDTO> getSortedPagingData(List<FileItemDTO> fileItemList, Integer page) {
// 排序, 先按照文件类型比较, 文件夹在前, 文件在后, 然后根据 sortBy 字段排序, 默认为升序;
fileItemList.sort(new FileComparator());
filterFileList(fileItemList);
int total = fileItemList.size();
int totalPage = (total + PAGE_SIZE - 1) / PAGE_SIZE;
if (page > totalPage) {
return new ArrayList<>();
}
int start = (page - 1) * PAGE_SIZE;
int end = page * PAGE_SIZE;
end = Math.min(end, total);
return new ArrayList<>(fileItemList.subList(start, end));
}
}

View File

@@ -6,26 +6,21 @@ import im.zhaojun.common.model.dto.InstallModelDTO;
import im.zhaojun.common.model.dto.ResultBean;
import im.zhaojun.common.model.dto.SystemConfigDTO;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.FileAsyncCacheService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.service.SystemConfigService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 系统安装初始化
* @author zhaojun
*/
@Controller
@RestController
public class InstallController {
@Resource
@@ -37,11 +32,7 @@ public class InstallController {
@Resource
private AdminController adminController;
@Resource
private FileAsyncCacheService fileAsyncCacheService;
@GetMapping("/is-installed")
@ResponseBody
public ResultBean isInstall() {
if (systemConfigService.getCurrentStorageStrategy() == null) {
return ResultBean.success();
@@ -51,7 +42,6 @@ public class InstallController {
@PostMapping("/install")
@ResponseBody
public ResultBean install(InstallModelDTO installModelDTO) throws Exception {
SystemConfigDTO systemConfigDTO = systemConfigService.getSystemConfig();
@@ -81,28 +71,13 @@ public class InstallController {
return ResultBean.success();
}
@PostMapping("/storage-strategy")
@ResponseBody
public ResultBean save(@RequestParam Map<String, String> storageStrategyConfig, StorageTypeEnum storageStrategy) throws Exception {
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageStrategy);
for (StorageConfig storageConfig : storageConfigList) {
String key = storageConfig.getKey();
String value = storageStrategyConfig.get(key);
storageConfig.setValue(value);
}
storageConfigService.updateStorageConfig(storageConfigList);
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
if (Objects.equals(storageStrategy, currentStorageStrategy)) {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.clearCache();
if (systemConfigService.getEnableCache()) {
fileAsyncCacheService.cacheGlobalFile();
}
fileService.init();
}
return ResultBean.success();
@GetMapping("/form")
public ResultBean getFormByStorageType(String storageType) {
StorageTypeEnum storageTypeEnum = StorageTypeEnum.getEnum(storageType);
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageTypeEnum);
storageConfigList.forEach(storageConfig -> storageConfig.setValue(null));
return ResultBean.success(storageConfigList);
}
}
}

View File

@@ -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})
@ResponseStatus(HttpStatus.NOT_FOUND)
public String notExistFile(Exception ex) {
return "error/404";
}
/**
* 捕获 ClientAbortException 异常, 不做任何处理, 防止出现大量堆栈日志输出, 此异常不影响功能.
*/
@@ -53,6 +64,7 @@ public class GlobleExceptionHandler {
}
@ExceptionHandler
@ResponseBody
@ResponseStatus(code= HttpStatus.INTERNAL_SERVER_ERROR)
public ResultBean searchDisableExceptionHandler(Exception e) {
if (log.isDebugEnabled()) {

View File

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

View File

@@ -27,6 +27,7 @@ public class StorageConfig {
private String title;
@Column(length = 2048)
private String value;
public Integer getId() {

View File

@@ -30,4 +30,8 @@ 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";
}

View File

@@ -10,7 +10,9 @@ import java.util.Set;
*/
@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;
}

View File

@@ -0,0 +1,21 @@
package im.zhaojun.common.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Zhao Jun
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StorageStrategyDTO {
private String key;
private String description;
private boolean available;
}

View File

@@ -1,24 +1,28 @@
package im.zhaojun.common.model.enums;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.HashMap;
import java.util.Map;
/**
* @author zhaojun
*/
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum StorageTypeEnum {
/**
* 当前系统支持的所有存储策略
*/
UPYUN("upyun", "又拍云 USS"),
QINIU("qiniu", "七牛云 KODO"),
HUAWEI("huawei", "华为云 OBS"),
ALIYUN("aliyun", "阿里云 OSS"),
FTP("ftp", "FTP"),
HUAWEI("huawei", "华为云 OBS"),
LOCAL("local", "本地存储"),
MINIO("minio", "MINIO"),
ONE_DRIVE("onedrive", "OneDrive"),
QINIU("qiniu", "七牛云 KODO"),
TENCENT("tencent", "腾讯云 COS"),
MINIO("minio", "MINIO");
UPYUN("upyun", "又拍云 USS");
private String key;
private String description;
@@ -53,7 +57,7 @@ public enum StorageTypeEnum {
}
public static StorageTypeEnum getEnum(String value) {
return enumMap.get(value);
return enumMap.get(value.toLowerCase());
}
}

View File

@@ -20,4 +20,12 @@ public interface StorageConfigRepository extends JpaRepository<StorageConfig, In
*/
List<StorageConfig> findByTypeOrderById(StorageTypeEnum type);
/**
* 根据存储类型找到某个 KEY 的值
* @param type 存储类型
* @param key KEY
* @return KEY 对应的对象
*/
StorageConfig findByTypeAndKey(StorageTypeEnum type, String key);
}

View File

@@ -12,6 +12,8 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.StrictHttpFirewall;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
@@ -98,6 +100,13 @@ public class MySecurityConfig extends WebSecurityConfigurerAdapter {
http.csrf().disable();
}
@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedPercent(true);
return firewall;
}
@Override
public void configure(AuthenticationManagerBuilder web) throws Exception {
web.userDetailsService(myUserDetailsServiceImpl()).passwordEncoder(passwordEncoder());
@@ -112,6 +121,7 @@ public class MySecurityConfig extends WebSecurityConfigurerAdapter {
public void configure(WebSecurity web) {
//对于在header里面增加token等类似情况放行所有OPTIONS请求。
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
}
@Bean

View File

@@ -1,5 +1,6 @@
package im.zhaojun.common.service;
import cn.hutool.core.util.StrUtil;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.RefreshPolicy;
import com.alicp.jetcache.anno.CacheRefresh;
@@ -29,14 +30,14 @@ import java.util.stream.Collectors;
* @date 2019/12/28 19:27
*/
@Slf4j
public abstract class AbstractFileService implements FileService {
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;
@Resource
private SystemConfigService systemConfigService;
@@ -61,12 +62,17 @@ public abstract class AbstractFileService implements FileService {
public abstract List<FileItemDTO> fileList(String path) throws Exception;
/**
* 清理当前存储引擎的缓存
* 清理当前存储策略的缓存
* 1. 删除全部缓存
* 2. 关闭自动刷新
* 3. 重置缓存个数
* 4. 标记为当前处于未完成缓存状态
*/
public void clearCache() throws Exception {
public void clearFileCache() throws Exception {
Set<String> cacheKeys = getCacheKeys();
cache.removeAll(cacheKeys);
closeCacheAutoRefresh();
fileAsyncCacheService.resetCacheCount();
fileAsyncCacheService.setCacheFinish(false);
}
@@ -81,7 +87,7 @@ public abstract class AbstractFileService implements FileService {
try {
fileList("/");
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常", e);
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常", e);
flag = false;
}
return flag;
@@ -96,8 +102,16 @@ public abstract class AbstractFileService implements FileService {
}
/**
* 获取存储引擎类型
* @return 存储引擎类型枚举
* 获取是否初始化成功
* @return 初始化成功与否
*/
public boolean getIsInitialized() {
return isInitialized;
}
/**
* 获取存储策略类型
* @return 存储策略类型枚举
*/
public abstract StorageTypeEnum getStorageTypeEnum();
@@ -110,9 +124,20 @@ public abstract class AbstractFileService implements FileService {
public List<FileItemDTO> search(String name) throws Exception {
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 +149,7 @@ public abstract class AbstractFileService implements FileService {
* 查询所有文件, 仅去缓存中查询.
* @return 所有文件
*/
public List<FileItemDTO> selectAllFileList() throws Exception {
public List<FileItemDTO> selectAllFileList() {
List<FileItemDTO> result = new ArrayList<>();
boolean enableCache = systemConfigService.getEnableCache();
if (!enableCache) {
@@ -187,7 +212,7 @@ public abstract class AbstractFileService implements FileService {
}
public void openCacheAutoRefresh() {
RefreshPolicy refreshPolicy = RefreshPolicy.newPolicy(1, TimeUnit.MINUTES);
RefreshPolicy refreshPolicy = RefreshPolicy.newPolicy(30, TimeUnit.MINUTES);
cache.config().setRefreshPolicy(refreshPolicy);
}

View File

@@ -15,12 +15,14 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* @author zhaojun
* @date 2019/12/26 22:26
*/
public abstract class AbstractS3FileService extends AbstractFileService {
@Resource
protected StorageConfigService storageConfigService;
@@ -73,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);

View File

@@ -1,5 +1,9 @@
package im.zhaojun.common.service;
import cn.hutool.core.util.ObjectUtil;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.CreateCache;
import im.zhaojun.common.config.StorageTypeFactory;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
@@ -20,6 +24,15 @@ import java.util.List;
@Service
public class FileAsyncCacheService {
public static final String CACHE_PROCESS_PREFIX = "zfile-process-cache:";
public static final String CACHE_FILE_COUNT_KEY = "file-count";
public static final String CACHE_DIRECTORY_COUNT_KEY = "directory-count";
@CreateCache(name = "SYSTEM_CONFIG_CACHE_PREFIX", cacheType = CacheType.LOCAL)
private Cache<String, Integer> cache;
private boolean cacheFinish;
@Resource
@@ -30,50 +43,93 @@ public class FileAsyncCacheService {
StorageTypeEnum storageStrategy = systemConfigService.getCurrentStorageStrategy();
if (storageStrategy == null) {
log.info("尚未配置存储策略. 跳过启动缓存.");
log.debug("尚未配置存储策略. 跳过启动缓存.");
return;
}
boolean enableCache = systemConfigService.getEnableCache();
if (!enableCache) {
log.info("未开启缓存, 跳过启动缓存");
log.debug("存储策略 {} 未启用缓存, 跳过缓存.", storageStrategy.getDescription());
return;
}
AbstractFileService fileService = StorageTypeFactory.getStorageTypeService(storageStrategy);
if (fileService.getIsUnInitialized()) {
log.info("存储引擎 {} 未初始化成功, 跳过启动缓存.", storageStrategy.getDescription());
log.debug("存储策略 {} 未初始化成功, 跳过缓存.", storageStrategy.getDescription());
return;
}
Integer cacheDirectoryCount = cache.get(CACHE_DIRECTORY_COUNT_KEY);
if (cacheDirectoryCount == null) {
cacheDirectoryCount = 0;
}
Integer cacheFileCount = cache.get(CACHE_FILE_COUNT_KEY);
if (cacheFileCount == null) {
cacheFileCount = 0;
}
log.info("缓存 {} 所有文件开始", storageStrategy.getDescription());
long startTime = System.currentTimeMillis();
try {
String path = "/";
FileService currentFileService = systemConfigService.getCurrentFileService();
List<FileItemDTO> fileItemList = currentFileService.fileList(path);
ArrayDeque<FileItemDTO> queue = new ArrayDeque<>(fileItemList);
List<FileItemDTO> rootFileItems = currentFileService.fileList("/");
ArrayDeque<FileItemDTO> queue = new ArrayDeque<>(rootFileItems);
while (!queue.isEmpty()) {
FileItemDTO fileItemDTO = queue.pop();
if (FileTypeEnum.FOLDER.equals(fileItemDTO.getType())) {
cacheDirectoryCount++;
}
if (FileTypeEnum.FILE.equals(fileItemDTO.getType())) {
cacheFileCount++;
}
log.debug("已缓存 {} 个文件夹", cacheDirectoryCount);
cache.put(CACHE_DIRECTORY_COUNT_KEY, cacheDirectoryCount);
log.debug("已缓存 {} 个文件", cacheFileCount);
cache.put(CACHE_FILE_COUNT_KEY, cacheFileCount);
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
String filePath = StringUtils.removeDuplicateSeparator("/" + fileItemDTO.getPath() + "/" + fileItemDTO.getName() + "/");
queue.addAll(currentFileService.fileList(filePath));
List<FileItemDTO> fileItems = currentFileService.fileList(filePath);
queue.addAll(fileItems);
}
}
cache.put(CACHE_DIRECTORY_COUNT_KEY, cacheDirectoryCount);
cache.put(CACHE_FILE_COUNT_KEY, cacheFileCount);
} catch (Exception e) {
log.error("缓存所有文件失败", e);
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
log.info("缓存 {} 所有文件结束, 用时: {} 秒", storageStrategy.getDescription(), ( (endTime - startTime) / 1000 ));
log.info("缓存 {} 所有文件结束, 用时: {} 秒, 文件夹共 {} 个, 文件共 {} 个",
storageStrategy.getDescription(),
( (endTime - startTime) / 1000 ), cacheDirectoryCount, cacheFileCount);
cacheFinish = true;
}
/**
* 清理缓存的文件/文件夹数量统计
*/
public void resetCacheCount() {
cache.remove(CACHE_DIRECTORY_COUNT_KEY);
cache.remove(CACHE_FILE_COUNT_KEY);
}
public Integer getCacheDirectoryCount() {
return ObjectUtil.defaultIfNull(cache.get(CACHE_DIRECTORY_COUNT_KEY), 0);
}
public Integer getCacheFileCount() {
return ObjectUtil.defaultIfNull(cache.get(CACHE_FILE_COUNT_KEY), 0);
}
public boolean isCacheFinish() {
return cacheFinish;
}

View File

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

View File

@@ -23,6 +23,12 @@ public class StorageConfigService {
return storageConfigRepository.findByTypeOrderById(storageTypeEnum);
}
public StorageConfig selectByTypeAndKey(StorageTypeEnum storageType, String key) {
return storageConfigRepository.findByTypeAndKey(storageType, key);
}
public Map<String, StorageConfig> selectStorageConfigMapByKey(StorageTypeEnum storageTypeEnum) {
Map<String, StorageConfig> map = new HashMap<>(24);
for (StorageConfig storageConfig : selectStorageConfigByType(storageTypeEnum)) {
@@ -31,6 +37,7 @@ public class StorageConfigService {
return map;
}
public void updateStorageConfig(List<StorageConfig> storageConfigList) {
storageConfigRepository.saveAll(storageConfigList);
}

View File

@@ -1,5 +1,7 @@
package im.zhaojun.common.service;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CacheType;
@@ -10,11 +12,11 @@ import im.zhaojun.common.model.constant.SystemConfigConstant;
import im.zhaojun.common.model.dto.SystemConfigDTO;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.repository.SystemConfigRepository;
import im.zhaojun.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
@@ -36,7 +38,9 @@ public class SystemConfigService {
private SystemConfigRepository systemConfigRepository;
@Resource
private FileAsyncCacheService fileAsyncCacheService;
private FileCacheService fileCacheService;
private Class<SystemConfigDTO> systemConfigDTOClass = SystemConfigDTO.class;
public SystemConfigDTO getSystemConfig() {
Object cache = configCache.get(SYSTEM_CONFIG_CACHE_KEY);
@@ -48,36 +52,20 @@ public class SystemConfigService {
List<SystemConfig> systemConfigList = systemConfigRepository.findAll();
for (SystemConfig systemConfig : systemConfigList) {
switch (systemConfig.getKey()) {
case SystemConfigConstant.SITE_NAME:
systemConfigDTO.setSiteName(systemConfig.getValue());
break;
case SystemConfigConstant.INFO_ENABLE:
systemConfigDTO.setInfoEnable("true".equals(systemConfig.getValue()));
break;
case SystemConfigConstant.SEARCH_ENABLE:
systemConfigDTO.setSearchEnable("true".equals(systemConfig.getValue()));
break;
case SystemConfigConstant.SEARCH_IGNORE_CASE:
systemConfigDTO.setSearchIgnoreCase("true".equals(systemConfig.getValue()));
break;
case SystemConfigConstant.STORAGE_STRATEGY:
String value = systemConfig.getValue();
systemConfigDTO.setStorageStrategy(StorageTypeEnum.getEnum(value));
break;
case SystemConfigConstant.USERNAME:
systemConfigDTO.setUsername(systemConfig.getValue());
break;
case SystemConfigConstant.PASSWORD:
systemConfigDTO.setPassword(systemConfig.getValue());
break;
case SystemConfigConstant.DOMAIN:
systemConfigDTO.setDomain(systemConfig.getValue());
break;
case SystemConfigConstant.ENABLE_CACHE:
systemConfigDTO.setEnableCache("true".equals(systemConfig.getValue()));
break;
default:break;
String key = systemConfig.getKey();
try {
Field field = systemConfigDTOClass.getDeclaredField(key);
if (field != null) {
field.setAccessible(true);
String strVal = systemConfig.getValue();
Object convertVal = Convert.convert(field.getType(), strVal);
field.set(systemConfigDTO, convertVal);
}
} catch (NoSuchFieldException | IllegalAccessException e) {
if (log.isDebugEnabled()) {
log.debug("通过反射, 将字段 {" + key + "}注入 SystemConfigDTO 时出现异常:", e);
}
}
}
@@ -85,71 +73,43 @@ public class SystemConfigService {
return systemConfigDTO;
}
public void updateSystemConfig(SystemConfigDTO systemConfigDTO) throws Exception {
public void updateSystemConfig(SystemConfigDTO systemConfigDTO) throws Exception {
List<SystemConfig> systemConfigList = new ArrayList<>();
SystemConfig systemConfig = systemConfigRepository.findByKey(SystemConfigConstant.SITE_NAME);
systemConfig.setValue(systemConfigDTO.getSiteName());
systemConfigList.add(systemConfig);
SystemConfig domainConfig = systemConfigRepository.findByKey(SystemConfigConstant.DOMAIN);
domainConfig.setValue(systemConfigDTO.getDomain());
systemConfigList.add(domainConfig);
SystemConfig infoEnableSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.INFO_ENABLE);
infoEnableSystemConfig.setValue(systemConfigDTO.getInfoEnable() ? "true" : "false");
systemConfigList.add(infoEnableSystemConfig);
SystemConfig searchEnableSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.SEARCH_ENABLE);
searchEnableSystemConfig.setValue(systemConfigDTO.getSearchEnable() ? "true" : "false");
systemConfigList.add(searchEnableSystemConfig);
SystemConfig searchIgnoreCaseSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.SEARCH_IGNORE_CASE);
searchIgnoreCaseSystemConfig.setValue(systemConfigDTO.getSearchIgnoreCase() ? "true" : "false");
systemConfigList.add(searchIgnoreCaseSystemConfig);
Field[] fields = systemConfigDTOClass.getDeclaredFields();
for (Field field : fields) {
String key = field.getName();
SystemConfig systemConfig = systemConfigRepository.findByKey(key);
if (systemConfig != null) {
field.setAccessible(true);
Object val = field.get(systemConfigDTO);
if (val != null) {
systemConfig.setValue(val.toString());
systemConfigList.add(systemConfig);
}
}
}
boolean oldEnableCache = getEnableCache();
Boolean curEnableCache = systemConfigDTO.getEnableCache();
SystemConfig enableCacheSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.ENABLE_CACHE);
enableCacheSystemConfig.setValue(systemConfigDTO.getEnableCache() ? "true" : "false");
systemConfigList.add(enableCacheSystemConfig);
SystemConfig storageStrategySystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.STORAGE_STRATEGY);
storageStrategySystemConfig.setValue(systemConfigDTO.getStorageStrategy().getKey());
systemConfigList.add(storageStrategySystemConfig);
if (StringUtils.isNotNullOrEmpty(systemConfigDTO.getUsername())) {
SystemConfig usernameSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.USERNAME);
usernameSystemConfig.setValue(systemConfigDTO.getUsername());
systemConfigList.add(usernameSystemConfig);
}
if (StringUtils.isNotNullOrEmpty(systemConfigDTO.getPassword())) {
SystemConfig passwordSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.PASSWORD);
passwordSystemConfig.setValue(systemConfigDTO.getPassword());
systemConfigList.add(passwordSystemConfig);
}
boolean curEnableCache = ObjectUtil.defaultIfNull(systemConfigDTO.getEnableCache(), false);
configCache.remove(SYSTEM_CONFIG_CACHE_KEY);
systemConfigRepository.saveAll(systemConfigList);
AbstractFileService currentFileService = getCurrentFileService();
if (!oldEnableCache && curEnableCache) {
log.debug("检测到开启了缓存, 开启预热缓存");
currentFileService.openCacheAutoRefresh();
fileAsyncCacheService.cacheGlobalFile();
fileCacheService.enableCache();
}
if (oldEnableCache && !curEnableCache) {
log.debug("检测到关闭了缓存, 正在清理缓存数据及关闭自动刷新");
currentFileService.clearCache();
fileCacheService.disableCache();
}
}
public void updateUsernameAndPwd(String username, String password) {
SystemConfig usernameConfig = systemConfigRepository.findByKey(SystemConfigConstant.USERNAME);
usernameConfig.setValue(username);
@@ -164,11 +124,21 @@ public class SystemConfigService {
systemConfigRepository.save(systemConfig);
}
public void updateCacheEnableConfig(Boolean isEnable) {
SystemConfig enableConfig = systemConfigRepository.findByKey(SystemConfigConstant.ENABLE_CACHE);
enableConfig.setValue(isEnable.toString());
systemConfigRepository.save(enableConfig);
configCache.remove(SYSTEM_CONFIG_CACHE_KEY);
}
public AbstractFileService getCurrentFileService() {
StorageTypeEnum storageStrategy = getCurrentStorageStrategy();
return StorageTypeFactory.getStorageTypeService(storageStrategy);
}
public StorageTypeEnum getCurrentStorageStrategy() {
SystemConfigDTO systemConfigDTO = getSystemConfig();
return systemConfigDTO.getStorageStrategy();
@@ -176,7 +146,13 @@ public class SystemConfigService {
public boolean getEnableCache() {
SystemConfigDTO systemConfigDTO = getSystemConfig();
return systemConfigDTO.getEnableCache();
return ObjectUtil.defaultIfNull(systemConfigDTO.getEnableCache(), false);
}
public boolean getSearchIgnoreCase() {
SystemConfigDTO systemConfigDTO = getSystemConfig();
return ObjectUtil.defaultIfNull(systemConfigDTO.getSearchIgnoreCase(), false);
}
}

View File

@@ -48,7 +48,7 @@ public class AudioHelper {
AudioInfoDTO audioInfoDTO = new AudioInfoDTO();
audioInfoDTO.setTitle("未知歌曲");
audioInfoDTO.setArtist("未知");
audioInfoDTO.setCover("/shikwasa/audio.png");
audioInfoDTO.setCover("http://c.jun6.net/audio.png");
Mp3File mp3File = null;
try {

View File

@@ -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,6 +30,14 @@ public class FileComparator implements Comparator<FileItemDTO> {
@Override
public int compare(FileItemDTO o1, FileItemDTO o2) {
if (sortBy == null) {
sortBy = "name";
}
if (order == null) {
order = "asc";
}
FileTypeEnum o1Type = o1.getType();
FileTypeEnum o2Type = o2.getType();

View File

@@ -1,26 +1,27 @@
package im.zhaojun.common.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
/**
* @author zhaojun
*/
@Slf4j
public class HttpUtil {
public static String getTextContent(String url) {
RestTemplate restTemplate = SpringContextHolder.getBean(RestTemplate.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;
} catch (RestClientException e) {
e.printStackTrace();
} catch (RestClientException ignored) {
}
return false;
}

View File

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

View File

@@ -49,14 +49,14 @@ public class FtpServiceImpl extends AbstractFileService implements FileService {
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).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() + " 初始化异常, 已跳过");
}
}

View File

@@ -37,6 +37,7 @@ public class HuaweiServiceImpl extends AbstractS3FileService implements FileServ
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
isInitialized = false;
} else {
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
@@ -47,7 +48,7 @@ public class HuaweiServiceImpl extends AbstractS3FileService implements FileServ
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}

View File

@@ -1,6 +1,7 @@
package im.zhaojun.local.controller;
import cn.hutool.core.util.URLUtil;
import im.zhaojun.common.exception.NotExistFileException;
import im.zhaojun.common.util.StringUtils;
import im.zhaojun.local.service.LocalServiceImpl;
import org.springframework.core.io.FileSystemResource;
@@ -32,7 +33,7 @@ public class LocalController {
public ResponseEntity<FileSystemResource> downAttachment(final HttpServletRequest request) {
String path = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
String bestMatchPattern = (String ) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
AntPathMatcher apm = new AntPathMatcher();
String filePath = apm.extractPathWithinPattern(bestMatchPattern, path);
@@ -41,6 +42,11 @@ public class LocalController {
private ResponseEntity<FileSystemResource> export(File file) {
if (!file.exists()) {
throw new NotExistFileException();
}
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
HttpHeaders headers = new HttpHeaders();

View File

@@ -1,5 +1,6 @@
package im.zhaojun.local.service;
import cn.hutool.core.util.URLUtil;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.SystemConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
@@ -47,12 +48,13 @@ public class LocalServiceImpl extends AbstractFileService implements FileService
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.LOCAL);
filePath = stringStorageConfigMap.get(StorageConfigConstant.FILE_PATH_KEY).getValue();
if (Objects.isNull(filePath)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
isInitialized = false;
} else {
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@@ -87,7 +89,7 @@ public class LocalServiceImpl extends AbstractFileService implements FileService
@Override
public String getDownloadUrl(String path) {
SystemConfig usernameConfig = systemConfigRepository.findByKey(SystemConfigConstant.DOMAIN);
return StringUtils.removeDuplicateSeparator(usernameConfig.getValue() + "/file/" + path);
return URLUtil.encode(StringUtils.removeDuplicateSeparator(usernameConfig.getValue() + "/file/" + path));
}
public String getFilePath() {

View File

@@ -35,17 +35,19 @@ public class MinIOServiceImpl extends AbstractS3FileService implements FileServi
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
isInitialized = false;
} else {
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
s3Client = AmazonS3ClientBuilder.standard()
.withPathStyleAccessEnabled(true)
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "minio")).build();
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}

View File

@@ -1,35 +1,41 @@
// 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);
// }
// }
package im.zhaojun.onedrive.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 org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.Collections;
/**
* @author zhaojun
* @date 2020/1/18 17:13
*/
@Configuration
public class OneDriveConfig {
@Resource
private StorageConfigService storageConfigService;
@Bean
public RestTemplate oneDriveRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
ClientHttpRequestInterceptor interceptor = (httpRequest, bytes, clientHttpRequestExecution) -> {
StorageConfig accessTokenConfig =
storageConfigService.selectByTypeAndKey(StorageTypeEnum.ONE_DRIVE, StorageConfigConstant.ACCESS_TOKEN_KEY);
String tokenValue = String.format("%s %s", "Bearer", accessTokenConfig.getValue());
httpRequest.getHeaders().add("Authorization", tokenValue);
return clientHttpRequestExecution.execute(httpRequest, bytes);
};
restTemplate.setInterceptors(Collections.singletonList(interceptor));
return restTemplate;
}
}

View File

@@ -1,32 +1,30 @@
package im.zhaojun.onedrive.controller;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import im.zhaojun.onedrive.service.OneDriveService;
import im.zhaojun.onedrive.service.OneDriveToken;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.annotation.Resource;
/**
* @author zhaojun
*/
@Controller
@RequestMapping("/onedirve")
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";
@Resource
private OneDriveService oneDriveService;
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();
@GetMapping("/callback")
public String onedriveCallback(String code, Model model) {
OneDriveToken oneDriveToken = oneDriveService.getToken(code);
model.addAttribute("accessToken", oneDriveToken.getAccessToken());
model.addAttribute("refreshToken", oneDriveToken.getRefreshToken());
return "callback";
}
}

View File

@@ -0,0 +1,124 @@
package im.zhaojun.onedrive.service;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.repository.StorageConfigRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* @author zhaojun
* @date 2020/1/12 12:17
*/
@Service
public class OneDriveService {
public static final String DRIVER_INFO_URL = "https://graph.microsoft.com/v1.0/drive";
public static final String DRIVER_ROOT_URL = "https://graph.microsoft.com/v1.0/drive/root/children";
public static final String DRIVER_ITEMS_URL = "https://graph.microsoft.com/v1.0/drive/root:{path}:/children";
public static final String AUTHENTICATE_URL = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
@Value("${zfile.onedirve.clientId}")
private String clientId;
@Value("${zfile.onedirve.redirectUri}")
private String redirectUri;
@Value("${zfile.onedirve.clientSecret}")
private String clientSecret;
@Value("${zfile.onedirve.scope}")
private String scope;
@Resource
private RestTemplate oneDriveRestTemplate;
@Resource
private StorageConfigRepository storageConfigRepository;
public OneDriveToken getToken(String code) {
String param = "client_id=" + clientId +
"&redirect_uri=" + redirectUri +
"&client_secret=" + clientSecret +
"&code=" + code +
"&scope=" + scope +
"&grant_type=authorization_code";
HttpRequest post = HttpUtil.createPost(AUTHENTICATE_URL);
post.body(param, "application/x-www-form-urlencoded");
HttpResponse response = post.execute();
return JSONObject.parseObject(response.body(), OneDriveToken.class);
}
public OneDriveToken getRefreshToken() {
StorageConfig refreshStorageConfig =
storageConfigRepository.findByTypeAndKey(StorageTypeEnum.ONE_DRIVE, StorageConfigConstant.REFRESH_TOKEN_KEY);
String param = "client_id=" + clientId +
"&redirect_uri=" + redirectUri +
"&client_secret=" + clientSecret +
"&refresh_token=" + refreshStorageConfig.getValue() +
"&grant_type=refresh_token";
HttpRequest post = HttpUtil.createPost(AUTHENTICATE_URL);
post.body(param, "application/x-www-form-urlencoded");
HttpResponse response = post.execute();
return JSONObject.parseObject(response.body(), OneDriveToken.class);
}
public String getUserInfo() {
return oneDriveRestTemplate.getForObject(DRIVER_INFO_URL, String.class);
}
public List<FileItemDTO> list(String path) {
ResponseEntity<String> responseEntity = oneDriveRestTemplate.getForEntity("/".equalsIgnoreCase(path) ? DRIVER_ROOT_URL : DRIVER_ITEMS_URL, String.class, path);
String body = responseEntity.getBody();
JSONObject root = JSON.parseObject(body);
JSONArray fileList = root.getJSONArray("value");
List<FileItemDTO> result = new ArrayList<>();
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);
}
return result;
}
}

View File

@@ -0,0 +1,76 @@
package im.zhaojun.onedrive.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.dto.FileItemDTO;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.FileService;
import im.zhaojun.common.service.StorageConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
/**
* @author zhaojun
* @date 2020/1/12 13:53
*/
@Service
@Slf4j
public class OneDriveServiceImpl extends AbstractFileService implements FileService {
public static final String SYSTEM_ONEDRIVE_CACHE_PREFIX = "zfile-onedrive-cache:";
public static final String SYSTEM_ONEDRIVE_CACHE_ASSESS_TOKEN_KEY = "accessToken";
public static final String SYSTEM_ONEDRIVE_CACHE_REFRESH_TOKEN_KEY = "refreshToken";
@Resource
private GlobalScheduleTask globalScheduleTask;
@Resource
private StorageConfigService storageConfigService;
@Resource
private OneDriveService oneDriveService;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.ONE_DRIVE);
String accessToken = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_TOKEN_KEY).getValue();
String refreshToken = stringStorageConfigMap.get(StorageConfigConstant.REFRESH_TOKEN_KEY).getValue();
if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
isInitialized = false;
} else {
globalScheduleTask.refreshOneDriveToken();
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}
@Override
public List<FileItemDTO> fileList(String path) {
return oneDriveService.list(path);
}
@Override
public String getDownloadUrl(String path) {
return null;
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.ONE_DRIVE;
}
}

View File

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

View File

@@ -37,6 +37,7 @@ public class QiniuServiceImpl extends AbstractS3FileService implements FileServi
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
isInitialized = false;
} else {
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
@@ -47,7 +48,7 @@ public class QiniuServiceImpl extends AbstractS3FileService implements FileServi
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}

View File

@@ -36,6 +36,7 @@ public class TencentServiceImpl extends AbstractS3FileService implements FileSer
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
if (Objects.isNull(secretId) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
isInitialized = false;
} else {
BasicAWSCredentials credentials = new BasicAWSCredentials(secretId, secretKey);
@@ -46,7 +47,7 @@ public class TencentServiceImpl extends AbstractS3FileService implements FileSer
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}

View File

@@ -12,8 +12,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 +26,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
@@ -55,13 +53,14 @@ public class UpYunServiceImpl extends AbstractFileService implements FileService
basePath = ObjectUtil.defaultIfNull(basePath, "");
if (Objects.isNull(bucketName) || Objects.isNull(username) || Objects.isNull(password)) {
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
isInitialized = false;
} else {
upYun = new UpYun(bucketName, username, password);
isInitialized = testConnection();
}
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过");
}
}

View File

@@ -22,6 +22,26 @@
"type": "java.lang.String",
"defaultValue": "password.txt",
"description": "密码文件 文件名."
},
{
"name": "zfile.onedirve.clientId",
"type": "java.lang.String",
"description": "OneDrive ClientId."
},
{
"name": "zfile.onedirve.clientSecret",
"type": "java.lang.String",
"description": "OneDrive ClientSecret."
},
{
"name": "zfile.onedirve.redirectUri",
"type": "java.lang.String",
"description": "OneDrive 认证重定向地址."
},
{
"name": "zfile.onedirve.scope",
"type": "java.lang.String",
"description": "OneDrive 认证权限."
}
]
}

View File

@@ -1,9 +1,3 @@
logging:
level:
im:
zhaojun: info
path: ${user.home}/.zfile/logs
server:
port: 8080
servlet:
@@ -40,10 +34,12 @@ spring:
properties:
hibernate:
format_sql: false
show-sql: true
show-sql: false
resources:
chain:
gzipped: true
profiles:
active: prod
zfile:
cache:
timeout: 300
@@ -51,6 +47,11 @@ zfile:
header: header.md
footer: footer.md
password: password.txt
onedirve:
clientId: 09939809-c617-43c8-a220-a93c1513c5d4
clientSecret: _l:zI-_yrW75lV8M61K@z.I2K@B/On6Q
redirectUri: https://zfile.jun6.net/onedirve/callback
scope: offline_access User.Read Files.ReadWrite.All
jetcache:
statIntervalMinutes: 0
@@ -58,5 +59,4 @@ jetcache:
local:
default:
type: caffeine
keyConvertor: fastjson
defaultExpireInMillis: 3600000
keyConvertor: fastjson

View File

@@ -38,7 +38,7 @@ INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (26, 'secretKey',
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');
@@ -50,3 +50,5 @@ INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (38, 'base-path',
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (39, 'base-path', '基路径', 'aliyun');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (40, 'base-path', '基路径', 'huawei');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (41, 'ftp', '基路径', 'ftp');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (42, 'accessToken', '访问令牌', 'onedrive');
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`) VALUES (43, 'refreshToken', '刷新令牌', 'onedrive');

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1 +1 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title></title><link href=/css/adminIndex.67225c93.css rel=prefetch><link href=/css/install.39da241e.css rel=prefetch><link href=/js/adminIndex.57637329.js rel=prefetch><link href=/js/dplayer.acc587f7.js rel=prefetch><link href=/js/install.3ac7201f.js rel=prefetch><link href=/css/app.46f70347.css rel=preload as=style><link href=/css/chunk-vendors.b9a3d604.css rel=preload as=style><link href=/js/app.362c49b5.js rel=preload as=script><link href=/js/chunk-vendors.10f10391.js rel=preload as=script><link href=/css/chunk-vendors.b9a3d604.css rel=stylesheet><link href=/css/app.46f70347.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.10f10391.js></script><script src=/js/app.362c49b5.js></script></body></html>
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title></title><link href=/css/adminIndex.25eab1c6.css rel=prefetch><link href=/css/install.b4e8b552.css rel=prefetch><link href=/js/adminIndex.456ac23a.js rel=prefetch><link href=/js/dplayer.acc587f7.js rel=prefetch><link href=/js/install.f6845f54.js rel=prefetch><link href=/css/app.a731b1dd.css rel=preload as=style><link href=/css/chunk-vendors.39edcc5c.css rel=preload as=style><link href=/js/app.e0beab33.js rel=preload as=script><link href=/js/chunk-vendors.065da641.js rel=preload as=script><link href=/css/chunk-vendors.39edcc5c.css rel=stylesheet><link href=/css/app.a731b1dd.css rel=stylesheet></head><body><noscript><strong>We're sorry but zfile doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.065da641.js></script><script src=/js/app.e0beab33.js></script></body></html>

View File

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

View File

@@ -1 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["adminIndex"],{"7bf3":function(t,e,a){"use strict";var s=a("ed6d"),i=a.n(s);i.a},adf4:function(t,e,a){"use strict";a.r(e);var s=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:12}},[a("keep-alive",{attrs:{exclude:"CacheManager"}},[a("router-view")],1)],1)],1)},i=[],n={name:"Index",data:function(){return{active:"/admin/storage"}}},l=n,o=(a("7bf3"),a("2877")),r=Object(o["a"])(l,s,i,!1,null,"4fea46ab",null);e["default"]=r.exports},ed6d:function(t,e,a){}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["install"],{4904:function(e,t,a){"use strict";var r=a("dfa0"),o=a.n(r);o.a},dfa0:function(e,t,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("api/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("4904"),a("2877")),f=Object(u["a"])(m,r,o,!1,null,"621306e1",null);t["default"]=f.exports}}]);

View File

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["install"],{"0e86":function(t,e,r){},"63e8":function(t,e,r){"use strict";var a=r("0e86"),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",{ref:"form",attrs:{rules:t.rules,model:t.form,"label-width":"auto","status-icon":!0}},[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:""}},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.$refs[t].validate((function(t){if(!t)return!1;var r=e;e.$http.post("install",s.a.stringify(e.form)).then((function(t){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,f=(r("63e8"),r("2877")),u=Object(f["a"])(m,a,o,!1,null,"7e472c9a",null);e["default"]=u.exports}}]);

View 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>

View 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>