mirror of
https://github.com/zfile-dev/zfile.git
synced 2025-04-19 05:34:52 +00:00
Compare commits
111 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ec8a5df1f | ||
|
|
47b5f6ac12 | ||
|
|
c64c8465f2 | ||
|
|
f636681dd8 | ||
|
|
eadd2434e0 | ||
|
|
b84c0bff42 | ||
|
|
4cb5b84bfe | ||
|
|
0351b4401c | ||
|
|
48cb14be8a | ||
|
|
eea2ff11f9 | ||
|
|
325ec1a348 | ||
|
|
93205266d3 | ||
|
|
6849a4347f | ||
|
|
cb5c6a5945 | ||
|
|
5ed45c3bb3 | ||
|
|
4442ec3165 | ||
|
|
b268a24333 | ||
|
|
84f9354d4e | ||
|
|
8dfc4f8004 | ||
|
|
1158f5c2b9 | ||
|
|
c5f0e15207 | ||
|
|
68a842ce75 | ||
|
|
3bd4f74dae | ||
|
|
4a0bdc3baf | ||
|
|
603c1b4654 | ||
|
|
1136d582df | ||
|
|
187544fc06 | ||
|
|
aa3cde8f59 | ||
|
|
e29a702b6e | ||
|
|
e4f663c9f0 | ||
|
|
fb08ef6e78 | ||
|
|
10c465d159 | ||
|
|
d15d1203b7 | ||
|
|
de48ed3b61 | ||
|
|
d22e2e872a | ||
|
|
7da1b454dc | ||
|
|
463f311dd3 | ||
|
|
73b42cf654 | ||
|
|
1fac59c4cd | ||
|
|
6ed6b4a019 | ||
|
|
7bd02437f0 | ||
|
|
57aeb5771c | ||
|
|
695c03a530 | ||
|
|
6ebc403572 | ||
|
|
7ff6fe43b5 | ||
|
|
b34f141181 | ||
|
|
87dd7b58d1 | ||
|
|
2a949db5d2 | ||
|
|
b889e91fb5 | ||
|
|
b5c757f9f0 | ||
|
|
a465f48b94 | ||
|
|
797a0a0e4c | ||
|
|
e7ff159b6d | ||
|
|
a9fbf54bb2 | ||
|
|
81f9e262f5 | ||
|
|
23bb3960ab | ||
|
|
c4a17985a4 | ||
|
|
75ddcd47f4 | ||
|
|
2dd03ae490 | ||
|
|
5b383c8741 | ||
|
|
73198d7852 | ||
|
|
fb0d9721aa | ||
|
|
b24c663fd6 | ||
|
|
6e62cfc84d | ||
|
|
eee22e9dc9 | ||
|
|
5109c51ffc | ||
|
|
66d6d311ea | ||
|
|
ef7cbdcbd7 | ||
|
|
63bcbebb4b | ||
|
|
50ce1bb6db | ||
|
|
3c88659679 | ||
|
|
d79a993eea | ||
|
|
afafb311b8 | ||
|
|
2e280e4931 | ||
|
|
ed6efac8b7 | ||
|
|
7409df85d7 | ||
|
|
4d42529c4d | ||
|
|
65224685c8 | ||
|
|
080a84986e | ||
|
|
3f8beb2f0b | ||
|
|
410a87c426 | ||
|
|
c14b8343d2 | ||
|
|
bea440f6c3 | ||
|
|
6a5fe15121 | ||
|
|
e920ab0ec0 | ||
|
|
537e3e0563 | ||
|
|
e208dc7c4c | ||
|
|
ed64910a53 | ||
|
|
5b075c3505 | ||
|
|
a8e6d9af6a | ||
|
|
2b21d8a73c | ||
|
|
e30289d21b | ||
|
|
3b6e2be7fe | ||
|
|
43c12aa8a7 | ||
|
|
c03a7710c0 | ||
|
|
1833b23d84 | ||
|
|
f3e393972d | ||
|
|
4f46c13369 | ||
|
|
f181959218 | ||
|
|
11effc0ae7 | ||
|
|
c8397e17bf | ||
|
|
ed32b9f1d4 | ||
|
|
4e184936db | ||
|
|
fe6aebfdee | ||
|
|
d65e1a442d | ||
|
|
34647793c8 | ||
|
|
e8c249b9ea | ||
|
|
d1e613dc10 | ||
|
|
1adcfee96f | ||
|
|
75f5de6b9a | ||
|
|
499942ef70 |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -16,7 +16,7 @@ assignees: ''
|
||||
请确认你已经做了下面这些事情,若 bug 还是未解决,那么请尽可详细地描述你的问题。
|
||||
|
||||
- 我已经安装了最新版的 ZFile
|
||||
- 我已经阅读了 ZFile 的文档:http://docs.zhaojun.im/zfile
|
||||
- 我已经阅读了 ZFile 的文档:https://docs.zfile.vip
|
||||
- 我已经搜索了已有的 Issues 列表中有关的信息
|
||||
- 我已经清理过浏览器缓存并重试
|
||||
-->
|
||||
|
||||
125
README.md
125
README.md
@@ -1,32 +1,49 @@
|
||||
# Z-File
|
||||
<p align = "center">
|
||||
<img alt="ZFile" src="https://cdn.jun6.net/2021/04/21/69a89344e2a84.png" height="150px">
|
||||
<br><br>
|
||||
基于 Java 的在线网盘程序,支持对接 S3、OneDrive、SharePoint、又拍云、本地存储、FTP 等存储源,支持在线浏览图片、播放音视频,文本文件等文件类型。
|
||||
<br><br>
|
||||
<img src="https://img.shields.io/badge/license-MIT-blue.svg?longCache=true&style=flat-square">
|
||||
<img src="https://api.codacy.com/project/badge/Grade/70b793267f7941d58cbd93f50c9a8e0a">
|
||||
<img src="https://img.shields.io/github/last-commit/zhaojun1998/zfile.svg?style=flat-square">
|
||||
<img src="https://img.shields.io/github/downloads/zhaojun1998/zfile/total?style=flat-square">
|
||||
<img src="https://img.shields.io/github/v/release/zhaojun1998/zfile?style=flat-square">
|
||||
<img src="https://img.shields.io/github/commit-activity/y/zhaojun1998/zfile?style=flat-square">
|
||||
<br>
|
||||
<img src="https://img.shields.io/github/issues/zhaojun1998/zfile?style=flat-square">
|
||||
<img src="https://img.shields.io/github/issues-closed-raw/zhaojun1998/zfile?style=flat-square">
|
||||
<img src="https://img.shields.io/github/forks/zhaojun1998/zfile?style=flat-square">
|
||||
<img src="https://img.shields.io/github/stars/zhaojun1998/zfile?style=flat-square">
|
||||
<img src="https://img.shields.io/github/watchers/zhaojun1998/zfile?style=flat-square">
|
||||
</p>
|
||||
|
||||

|
||||
[](https://www.codacy.com/manual/zhaojun1998/zfile?utm_source=github.com&utm_medium=referral&utm_content=zhaojun1998/zfile&utm_campaign=Badge_Grade)
|
||||

|
||||

|
||||
## 相关地址
|
||||
|
||||
此项目是一个在线文件目录的程序, 支持各种对象存储和本地存储, 使用定位是个人放常用工具下载, 或做公共的文件库. 不会向多账户方向开发.
|
||||
预览地址: [https://zfile.vip](https://zfile.vip)
|
||||
|
||||
前端基于 [h5ai](https://larsjung.de/h5ai/) 的原有功能使用 Vue 重新开发了一遍. 后端采用 SpringBoot, 数据库采用内嵌数据库.
|
||||
文档地址: [https://docs.zfile.vip](https://docs.zfile.vip)
|
||||
|
||||
预览地址: [https://zfile.jun6.net](https://zfile.jun6.net)
|
||||
社区地址: [https://bbs.zfile.vip](https://bbs.zfile.vip)
|
||||
|
||||
文档地址: [http://docs.zhaojun.im/zfile](http://docs.zhaojun.im/zfile)
|
||||
项目源码: [https://github.com/zhaojun1998/zfile](https://github.com/zhaojun1998/zfile)
|
||||
|
||||
前端源码: [https://github.com/zhaojun1998/zfile-vue](https://github.com/zhaojun1998/zfile-vue)
|
||||
|
||||
## 系统特色
|
||||
|
||||
* 内存缓存 (免安装)
|
||||
* 内存数据库 (免安装)
|
||||
* 个性化配置
|
||||
* 自定义目录的 readme 说明文件
|
||||
* 自定义 JS, CSS
|
||||
* 文件夹密码
|
||||
* 目录 README 说明
|
||||
* 文件直链(短链,永久直链,二维码)
|
||||
* 支持在线浏览文本文件, 视频, 图片, 音乐. (支持 FLV 和 HLS)
|
||||
* 文件/目录二维码
|
||||
* 图片模式
|
||||
* Docker 支持
|
||||
* 隐藏指定文件夹(通配符支持)
|
||||
* 自定义 JS, CSS
|
||||
* 自定义目录 README 说明文件和密码文件名称
|
||||
* 同时挂载多个存储策略
|
||||
* 缓存动态开启, ~~缓存自动刷新 (v2.2 及以前版本支持)~~
|
||||
* ~~全局搜索 (v2.2 及以前版本支持)~~
|
||||
* 同时挂载多个存储策略
|
||||
* 支持 阿里云 OSS, FTP, 华为云 OBS, 本地存储, MINIO, OneDrive 国际/家庭/个人版, OneDrive 世纪互联版, 七牛云 KODO, 腾讯云 COS, 又拍云 USS.
|
||||
* 支持 S3 协议, 阿里云 OSS, FTP, 华为云 OBS, 本地存储, MINIO, OneDrive 国际/家庭/个人版/世纪互联版/SharePoint, , 七牛云 KODO, 腾讯云 COS, 又拍云 USS.
|
||||
|
||||
## 快速开始
|
||||
|
||||
@@ -54,14 +71,14 @@ apt update && apt install -y adoptopenjdk-8-hotspot-jre
|
||||
|
||||
> 如为更新程序, 则请先执行 `~/zfile/bin/stop.sh && rm -rf ~/zfile` 清理旧程序. 首次安装请忽略此选项.
|
||||
|
||||
|
||||
下载项目:
|
||||
|
||||
```bash
|
||||
cd ~
|
||||
export ZFILE_INSTALL_PATH=~/zfile
|
||||
mkdir -p $ZFILE_INSTALL_PATH && cd $ZFILE_INSTALL_PATH
|
||||
wget https://c.jun6.net/ZFILE/zfile-release.war
|
||||
mkdir zfile && unzip zfile-release.war -d zfile && rm -rf zfile-release.war
|
||||
chmod +x zfile/bin/*.sh
|
||||
unzip zfile-release.war && rm -rf zfile-release.war
|
||||
chmod +x $ZFILE_INSTALL_PATH/bin/*.sh
|
||||
```
|
||||
|
||||
> 下载指定版本可以将 `zfile-release.war` 改为 `zfile-x.x.war`,如 `zfile-2.2.war`。
|
||||
@@ -83,62 +100,38 @@ chmod +x zfile/bin/*.sh
|
||||
~/zfile/bin/start.sh
|
||||
```
|
||||
|
||||
篇幅有限, 更详细的安装教程及介绍请参考: [ZFile 文档](http://zhaojun.im/zfile-install)
|
||||
篇幅有限, 更详细的安装教程及介绍请参考: [ZFile 文档](https://docs.zfile.vip)
|
||||
|
||||
访问地址:
|
||||
|
||||
用户前台: http://127.0.0.1:8080/#/main
|
||||
用户前台: http://127.0.0.1:8080/main
|
||||
|
||||
初始安装: http://127.0.0.1:8080/#/install
|
||||
初始安装: http://127.0.0.1:8080/install
|
||||
|
||||
管理后台: http://127.0.0.1:8080/#/admin
|
||||
管理后台: http://127.0.0.1:8080/admin
|
||||
|
||||
|
||||
## 预览
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## 常见问题
|
||||
|
||||
|
||||
### 默认路径
|
||||
|
||||
默认 H2 数据库文件地址: `~/.zfile/db/`, `~` 表示用户目录
|
||||
|
||||
windows 为 `C:/Users/用户名/`
|
||||
|
||||
linux 为 `/home/用户名/`, root 用户为 `/root/`
|
||||
|
||||
> 2.3 及以后版本路径为 `~/.zfile-new/db/`
|
||||
|
||||
### 文档文件和加密文件
|
||||
|
||||
- 目录文档显示文件名为 `readme.md`
|
||||
- 目录需要密码访问, 添加文件 `password.txt` (无法拦截此文件被下载, 但可以改名文件)
|
||||
|
||||
## 开发计划
|
||||
|
||||
- [x] API 支持 [点击查看文档](https://github.com/zhaojun1998/zfile/blob/master/API.md)
|
||||
- [x] 更方便的部署方式
|
||||
- [x] 布局优化 - 自定义操作按钮 (现为右键实现)
|
||||
- [x] 后台优化 - 设置按照其功能进行分离
|
||||
- [x] 体验优化 - 支持前后端分离部署
|
||||
- [x] 体验优化 - 文本预览更换 vscode 同款编辑器 monaco editor
|
||||
- [x] 新功能 - Docker 支持
|
||||
- [x] 架构调整 - 支持多存储策略
|
||||
- [ ] 新功能 - 后台支持上传、编辑、删除等操作
|
||||
- [ ] 新功能 - WebDav 支持
|
||||
- [ ] 新功能 - 离线下载 (aria2)
|
||||
- [ ] 体验优化 - 忽略文件列表 (正则表达式)
|
||||
- [ ] 体验优化 - 自定义支持预览的文件后缀 (正则表达式)
|
||||
- [ ] 体验优化 - 一键安装脚本
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## 支持作者
|
||||
|
||||
如果本项目对你有帮助,请作者喝杯咖啡吧。
|
||||
|
||||
<img src="http://cdn.jun6.net/alipay.png" width="200" height="312">
|
||||
<img src="http://cdn.jun6.net/wechat.png" width="222" height="300">
|
||||
<img src="https://cdn.jun6.net/2021/03/27/152704e91f13d.png" width="400" alt="赞助我">
|
||||
|
||||
## Stargazers over time
|
||||
|
||||
[](https://starchart.cc/zhaojun1998/zfile.svg)
|
||||
|
||||
## 开发工具赞助
|
||||
|
||||
<a href="https://www.jetbrains.com/?from=zfile"><img src="https://cdn.jun6.net/2021/04/21/26e410d60b0b0.png?1=1" width="100px"></a>
|
||||
|
||||
24
pom.xml
24
pom.xml
@@ -6,19 +6,20 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.6.RELEASE</version>
|
||||
<version>2.5.4</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<groupId>im.zhaojun</groupId>
|
||||
<artifactId>zfile</artifactId>
|
||||
<version>2.8</version>
|
||||
<version>3.2.1</version>
|
||||
<name>zfile</name>
|
||||
<packaging>war</packaging>
|
||||
<description>一个在线的文件浏览系统</description>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<log4j2.version>2.17.1</log4j2.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -32,10 +33,6 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
@@ -60,11 +57,13 @@
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<version>1.4.197</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<version>8.0.27</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类 -->
|
||||
@@ -88,7 +87,6 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
@@ -112,7 +110,15 @@
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.61</version>
|
||||
<version>1.2.69</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.26.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
@@ -145,4 +151,4 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
@@ -3,6 +3,7 @@ package im.zhaojun.zfile;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
/**
|
||||
@@ -10,6 +11,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
|
||||
*/
|
||||
@EnableAsync
|
||||
@SpringBootApplication
|
||||
@EnableJpaRepositories("im.zhaojun.zfile.repository")
|
||||
@EnableAspectJAutoProxy(exposeProxy = true)
|
||||
public class ZfileApplication {
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -50,7 +51,7 @@ public class FileListCacheAspect {
|
||||
if (enableCache) {
|
||||
List<FileItemDTO> cacheFileList = zFileCache.get(driveId, path);
|
||||
if (cacheFileList == null) {
|
||||
result = (List<FileItemDTO>) point.proceed();
|
||||
result = Collections.unmodifiableList((List<FileItemDTO>) point.proceed());
|
||||
zFileCache.put(driveId, path, result);
|
||||
} else {
|
||||
result = cacheFileList;
|
||||
|
||||
@@ -11,7 +11,7 @@ import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
@@ -31,7 +31,7 @@ public class OneDriveConfig {
|
||||
|
||||
ClientHttpRequestInterceptor interceptor = (httpRequest, bytes, clientHttpRequestExecution) -> {
|
||||
HttpHeaders headers = httpRequest.getHeaders();
|
||||
Integer driveId = Integer.valueOf(((LinkedList)headers.get("driveId")).get(0).toString());
|
||||
Integer driveId = Integer.valueOf(((List)headers.get("driveId")).get(0).toString());
|
||||
|
||||
StorageConfig accessTokenConfig =
|
||||
storageConfigService.findByDriveIdAndKey(driveId, StorageConfigConstant.ACCESS_TOKEN_KEY);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
package im.zhaojun.zfile.config;
|
||||
@@ -2,12 +2,19 @@ package im.zhaojun.zfile.config;
|
||||
|
||||
import im.zhaojun.zfile.model.enums.StorageTypeEnumDeSerializerConvert;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.ConfigurableWebServerFactory;
|
||||
import org.springframework.boot.web.server.ErrorPage;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@@ -31,4 +38,16 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
||||
return webServerFactory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
|
||||
return factory -> {
|
||||
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/index.html");
|
||||
ErrorPage error200Page = new ErrorPage(HttpStatus.OK, "/index.html");
|
||||
Set<ErrorPage> errorPages = new HashSet<>();
|
||||
errorPages.add(error404Page);
|
||||
errorPages.add(error200Page);
|
||||
factory.setErrorPages(errorPages);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
package im.zhaojun.zfile.config;
|
||||
|
||||
import im.zhaojun.zfile.filter.CorsFilter;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@@ -18,7 +23,10 @@ public class ZFileConfiguration {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate(){
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
|
||||
HttpClient httpClient = HttpClientBuilder.create().build();
|
||||
httpRequestFactory.setHttpClient(httpClient);
|
||||
RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
|
||||
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
|
||||
|
||||
restTemplate.setInterceptors(Collections.singletonList((request, body, execution) -> {
|
||||
|
||||
@@ -128,4 +128,20 @@ public class DriveContext implements ApplicationContextAware {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新上下文环境中的驱动器 ID
|
||||
*
|
||||
* @param updateId
|
||||
* 驱动器原 ID
|
||||
*
|
||||
* @param newId
|
||||
* 驱动器新 ID
|
||||
*/
|
||||
public void updateDriveId(Integer updateId, Integer newId) {
|
||||
AbstractBaseFileService fileService = drivesServiceMap.remove(updateId);
|
||||
fileService.setDriveId(newId);
|
||||
drivesServiceMap.put(newId, fileService);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package im.zhaojun.zfile.controller.admin;
|
||||
|
||||
|
||||
import im.zhaojun.zfile.model.support.ResultBean;
|
||||
import im.zhaojun.zfile.service.SystemConfigService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Controller
|
||||
public class DebugController {
|
||||
|
||||
@Value("${zfile.debug}")
|
||||
private Boolean debug;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@ResponseBody
|
||||
@GetMapping("/debug/resetPwd")
|
||||
public ResultBean resetPwd() {
|
||||
if (debug) {
|
||||
systemConfigService.updateUsernameAndPwd("admin", "123456");
|
||||
return ResultBean.success();
|
||||
} else {
|
||||
return ResultBean.error("未开启 DEBUG 模式,不允许进行此操作。");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,6 +32,7 @@ public class DriveController {
|
||||
@Resource
|
||||
private FilterConfigService filterConfigService;
|
||||
|
||||
|
||||
/**
|
||||
* 获取所有驱动器列表
|
||||
*
|
||||
@@ -47,14 +48,14 @@ public class DriveController {
|
||||
/**
|
||||
* 获取指定驱动器基本信息及其参数
|
||||
*
|
||||
* @param id
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
*
|
||||
* @return 驱动器基本信息
|
||||
*/
|
||||
@GetMapping("/drive/{id}")
|
||||
public ResultBean driveItem(@PathVariable Integer id) {
|
||||
DriveConfigDTO driveConfig = driveConfigService.findDriveConfigDTOById(id);
|
||||
@GetMapping("/drive/{driveId}")
|
||||
public ResultBean driveItem(@PathVariable Integer driveId) {
|
||||
DriveConfigDTO driveConfig = driveConfigService.findDriveConfigDTOById(driveId);
|
||||
return ResultBean.success(driveConfig);
|
||||
}
|
||||
|
||||
@@ -72,12 +73,12 @@ public class DriveController {
|
||||
/**
|
||||
* 删除驱动器设置
|
||||
*
|
||||
* @param id
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
*/
|
||||
@DeleteMapping("/drive/{id}")
|
||||
public ResultBean deleteDriveItem(@PathVariable Integer id) {
|
||||
driveConfigService.deleteById(id);
|
||||
@DeleteMapping("/drive/{driveId}")
|
||||
public ResultBean deleteDriveItem(@PathVariable Integer driveId) {
|
||||
driveConfigService.deleteById(driveId);
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
@@ -85,14 +86,14 @@ public class DriveController {
|
||||
/**
|
||||
* 启用驱动器
|
||||
*
|
||||
* @param id
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
*/
|
||||
@PostMapping("/drive/{id}/enable")
|
||||
public ResultBean enable(@PathVariable("id") Integer id) {
|
||||
DriveConfig driveConfig = driveConfigService.findById(id);
|
||||
@PostMapping("/drive/{driveId}/enable")
|
||||
public ResultBean enable(@PathVariable Integer driveId) {
|
||||
DriveConfig driveConfig = driveConfigService.findById(driveId);
|
||||
driveConfig.setEnable(true);
|
||||
driveConfigService.saveOrUpdate(driveConfig);
|
||||
driveConfigService.updateDriveConfig(driveConfig);
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
@@ -100,33 +101,73 @@ public class DriveController {
|
||||
/**
|
||||
* 停止驱动器
|
||||
*
|
||||
* @param id
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
*/
|
||||
@PostMapping("/drive/{id}/disable")
|
||||
public ResultBean disable(@PathVariable("id") Integer id) {
|
||||
DriveConfig driveConfig = driveConfigService.findById(id);
|
||||
@PostMapping("/drive/{driveId}/disable")
|
||||
public ResultBean disable(@PathVariable Integer driveId) {
|
||||
DriveConfig driveConfig = driveConfigService.findById(driveId);
|
||||
driveConfig.setEnable(false);
|
||||
driveConfigService.saveOrUpdate(driveConfig);
|
||||
driveConfigService.updateDriveConfig(driveConfig);
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/drive/{id}/filters")
|
||||
public ResultBean getFilters(@PathVariable("id") Integer id) {
|
||||
return ResultBean.success(filterConfigService.findByDriveId(id));
|
||||
/**
|
||||
* 根据驱动器 ID 获取过滤文件列表
|
||||
*
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
*/
|
||||
@GetMapping("/drive/{driveId}/filters")
|
||||
public ResultBean getFilters(@PathVariable Integer driveId) {
|
||||
return ResultBean.success(filterConfigService.findByDriveId(driveId));
|
||||
}
|
||||
|
||||
@PostMapping("/drive/{id}/filters")
|
||||
public ResultBean saveFilters(@RequestBody List<FilterConfig> filter, @PathVariable("id") Integer driveId) {
|
||||
|
||||
/**
|
||||
* 停止驱动器
|
||||
*
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
*/
|
||||
@PostMapping("/drive/{driveId}/filters")
|
||||
public ResultBean saveFilters(@RequestBody List<FilterConfig> filter, @PathVariable Integer driveId) {
|
||||
filterConfigService.batchSave(filter, driveId);
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存拖拽排序信息
|
||||
*
|
||||
* @param driveConfigs
|
||||
* 拖拽排序信息
|
||||
*/
|
||||
@PostMapping("/drive/drag")
|
||||
public ResultBean saveDriveDrag(@RequestBody List<JSONObject> driveConfigs) {
|
||||
driveConfigService.saveDriveDrag(driveConfigs);
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新驱动器 ID
|
||||
*
|
||||
* @param updateId
|
||||
* 驱动器原 ID
|
||||
*
|
||||
* @param newId
|
||||
* 驱动器新 ID
|
||||
*/
|
||||
@PostMapping("/drive/updateId")
|
||||
public ResultBean updateDriveId(Integer updateId, Integer newId) {
|
||||
DriveConfig driveConfig = driveConfigService.findById(newId);
|
||||
if (driveConfig != null) {
|
||||
return ResultBean.error("已存在的 ID,请更换 ID 后重试。");
|
||||
}
|
||||
driveConfigService.updateId(updateId, newId);
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
|
||||
@@ -26,14 +25,14 @@ public class LogController {
|
||||
* 系统日志下载
|
||||
*/
|
||||
@GetMapping("/log")
|
||||
public ResponseEntity<Object> downloadLog(HttpServletResponse response) {
|
||||
public ResponseEntity<Object> downloadLog() {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("下载诊断日志");
|
||||
}
|
||||
String userHome = System.getProperty("user.home");
|
||||
File fileZip = ZipUtil.zip(userHome + "/.zfile/logs");
|
||||
String currentDate = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
|
||||
return FileUtil.export(fileZip, "ZFile 诊断日志 - " + currentDate + ".zip");
|
||||
return FileUtil.exportSingleThread(fileZip, "ZFile 诊断日志 - " + currentDate + ".zip");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package im.zhaojun.zfile.controller.admin;
|
||||
@@ -1,26 +0,0 @@
|
||||
package im.zhaojun.zfile.controller.admin;
|
||||
|
||||
import im.zhaojun.zfile.model.support.ResultBean;
|
||||
import im.zhaojun.zfile.model.support.SystemMonitorInfo;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 系统监控 Controller
|
||||
* @author zhaojun
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin")
|
||||
public class MonitorController {
|
||||
|
||||
|
||||
/**
|
||||
* 获取系统监控信息
|
||||
*/
|
||||
@GetMapping("monitor")
|
||||
public ResultBean monitor() {
|
||||
return ResultBean.success(new SystemMonitorInfo());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package im.zhaojun.zfile.controller.admin;
|
||||
|
||||
import im.zhaojun.zfile.model.support.ResultBean;
|
||||
import im.zhaojun.zfile.service.ShortLinkConfigService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 直链管理 Controller
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/admin")
|
||||
public class ShortLinkManagerController {
|
||||
|
||||
@Resource
|
||||
private ShortLinkConfigService shortLinkConfigService;
|
||||
|
||||
@GetMapping("/link/list")
|
||||
@ResponseBody
|
||||
public ResultBean list(String key,
|
||||
String url,
|
||||
String dateFrom,
|
||||
String dateTo,
|
||||
Integer page,
|
||||
Integer limit,
|
||||
@RequestParam(required = false, defaultValue = "createDate") String orderBy,
|
||||
@RequestParam(required = false, defaultValue = "desc") String orderDirection) {
|
||||
return ResultBean.success(shortLinkConfigService.find(key, url, dateFrom, dateTo, page, limit, orderBy, orderDirection));
|
||||
}
|
||||
|
||||
@GetMapping("/link/delete/{id}")
|
||||
@ResponseBody
|
||||
public ResultBean deleteById(@PathVariable Integer id) {
|
||||
shortLinkConfigService.deleteById(id);
|
||||
return ResultBean.success();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,17 @@
|
||||
package im.zhaojun.zfile.controller.home;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import im.zhaojun.zfile.context.DriveContext;
|
||||
import im.zhaojun.zfile.exception.NotEnabledDriveException;
|
||||
import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import im.zhaojun.zfile.model.dto.FileItemDTO;
|
||||
import im.zhaojun.zfile.model.entity.DriveConfig;
|
||||
import im.zhaojun.zfile.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.zfile.service.DriveConfigService;
|
||||
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
|
||||
import im.zhaojun.zfile.util.HttpUtil;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -14,9 +20,13 @@ import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 直链 Controller
|
||||
* @author Zhao Jun
|
||||
*/
|
||||
@Controller
|
||||
@@ -25,6 +35,9 @@ public class DirectLinkController {
|
||||
@Resource
|
||||
private DriveContext driveContext;
|
||||
|
||||
@Resource
|
||||
private DriveConfigService driveConfigService;
|
||||
|
||||
/**
|
||||
* 获取指定驱动器, 某个文件的直链, 然后重定向过去.
|
||||
* @param driveId
|
||||
@@ -32,8 +45,16 @@ public class DirectLinkController {
|
||||
*
|
||||
* @return 重定向至文件直链
|
||||
*/
|
||||
@GetMapping("/directlink/{driveId}/**")
|
||||
public String directlink(@PathVariable("driveId") Integer driveId, final HttpServletRequest request) {
|
||||
@GetMapping("/${zfile.directLinkPrefix}/{driveId}/**")
|
||||
public String directlink(@PathVariable("driveId") Integer driveId,
|
||||
final HttpServletRequest request,
|
||||
final HttpServletResponse response) throws IOException {
|
||||
DriveConfig driveConfig = driveConfigService.findById(driveId);
|
||||
Boolean enable = driveConfig.getEnable();
|
||||
if (!enable) {
|
||||
throw new NotEnabledDriveException();
|
||||
}
|
||||
|
||||
String path = (String) request.getAttribute(
|
||||
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
|
||||
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
|
||||
@@ -49,6 +70,16 @@ public class DirectLinkController {
|
||||
|
||||
String url = fileItem.getUrl();
|
||||
|
||||
if (StrUtil.equalsIgnoreCase(FileUtil.extName(fileItem.getName()), "m3u8")) {
|
||||
String textContent = HttpUtil.getTextContent(url);
|
||||
response.setContentType("application/vnd.apple.mpegurl;charset=utf-8");
|
||||
PrintWriter out = response.getWriter();
|
||||
out.write(textContent);
|
||||
out.flush();
|
||||
out.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
int queryIndex = url.indexOf('?');
|
||||
|
||||
if (queryIndex != -1) {
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
package im.zhaojun.zfile.controller.home;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import im.zhaojun.zfile.context.DriveContext;
|
||||
import im.zhaojun.zfile.exception.NotExistFileException;
|
||||
import im.zhaojun.zfile.exception.NotEnabledDriveException;
|
||||
import im.zhaojun.zfile.exception.PasswordVerifyException;
|
||||
import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import im.zhaojun.zfile.model.dto.DriveListDTO;
|
||||
import im.zhaojun.zfile.model.dto.FileItemDTO;
|
||||
import im.zhaojun.zfile.model.dto.FileListDTO;
|
||||
import im.zhaojun.zfile.model.dto.SystemFrontConfigDTO;
|
||||
import im.zhaojun.zfile.model.entity.DriveConfig;
|
||||
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.zfile.model.support.FilePageModel;
|
||||
import im.zhaojun.zfile.model.support.ResultBean;
|
||||
import im.zhaojun.zfile.model.support.VerifyResult;
|
||||
import im.zhaojun.zfile.service.DriveConfigService;
|
||||
import im.zhaojun.zfile.service.FilterConfigService;
|
||||
import im.zhaojun.zfile.service.SystemConfigService;
|
||||
@@ -18,6 +22,7 @@ import im.zhaojun.zfile.util.FileComparator;
|
||||
import im.zhaojun.zfile.util.HttpUtil;
|
||||
import im.zhaojun.zfile.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -27,10 +32,9 @@ import org.springframework.web.client.HttpClientErrorException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 前台文件管理
|
||||
@@ -41,6 +45,9 @@ import java.util.Objects;
|
||||
@RestController
|
||||
public class FileController {
|
||||
|
||||
@Value("${zfile.debug}")
|
||||
private Boolean debug;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@@ -53,11 +60,6 @@ public class FileController {
|
||||
@Resource
|
||||
private FilterConfigService filterConfigService;
|
||||
|
||||
/**
|
||||
* 滚动加载每页条数.
|
||||
*/
|
||||
private static final Integer PAGE_SIZE = 30;
|
||||
|
||||
|
||||
/**
|
||||
* 获取所有已启用的驱动器
|
||||
@@ -66,11 +68,14 @@ public class FileController {
|
||||
*/
|
||||
@GetMapping("/drive/list")
|
||||
public ResultBean drives() {
|
||||
return ResultBean.success(driveConfigService.listOnlyEnable());
|
||||
List<DriveConfig> driveList = driveConfigService.listOnlyEnable();
|
||||
boolean isInstall = systemConfigService.getIsInstall();
|
||||
DriveListDTO driveListDTO = new DriveListDTO(driveList, isInstall);
|
||||
return ResultBean.success(driveListDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个驱动器下, 指定路径的数据, 每页固定 {@link #PAGE_SIZE} 条数据.
|
||||
* 获取某个驱动器下, 指定路径的数据
|
||||
*
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
@@ -81,19 +86,75 @@ public class FileController {
|
||||
* @param password
|
||||
* 文件夹密码, 某些文件夹需要密码才能访问, 当不需要密码时, 此参数可以为空
|
||||
*
|
||||
* @param page
|
||||
* 页数
|
||||
*
|
||||
* @return 当前路径下所有文件及文件夹
|
||||
*/
|
||||
@GetMapping("/list/{driveId}")
|
||||
public ResultBean list(@PathVariable(name = "driveId") Integer driveId,
|
||||
@RequestParam(defaultValue = "/") String path,
|
||||
@RequestParam(required = false) String password,
|
||||
@RequestParam(defaultValue = "1") Integer page) throws Exception {
|
||||
@RequestParam(required = false) String orderBy,
|
||||
@RequestParam(required = false, defaultValue = "asc") String orderDirection) throws Exception {
|
||||
AbstractBaseFileService fileService = driveContext.get(driveId);
|
||||
List<FileItemDTO> fileItemList = fileService.fileList(StringUtils.removeDuplicateSeparator(ZFileConstant.PATH_SEPARATOR + path + ZFileConstant.PATH_SEPARATOR));
|
||||
|
||||
// 创建副本, 防止排序和过滤对原数据产生影响
|
||||
List<FileItemDTO> copyList = new ArrayList<>(fileItemList);
|
||||
|
||||
// 校验密码, 如果校验不通过, 则返回错误消息
|
||||
VerifyResult verifyResult = verifyPassword(copyList, driveId, path, password);
|
||||
if (!verifyResult.isPassed()) {
|
||||
return ResultBean.error(verifyResult.getMsg(), verifyResult.getCode());
|
||||
}
|
||||
|
||||
// 过滤掉驱动器配置的表达式中要隐藏的数据
|
||||
filterFileList(copyList, driveId);
|
||||
|
||||
// 按照自然排序
|
||||
copyList.sort(new FileComparator(orderBy, orderDirection));
|
||||
|
||||
|
||||
|
||||
// 开始获取参数信息
|
||||
SystemFrontConfigDTO systemConfig = systemConfigService.getSystemFrontConfig(driveId);
|
||||
DriveConfig driveConfig = driveConfigService.findById(driveId);
|
||||
Boolean enable = driveConfig.getEnable();
|
||||
if (!enable) {
|
||||
throw new NotEnabledDriveException();
|
||||
}
|
||||
|
||||
systemConfig.setDebugMode(debug);
|
||||
systemConfig.setDefaultSwitchToImgMode(driveConfig.getDefaultSwitchToImgMode());
|
||||
systemConfig.setDirectLinkPrefix(ZFileConstant.DIRECT_LINK_PREFIX);
|
||||
|
||||
// 如果不是 FTP 模式,则尝试获取当前文件夹中的 README 文件,有则读取,没有则停止
|
||||
if (!Objects.equals(driveConfig.getType(), StorageTypeEnum.FTP)) {
|
||||
fileItemList.stream()
|
||||
.filter(fileItemDTO -> Objects.equals(ZFileConstant.README_FILE_NAME, fileItemDTO.getName()))
|
||||
.findFirst()
|
||||
.ifPresent(fileItemDTO -> {
|
||||
String readme = HttpUtil.getTextContent(fileItemDTO.getUrl());
|
||||
systemConfig.setReadme(readme);
|
||||
});
|
||||
}
|
||||
|
||||
return ResultBean.successData(new FileListDTO(copyList, systemConfig));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验密码
|
||||
* @param fileItemList
|
||||
* 文件列表
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
* @param path
|
||||
* 请求路径
|
||||
* @param inputPassword
|
||||
* 用户输入的密码
|
||||
* @return 是否校验通过
|
||||
*/
|
||||
private VerifyResult verifyPassword(List<FileItemDTO> fileItemList, Integer driveId, String path, String inputPassword) {
|
||||
AbstractBaseFileService fileService = driveContext.get(driveId);
|
||||
List<FileItemDTO> fileItemList =
|
||||
fileService.fileList(StringUtils.removeDuplicateSeparator(ZFileConstant.PATH_SEPARATOR + path + ZFileConstant.PATH_SEPARATOR));
|
||||
|
||||
for (FileItemDTO fileItemDTO : fileItemList) {
|
||||
if (ZFileConstant.PASSWORD_FILE_NAME.equals(fileItemDTO.getName())) {
|
||||
@@ -101,143 +162,82 @@ public class FileController {
|
||||
try {
|
||||
expectedPasswordContent = HttpUtil.getTextContent(fileItemDTO.getUrl());
|
||||
} catch (HttpClientErrorException httpClientErrorException) {
|
||||
log.error("尝试重新获取密码文件缓存中链接后仍失败, driveId: {}, path: {}, inputPassword: {}, passwordFile:{} ",
|
||||
driveId, path, password, JSON.toJSONString(fileItemDTO), httpClientErrorException);
|
||||
log.trace("尝试重新获取密码文件缓存中链接后仍失败, driveId: {}, path: {}, inputPassword: {}, passwordFile:{} ",
|
||||
driveId, path, inputPassword, JSON.toJSONString(fileItemDTO), httpClientErrorException);
|
||||
try {
|
||||
String fullPath = StringUtils.removeDuplicateSeparator(fileItemDTO.getPath() + ZFileConstant.PATH_SEPARATOR + fileItemDTO.getName());
|
||||
FileItemDTO fileItem = fileService.getFileItem(fullPath);
|
||||
expectedPasswordContent = HttpUtil.getTextContent(fileItem.getUrl());
|
||||
String pwdFileFullPath = StringUtils.removeDuplicateSeparator(fileItemDTO.getPath() + ZFileConstant.PATH_SEPARATOR + fileItemDTO.getName());
|
||||
FileItemDTO pwdFileItem = fileService.getFileItem(pwdFileFullPath);
|
||||
expectedPasswordContent = HttpUtil.getTextContent(pwdFileItem.getUrl());
|
||||
} catch (Exception e) {
|
||||
log.error("尝试重新获取密码文件链接后仍失败, 已暂时取消密码", e);
|
||||
break;
|
||||
throw new PasswordVerifyException("此文件夹为加密文件夹, 但密码检查异常, 请联系管理员检查密码设置", e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new PasswordVerifyException("此文件夹为加密文件夹, 但密码检查异常, 请联系管理员检查密码设置", e);
|
||||
}
|
||||
|
||||
if (Objects.equals(expectedPasswordContent, password)) {
|
||||
if (matchPassword(expectedPasswordContent, inputPassword)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (password != null && !"".equals(password)) {
|
||||
return ResultBean.error("密码错误.", ResultBean.INVALID_PASSWORD);
|
||||
if (StrUtil.isEmpty(inputPassword)) {
|
||||
return VerifyResult.fail("此文件夹需要密码.", ResultBean.REQUIRED_PASSWORD);
|
||||
}
|
||||
return ResultBean.error("此文件夹需要密码.", ResultBean.REQUIRED_PASSWORD);
|
||||
return VerifyResult.fail("密码错误.", ResultBean.INVALID_PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
// 过滤掉表达式中不存在的数据.
|
||||
fileItemList.removeIf(next -> filterConfigService.filterResultIsHidden(driveId, StringUtils.concatUrl(next.getPath(), next.getName())));
|
||||
return ResultBean.successData(getSortedPagingData(fileItemList, page));
|
||||
return VerifyResult.success();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取系统配置信息和当前页的标题, 页面文档信息
|
||||
* 校验两个密码是否相同, 忽略空白字符
|
||||
*
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
* @param expectedPasswordContent
|
||||
* 预期密码
|
||||
*
|
||||
* @return 返回指定驱动器的系统配置信息
|
||||
* @param password
|
||||
* 实际输入密码
|
||||
*
|
||||
* @return 是否匹配
|
||||
*/
|
||||
@GetMapping("/config/{driveId}")
|
||||
public ResultBean getConfig(@PathVariable(name = "driveId") Integer driveId, String path) {
|
||||
SystemFrontConfigDTO systemConfig = systemConfigService.getSystemFrontConfig(driveId);
|
||||
|
||||
AbstractBaseFileService fileService = driveContext.get(driveId);
|
||||
DriveConfig driveConfig = driveConfigService.findById(driveId);
|
||||
String fullPath = StringUtils.removeDuplicateSeparator(path + ZFileConstant.PATH_SEPARATOR + ZFileConstant.README_FILE_NAME);
|
||||
FileItemDTO fileItem = null;
|
||||
try {
|
||||
fileItem = fileService.getFileItem(fullPath);
|
||||
|
||||
if (!Objects.equals(driveConfig.getType(), StorageTypeEnum.FTP)) {
|
||||
String readme = HttpUtil.getTextContent(fileItem.getUrl());
|
||||
systemConfig.setReadme(readme);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e instanceof NotExistFileException) {
|
||||
log.debug("不存在 README 文件, 已跳过, fullPath: {}, fileItem: {}", fullPath, JSON.toJSONString(fileItem));
|
||||
} else {
|
||||
log.error("获取 README 文件异常, fullPath: {}, fileItem: {}", fullPath, JSON.toJSONString(fileItem), e);
|
||||
}
|
||||
private boolean matchPassword(String expectedPasswordContent, String password) {
|
||||
if (Objects.equals(expectedPasswordContent, password)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ResultBean.successData(systemConfig);
|
||||
}
|
||||
if (expectedPasswordContent == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (password == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@GetMapping("/search/{driveId}")
|
||||
public ResultBean search(@RequestParam(value = "name", defaultValue = "/") String name,
|
||||
@RequestParam(defaultValue = "name") String sortBy,
|
||||
@RequestParam(defaultValue = "asc") String order,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@PathVariable("driveId") Integer driveId) {
|
||||
return ResultBean.error("暂不支持搜索功能");
|
||||
expectedPasswordContent = expectedPasswordContent.replace("\n", "").trim();
|
||||
password = password.replace("\n", "").trim();
|
||||
return Objects.equals(expectedPasswordContent, password);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 过滤文件列表, 去除密码, 文档文件.
|
||||
* 过滤文件列表, 去除密码, 文档文件和此驱动器通过规则过滤的文件.
|
||||
*
|
||||
* @param fileItemList
|
||||
* 文件列表
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
*/
|
||||
private void filterFileList(List<FileItemDTO> fileItemList) {
|
||||
private void filterFileList(List<FileItemDTO> fileItemList, Integer driveId) {
|
||||
if (fileItemList == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
fileItemList.removeIf(fileItem -> ZFileConstant.PASSWORD_FILE_NAME.equals(fileItem.getName())
|
||||
|| ZFileConstant.README_FILE_NAME.equals(fileItem.getName()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对传入的文件列表, 按照文件名进行排序, 然后取相应页数的文件
|
||||
*
|
||||
* @param fileItemList
|
||||
* 文件列表
|
||||
*
|
||||
* @param page
|
||||
* 要取的页数
|
||||
*
|
||||
* @return 排序及分页后的那段数据
|
||||
*/
|
||||
private FilePageModel getSortedPagingData(List<FileItemDTO> fileItemList, Integer page) {
|
||||
ArrayList<FileItemDTO> copy = new ArrayList<>(Arrays.asList(new FileItemDTO[fileItemList.size()]));
|
||||
Collections.copy(copy, fileItemList);
|
||||
|
||||
// 排序, 先按照文件类型比较, 文件夹在前, 文件在后, 然后根据 sortBy 字段排序, 默认为升序;
|
||||
copy.sort(new FileComparator());
|
||||
filterFileList(copy);
|
||||
|
||||
int total = copy.size();
|
||||
int totalPage = (total + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
|
||||
if (page > totalPage) {
|
||||
return new FilePageModel(totalPage, Collections.emptyList());
|
||||
}
|
||||
|
||||
int start = (page - 1) * PAGE_SIZE;
|
||||
int end = page * PAGE_SIZE;
|
||||
end = Math.min(end, total);
|
||||
return new FilePageModel(totalPage, copy.subList(start, end));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定路径下的文件信息内容
|
||||
*
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
*
|
||||
* @param path
|
||||
* 文件全路径
|
||||
*
|
||||
* @return 该文件的名称, 路径, 大小, 下载地址等信息.
|
||||
*/
|
||||
@GetMapping("/directlink/{driveId}")
|
||||
public ResultBean directlink(@PathVariable(name = "driveId") Integer driveId, String path) {
|
||||
AbstractBaseFileService fileService = driveContext.get(driveId);
|
||||
return ResultBean.successData(fileService.getFileItem(path));
|
||||
fileItemList.removeIf(
|
||||
fileItem -> ZFileConstant.PASSWORD_FILE_NAME.equals(fileItem.getName())
|
||||
|| ZFileConstant.README_FILE_NAME.equals(fileItem.getName())
|
||||
|| filterConfigService.filterResultIsHidden(driveId, StringUtils.concatUrl(fileItem.getPath(), fileItem.getName()))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import im.zhaojun.zfile.service.impl.LocalServiceImpl;
|
||||
import im.zhaojun.zfile.util.FileUtil;
|
||||
import im.zhaojun.zfile.util.StringUtils;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -15,6 +14,7 @@ import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
@@ -32,18 +32,21 @@ public class LocalController {
|
||||
*
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
*
|
||||
* @return 文件
|
||||
* @param type
|
||||
* 附件预览类型:
|
||||
* download:下载
|
||||
* default: 浏览器默认行为
|
||||
*/
|
||||
@GetMapping("/file/{driveId}/**")
|
||||
@ResponseBody
|
||||
public ResponseEntity<Object> downAttachment(@PathVariable("driveId") Integer driveId, final HttpServletRequest request) {
|
||||
public void downAttachment(@PathVariable("driveId") Integer driveId, String type, final HttpServletRequest request, final HttpServletResponse response) {
|
||||
String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
|
||||
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
|
||||
AntPathMatcher apm = new AntPathMatcher();
|
||||
String filePath = apm.extractPathWithinPattern(bestMatchPattern, path);
|
||||
LocalServiceImpl localService = (LocalServiceImpl) driveContext.get(driveId);
|
||||
return FileUtil.export(new File(StringUtils.removeDuplicateSeparator(localService.getFilePath() + ZFileConstant.PATH_SEPARATOR + filePath)));
|
||||
File file = new File(StringUtils.removeDuplicateSeparator(localService.getFilePath() + ZFileConstant.PATH_SEPARATOR + filePath));
|
||||
FileUtil.export(request, response, file, type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package im.zhaojun.zfile.controller.home;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import im.zhaojun.zfile.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.zfile.model.entity.ShortLinkConfig;
|
||||
import im.zhaojun.zfile.model.support.ResultBean;
|
||||
import im.zhaojun.zfile.service.ShortLinkConfigService;
|
||||
import im.zhaojun.zfile.service.SystemConfigService;
|
||||
import im.zhaojun.zfile.util.StringUtils;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 短链 Controller
|
||||
* @author zhao
|
||||
*/
|
||||
@Controller
|
||||
public class ShortLinkController {
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Resource
|
||||
private ShortLinkConfigService shortLinkConfigService;
|
||||
|
||||
@GetMapping("/api/short-link")
|
||||
@ResponseBody
|
||||
public ResultBean shortLink(String driveId, String path) {
|
||||
SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();
|
||||
String domain = systemConfig.getDomain();
|
||||
// 拼接直链地址.
|
||||
String fullPath = StringUtils.concatUrl(StringUtils.DELIMITER_STR, ZFileConstant.DIRECT_LINK_PREFIX, driveId, path);
|
||||
ShortLinkConfig shortLinkConfig = shortLinkConfigService.findByUrl(fullPath);
|
||||
|
||||
if (shortLinkConfig == null) {
|
||||
|
||||
String randomKey;
|
||||
do {
|
||||
// 获取短链
|
||||
randomKey = RandomUtil.randomString(6);
|
||||
shortLinkConfig = shortLinkConfigService.findByKey(randomKey);
|
||||
} while (shortLinkConfig != null);
|
||||
|
||||
shortLinkConfig = new ShortLinkConfig();
|
||||
shortLinkConfig.setKey(randomKey);
|
||||
shortLinkConfig.setUrl(fullPath);
|
||||
shortLinkConfigService.save(shortLinkConfig);
|
||||
}
|
||||
|
||||
|
||||
String shortUrl = StringUtils.removeDuplicateSeparator(domain + "/s/" + shortLinkConfig.getKey());
|
||||
return ResultBean.successData(shortUrl);
|
||||
}
|
||||
|
||||
@GetMapping("/s/{key}")
|
||||
public String parseShortKey(@PathVariable String key) {
|
||||
ShortLinkConfig shortLinkConfig = shortLinkConfigService.findByKey(key);
|
||||
if (shortLinkConfig == null) {
|
||||
throw new RuntimeException("此直链不存在或已失效.");
|
||||
}
|
||||
|
||||
SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();
|
||||
String domain = systemConfig.getDomain();
|
||||
|
||||
String url = URLUtil.encode(StringUtils.removeDuplicateSeparator(domain + shortLinkConfig.getUrl()));
|
||||
return "redirect:" + url;
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("admin/api/short-link/key")
|
||||
@ResponseBody
|
||||
public ResultBean updateShortKey(Integer id, String newKey) {
|
||||
ShortLinkConfig newShortLinkConfig = shortLinkConfigService.findByKey(newKey);
|
||||
if (newShortLinkConfig != null) {
|
||||
throw new RuntimeException("您输入的 Key 已存在,请重新输入");
|
||||
}
|
||||
|
||||
ShortLinkConfig shortLinkConfig = shortLinkConfigService.findById(id);
|
||||
if (shortLinkConfig == null) {
|
||||
throw new RuntimeException("此直链不存在或已失效.");
|
||||
}
|
||||
|
||||
shortLinkConfig.setKey(newKey);
|
||||
shortLinkConfigService.save(shortLinkConfig);
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除直链
|
||||
*/
|
||||
@DeleteMapping("admin/api/short-link")
|
||||
@ResponseBody
|
||||
public ResultBean batchDelete(@RequestParam("id[]") Integer[] ids) {
|
||||
for (Integer id : ids) {
|
||||
shortLinkConfigService.deleteById(id);
|
||||
}
|
||||
return ResultBean.success();
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public class InstallController {
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/install")
|
||||
@PostMapping("/doInstall")
|
||||
public ResultBean install(SystemConfigDTO systemConfigDTO) {
|
||||
if (!StringUtils.isEmpty(systemConfigService.getAdminUsername())) {
|
||||
return ResultBean.error("请勿重复初始化.");
|
||||
|
||||
@@ -14,7 +14,7 @@ import javax.annotation.Resource;
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/onedrive")
|
||||
@RequestMapping(value = {"/onedrive", "/onedirve"})
|
||||
public class OneDriveCallbackController {
|
||||
|
||||
@Resource
|
||||
@@ -31,6 +31,13 @@ public class OneDriveCallbackController {
|
||||
return "callback";
|
||||
}
|
||||
|
||||
@GetMapping("/authorize")
|
||||
public String authorize() {
|
||||
return "redirect:https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=" + oneDriveServiceImpl.getClientId() +
|
||||
"&response_type=code&redirect_uri=" + oneDriveServiceImpl.getRedirectUri() +
|
||||
"&scope=" + oneDriveServiceImpl.getScope();
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/china-callback")
|
||||
public String oneDriveChinaCallback(String code, Model model) {
|
||||
@@ -40,4 +47,11 @@ public class OneDriveCallbackController {
|
||||
return "callback";
|
||||
}
|
||||
|
||||
@GetMapping("/china-authorize")
|
||||
public String authorizeChina() {
|
||||
return "redirect:https://login.chinacloudapi.cn/common/oauth2/v2.0/authorize?client_id=" + oneDriveChinaServiceImpl.getClientId() +
|
||||
"&response_type=code&redirect_uri=" + oneDriveChinaServiceImpl.getRedirectUri() +
|
||||
"&scope=" + oneDriveChinaServiceImpl.getScope();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package im.zhaojun.zfile.controller.onedrive;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import im.zhaojun.zfile.model.dto.SharePointInfoVO;
|
||||
import im.zhaojun.zfile.model.support.ResultBean;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* SharePoint 工具类
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/sharepoint")
|
||||
public class SharePointHelperController {
|
||||
|
||||
|
||||
/**
|
||||
* 根据 AccessToken 获取域名前缀
|
||||
*/
|
||||
@PostMapping("/getDomainPrefix")
|
||||
@ResponseBody
|
||||
public ResultBean getDomainPrefix(@RequestBody SharePointInfoVO sharePointInfoVO) {
|
||||
|
||||
String host = "";
|
||||
|
||||
// 判断是标准版还是世纪互联版
|
||||
if (Objects.equals(sharePointInfoVO.getType(), "Standard")) {
|
||||
host = "graph.microsoft.com";
|
||||
} else if (Objects.equals(sharePointInfoVO.getType(), "China")) {
|
||||
host = "microsoftgraph.chinacloudapi.cn";
|
||||
}
|
||||
|
||||
// 请求 URL
|
||||
String requestUrl = StrUtil.format("https://{}/v1.0/sites/root", host);
|
||||
|
||||
// 构建请求认证 Token 信息
|
||||
String tokenValue = String.format("%s %s", "Bearer", sharePointInfoVO.getAccessToken());
|
||||
HashMap<String, String> headers = new HashMap<>();
|
||||
headers.put("Authorization", tokenValue);
|
||||
|
||||
// 请求接口
|
||||
HttpRequest getRequest = HttpUtil.createGet(requestUrl);
|
||||
HttpResponse execute = getRequest.addHeaders(headers).execute();
|
||||
String body = execute.body();
|
||||
if (execute.getStatus() != HttpStatus.OK.value()) {
|
||||
return ResultBean.error(body);
|
||||
}
|
||||
|
||||
// 解析前缀
|
||||
JSONObject jsonObject = JSONObject.parseObject(body);
|
||||
String hostname = jsonObject.getJSONObject("siteCollection").getString("hostname");
|
||||
String domainPrefix = StrUtil.subBefore(hostname, ".sharepoint", false);
|
||||
return ResultBean.successData(domainPrefix);
|
||||
}
|
||||
|
||||
@PostMapping("/getSiteId")
|
||||
@ResponseBody
|
||||
public ResultBean getSiteId(@RequestBody SharePointInfoVO sharePointInfoVO) {
|
||||
|
||||
// 判断必填参数
|
||||
if (sharePointInfoVO == null || sharePointInfoVO.getAccessToken() == null || sharePointInfoVO.getSiteName() == null) {
|
||||
return ResultBean.error("参数不全");
|
||||
}
|
||||
|
||||
String host = "";
|
||||
|
||||
// 判断是标准版还是世纪互联版
|
||||
if (Objects.equals(sharePointInfoVO.getType(), "Standard")) {
|
||||
host = "graph.microsoft.com";
|
||||
sharePointInfoVO.setDomainType("com");
|
||||
} else if (Objects.equals(sharePointInfoVO.getType(), "China")) {
|
||||
host = "microsoftgraph.chinacloudapi.cn";
|
||||
sharePointInfoVO.setDomainType("cn");
|
||||
} else {
|
||||
return ResultBean.error("参数不全");
|
||||
}
|
||||
|
||||
// 构建请求认证 Token 信息
|
||||
String tokenValue = String.format("%s %s", "Bearer", sharePointInfoVO.getAccessToken());
|
||||
HashMap<String, String> authorizationHeaders = new HashMap<>();
|
||||
authorizationHeaders.put("Authorization", tokenValue);
|
||||
|
||||
|
||||
// 如果没有域名前缀, 则先获取
|
||||
if (sharePointInfoVO.getDomainPrefix() == null || sharePointInfoVO.getDomainType() == null) {
|
||||
String requestUrl = StrUtil.format("https://{}/v1.0/sites/root", host);
|
||||
HttpRequest getRequest = HttpUtil.createGet(requestUrl);
|
||||
HttpResponse execute = getRequest.addHeaders(authorizationHeaders).execute();
|
||||
String body = execute.body();
|
||||
if (execute.getStatus() != HttpStatus.OK.value()) {
|
||||
return ResultBean.error(body);
|
||||
}
|
||||
JSONObject jsonObject = JSONObject.parseObject(body);
|
||||
String hostname = jsonObject.getJSONObject("siteCollection").getString("hostname");
|
||||
String domainPrefix = StrUtil.subBefore(hostname, ".sharepoint", false);
|
||||
sharePointInfoVO.setDomainPrefix(domainPrefix);
|
||||
}
|
||||
|
||||
|
||||
if (StrUtil.isEmpty(sharePointInfoVO.getSiteType())) {
|
||||
sharePointInfoVO.setSiteType("/sites/");
|
||||
}
|
||||
|
||||
// 请求接口
|
||||
String requestUrl = StrUtil.format("https://{}/v1.0/sites/{}.sharepoint.{}:/{}/{}", host,
|
||||
sharePointInfoVO.getDomainPrefix(),
|
||||
sharePointInfoVO.getDomainType(),
|
||||
sharePointInfoVO.getSiteType(),
|
||||
sharePointInfoVO.getSiteName());
|
||||
HttpRequest getRequest = HttpUtil.createGet(requestUrl);
|
||||
HttpResponse execute = getRequest.addHeaders(authorizationHeaders).execute();
|
||||
String body = execute.body();
|
||||
|
||||
// 解析数据
|
||||
if (execute.getStatus() != HttpStatus.OK.value()) {
|
||||
return ResultBean.error(body);
|
||||
}
|
||||
JSONObject jsonObject = JSONObject.parseObject(body);
|
||||
return ResultBean.successData(jsonObject.getString("id"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
package im.zhaojun.zfile.exception;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import im.zhaojun.zfile.model.support.ResultBean;
|
||||
import org.apache.catalina.connector.ClientAbortException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
import java.net.ConnectException;
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
* @author zhaojun
|
||||
@@ -20,12 +22,22 @@ public class GlobleExceptionHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(GlobleExceptionHandler.class);
|
||||
|
||||
|
||||
/**
|
||||
* 不存在的文件异常
|
||||
*/
|
||||
@ExceptionHandler({NotEnabledDriveException.class})
|
||||
@ResponseBody
|
||||
public ResultBean notEnabledDrive() {
|
||||
return ResultBean.error("驱动器已关闭");
|
||||
}
|
||||
|
||||
/**
|
||||
* 不存在的文件异常
|
||||
*/
|
||||
@ExceptionHandler({NotExistFileException.class})
|
||||
@ResponseBody
|
||||
public ResultBean notExistFile(Exception ex) {
|
||||
public ResultBean notExistFile() {
|
||||
return ResultBean.error("文件不存在");
|
||||
}
|
||||
|
||||
@@ -36,12 +48,33 @@ public class GlobleExceptionHandler {
|
||||
@ExceptionHandler({HttpMediaTypeNotAcceptableException.class, ClientAbortException.class})
|
||||
@ResponseBody
|
||||
@ResponseStatus
|
||||
public void clientAbortException(Exception ex) {
|
||||
public void clientAbortException() {
|
||||
// if (log.isDebugEnabled()) {
|
||||
// log.debug("出现了断开异常:", ex);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件预览异常
|
||||
*/
|
||||
@ExceptionHandler({PasswordVerifyException.class})
|
||||
@ResponseBody
|
||||
@ResponseStatus
|
||||
public ResultBean passwordVerifyException(PasswordVerifyException ex) {
|
||||
return ResultBean.error(ex.getMessage());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 无效的驱动器异常
|
||||
*/
|
||||
@ExceptionHandler({InvalidDriveException.class})
|
||||
@ResponseBody
|
||||
@ResponseStatus
|
||||
public ResultBean invalidDriveException(InvalidDriveException ex) {
|
||||
return ResultBean.error(ex.getMessage());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 文件预览异常
|
||||
@@ -64,11 +97,32 @@ public class GlobleExceptionHandler {
|
||||
return ResultBean.error(ex.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录异常拦截器
|
||||
*/
|
||||
@ExceptionHandler(NotLoginException.class)
|
||||
@ResponseBody
|
||||
@ResponseStatus
|
||||
public ResultBean handlerNotLoginException(NotLoginException e) {
|
||||
return ResultBean.error("未登录");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 登录异常拦截器
|
||||
*/
|
||||
@ExceptionHandler(ConnectException.class)
|
||||
@ResponseBody
|
||||
public ResultBean handlerConnectException(ConnectException e) {
|
||||
return ResultBean.error("请求失败, 清稍后再试");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
@ResponseStatus(code= HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public ResultBean searchDisableExceptionHandler(Exception e) {
|
||||
@ResponseStatus
|
||||
public ResultBean extraExceptionHandler(Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
|
||||
if (e.getClass() == Exception.class) {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package im.zhaojun.zfile.exception;
|
||||
|
||||
/**
|
||||
* 无效的直链异常
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class InvalidShortLinkException extends RuntimeException {
|
||||
public InvalidShortLinkException() {
|
||||
}
|
||||
|
||||
public InvalidShortLinkException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InvalidShortLinkException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public InvalidShortLinkException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public InvalidShortLinkException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package im.zhaojun.zfile.exception;
|
||||
|
||||
/**
|
||||
* 文件不允许下载异常
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class NotAllowedDownloadException extends RuntimeException {
|
||||
public NotAllowedDownloadException() {
|
||||
}
|
||||
|
||||
public NotAllowedDownloadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public NotAllowedDownloadException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public NotAllowedDownloadException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public NotAllowedDownloadException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package im.zhaojun.zfile.exception;
|
||||
|
||||
/**
|
||||
* 未启用的驱动器异常
|
||||
*/
|
||||
public class NotEnabledDriveException extends RuntimeException {
|
||||
|
||||
public NotEnabledDriveException() {
|
||||
}
|
||||
|
||||
public NotEnabledDriveException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public NotEnabledDriveException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public NotEnabledDriveException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public NotEnabledDriveException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package im.zhaojun.zfile.exception;
|
||||
|
||||
/**
|
||||
* 不存在的文件异常
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class NotExistFileException extends RuntimeException {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package im.zhaojun.zfile.exception;
|
||||
|
||||
/**
|
||||
* 密码校验失败异常
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class PasswordVerifyException extends RuntimeException {
|
||||
|
||||
public PasswordVerifyException() {
|
||||
}
|
||||
|
||||
public PasswordVerifyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PasswordVerifyException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public PasswordVerifyException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public PasswordVerifyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package im.zhaojun.zfile.exception;
|
||||
|
||||
/**
|
||||
* 文件解析异常
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class TextParseException extends RuntimeException {
|
||||
|
||||
public TextParseException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TextParseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public TextParseException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public TextParseException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
protected TextParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package im.zhaojun.zfile.filter;
|
||||
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.cors.CorsUtils;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
@@ -8,6 +10,7 @@ import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.annotation.WebFilter;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
@@ -16,6 +19,9 @@ import java.io.IOException;
|
||||
* 开启跨域支持. 一般用于开发环境, 或前后端分离部署时开启.
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Order(1)
|
||||
@WebFilter(value = "/*")
|
||||
@Component
|
||||
public class CorsFilter extends GenericFilterBean {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package im.zhaojun.zfile.filter;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Configuration
|
||||
public class MyCorsFilter {
|
||||
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
|
||||
final CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.setAllowCredentials(true);
|
||||
corsConfiguration.addAllowedOrigin("*");
|
||||
corsConfiguration.addAllowedHeader("*");
|
||||
corsConfiguration.addAllowedMethod("*");
|
||||
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
|
||||
return new CorsFilter(urlBasedCorsConfigurationSource);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package im.zhaojun.zfile.filter;
|
||||
@@ -0,0 +1 @@
|
||||
package im.zhaojun.zfile.model.constant;
|
||||
@@ -33,8 +33,16 @@ public class StorageConfigConstant {
|
||||
|
||||
public static final String REFRESH_TOKEN_KEY = "refreshToken";
|
||||
|
||||
public static final String SHAREPOINT_SITE_ID = "siteId";
|
||||
|
||||
public static final String SHAREPOINT_SITE_NAME = "siteName";
|
||||
|
||||
public static final String PATH_STYLE = "pathStyle";
|
||||
|
||||
public static final String IS_PRIVATE = "isPrivate";
|
||||
|
||||
public static final String PROXY_DOMAIN = "proxyDomain";
|
||||
|
||||
public static final String REGION_KEY = "region";
|
||||
|
||||
}
|
||||
@@ -16,6 +16,10 @@ public class ZFileConstant {
|
||||
|
||||
public static final String PATH_SEPARATOR = "/";
|
||||
|
||||
/**
|
||||
* 直链前缀名称
|
||||
*/
|
||||
public static String DIRECT_LINK_PREFIX = "directlink";
|
||||
|
||||
/**
|
||||
* 系统产生的临时文件路径
|
||||
@@ -68,5 +72,9 @@ public class ZFileConstant {
|
||||
ZFileConstant.TEXT_MAX_FILE_SIZE_KB = maxFileSizeKb;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setDirectLinkPrefix(@Value("${zfile.directLinkPrefix}") String directLinkPrefix) {
|
||||
ZFileConstant.DIRECT_LINK_PREFIX = directLinkPrefix;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,4 +34,6 @@ public class DriveConfigDTO {
|
||||
|
||||
private StorageStrategyConfig storageStrategyConfig;
|
||||
|
||||
private boolean defaultSwitchToImgMode;
|
||||
|
||||
}
|
||||
21
src/main/java/im/zhaojun/zfile/model/dto/DriveListDTO.java
Normal file
21
src/main/java/im/zhaojun/zfile/model/dto/DriveListDTO.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package im.zhaojun.zfile.model.dto;
|
||||
|
||||
import im.zhaojun.zfile.model.entity.DriveConfig;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Zhao Jun
|
||||
* 2021/5/26 15:17
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class DriveListDTO {
|
||||
|
||||
private List<DriveConfig> driveList;
|
||||
|
||||
private Boolean isInstall;
|
||||
|
||||
}
|
||||
18
src/main/java/im/zhaojun/zfile/model/dto/FileListDTO.java
Normal file
18
src/main/java/im/zhaojun/zfile/model/dto/FileListDTO.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package im.zhaojun.zfile.model.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FileListDTO {
|
||||
|
||||
private List<FileItemDTO> files;
|
||||
|
||||
private SystemFrontConfigDTO config;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package im.zhaojun.zfile.model.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SharePointInfoVO {
|
||||
|
||||
private String type;
|
||||
|
||||
private String accessToken;
|
||||
|
||||
private String domainPrefix;
|
||||
|
||||
private String siteType;
|
||||
|
||||
private String siteName;
|
||||
|
||||
private String domainType;
|
||||
|
||||
}
|
||||
@@ -40,4 +40,14 @@ public class StorageStrategyConfig {
|
||||
|
||||
private String basePath;
|
||||
|
||||
private String siteId;
|
||||
|
||||
private String siteName;
|
||||
|
||||
private String siteType;
|
||||
|
||||
private String proxyDomain;
|
||||
|
||||
private String region;
|
||||
|
||||
}
|
||||
@@ -46,4 +46,10 @@ public class SystemConfigDTO {
|
||||
private Boolean showAnnouncement;
|
||||
|
||||
private String layout;
|
||||
|
||||
private Boolean showLinkBtn;
|
||||
|
||||
private Boolean showShortLink;
|
||||
|
||||
private Boolean showPathLink;
|
||||
}
|
||||
@@ -41,4 +41,16 @@ public class SystemFrontConfigDTO {
|
||||
|
||||
private String readme;
|
||||
|
||||
private Boolean debugMode;
|
||||
|
||||
private Boolean defaultSwitchToImgMode;
|
||||
|
||||
private Boolean showLinkBtn;
|
||||
|
||||
private Boolean showShortLink;
|
||||
|
||||
private Boolean showPathLink;
|
||||
|
||||
private String directLinkPrefix;
|
||||
|
||||
}
|
||||
@@ -4,8 +4,6 @@ import im.zhaojun.zfile.model.enums.StorageTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
@@ -18,7 +16,6 @@ import javax.persistence.Id;
|
||||
public class DriveConfig {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
private Boolean enable;
|
||||
@@ -39,4 +36,6 @@ public class DriveConfig {
|
||||
|
||||
private Integer orderNum;
|
||||
|
||||
private Boolean defaultSwitchToImgMode;
|
||||
|
||||
}
|
||||
@@ -2,12 +2,10 @@ package im.zhaojun.zfile.model.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Lob;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package im.zhaojun.zfile.model.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.Date;
|
||||
|
||||
@Entity(name = "SHORT_LINK")
|
||||
@Data
|
||||
public class ShortLinkConfig {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
@Column(name = "`key`")
|
||||
private String key;
|
||||
|
||||
private String url;
|
||||
|
||||
private Date createDate;
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package im.zhaojun.zfile.model.entity;
|
||||
|
||||
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
@@ -15,6 +16,7 @@ import javax.persistence.Lob;
|
||||
*/
|
||||
@Entity(name = "STORAGE_CONFIG")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class StorageConfig {
|
||||
|
||||
@Id
|
||||
|
||||
@@ -25,6 +25,8 @@ public enum StorageTypeEnum {
|
||||
S3("s3", "S3通用协议"),
|
||||
ONE_DRIVE("onedrive", "OneDrive"),
|
||||
ONE_DRIVE_CHINA("onedrive-china", "OneDrive 世纪互联"),
|
||||
SHAREPOINT_DRIVE("sharepoint", "SharePoint"),
|
||||
SHAREPOINT_DRIVE_CHINA("sharepoint-china", "SharePoint 世纪互联"),
|
||||
QINIU("qiniu", "七牛云 KODO");
|
||||
|
||||
private String key;
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package im.zhaojun.zfile.model.support;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
public class Jvm {
|
||||
|
||||
/**
|
||||
* 当前 JVM 占用的内存总数 (M)
|
||||
*/
|
||||
private double total;
|
||||
|
||||
/**
|
||||
* JVM 最大可用内存总数 (M)
|
||||
*/
|
||||
private double max;
|
||||
|
||||
/**
|
||||
* JVM 空闲内存 (M)
|
||||
*/
|
||||
private double free;
|
||||
|
||||
/**
|
||||
* JDK 版本
|
||||
*/
|
||||
private String version;
|
||||
|
||||
public Jvm() {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
this.total = runtime.totalMemory();
|
||||
this.free = runtime.freeMemory();
|
||||
this.max = runtime.maxMemory();
|
||||
this.version = System.getProperty("java.version");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package im.zhaojun.zfile.model.support;
|
||||
|
||||
import com.sun.management.OperatingSystemMXBean;
|
||||
import lombok.Data;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
public class Mem {
|
||||
|
||||
/**
|
||||
* 内存总量
|
||||
*/
|
||||
private double total;
|
||||
|
||||
/**
|
||||
* 已用内存
|
||||
*/
|
||||
private double used;
|
||||
|
||||
/**
|
||||
* 剩余内存
|
||||
*/
|
||||
private double free;
|
||||
|
||||
public Mem() {
|
||||
OperatingSystemMXBean osb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
|
||||
// 总的物理内存+虚拟内存
|
||||
long totalVirtualMemory = osb.getTotalSwapSpaceSize();
|
||||
// 剩余的物理内存
|
||||
long freePhysicalMemorySize = osb.getFreePhysicalMemorySize();
|
||||
this.total = totalVirtualMemory;
|
||||
this.free = freePhysicalMemorySize;
|
||||
this.used = totalVirtualMemory - freePhysicalMemorySize;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package im.zhaojun.zfile.model.support;
|
||||
|
||||
import cn.hutool.core.date.BetweenFormater;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import lombok.Data;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
public class Sys {
|
||||
|
||||
/**
|
||||
* 项目路径
|
||||
*/
|
||||
private String projectDir;
|
||||
|
||||
/**
|
||||
* 操作系统
|
||||
*/
|
||||
private String osName;
|
||||
|
||||
/**
|
||||
* 系统架构
|
||||
*/
|
||||
private String osArch;
|
||||
|
||||
/**
|
||||
* 系统版本
|
||||
*/
|
||||
private String osVersion;
|
||||
|
||||
/**
|
||||
* 启动时间
|
||||
*/
|
||||
private String upTime;
|
||||
|
||||
public Sys() {
|
||||
this.osName = System.getProperty("os.name");
|
||||
this.osArch = System.getProperty("os.arch");
|
||||
this.osVersion = System.getProperty("os.version");
|
||||
Resource resource = new ClassPathResource("");
|
||||
try {
|
||||
this.projectDir = resource.getFile().getAbsolutePath();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
long uptime = ManagementFactory.getRuntimeMXBean().getUptime();
|
||||
this.upTime = DateUtil.formatBetween(uptime, BetweenFormater.Level.SECOND);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package im.zhaojun.zfile.model.support;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
public class SystemMonitorInfo implements Serializable {
|
||||
|
||||
/**
|
||||
* 服务器基本信息
|
||||
*/
|
||||
private Sys sys;
|
||||
|
||||
/**
|
||||
* JVM 信息
|
||||
*/
|
||||
private Jvm jvm;
|
||||
|
||||
/**
|
||||
* 系统内存
|
||||
*/
|
||||
private Mem mem;
|
||||
|
||||
public SystemMonitorInfo() {
|
||||
this.jvm = new Jvm();
|
||||
this.sys = new Sys();
|
||||
this.mem = new Mem();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package im.zhaojun.zfile.model.support;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用于表示校验结果的类
|
||||
*/
|
||||
@Data
|
||||
public class VerifyResult {
|
||||
|
||||
/**
|
||||
* 是否成功
|
||||
*/
|
||||
private boolean passed;
|
||||
|
||||
/**
|
||||
* 消息
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 代码
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
public static VerifyResult success() {
|
||||
VerifyResult verifyResult = new VerifyResult();
|
||||
verifyResult.setPassed(true);
|
||||
return verifyResult;
|
||||
}
|
||||
|
||||
public static VerifyResult fail(String msg) {
|
||||
VerifyResult verifyResult = new VerifyResult();
|
||||
verifyResult.setPassed(false);
|
||||
verifyResult.setMsg(msg);
|
||||
return verifyResult;
|
||||
}
|
||||
|
||||
public static VerifyResult fail(String msg, Integer code) {
|
||||
VerifyResult verifyResult = new VerifyResult();
|
||||
verifyResult.setPassed(false);
|
||||
verifyResult.setMsg(msg);
|
||||
verifyResult.setCode(code);
|
||||
return verifyResult;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,8 +26,40 @@ public interface DriverConfigRepository extends JpaRepository<DriveConfig, Integ
|
||||
List<DriveConfig> findByType(StorageTypeEnum type);
|
||||
|
||||
|
||||
/**
|
||||
* 更新驱动器 ID 的排序值
|
||||
*
|
||||
* @param orderNum
|
||||
* 排序值
|
||||
*
|
||||
* @param id
|
||||
* 驱动器 ID
|
||||
*/
|
||||
@Modifying
|
||||
@Query(value="update DRIVER_CONFIG set orderNum = :orderNum where id = :id")
|
||||
void updateSetOrderNumById(Integer orderNum, Integer id);
|
||||
|
||||
|
||||
/**
|
||||
* 查询驱动器最大的 ID
|
||||
*
|
||||
* @return 驱动器最大 ID
|
||||
*/
|
||||
@Query(nativeQuery = true, value = "select max(id) max from DRIVER_CONFIG")
|
||||
Integer selectMaxId();
|
||||
|
||||
|
||||
/**
|
||||
* 更新驱动器 ID
|
||||
*
|
||||
* @param updateId
|
||||
* 驱动器原 ID
|
||||
*
|
||||
* @param newId
|
||||
* 驱动器新 ID
|
||||
*/
|
||||
@Modifying
|
||||
@Query(value="update DRIVER_CONFIG set id = :newId where id = :updateId")
|
||||
void updateId(Integer updateId, Integer newId);
|
||||
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package im.zhaojun.zfile.repository;
|
||||
|
||||
import im.zhaojun.zfile.model.entity.FilterConfig;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
@@ -14,14 +16,29 @@ public interface FilterConfigRepository extends JpaRepository<FilterConfig, Inte
|
||||
|
||||
/**
|
||||
* 获取驱动器下的所有规则
|
||||
* @param driveId 驱动器 ID
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
*/
|
||||
List<FilterConfig> findByDriveId(Integer driveId);
|
||||
|
||||
/**
|
||||
* 根据驱动器 ID 删除其所有的规则
|
||||
* @param driveId 驱动器 ID
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
*/
|
||||
void deleteByDriveId(Integer driveId);
|
||||
|
||||
|
||||
/**
|
||||
* 更新驱动器 ID 对应的参数设置为新的驱动器 ID
|
||||
*
|
||||
* @param updateId
|
||||
* 驱动器原 ID
|
||||
*
|
||||
* @param newId
|
||||
* 驱动器新 ID
|
||||
*/
|
||||
@Modifying
|
||||
@Query(value="update FILTER_CONFIG set driveId = :newId where driveId = :updateId")
|
||||
void updateDriveId(Integer updateId, Integer newId);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package im.zhaojun.zfile.repository;
|
||||
|
||||
import im.zhaojun.zfile.model.entity.ShortLinkConfig;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Repository
|
||||
public interface ShortLinkConfigRepository extends JpaRepository<ShortLinkConfig, Integer>, JpaSpecificationExecutor<ShortLinkConfig> {
|
||||
|
||||
/**
|
||||
* 获取驱动器下的所有规则
|
||||
*
|
||||
* @param key
|
||||
* 短链 Key
|
||||
*/
|
||||
ShortLinkConfig findFirstByKey(String key);
|
||||
|
||||
@Query(nativeQuery = true,
|
||||
value = " select * from SHORT_LINK where " +
|
||||
" key like concat('%', :key,'%') " +
|
||||
" and url like concat('%', :url,'%') " +
|
||||
" and (:dateFrom is null or create_date >= :dateFrom" +
|
||||
" and (:dateTo is null or create_date <= :dateTo) ",
|
||||
countQuery = " select count(1) from SHORT_LINK where " +
|
||||
" key like concat('%', :key,'%') " +
|
||||
" and url like concat('%', :url,'%') " +
|
||||
" and (:dateFrom is null or create_date >= :dateFrom" +
|
||||
" and (:dateTo is null or create_date <= :dateTo) "
|
||||
)
|
||||
// @Query(nativeQuery = true,
|
||||
// value = " select * from SHORT_LINK where " +
|
||||
// " key like concat('%', :key,'%') " +
|
||||
// " and url like concat('%', :url,'%') " +
|
||||
// " and (:dateFrom is null or date_format(create_date, '%Y-%m-%d') >= date_format(:dateFrom, '%Y-%m-%d'))" +
|
||||
// " and (:dateTo is null or date_format(create_date, '%Y-%m-%d') <= date_format(:dateTo, '%Y-%m-%d')) ) ",
|
||||
// countQuery = " select count(1) from SHORT_LINK where " +
|
||||
// " key like concat('%', :key,'%') " +
|
||||
// " and url like concat('%', :url,'%') " +
|
||||
// " and (:dateFrom is null or date_format(create_date, '%Y-%m-%d') >= date_format(:dateFrom, '%Y-%m-%d'))" +
|
||||
// " and (:dateTo is null or date_format(create_date, '%Y-%m-%d') <= date_format(:dateTo, '%Y-%m-%d')) ) "
|
||||
// )
|
||||
Page<ShortLinkConfig> findByPage(String key, String url, Date dateFrom, Date dateTo, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 获取驱动器下的所有规则
|
||||
*
|
||||
* @param url
|
||||
* 短链 URL
|
||||
*/
|
||||
ShortLinkConfig findFirstByUrl(String url);
|
||||
|
||||
|
||||
/**
|
||||
* 更新驱动器 ID
|
||||
*
|
||||
* @param updateSubPath
|
||||
* 原路径部分名称
|
||||
*
|
||||
* @param newSubPath
|
||||
* 修改后路径部分名称
|
||||
*/
|
||||
@Modifying
|
||||
@Query(value="update SHORT_LINK set url = replace(url, :updateSubPath, :newSubPath)")
|
||||
void updateUrlDriveId(String updateSubPath, String newSubPath);
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package im.zhaojun.zfile.repository;
|
||||
import im.zhaojun.zfile.model.entity.StorageConfig;
|
||||
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
@@ -68,4 +70,18 @@ public interface StorageConfigRepository extends JpaRepository<StorageConfig, In
|
||||
*/
|
||||
StorageConfig findByDriveIdAndKey(Integer driveId, String key);
|
||||
|
||||
|
||||
/**
|
||||
* 更新驱动器 ID 对应的参数设置为新的驱动器 ID
|
||||
*
|
||||
* @param updateId
|
||||
* 驱动器原 ID
|
||||
*
|
||||
* @param newId
|
||||
* 驱动器新 ID
|
||||
*/
|
||||
@Modifying
|
||||
@Query(value="update STORAGE_CONFIG set driveId = :newId where driveId = :updateId")
|
||||
void updateDriveId(Integer updateId, Integer newId);
|
||||
|
||||
}
|
||||
@@ -40,11 +40,11 @@ public class OneDriveTokenRefreshSchedule {
|
||||
|
||||
List<DriveConfig> driveConfigList = driveConfigService.findByType(StorageTypeEnum.ONE_DRIVE);
|
||||
driveConfigList.addAll(driveConfigService.findByType(StorageTypeEnum.ONE_DRIVE_CHINA));
|
||||
driveConfigList.addAll(driveConfigService.findByType(StorageTypeEnum.SHAREPOINT_DRIVE));
|
||||
driveConfigList.addAll(driveConfigService.findByType(StorageTypeEnum.SHAREPOINT_DRIVE_CHINA));
|
||||
|
||||
|
||||
driveConfigList.forEach(driveConfig -> {
|
||||
StorageTypeEnum storageType = driveConfig.getType();
|
||||
String name = driveConfig.getName();
|
||||
|
||||
try {
|
||||
AbstractOneDriveServiceBase driveService = (AbstractOneDriveServiceBase) driveContext.get(driveConfig.getId());
|
||||
driveService.refreshOneDriveToken();
|
||||
@@ -52,7 +52,6 @@ public class OneDriveTokenRefreshSchedule {
|
||||
} catch (Exception e) {
|
||||
log.error("刷新 OneDrive Token 失败, DriveInfo: {}", JSON.toJSONString(driveConfig), e);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
} catch (Throwable e) {
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package im.zhaojun.zfile.security;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class Md5PasswordEncoder implements PasswordEncoder {
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
return SecureUtil.md5(rawPassword.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
return SecureUtil.md5(rawPassword.toString()).equals(encodedPassword);
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
package im.zhaojun.zfile.security;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import im.zhaojun.zfile.model.support.ResultBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.firewall.HttpFirewall;
|
||||
import org.springframework.security.web.firewall.StrictHttpFirewall;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* 自定义 Security 配置类
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@EnableWebSecurity
|
||||
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Resource
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// .authenticationProvider(authenticationProvider())
|
||||
.exceptionHandling()
|
||||
// 未登录时,进行 json 格式的提示.
|
||||
.authenticationEntryPoint((request, response, authException) -> {
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
PrintWriter out = response.getWriter();
|
||||
out.write(objectMapper.writeValueAsString(ResultBean.error("未登录")));
|
||||
out.flush();
|
||||
out.close();
|
||||
})
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/").permitAll()
|
||||
.antMatchers("/admin/**").authenticated()
|
||||
.and()
|
||||
.formLogin() // 使用自带的登录
|
||||
// 登录失败,返回json
|
||||
.failureHandler((request, response, ex) -> {
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||
PrintWriter out = response.getWriter();
|
||||
String msg;
|
||||
if (ex instanceof UsernameNotFoundException || ex instanceof BadCredentialsException) {
|
||||
msg = "用户名或密码错误";
|
||||
} else {
|
||||
msg = "登录失败";
|
||||
}
|
||||
out.write(objectMapper.writeValueAsString(ResultBean.error(msg)));
|
||||
out.flush();
|
||||
out.close();
|
||||
})
|
||||
// 登录成功,返回json
|
||||
.successHandler((request, response, authentication) -> {
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
PrintWriter out = response.getWriter();
|
||||
out.write(objectMapper.writeValueAsString(ResultBean.success(authentication)));
|
||||
out.flush();
|
||||
out.close();
|
||||
})
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
// 没有权限,返回json
|
||||
.accessDeniedHandler((request, response, ex) -> {
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||
PrintWriter out = response.getWriter();
|
||||
out.write(objectMapper.writeValueAsString(ResultBean.error("权限不足")));
|
||||
out.flush();
|
||||
out.close();
|
||||
})
|
||||
.and()
|
||||
.logout()
|
||||
// 退出成功,返回 json
|
||||
.logoutSuccessHandler((request, response, authentication) -> {
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
PrintWriter out = response.getWriter();
|
||||
out.write(objectMapper.writeValueAsString(ResultBean.error("注销成功")));
|
||||
out.flush();
|
||||
out.close();
|
||||
})
|
||||
.and()
|
||||
.logout().permitAll();
|
||||
|
||||
http.cors();
|
||||
http.csrf().disable();
|
||||
http.headers().frameOptions().sameOrigin();
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MyUserDetailsServiceImpl myUserDetailsServiceImpl() {
|
||||
return new MyUserDetailsServiceImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) {
|
||||
// 对于在 header 里面增加 token 等类似情况,放行所有 OPTIONS 请求。
|
||||
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
|
||||
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public static PasswordEncoder passwordEncoder() {
|
||||
return new Md5PasswordEncoder();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package im.zhaojun.zfile.security;
|
||||
|
||||
import im.zhaojun.zfile.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.zfile.service.SystemConfigService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class MyUserDetailsServiceImpl implements UserDetailsService {
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
/**
|
||||
* 授权的时候是对角色授权,认证的时候应该基于资源,而不是角色,因为资源是不变的,而用户的角色是会变的
|
||||
*/
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();
|
||||
if (!Objects.equals(systemConfig.getUsername(), username)) {
|
||||
throw new UsernameNotFoundException("用户名不存在");
|
||||
}
|
||||
return new User(systemConfig.getUsername(), systemConfig.getPassword(), Collections.emptyList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import im.zhaojun.zfile.context.DriveContext;
|
||||
import im.zhaojun.zfile.context.StorageTypeContext;
|
||||
import im.zhaojun.zfile.exception.InitializeDriveException;
|
||||
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import im.zhaojun.zfile.model.dto.CacheInfoDTO;
|
||||
import im.zhaojun.zfile.model.dto.DriveConfigDTO;
|
||||
import im.zhaojun.zfile.model.dto.StorageStrategyConfig;
|
||||
@@ -13,8 +14,11 @@ import im.zhaojun.zfile.model.entity.DriveConfig;
|
||||
import im.zhaojun.zfile.model.entity.StorageConfig;
|
||||
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.zfile.repository.DriverConfigRepository;
|
||||
import im.zhaojun.zfile.repository.FilterConfigRepository;
|
||||
import im.zhaojun.zfile.repository.ShortLinkConfigRepository;
|
||||
import im.zhaojun.zfile.repository.StorageConfigRepository;
|
||||
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
|
||||
import im.zhaojun.zfile.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.data.domain.Example;
|
||||
@@ -42,6 +46,12 @@ public class DriveConfigService {
|
||||
@Resource
|
||||
private StorageConfigRepository storageConfigRepository;
|
||||
|
||||
@Resource
|
||||
private FilterConfigRepository filterConfigRepository;
|
||||
|
||||
@Resource
|
||||
private ShortLinkConfigRepository shortLinkConfigRepository;
|
||||
|
||||
@Resource
|
||||
private DriveContext driveContext;
|
||||
|
||||
@@ -50,13 +60,14 @@ public class DriveConfigService {
|
||||
|
||||
public static final Class<StorageStrategyConfig> STORAGE_STRATEGY_CONFIG_CLASS = StorageStrategyConfig.class;
|
||||
|
||||
|
||||
/**
|
||||
* 获取所有驱动器列表
|
||||
*
|
||||
* @return 驱动器列表
|
||||
*/
|
||||
public List<DriveConfig> list() {
|
||||
Sort sort = new Sort(Sort.Direction.ASC,"orderNum");
|
||||
Sort sort = Sort.by(Sort.Direction.ASC,"orderNum");
|
||||
return driverConfigRepository.findAll(sort);
|
||||
}
|
||||
|
||||
@@ -70,10 +81,11 @@ public class DriveConfigService {
|
||||
DriveConfig driveConfig = new DriveConfig();
|
||||
driveConfig.setEnable(true);
|
||||
Example<DriveConfig> example = Example.of(driveConfig);
|
||||
Sort sort = new Sort(Sort.Direction.ASC,"orderNum");
|
||||
Sort sort = Sort.by(Sort.Direction.ASC,"orderNum");
|
||||
return driverConfigRepository.findAll(example, sort);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定驱动器设置
|
||||
*
|
||||
@@ -101,6 +113,10 @@ public class DriveConfigService {
|
||||
DriveConfigDTO driveConfigDTO = new DriveConfigDTO();
|
||||
|
||||
List<StorageConfig> storageConfigList = storageConfigRepository.findByDriveId(driveConfig.getId());
|
||||
Boolean defaultSwitchToImgMode = driveConfig.getDefaultSwitchToImgMode();
|
||||
if (defaultSwitchToImgMode == null) {
|
||||
driveConfig.setDefaultSwitchToImgMode(false);
|
||||
}
|
||||
BeanUtils.copyProperties(driveConfig, driveConfigDTO);
|
||||
|
||||
StorageStrategyConfig storageStrategyConfig = new StorageStrategyConfig();
|
||||
@@ -127,6 +143,7 @@ public class DriveConfigService {
|
||||
return driveConfigDTO;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定驱动器的存储策略.
|
||||
*
|
||||
@@ -141,12 +158,11 @@ public class DriveConfigService {
|
||||
|
||||
|
||||
/**
|
||||
* 新增或设置驱动器设置
|
||||
* 更新驱动器设置
|
||||
* @param driveConfig 驱动器设置
|
||||
* @return 保存后的驱动器设置
|
||||
*/
|
||||
public DriveConfig saveOrUpdate(DriveConfig driveConfig) {
|
||||
return driverConfigRepository.save(driveConfig);
|
||||
public void updateDriveConfig(DriveConfig driveConfig) {
|
||||
driverConfigRepository.save(driveConfig);
|
||||
}
|
||||
|
||||
|
||||
@@ -165,6 +181,11 @@ public class DriveConfigService {
|
||||
DriveConfig driveConfig = new DriveConfig();
|
||||
StorageTypeEnum storageType = driveConfigDTO.getType();
|
||||
BeanUtils.copyProperties(driveConfigDTO, driveConfig);
|
||||
|
||||
if (driveConfig.getId() == null) {
|
||||
Integer nextId = selectNextId();
|
||||
driveConfig.setId(nextId);
|
||||
}
|
||||
driverConfigRepository.save(driveConfig);
|
||||
|
||||
// 保存存储策略设置.
|
||||
@@ -172,12 +193,8 @@ public class DriveConfigService {
|
||||
|
||||
AbstractBaseFileService storageTypeService = StorageTypeContext.getStorageTypeService(storageType);
|
||||
|
||||
List<StorageConfig> storageConfigList;
|
||||
if (updateFlag) {
|
||||
storageConfigList = storageConfigRepository.findByDriveId(driveConfigDTO.getId());
|
||||
} else {
|
||||
storageConfigList = storageTypeService.storageStrategyConfigList();
|
||||
}
|
||||
List<StorageConfig> storageConfigList = storageTypeService.storageStrategyConfigList();
|
||||
storageConfigRepository.deleteByDriveId(driveConfigDTO.getId());
|
||||
|
||||
for (StorageConfig storageConfig : storageConfigList) {
|
||||
String key = storageConfig.getKey();
|
||||
@@ -213,6 +230,46 @@ public class DriveConfigService {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询驱动器最大的 ID
|
||||
*
|
||||
* @return 驱动器最大 ID
|
||||
*/
|
||||
public Integer selectNextId() {
|
||||
Integer maxId = driverConfigRepository.selectMaxId();
|
||||
if (maxId == null) {
|
||||
return 1;
|
||||
} else {
|
||||
return maxId + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新驱动器 ID
|
||||
*
|
||||
* @param updateId
|
||||
* 驱动器原 ID
|
||||
*
|
||||
* @param newId
|
||||
* 驱动器新 ID
|
||||
*/
|
||||
@Transactional
|
||||
public void updateId(Integer updateId, Integer newId) {
|
||||
zFileCache.clear(updateId);
|
||||
driverConfigRepository.updateId(updateId, newId);
|
||||
storageConfigRepository.updateDriveId(updateId, newId);
|
||||
filterConfigRepository.updateDriveId(updateId, newId);
|
||||
|
||||
|
||||
String updateSubPath = StringUtils.concatUrl(StringUtils.DELIMITER_STR, ZFileConstant.DIRECT_LINK_PREFIX, String.valueOf(updateId));
|
||||
String newSubPath = StringUtils.concatUrl(StringUtils.DELIMITER_STR, ZFileConstant.DIRECT_LINK_PREFIX, String.valueOf(newId));
|
||||
|
||||
shortLinkConfigRepository.updateUrlDriveId(updateSubPath, newSubPath);
|
||||
driveContext.updateDriveId(updateId, newId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除指定驱动器设置, 会级联删除其参数设置
|
||||
*
|
||||
@@ -269,24 +326,6 @@ public class DriveConfigService {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新指定驱动器的缓存启用状态
|
||||
*
|
||||
* @param driveId
|
||||
* 驱动器 ID
|
||||
*
|
||||
* @param autoRefreshCache
|
||||
* 是否启用缓存自动刷新
|
||||
*/
|
||||
public void updateAutoRefreshCacheStatus(Integer driveId, Boolean autoRefreshCache) {
|
||||
DriveConfig driveConfig = findById(driveId);
|
||||
if (driveConfig != null) {
|
||||
driveConfig.setAutoRefreshCache(autoRefreshCache);
|
||||
driverConfigRepository.save(driveConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定驱动器的缓存信息
|
||||
* @param driveId
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package im.zhaojun.zfile.service;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import im.zhaojun.zfile.model.entity.FilterConfig;
|
||||
import im.zhaojun.zfile.repository.FilterConfigRepository;
|
||||
@@ -11,13 +9,9 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.PathMatcher;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package im.zhaojun.zfile.service;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import im.zhaojun.zfile.model.entity.ShortLinkConfig;
|
||||
import im.zhaojun.zfile.repository.ShortLinkConfigRepository;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class ShortLinkConfigService {
|
||||
|
||||
@Resource
|
||||
private ShortLinkConfigRepository shortLinkConfigRepository;
|
||||
|
||||
public ShortLinkConfig findByKey(String key) {
|
||||
return shortLinkConfigRepository.findFirstByKey(key);
|
||||
}
|
||||
|
||||
public ShortLinkConfig findById(Integer id) {
|
||||
Optional<ShortLinkConfig> shortLinkConfigOptional = shortLinkConfigRepository.findById(id);
|
||||
return shortLinkConfigOptional.orElse(null);
|
||||
}
|
||||
|
||||
public ShortLinkConfig findByUrl(String url) {
|
||||
return shortLinkConfigRepository.findFirstByUrl(url);
|
||||
}
|
||||
|
||||
public void save(ShortLinkConfig shortLinkConfig) {
|
||||
shortLinkConfig.setCreateDate(new Date());
|
||||
shortLinkConfigRepository.save(shortLinkConfig);
|
||||
}
|
||||
|
||||
public Page<ShortLinkConfig> find(String key,
|
||||
String url,
|
||||
String dateFrom,
|
||||
String dateTo,
|
||||
Integer page,
|
||||
Integer limit,
|
||||
String orderBy,
|
||||
String orderDirection) {
|
||||
|
||||
Sort sort = Sort.by("desc".equals(orderDirection) ? Sort.Direction.DESC : Sort.Direction.ASC, orderBy);
|
||||
Pageable pageable = PageRequest.of(page - 1, limit, sort);
|
||||
|
||||
Specification<ShortLinkConfig> specification = (root, criteriaQuery, criteriaBuilder) -> {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
if (StrUtil.isNotEmpty(dateFrom) && StrUtil.isNotEmpty(dateTo)) {
|
||||
predicates.add(criteriaBuilder.between(root.get("createDate"),
|
||||
DateUtil.parseDateTime(dateFrom + " 00:00:00"),
|
||||
DateUtil.parseDateTime(dateTo + " 23:59:59")));
|
||||
}
|
||||
|
||||
if (StrUtil.isNotEmpty(key)) {
|
||||
predicates.add(criteriaBuilder.like(root.get("key"), "%" + key + '%'));
|
||||
}
|
||||
|
||||
if (StrUtil.isNotEmpty(url)) {
|
||||
predicates.add(criteriaBuilder.like(root.get("url"), "%" + url + '%'));
|
||||
}
|
||||
|
||||
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
|
||||
};
|
||||
return shortLinkConfigRepository.findAll(specification, pageable);
|
||||
}
|
||||
|
||||
public void deleteById(Integer id) {
|
||||
shortLinkConfigRepository.deleteById(id);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package im.zhaojun.zfile.service;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import im.zhaojun.zfile.cache.ZFileCache;
|
||||
import im.zhaojun.zfile.exception.InvalidDriveException;
|
||||
@@ -163,4 +164,24 @@ public class SystemConfigService {
|
||||
return systemConfigDTO.getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取站点域名
|
||||
*
|
||||
* @return 站点域名
|
||||
*/
|
||||
public String getDomain() {
|
||||
SystemConfigDTO systemConfigDTO = getSystemConfig();
|
||||
return systemConfigDTO.getDomain();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取是否已安装初始化
|
||||
*
|
||||
* @return 是否已安装初始化
|
||||
*/
|
||||
public boolean getIsInstall() {
|
||||
SystemConfigDTO systemConfigDTO = getSystemConfig();
|
||||
return StrUtil.isNotEmpty(systemConfigDTO.getUsername());
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,9 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
@@ -121,6 +124,30 @@ public abstract class AbstractBaseFileService implements BaseFileService {
|
||||
public abstract List<StorageConfig> storageStrategyConfigList();
|
||||
|
||||
|
||||
/**
|
||||
* 合并数据库查询到的驱动器参数和驱动器本身支持的参数列表, 防止获取新增参数字段时出现空指针异常
|
||||
*
|
||||
* @param dbStorageConfigList
|
||||
* 数据库查询到的存储列表
|
||||
*/
|
||||
public void mergeStrategyConfig(Map<String, StorageConfig> dbStorageConfigList) {
|
||||
// 获取驱动器支持的参数列表
|
||||
List<StorageConfig> storageConfigs = this.storageStrategyConfigList();
|
||||
|
||||
// 比对数据库已存储的参数列表和驱动器支持的参数列表, 找出新增的支持项
|
||||
Set<String> dbConfigKeySet = dbStorageConfigList.keySet();
|
||||
Set<String> allKeySet = storageConfigs.stream().map(StorageConfig::getKey).collect(Collectors.toSet());
|
||||
|
||||
allKeySet.removeAll(dbConfigKeySet);
|
||||
|
||||
// 对于新增的参数, put 到数据库查询的 Map 中, 防止程序获取时出现 NPE.
|
||||
for (String key : allKeySet) {
|
||||
StorageConfig storageConfig = new StorageConfig();
|
||||
storageConfig.setValue("");
|
||||
dbStorageConfigList.put(key, storageConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索文件
|
||||
*
|
||||
@@ -144,4 +171,11 @@ public abstract class AbstractBaseFileService implements BaseFileService {
|
||||
*/
|
||||
public abstract FileItemDTO getFileItem(String path);
|
||||
|
||||
public Integer getDriveId() {
|
||||
return driveId;
|
||||
}
|
||||
|
||||
public void setDriveId(Integer driveId) {
|
||||
this.driveId = driveId;
|
||||
}
|
||||
}
|
||||
@@ -1,260 +1,26 @@
|
||||
package im.zhaojun.zfile.service.base;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import im.zhaojun.zfile.model.dto.FileItemDTO;
|
||||
import im.zhaojun.zfile.model.entity.StorageConfig;
|
||||
import im.zhaojun.zfile.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.zfile.model.support.OneDriveToken;
|
||||
import im.zhaojun.zfile.repository.StorageConfigRepository;
|
||||
import im.zhaojun.zfile.service.StorageConfigService;
|
||||
import im.zhaojun.zfile.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Zhao Jun
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractOneDriveServiceBase extends AbstractBaseFileService {
|
||||
|
||||
protected static final String DRIVER_INFO_URL = "https://{graphEndPoint}/v1.0/me/drives";
|
||||
|
||||
protected static final String DRIVER_ROOT_URL = "https://{graphEndPoint}/v1.0/me/drive/root/children";
|
||||
|
||||
protected static final String DRIVER_ITEMS_URL = "https://{graphEndPoint}/v1.0/me/drive/root:{path}:/children";
|
||||
|
||||
protected static final String DRIVER_ITEM_URL = "https://{graphEndPoint}/v1.0/me/drive/root:{path}";
|
||||
|
||||
protected static final String AUTHENTICATE_URL = "https://{authenticateEndPoint}/common/oauth2/v2.0/token";
|
||||
|
||||
private static final String ONE_DRIVE_FILE_FLAG = "file";
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private RestTemplate oneDriveRestTemplate;
|
||||
|
||||
@Resource
|
||||
private StorageConfigRepository storageConfigRepository;
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
/**
|
||||
* 根据 RefreshToken 刷新 AccessToken, 返回刷新后的 Token.
|
||||
*
|
||||
* @return 刷新后的 Token
|
||||
*/
|
||||
public OneDriveToken getRefreshToken() {
|
||||
StorageConfig refreshStorageConfig =
|
||||
storageConfigRepository.findByDriveIdAndKey(driveId, StorageConfigConstant.REFRESH_TOKEN_KEY);
|
||||
|
||||
String param = "client_id=" + getClientId() +
|
||||
"&redirect_uri=" + getRedirectUri() +
|
||||
"&client_secret=" + getClientSecret() +
|
||||
"&refresh_token=" + refreshStorageConfig.getValue() +
|
||||
"&grant_type=refresh_token";
|
||||
|
||||
String fullAuthenticateUrl = AUTHENTICATE_URL.replace("{authenticateEndPoint}", getAuthenticateEndPoint());
|
||||
HttpRequest post = HttpUtil.createPost(fullAuthenticateUrl);
|
||||
|
||||
post.body(param, "application/x-www-form-urlencoded");
|
||||
HttpResponse response = post.execute();
|
||||
return JSONObject.parseObject(response.body(), OneDriveToken.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* OAuth2 协议中, 根据 code 换取 access_token 和 refresh_token.
|
||||
*
|
||||
* @param code
|
||||
* 代码
|
||||
*
|
||||
* @return 获取的 Token 信息.
|
||||
*/
|
||||
public OneDriveToken getToken(String code) {
|
||||
String param = "client_id=" + getClientId() +
|
||||
"&redirect_uri=" + getRedirectUri() +
|
||||
"&client_secret=" + getClientSecret() +
|
||||
"&code=" + code +
|
||||
"&scope=" + getScope() +
|
||||
"&grant_type=authorization_code";
|
||||
|
||||
String fullAuthenticateUrl = AUTHENTICATE_URL.replace("{authenticateEndPoint}", getAuthenticateEndPoint());
|
||||
HttpRequest post = HttpUtil.createPost(fullAuthenticateUrl);
|
||||
|
||||
post.body(param, "application/x-www-form-urlencoded");
|
||||
HttpResponse response = post.execute();
|
||||
return JSONObject.parseObject(response.body(), OneDriveToken.class);
|
||||
}
|
||||
public abstract class AbstractOneDriveServiceBase extends MicrosoftDriveServiceBase {
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) {
|
||||
path = StringUtils.removeFirstSeparator(path);
|
||||
String fullPath = StringUtils.getFullPath(basePath, path);
|
||||
|
||||
List<FileItemDTO> result = new ArrayList<>();
|
||||
String nextLink = null;
|
||||
|
||||
do {
|
||||
|
||||
String requestUrl;
|
||||
|
||||
if (nextLink != null) {
|
||||
nextLink = nextLink.replace("+", "%2B");
|
||||
requestUrl = URLUtil.decode(nextLink);
|
||||
}else if (ZFileConstant.PATH_SEPARATOR.equalsIgnoreCase(fullPath) || "".equalsIgnoreCase(fullPath)) {
|
||||
requestUrl = DRIVER_ROOT_URL;
|
||||
} else {
|
||||
requestUrl = DRIVER_ITEMS_URL;
|
||||
}
|
||||
fullPath = StringUtils.removeLastSeparator(fullPath);
|
||||
|
||||
JSONObject root;
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("driveId", driveId.toString());
|
||||
HttpEntity<Object> entity = new HttpEntity<>(headers);
|
||||
|
||||
try {
|
||||
root = oneDriveRestTemplate.exchange(requestUrl, HttpMethod.GET, entity, JSONObject.class, getGraphEndPoint(), fullPath).getBody();
|
||||
} catch (HttpClientErrorException e) {
|
||||
log.debug("调用 OneDrive 时出现了网络异常, 已尝试重新刷新 token 后再试.", e);
|
||||
refreshOneDriveToken();
|
||||
root = oneDriveRestTemplate.exchange(requestUrl, HttpMethod.GET, entity, JSONObject.class, getGraphEndPoint(), fullPath).getBody();
|
||||
}
|
||||
|
||||
if (root == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
nextLink = root.getString("@odata.nextLink");
|
||||
|
||||
JSONArray fileList = root.getJSONArray("value");
|
||||
|
||||
for (int i = 0; i < fileList.size(); i++) {
|
||||
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
JSONObject fileItem = fileList.getJSONObject(i);
|
||||
fileItemDTO.setName(fileItem.getString("name"));
|
||||
fileItemDTO.setSize(fileItem.getLong("size"));
|
||||
fileItemDTO.setTime(fileItem.getDate("lastModifiedDateTime"));
|
||||
|
||||
if (fileItem.containsKey("file")) {
|
||||
fileItemDTO.setUrl(fileItem.getString("@microsoft.graph.downloadUrl"));
|
||||
fileItemDTO.setType(FileTypeEnum.FILE);
|
||||
} else {
|
||||
fileItemDTO.setType(FileTypeEnum.FOLDER);
|
||||
}
|
||||
|
||||
fileItemDTO.setPath(path);
|
||||
result.add(fileItemDTO);
|
||||
}
|
||||
} while (nextLink != null);
|
||||
|
||||
return result;
|
||||
public String getType() {
|
||||
return "me";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public FileItemDTO getFileItem(String path) {
|
||||
|
||||
String fullPath = StringUtils.getFullPath(basePath, path);
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("driveId", driveId.toString());
|
||||
HttpEntity<Object> entity = new HttpEntity<>(headers);
|
||||
|
||||
JSONObject fileItem = oneDriveRestTemplate.exchange(DRIVER_ITEM_URL, HttpMethod.GET, entity, JSONObject.class, getGraphEndPoint(), fullPath).getBody();
|
||||
|
||||
if (fileItem == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
fileItemDTO.setName(fileItem.getString("name"));
|
||||
fileItemDTO.setSize(fileItem.getLong("size"));
|
||||
fileItemDTO.setTime(fileItem.getDate("lastModifiedDateTime"));
|
||||
|
||||
if (fileItem.containsKey(ONE_DRIVE_FILE_FLAG)) {
|
||||
fileItemDTO.setUrl(fileItem.getString("@microsoft.graph.downloadUrl"));
|
||||
fileItemDTO.setType(FileTypeEnum.FILE);
|
||||
} else {
|
||||
fileItemDTO.setType(FileTypeEnum.FOLDER);
|
||||
}
|
||||
|
||||
fileItemDTO.setPath(path);
|
||||
return fileItemDTO;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取 GraphEndPoint, 对于不同版本的 OneDrive, 此地址会不同.
|
||||
* @return Graph 连接点
|
||||
*/
|
||||
public abstract String getGraphEndPoint();
|
||||
|
||||
|
||||
/**
|
||||
* 获取 AuthenticateEndPoint, 对于不同版本的 OneDrive, 此地址会不同.
|
||||
* @return Authenticate 连接点
|
||||
*/
|
||||
public abstract String getAuthenticateEndPoint();
|
||||
|
||||
/**
|
||||
* 获取 Client ID.
|
||||
* @return Client Id
|
||||
*/
|
||||
public abstract String getClientId();
|
||||
|
||||
/**
|
||||
* 获取重定向地址.
|
||||
* @return 重定向地址
|
||||
*/
|
||||
public abstract String getRedirectUri();
|
||||
|
||||
/**
|
||||
* 获取 Client Secret 密钥.
|
||||
* @return Client Secret 密钥.
|
||||
*/
|
||||
public abstract String getClientSecret();
|
||||
|
||||
/**
|
||||
* 获取 API Scope.
|
||||
* @return Scope
|
||||
*/
|
||||
public abstract String getScope();
|
||||
|
||||
public void refreshOneDriveToken() {
|
||||
OneDriveToken refreshToken = getRefreshToken();
|
||||
|
||||
if (refreshToken.getAccessToken() == null || refreshToken.getRefreshToken() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
StorageConfig accessTokenConfig =
|
||||
storageConfigService.findByDriveIdAndKey(driveId, StorageConfigConstant.ACCESS_TOKEN_KEY);
|
||||
StorageConfig refreshTokenConfig =
|
||||
storageConfigService.findByDriveIdAndKey(driveId, StorageConfigConstant.REFRESH_TOKEN_KEY);
|
||||
accessTokenConfig.setValue(refreshToken.getAccessToken());
|
||||
refreshTokenConfig.setValue(refreshToken.getRefreshToken());
|
||||
|
||||
storageConfigService.updateStorageConfig(Arrays.asList(accessTokenConfig, refreshTokenConfig));
|
||||
public String getDownloadUrl(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -263,7 +29,7 @@ public abstract class AbstractOneDriveServiceBase extends AbstractBaseFileServic
|
||||
add(new StorageConfig("accessToken", "访问令牌"));
|
||||
add(new StorageConfig("refreshToken", "刷新令牌"));
|
||||
add(new StorageConfig("basePath", "基路径"));
|
||||
add(new StorageConfig("proxyDomain", "反代域名"));
|
||||
}};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -73,7 +73,11 @@ public abstract class AbstractS3BaseFileService extends AbstractBaseFileService
|
||||
fileItemDTO.setTime(s.getLastModified());
|
||||
fileItemDTO.setType(FileTypeEnum.FILE);
|
||||
fileItemDTO.setPath(path);
|
||||
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(path, fileItemDTO.getName())));
|
||||
|
||||
String fullPathAndName = StringUtils.concatUrl(path, fileItemDTO.getName());
|
||||
String directlink = StringUtils.generatorLink(driveId, fullPathAndName);
|
||||
fileItemDTO.setUrl(directlink);
|
||||
|
||||
fileItemList.add(fileItemDTO);
|
||||
}
|
||||
|
||||
@@ -129,6 +133,7 @@ public abstract class AbstractS3BaseFileService extends AbstractBaseFileService
|
||||
for (FileItemDTO fileItemDTO : list) {
|
||||
String fullPath = StringUtils.concatUrl(fileItemDTO.getPath(), fileItemDTO.getName());
|
||||
if (Objects.equals(fullPath, path)) {
|
||||
fileItemDTO.setUrl(getDownloadUrl(path));
|
||||
return fileItemDTO;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package im.zhaojun.zfile.service.base;
|
||||
|
||||
import im.zhaojun.zfile.model.entity.StorageConfig;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractSharePointServiceBase extends MicrosoftDriveServiceBase {
|
||||
|
||||
protected String siteId;
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "sites/" + siteId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StorageConfig> storageStrategyConfigList() {
|
||||
return new ArrayList<StorageConfig>() {{
|
||||
add(new StorageConfig("accessToken", "访问令牌"));
|
||||
add(new StorageConfig("refreshToken", "刷新令牌"));
|
||||
add(new StorageConfig("proxyDomain", "反代域名"));
|
||||
add(new StorageConfig("basePath", "基路径"));
|
||||
add(new StorageConfig("siteName", "站点名称"));
|
||||
add(new StorageConfig("siteId", "SiteId"));
|
||||
add(new StorageConfig("siteType", "siteType"));
|
||||
}};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
package im.zhaojun.zfile.service.base;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import im.zhaojun.zfile.model.dto.FileItemDTO;
|
||||
import im.zhaojun.zfile.model.entity.StorageConfig;
|
||||
import im.zhaojun.zfile.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.zfile.model.support.OneDriveToken;
|
||||
import im.zhaojun.zfile.repository.StorageConfigRepository;
|
||||
import im.zhaojun.zfile.service.StorageConfigService;
|
||||
import im.zhaojun.zfile.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public abstract class MicrosoftDriveServiceBase extends AbstractBaseFileService {
|
||||
|
||||
/**
|
||||
* 获取根文件 API URI
|
||||
*/
|
||||
protected static final String DRIVER_ROOT_URL = "https://{graphEndPoint}/v1.0/{type}/drive/root/children";
|
||||
|
||||
/**
|
||||
* 获取非根文件 API URI
|
||||
*/
|
||||
protected static final String DRIVER_ITEMS_URL = "https://{graphEndPoint}/v1.0/{type}/drive/root:{path}:/children";
|
||||
|
||||
/**
|
||||
* 获取单文件 API URI
|
||||
*/
|
||||
protected static final String DRIVER_ITEM_URL = "https://{graphEndPoint}/v1.0/{type}/drive/root:{path}";
|
||||
|
||||
/**
|
||||
* 根据 RefreshToken 获取 AccessToken API URI
|
||||
*/
|
||||
protected static final String AUTHENTICATE_URL = "https://{authenticateEndPoint}/common/oauth2/v2.0/token";
|
||||
|
||||
/**
|
||||
* OneDrive 文件类型
|
||||
*/
|
||||
private static final String ONE_DRIVE_FILE_FLAG = "file";
|
||||
|
||||
protected String proxyDomain;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private RestTemplate oneDriveRestTemplate;
|
||||
|
||||
@Resource
|
||||
private StorageConfigRepository storageConfigRepository;
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
/**
|
||||
* 根据 RefreshToken 刷新 AccessToken, 返回刷新后的 Token.
|
||||
*
|
||||
* @return 刷新后的 Token
|
||||
*/
|
||||
public OneDriveToken getRefreshToken() {
|
||||
StorageConfig refreshStorageConfig =
|
||||
storageConfigRepository.findByDriveIdAndKey(driveId, StorageConfigConstant.REFRESH_TOKEN_KEY);
|
||||
|
||||
String param = "client_id=" + getClientId() +
|
||||
"&redirect_uri=" + getRedirectUri() +
|
||||
"&client_secret=" + getClientSecret() +
|
||||
"&refresh_token=" + refreshStorageConfig.getValue() +
|
||||
"&grant_type=refresh_token";
|
||||
|
||||
String fullAuthenticateUrl = AUTHENTICATE_URL.replace("{authenticateEndPoint}", getAuthenticateEndPoint());
|
||||
HttpRequest post = HttpUtil.createPost(fullAuthenticateUrl);
|
||||
|
||||
post.body(param, "application/x-www-form-urlencoded");
|
||||
HttpResponse response = post.execute();
|
||||
return JSONObject.parseObject(response.body(), OneDriveToken.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* OAuth2 协议中, 根据 code 换取 access_token 和 refresh_token.
|
||||
*
|
||||
* @param code
|
||||
* 代码
|
||||
*
|
||||
* @return 获取的 Token 信息.
|
||||
*/
|
||||
public OneDriveToken getToken(String code) {
|
||||
String param = "client_id=" + getClientId() +
|
||||
"&redirect_uri=" + getRedirectUri() +
|
||||
"&client_secret=" + getClientSecret() +
|
||||
"&code=" + code +
|
||||
"&scope=" + getScope() +
|
||||
"&grant_type=authorization_code";
|
||||
|
||||
String fullAuthenticateUrl = AUTHENTICATE_URL.replace("{authenticateEndPoint}", getAuthenticateEndPoint());
|
||||
HttpRequest post = HttpUtil.createPost(fullAuthenticateUrl);
|
||||
|
||||
post.body(param, "application/x-www-form-urlencoded");
|
||||
HttpResponse response = post.execute();
|
||||
return JSONObject.parseObject(response.body(), OneDriveToken.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) {
|
||||
path = StringUtils.removeFirstSeparator(path);
|
||||
String fullPath = StringUtils.getFullPath(basePath, path);
|
||||
|
||||
List<FileItemDTO> result = new ArrayList<>();
|
||||
String nextLink = null;
|
||||
|
||||
do {
|
||||
|
||||
String requestUrl;
|
||||
|
||||
if (nextLink != null) {
|
||||
nextLink = nextLink.replace("+", "%2B");
|
||||
requestUrl = URLUtil.decode(nextLink);
|
||||
}else if (ZFileConstant.PATH_SEPARATOR.equalsIgnoreCase(fullPath) || "".equalsIgnoreCase(fullPath)) {
|
||||
requestUrl = DRIVER_ROOT_URL;
|
||||
} else {
|
||||
requestUrl = DRIVER_ITEMS_URL;
|
||||
}
|
||||
fullPath = StringUtils.removeLastSeparator(fullPath);
|
||||
|
||||
JSONObject root;
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("driveId", driveId.toString());
|
||||
HttpEntity<Object> entity = new HttpEntity<>(headers);
|
||||
|
||||
try {
|
||||
root = oneDriveRestTemplate.exchange(requestUrl, HttpMethod.GET, entity, JSONObject.class, getGraphEndPoint(), getType(), fullPath).getBody();
|
||||
} catch (HttpClientErrorException e) {
|
||||
log.debug("调用 OneDrive 时出现了网络异常, 响应信息: {}, 已尝试重新刷新 token 后再试.", e.getResponseBodyAsString());
|
||||
refreshOneDriveToken();
|
||||
root = oneDriveRestTemplate.exchange(requestUrl, HttpMethod.GET, entity, JSONObject.class, getGraphEndPoint(), getType(), fullPath).getBody();
|
||||
}
|
||||
|
||||
if (root == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
nextLink = root.getString("@odata.nextLink");
|
||||
|
||||
JSONArray fileList = root.getJSONArray("value");
|
||||
|
||||
for (int i = 0; i < fileList.size(); i++) {
|
||||
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
JSONObject fileItem = fileList.getJSONObject(i);
|
||||
fileItemDTO.setName(fileItem.getString("name"));
|
||||
fileItemDTO.setSize(fileItem.getLong("size"));
|
||||
fileItemDTO.setTime(fileItem.getDate("lastModifiedDateTime"));
|
||||
|
||||
if (fileItem.containsKey("file")) {
|
||||
String originUrl = fileItem.getString("@microsoft.graph.downloadUrl");
|
||||
if (StringUtils.isNotNullOrEmpty(proxyDomain)) {
|
||||
originUrl = StringUtils.replaceHost(originUrl, proxyDomain);
|
||||
}
|
||||
fileItemDTO.setUrl(originUrl);
|
||||
fileItemDTO.setType(FileTypeEnum.FILE);
|
||||
} else {
|
||||
fileItemDTO.setType(FileTypeEnum.FOLDER);
|
||||
}
|
||||
|
||||
fileItemDTO.setPath(path);
|
||||
result.add(fileItemDTO);
|
||||
}
|
||||
} while (nextLink != null);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileItemDTO getFileItem(String path) {
|
||||
|
||||
String fullPath = StringUtils.getFullPath(basePath, path);
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("driveId", driveId.toString());
|
||||
HttpEntity<Object> entity = new HttpEntity<>(headers);
|
||||
|
||||
JSONObject fileItem;
|
||||
|
||||
try {
|
||||
fileItem = oneDriveRestTemplate.exchange(DRIVER_ITEM_URL, HttpMethod.GET, entity, JSONObject.class, getGraphEndPoint(), getType(), fullPath).getBody();
|
||||
} catch (HttpClientErrorException e) {
|
||||
log.debug("调用 OneDrive 时出现了网络异常, 响应信息: {}, 已尝试重新刷新 token 后再试.", e.getResponseBodyAsString());
|
||||
refreshOneDriveToken();
|
||||
fileItem = oneDriveRestTemplate.exchange(DRIVER_ITEM_URL, HttpMethod.GET, entity, JSONObject.class, getGraphEndPoint(), getType(), fullPath).getBody();
|
||||
}
|
||||
|
||||
if (fileItem == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
fileItemDTO.setName(fileItem.getString("name"));
|
||||
fileItemDTO.setSize(fileItem.getLong("size"));
|
||||
fileItemDTO.setTime(fileItem.getDate("lastModifiedDateTime"));
|
||||
|
||||
if (fileItem.containsKey(ONE_DRIVE_FILE_FLAG)) {
|
||||
String originUrl = fileItem.getString("@microsoft.graph.downloadUrl");
|
||||
if (StringUtils.isNotNullOrEmpty(proxyDomain)) {
|
||||
originUrl = StringUtils.replaceHost(originUrl, proxyDomain);
|
||||
}
|
||||
fileItemDTO.setUrl(originUrl);
|
||||
fileItemDTO.setType(FileTypeEnum.FILE);
|
||||
} else {
|
||||
fileItemDTO.setType(FileTypeEnum.FOLDER);
|
||||
}
|
||||
|
||||
fileItemDTO.setPath(path);
|
||||
return fileItemDTO;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取存储类型, 对于 OneDrive 或 SharePoint, 此地址会不同.
|
||||
* @return Graph 连接点
|
||||
*/
|
||||
public abstract String getType();
|
||||
|
||||
/**
|
||||
* 获取 GraphEndPoint, 对于不同版本的 OneDrive, 此地址会不同.
|
||||
* @return Graph 连接点
|
||||
*/
|
||||
public abstract String getGraphEndPoint();
|
||||
|
||||
|
||||
/**
|
||||
* 获取 AuthenticateEndPoint, 对于不同版本的 OneDrive, 此地址会不同.
|
||||
* @return Authenticate 连接点
|
||||
*/
|
||||
public abstract String getAuthenticateEndPoint();
|
||||
|
||||
/**
|
||||
* 获取 Client ID.
|
||||
* @return Client Id
|
||||
*/
|
||||
public abstract String getClientId();
|
||||
|
||||
/**
|
||||
* 获取重定向地址.
|
||||
* @return 重定向地址
|
||||
*/
|
||||
public abstract String getRedirectUri();
|
||||
|
||||
/**
|
||||
* 获取 Client Secret 密钥.
|
||||
* @return Client Secret 密钥.
|
||||
*/
|
||||
public abstract String getClientSecret();
|
||||
|
||||
/**
|
||||
* 获取 API Scope.
|
||||
* @return Scope
|
||||
*/
|
||||
public abstract String getScope();
|
||||
|
||||
|
||||
/**
|
||||
* 刷新当前驱动器 AccessToken
|
||||
*/
|
||||
public void refreshOneDriveToken() {
|
||||
OneDriveToken refreshToken = getRefreshToken();
|
||||
|
||||
if (refreshToken.getAccessToken() == null || refreshToken.getRefreshToken() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
StorageConfig accessTokenConfig =
|
||||
storageConfigService.findByDriveIdAndKey(driveId, StorageConfigConstant.ACCESS_TOKEN_KEY);
|
||||
StorageConfig refreshTokenConfig =
|
||||
storageConfigService.findByDriveIdAndKey(driveId, StorageConfigConstant.REFRESH_TOKEN_KEY);
|
||||
accessTokenConfig.setValue(refreshToken.getAccessToken());
|
||||
refreshTokenConfig.setValue(refreshToken.getRefreshToken());
|
||||
|
||||
storageConfigService.updateStorageConfig(Arrays.asList(accessTokenConfig, refreshTokenConfig));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,6 +35,7 @@ public class AliyunServiceImpl extends AbstractS3BaseFileService implements Base
|
||||
this.driveId = driveId;
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByDriveId(driveId);
|
||||
this.mergeStrategyConfig(stringStorageConfigMap);
|
||||
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
|
||||
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
|
||||
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
|
||||
|
||||
@@ -13,7 +13,6 @@ import im.zhaojun.zfile.service.base.BaseFileService;
|
||||
import im.zhaojun.zfile.util.StringUtils;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.net.ftp.FTP;
|
||||
import org.apache.commons.net.ftp.FTPClientConfig;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
@@ -55,6 +54,7 @@ public class FtpServiceImpl extends AbstractBaseFileService implements BaseFileS
|
||||
this.driveId = driveId;
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByDriveId(driveId);
|
||||
this.mergeStrategyConfig(stringStorageConfigMap);
|
||||
host = stringStorageConfigMap.get(StorageConfigConstant.HOST_KEY).getValue();
|
||||
port = stringStorageConfigMap.get(StorageConfigConstant.PORT_KEY).getValue();
|
||||
username = stringStorageConfigMap.get(StorageConfigConstant.USERNAME_KEY).getValue();
|
||||
@@ -65,7 +65,6 @@ public class FtpServiceImpl extends AbstractBaseFileService implements BaseFileS
|
||||
isInitialized = false;
|
||||
} else {
|
||||
ftp = new Ftp(host, Integer.parseInt(port), username, password, StandardCharsets.UTF_8);
|
||||
ftp.getClient().configure(new FTPClientConfig(FTPClientConfig.SYST_UNIX));
|
||||
ftp.getClient().type(FTP.BINARY_FILE_TYPE);
|
||||
testConnection();
|
||||
isInitialized = true;
|
||||
@@ -83,7 +82,6 @@ public class FtpServiceImpl extends AbstractBaseFileService implements BaseFileS
|
||||
ftpFiles = ftp.getClient().listFiles(fullPath);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// ignore
|
||||
}
|
||||
|
||||
List<FileItemDTO> fileItemList = new ArrayList<>();
|
||||
|
||||
@@ -35,6 +35,7 @@ public class HuaweiServiceImpl extends AbstractS3BaseFileService implements Base
|
||||
this.driveId = driveId;
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByDriveId(driveId);
|
||||
this.mergeStrategyConfig(stringStorageConfigMap);
|
||||
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
|
||||
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
|
||||
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package im.zhaojun.zfile.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import im.zhaojun.zfile.exception.InitializeDriveException;
|
||||
import im.zhaojun.zfile.exception.NotExistFileException;
|
||||
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
|
||||
@@ -25,6 +26,7 @@ import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -52,6 +54,7 @@ public class LocalServiceImpl extends AbstractBaseFileService implements BaseFil
|
||||
this.driveId = driveId;
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByDriveId(driveId);
|
||||
this.mergeStrategyConfig(stringStorageConfigMap);
|
||||
filePath = stringStorageConfigMap.get(StorageConfigConstant.FILE_PATH_KEY).getValue();
|
||||
if (Objects.isNull(filePath)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
@@ -71,6 +74,9 @@ public class LocalServiceImpl extends AbstractBaseFileService implements BaseFil
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) throws FileNotFoundException {
|
||||
if (StrUtil.startWith(path, "..")) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<FileItemDTO> fileItemList = new ArrayList<>();
|
||||
|
||||
String fullPath = StringUtils.removeDuplicateSeparator(filePath + path);
|
||||
@@ -123,7 +129,7 @@ public class LocalServiceImpl extends AbstractBaseFileService implements BaseFil
|
||||
|
||||
@Override
|
||||
public FileItemDTO getFileItem(String path) {
|
||||
String fullPath = StringUtils.concatPath(filePath, path);
|
||||
String fullPath = filePath + path;
|
||||
|
||||
File file = new File(fullPath);
|
||||
|
||||
|
||||
@@ -35,9 +35,12 @@ public class MinIOServiceImpl extends AbstractS3BaseFileService implements BaseF
|
||||
this.driveId = driveId;
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByDriveId(driveId);
|
||||
this.mergeStrategyConfig(stringStorageConfigMap);
|
||||
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
|
||||
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
|
||||
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
|
||||
String region = stringStorageConfigMap.get(StorageConfigConstant.REGION_KEY).getValue();
|
||||
|
||||
bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
|
||||
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
isPrivate = Convert.toBool(stringStorageConfigMap.get(StorageConfigConstant.IS_PRIVATE).getValue(), true);
|
||||
@@ -50,7 +53,7 @@ public class MinIOServiceImpl extends AbstractS3BaseFileService implements BaseF
|
||||
s3Client = AmazonS3ClientBuilder.standard()
|
||||
.withPathStyleAccessEnabled(true)
|
||||
.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "minio")).build();
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, region)).build();
|
||||
|
||||
testConnection();
|
||||
isInitialized = true;
|
||||
@@ -71,6 +74,7 @@ public class MinIOServiceImpl extends AbstractS3BaseFileService implements BaseF
|
||||
add(new StorageConfig("secretKey", "SecretKey"));
|
||||
add(new StorageConfig("endPoint", "服务地址"));
|
||||
add(new StorageConfig("bucketName", "存储空间名称"));
|
||||
add(new StorageConfig("region", "地域"));
|
||||
add(new StorageConfig("basePath", "基路径"));
|
||||
add(new StorageConfig("isPrivate", "是否是私有空间"));
|
||||
}};
|
||||
|
||||
@@ -44,9 +44,15 @@ public class OneDriveChinaServiceImpl extends AbstractOneDriveServiceBase implem
|
||||
this.driveId = driveId;
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByDriveId(driveId);
|
||||
this.mergeStrategyConfig(stringStorageConfigMap);
|
||||
String accessToken = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_TOKEN_KEY).getValue();
|
||||
String refreshToken = stringStorageConfigMap.get(StorageConfigConstant.REFRESH_TOKEN_KEY).getValue();
|
||||
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
StorageConfig proxyDomainStorageConfig = stringStorageConfigMap.get(StorageConfigConstant.PROXY_DOMAIN);
|
||||
if (proxyDomainStorageConfig != null) {
|
||||
super.proxyDomain = proxyDomainStorageConfig.getValue();
|
||||
}
|
||||
|
||||
|
||||
if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
@@ -58,11 +64,6 @@ public class OneDriveChinaServiceImpl extends AbstractOneDriveServiceBase implem
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.ONE_DRIVE_CHINA;
|
||||
|
||||
@@ -44,9 +44,14 @@ public class OneDriveServiceImpl extends AbstractOneDriveServiceBase implements
|
||||
this.driveId = driveId;
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByDriveId(driveId);
|
||||
this.mergeStrategyConfig(stringStorageConfigMap);
|
||||
String accessToken = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_TOKEN_KEY).getValue();
|
||||
String refreshToken = stringStorageConfigMap.get(StorageConfigConstant.REFRESH_TOKEN_KEY).getValue();
|
||||
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
StorageConfig proxyDomainStorageConfig = stringStorageConfigMap.get(StorageConfigConstant.PROXY_DOMAIN);
|
||||
if (proxyDomainStorageConfig != null) {
|
||||
super.proxyDomain = proxyDomainStorageConfig.getValue();
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
@@ -58,11 +63,6 @@ public class OneDriveServiceImpl extends AbstractOneDriveServiceBase implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.ONE_DRIVE;
|
||||
|
||||
@@ -35,6 +35,7 @@ public class QiniuServiceImpl extends AbstractS3BaseFileService implements BaseF
|
||||
this.driveId = driveId;
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByDriveId(driveId);
|
||||
this.mergeStrategyConfig(stringStorageConfigMap);
|
||||
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
|
||||
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
|
||||
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package im.zhaojun.zfile.service.impl;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
@@ -35,9 +36,11 @@ public class S3ServiceImpl extends AbstractS3BaseFileService implements BaseFile
|
||||
this.driveId = driveId;
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByDriveId(driveId);
|
||||
this.mergeStrategyConfig(stringStorageConfigMap);
|
||||
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
|
||||
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
|
||||
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
|
||||
String region = stringStorageConfigMap.get(StorageConfigConstant.REGION_KEY).getValue();
|
||||
|
||||
super.domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
|
||||
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
@@ -52,11 +55,14 @@ public class S3ServiceImpl extends AbstractS3BaseFileService implements BaseFile
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
if (StrUtil.isNotEmpty(endPoint) && StrUtil.isEmpty(region)) {
|
||||
region = endPoint.split("\\.")[1];
|
||||
}
|
||||
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
|
||||
s3Client = AmazonS3ClientBuilder.standard()
|
||||
.withPathStyleAccessEnabled(isPathStyle)
|
||||
.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "")).build();
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, region)).build();
|
||||
|
||||
testConnection();
|
||||
isInitialized = true;
|
||||
@@ -75,6 +81,7 @@ public class S3ServiceImpl extends AbstractS3BaseFileService implements BaseFile
|
||||
add(new StorageConfig("secretKey", "SecretKey"));
|
||||
add(new StorageConfig("endPoint", "服务地址(EndPoint)"));
|
||||
add(new StorageConfig("bucketName", "存储空间名称"));
|
||||
add(new StorageConfig("region", "地域"));
|
||||
add(new StorageConfig("basePath", "基路径"));
|
||||
add(new StorageConfig("domain", "加速域名"));
|
||||
add(new StorageConfig("pathStyle", "域名风格"));
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package im.zhaojun.zfile.service.impl;
|
||||
|
||||
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.zfile.model.entity.StorageConfig;
|
||||
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.zfile.service.StorageConfigService;
|
||||
import im.zhaojun.zfile.service.base.AbstractSharePointServiceBase;
|
||||
import im.zhaojun.zfile.service.base.BaseFileService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
||||
public class SharePointChinaServiceImpl extends AbstractSharePointServiceBase implements BaseFileService {
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Value("${zfile.onedrive-china.clientId}")
|
||||
private String clientId;
|
||||
|
||||
@Value("${zfile.onedrive-china.redirectUri}")
|
||||
private String redirectUri;
|
||||
|
||||
@Value("${zfile.onedrive-china.clientSecret}")
|
||||
private String clientSecret;
|
||||
|
||||
@Value("${zfile.onedrive-china.scope}")
|
||||
private String scope;
|
||||
|
||||
@Override
|
||||
public void init(Integer driveId) {
|
||||
this.driveId = driveId;
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByDriveId(driveId);
|
||||
this.mergeStrategyConfig(stringStorageConfigMap);
|
||||
String accessToken = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_TOKEN_KEY).getValue();
|
||||
String refreshToken = stringStorageConfigMap.get(StorageConfigConstant.REFRESH_TOKEN_KEY).getValue();
|
||||
super.siteId = stringStorageConfigMap.get(StorageConfigConstant.SHAREPOINT_SITE_ID).getValue();
|
||||
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
StorageConfig proxyDomainStorageConfig = stringStorageConfigMap.get(StorageConfigConstant.PROXY_DOMAIN);
|
||||
if (proxyDomainStorageConfig != null) {
|
||||
super.proxyDomain = proxyDomainStorageConfig.getValue();
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
refreshOneDriveToken();
|
||||
testConnection();
|
||||
isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.SHAREPOINT_DRIVE_CHINA;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGraphEndPoint() {
|
||||
return "microsoftgraph.chinacloudapi.cn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticateEndPoint() {
|
||||
return "login.partner.microsoftonline.cn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package im.zhaojun.zfile.service.impl;
|
||||
|
||||
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.zfile.model.entity.StorageConfig;
|
||||
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.zfile.service.StorageConfigService;
|
||||
import im.zhaojun.zfile.service.base.AbstractSharePointServiceBase;
|
||||
import im.zhaojun.zfile.service.base.BaseFileService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
||||
public class SharePointServiceImpl extends AbstractSharePointServiceBase implements BaseFileService {
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Value("${zfile.onedrive.clientId}")
|
||||
protected String clientId;
|
||||
|
||||
@Value("${zfile.onedrive.redirectUri}")
|
||||
protected String redirectUri;
|
||||
|
||||
@Value("${zfile.onedrive.clientSecret}")
|
||||
protected String clientSecret;
|
||||
|
||||
@Value("${zfile.onedrive.scope}")
|
||||
protected String scope;
|
||||
|
||||
@Override
|
||||
public void init(Integer driveId) {
|
||||
this.driveId = driveId;
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByDriveId(driveId);
|
||||
this.mergeStrategyConfig(stringStorageConfigMap);
|
||||
String accessToken = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_TOKEN_KEY).getValue();
|
||||
String refreshToken = stringStorageConfigMap.get(StorageConfigConstant.REFRESH_TOKEN_KEY).getValue();
|
||||
super.siteId = stringStorageConfigMap.get(StorageConfigConstant.SHAREPOINT_SITE_ID).getValue();
|
||||
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
|
||||
StorageConfig proxyDomainStorageConfig = stringStorageConfigMap.get(StorageConfigConstant.PROXY_DOMAIN);
|
||||
if (proxyDomainStorageConfig != null) {
|
||||
super.proxyDomain = proxyDomainStorageConfig.getValue();
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
refreshOneDriveToken();
|
||||
testConnection();
|
||||
isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.SHAREPOINT_DRIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGraphEndPoint() {
|
||||
return "graph.microsoft.com";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticateEndPoint() {
|
||||
return "login.microsoftonline.com";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,6 +35,7 @@ public class TencentServiceImpl extends AbstractS3BaseFileService implements Bas
|
||||
this.driveId = driveId;
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByDriveId(driveId);
|
||||
this.mergeStrategyConfig(stringStorageConfigMap);
|
||||
String secretId = stringStorageConfigMap.get(StorageConfigConstant.SECRET_ID_KEY).getValue();
|
||||
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
|
||||
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
|
||||
|
||||
@@ -53,6 +53,7 @@ public class UpYunServiceImpl extends AbstractBaseFileService implements BaseFil
|
||||
this.driveId = driveId;
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByDriveId(driveId);
|
||||
this.mergeStrategyConfig(stringStorageConfigMap);
|
||||
String bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
|
||||
String username = stringStorageConfigMap.get(StorageConfigConstant.USERNAME_KEY).getValue();
|
||||
String password = stringStorageConfigMap.get(StorageConfigConstant.PASSWORD_KEY).getValue();
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package im.zhaojun.zfile.service.support;
|
||||
|
||||
import im.zhaojun.zfile.model.support.SystemMonitorInfo;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
public class SystemMonitorService {
|
||||
|
||||
public SystemMonitorInfo systemMonitorInfo() {
|
||||
return new SystemMonitorInfo();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package im.zhaojun.zfile.util;
|
||||
|
||||
import cn.hutool.core.comparator.CompareUtil;
|
||||
import im.zhaojun.zfile.model.dto.FileItemDTO;
|
||||
import im.zhaojun.zfile.model.enums.FileTypeEnum;
|
||||
|
||||
@@ -43,8 +44,8 @@ public class FileComparator implements Comparator<FileItemDTO> {
|
||||
if (o1Type.equals(o2Type)) {
|
||||
int result;
|
||||
switch (sortBy) {
|
||||
case "time": result = o1.getTime().compareTo(o2.getTime()); break;
|
||||
case "size": result = o1.getSize().compareTo(o2.getSize()); break;
|
||||
case "time": result = CompareUtil.compare(o1.getTime(), o2.getTime()); break;
|
||||
case "size": result = CompareUtil.compare(o1.getSize(), o2.getSize()); break;
|
||||
default: result = naturalOrderComparator.compare(o1.getName(), o2.getName()); break;
|
||||
}
|
||||
return "asc".equals(order) ? result : -result;
|
||||
|
||||
@@ -1,21 +1,38 @@
|
||||
package im.zhaojun.zfile.util;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import im.zhaojun.zfile.model.constant.LocalFileResponseTypeConstant;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.catalina.connector.ClientAbortException;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Slf4j
|
||||
public class FileUtil {
|
||||
|
||||
public static ResponseEntity<Object> export(File file, String fileName) {
|
||||
/**
|
||||
* 文件下载,单线程,直接传
|
||||
* @param file 文件对象
|
||||
* @param fileName 要保存为的文件名
|
||||
* @return 文件下载对象
|
||||
*/
|
||||
public static ResponseEntity<Object> exportSingleThread(File file, String fileName) {
|
||||
if (!file.exists()) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("404 FILE NOT FOUND");
|
||||
}
|
||||
@@ -23,7 +40,7 @@ public class FileUtil {
|
||||
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
headers.add(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
|
||||
|
||||
if (StringUtils.isNullOrEmpty(fileName)) {
|
||||
fileName = file.getName();
|
||||
@@ -31,10 +48,10 @@ public class FileUtil {
|
||||
|
||||
headers.setContentDispositionFormData("attachment", URLUtil.encode(fileName));
|
||||
|
||||
headers.add("Pragma", "no-cache");
|
||||
headers.add("Expires", "0");
|
||||
headers.add("Last-Modified", new Date().toString());
|
||||
headers.add("ETag", String.valueOf(System.currentTimeMillis()));
|
||||
headers.add(HttpHeaders.PRAGMA, "no-cache");
|
||||
headers.add(HttpHeaders.EXPIRES, "0");
|
||||
headers.add(HttpHeaders.LAST_MODIFIED, new Date().toString());
|
||||
headers.add(HttpHeaders.ETAG, String.valueOf(System.currentTimeMillis()));
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.headers(headers)
|
||||
@@ -43,26 +60,137 @@ public class FileUtil {
|
||||
.body(new FileSystemResource(file));
|
||||
}
|
||||
|
||||
public static ResponseEntity<Object> export(File file) {
|
||||
/**
|
||||
* 返回文件给 response,支持断点续传和多线程下载
|
||||
* @param request 请求对象
|
||||
* @param response 响应对象
|
||||
* @param file 下载的文件
|
||||
*/
|
||||
public static void export(HttpServletRequest request, HttpServletResponse response, File file, String type) {
|
||||
export(request, response, file, file.getName(), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回文件给 response,支持断点续传和多线程下载 (动态变化的文件不支持)
|
||||
* @param request 请求对象
|
||||
* @param response 响应对象
|
||||
* @param file 下载的文件
|
||||
* @param fileName 下载的文件名,为空则默认读取文件名称
|
||||
*/
|
||||
public static void export(HttpServletRequest request, HttpServletResponse response, File file, String fileName, String type) {
|
||||
if (!file.exists()) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("404 FILE NOT FOUND");
|
||||
try {
|
||||
response.getWriter().write("404 FILE NOT FOUND");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
if (StringUtils.isNullOrEmpty(fileName)) {
|
||||
//文件名
|
||||
fileName = file.getName();
|
||||
}
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
headers.setContentDispositionFormData("attachment", URLUtil.encode(file.getName()));
|
||||
String range = request.getHeader(HttpHeaders.RANGE);
|
||||
|
||||
headers.add("Pragma", "no-cache");
|
||||
headers.add("Expires", "0");
|
||||
headers.add("Last-Modified", new Date().toString());
|
||||
headers.add("ETag", String.valueOf(System.currentTimeMillis()));
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.headers(headers)
|
||||
.contentLength(file.length())
|
||||
.contentType(mediaType)
|
||||
.body(new FileSystemResource(file));
|
||||
String rangeSeparator = "-";
|
||||
// 开始下载位置
|
||||
long startByte = 0;
|
||||
// 结束下载位置
|
||||
long endByte = file.length() - 1;
|
||||
|
||||
// 如果是断点续传
|
||||
if (range != null && range.contains("bytes=") && range.contains(rangeSeparator)) {
|
||||
// 设置响应状态码为 206
|
||||
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
|
||||
|
||||
range = range.substring(range.lastIndexOf("=") + 1).trim();
|
||||
String[] ranges = range.split(rangeSeparator);
|
||||
try {
|
||||
// 判断 range 的类型
|
||||
if (ranges.length == 1) {
|
||||
// 类型一:bytes=-2343
|
||||
if (range.startsWith(rangeSeparator)) {
|
||||
endByte = Long.parseLong(ranges[0]);
|
||||
}
|
||||
// 类型二:bytes=2343-
|
||||
else if (range.endsWith(rangeSeparator)) {
|
||||
startByte = Long.parseLong(ranges[0]);
|
||||
}
|
||||
}
|
||||
// 类型三:bytes=22-2343
|
||||
else if (ranges.length == 2) {
|
||||
startByte = Long.parseLong(ranges[0]);
|
||||
endByte = Long.parseLong(ranges[1]);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// 传参不规范,则直接返回所有内容
|
||||
startByte = 0;
|
||||
endByte = file.length() - 1;
|
||||
}
|
||||
} else {
|
||||
// 没有 ranges 即全部一次性传输,需要用 200 状态码,这一行应该可以省掉,因为默认返回是 200 状态码
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
//要下载的长度(endByte 为总长度 -1,这时候要加回去)
|
||||
long contentLength = endByte - startByte + 1;
|
||||
//文件类型
|
||||
String contentType = request.getServletContext().getMimeType(fileName);
|
||||
|
||||
if (Objects.equals(type, LocalFileResponseTypeConstant.DOWNLOAD) || StrUtil.isEmpty(contentType)) {
|
||||
contentType = "attachment";
|
||||
}
|
||||
|
||||
response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
|
||||
response.setHeader(HttpHeaders.CONTENT_TYPE, contentType);
|
||||
// 这里文件名换你想要的,inline 表示浏览器可以直接使用
|
||||
// 参考资料:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentType + ";filename=" + URLUtil.encode(fileName));
|
||||
response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(contentLength));
|
||||
// [要下载的开始位置]-[结束位置]/[文件总大小]
|
||||
response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + startByte + rangeSeparator + endByte + "/" + file.length());
|
||||
|
||||
BufferedOutputStream outputStream;
|
||||
RandomAccessFile randomAccessFile = null;
|
||||
//已传送数据大小
|
||||
long transmitted = 0;
|
||||
try {
|
||||
randomAccessFile = new RandomAccessFile(file, "r");
|
||||
outputStream = new BufferedOutputStream(response.getOutputStream());
|
||||
byte[] buff = new byte[4096];
|
||||
int len = 0;
|
||||
randomAccessFile.seek(startByte);
|
||||
while ((transmitted + len) <= contentLength && (len = randomAccessFile.read(buff)) != -1) {
|
||||
outputStream.write(buff, 0, len);
|
||||
transmitted += len;
|
||||
// 本地测试, 防止下载速度过快
|
||||
// Thread.sleep(1);
|
||||
}
|
||||
// 处理不足 buff.length 部分
|
||||
if (transmitted < contentLength) {
|
||||
len = randomAccessFile.read(buff, 0, (int) (contentLength - transmitted));
|
||||
outputStream.write(buff, 0, len);
|
||||
transmitted += len;
|
||||
}
|
||||
|
||||
outputStream.flush();
|
||||
response.flushBuffer();
|
||||
randomAccessFile.close();
|
||||
// log.trace("下载完毕: {}-{}, 已传输 {}", startByte, endByte, transmitted);
|
||||
} catch (ClientAbortException e) {
|
||||
// ignore 用户停止下载
|
||||
// log.trace("用户停止下载: {}-{}, 已传输 {}", startByte, endByte, transmitted);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (randomAccessFile != null) {
|
||||
randomAccessFile.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package im.zhaojun.zfile.util;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import im.zhaojun.zfile.exception.PreviewException;
|
||||
import im.zhaojun.zfile.exception.TextParseException;
|
||||
import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
@@ -16,16 +18,28 @@ import java.net.URLConnection;
|
||||
public class HttpUtil {
|
||||
|
||||
/**
|
||||
* 最大支持文件预览大小: 1M
|
||||
* 获取 URL 对应的文件内容
|
||||
*
|
||||
* @param url
|
||||
* 文件 URL
|
||||
* @return 文件 URL
|
||||
*/
|
||||
public static String getTextContent(String url) {
|
||||
RestTemplate restTemplate = SpringContextHolder.getBean("restTemplate");
|
||||
|
||||
if (getRemoteFileSize(url) > (1024 * ZFileConstant.TEXT_MAX_FILE_SIZE_KB)) {
|
||||
throw new PreviewException("存储源跨域请求失败, 服务器中转状态, 预览文件超出大小, 最大支持 1M");
|
||||
long maxFileSize = 1024 * ZFileConstant.TEXT_MAX_FILE_SIZE_KB;
|
||||
|
||||
if (getRemoteFileSize(url) > maxFileSize) {
|
||||
throw new PreviewException("预览文件超出大小, 最大支持 " + FileUtil.readableFileSize(maxFileSize));
|
||||
}
|
||||
|
||||
String result;
|
||||
try {
|
||||
result = restTemplate.getForObject(url, String.class);
|
||||
} catch (Exception e) {
|
||||
throw new TextParseException("文件解析异常, 请求 url = " + url + ", 异常信息为 = " + e.getMessage());
|
||||
}
|
||||
|
||||
String result = restTemplate.getForObject(url, String.class);
|
||||
return result == null ? "" : result;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public class StartupListener implements ApplicationListener<ApplicationStartedEv
|
||||
for (String localIp : localIps) {
|
||||
String addr = String.format("http://%s:%s", localIp, serverPort);
|
||||
indexAddr.append(addr).append("\t");
|
||||
indexAdminAddr.append(addr).append("/#/admin").append("\t");
|
||||
indexAdminAddr.append(addr).append("/admin").append("\t");
|
||||
}
|
||||
log.info("ZFile started at " + indexAddr);
|
||||
log.info("ZFile Admin started at " + indexAdminAddr);
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package im.zhaojun.zfile.util;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import im.zhaojun.zfile.service.SystemConfigService;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
@@ -11,6 +16,8 @@ public class StringUtils {
|
||||
|
||||
public static final char DELIMITER = '/';
|
||||
|
||||
public static final String DELIMITER_STR = "/";
|
||||
|
||||
public static final String HTTP_PROTOCAL = "http://";
|
||||
|
||||
public static final String HTTPS_PROTOCAL = "https://";
|
||||
@@ -101,4 +108,50 @@ public class StringUtils {
|
||||
path = ObjectUtil.defaultIfNull(path, "");
|
||||
return StringUtils.removeDuplicateSeparator(basePath + ZFileConstant.PATH_SEPARATOR + path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换 URL 中的 Host 部分,如替换 http://a.com/1.txt 为 https://abc.com/1.txt
|
||||
* @param originUrl
|
||||
* 原 URL
|
||||
* @param replaceHost
|
||||
* 替换的 HOST
|
||||
* @return 替换后的 URL
|
||||
*/
|
||||
public static String replaceHost(String originUrl, String replaceHost) {
|
||||
try {
|
||||
String path = new URL(originUrl).getFile();
|
||||
return concatPath(replaceHost, path);
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接 URL,并去除重复的分隔符 '/',但不会影响 http:// 和 https:// 这种头部
|
||||
* @param strs 拼接的字符数组
|
||||
* @return 拼接结果
|
||||
*/
|
||||
public static String concatUrl(String... strs) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < strs.length; i++) {
|
||||
sb.append(strs[i]);
|
||||
if (i != strs.length - 1) {
|
||||
sb.append(DELIMITER);
|
||||
}
|
||||
}
|
||||
return removeDuplicateSeparator(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接文件直链生成 URL
|
||||
* @param driveId 驱动器 ID
|
||||
* @param fullPath 文件全路径
|
||||
* @return 生成结果
|
||||
*/
|
||||
public static String generatorLink(Integer driveId, String fullPath) {
|
||||
SystemConfigService systemConfigService = SpringContextHolder.getBean(SystemConfigService.class);
|
||||
String domain = systemConfigService.getDomain();
|
||||
return concatUrl(domain, ZFileConstant.DIRECT_LINK_PREFIX, String.valueOf(driveId), fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,17 @@
|
||||
"name": "zfile.tmp.path",
|
||||
"type": "java.lang.String",
|
||||
"description": "临时文件路径."
|
||||
},
|
||||
{
|
||||
"name": "zfile.debug",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "是否开启 debug 模式."
|
||||
},
|
||||
{
|
||||
"name": "zfile.directLinkPrefix",
|
||||
"type": "java.lang.String",
|
||||
"defaultValue": "directlink",
|
||||
"description": "直链前缀名称, 默认为 directlink"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,9 +1,42 @@
|
||||
zfile:
|
||||
debug: false
|
||||
directLinkPrefix: directlink
|
||||
log:
|
||||
path: ${user.home}/.zfile/logs
|
||||
db:
|
||||
path: ${user.home}/.zfile/db/zfile
|
||||
tmp:
|
||||
path: ${user.home}/.zfile/tmp
|
||||
cache:
|
||||
auto-refresh:
|
||||
interval: 1
|
||||
timeout: 1800
|
||||
constant:
|
||||
readme: readme.md
|
||||
password: password.txt
|
||||
preview:
|
||||
audio:
|
||||
maxFileSizeMb: 5
|
||||
text:
|
||||
maxFileSizeKb: 512
|
||||
onedrive:
|
||||
clientId: 09939809-c617-43c8-a220-a93c1513c5d4
|
||||
clientSecret: _l:zI-_yrW75lV8M61K@z.I2K@B/On6Q
|
||||
redirectUri: https://zfile.jun6.net/onedrive/callback
|
||||
scope: offline_access User.Read Files.ReadWrite.All
|
||||
onedrive-china:
|
||||
clientId: 4a72d927-1907-488d-9eb2-1b465c53c1c5
|
||||
clientSecret: Y9CEA=82da5n-y_]KAWAgLH3?R9xf7Uw
|
||||
redirectUri: https://zfile.jun6.net/onedrive/china-callback
|
||||
scope: offline_access User.Read Files.ReadWrite.All
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
servlet:
|
||||
context-path: ''
|
||||
tomcat:
|
||||
max-threads: 20
|
||||
threads:
|
||||
max: 20
|
||||
compression:
|
||||
enabled: true
|
||||
|
||||
@@ -13,15 +46,8 @@ spring:
|
||||
settings:
|
||||
web-allow-others: true
|
||||
path: /h2-console
|
||||
enabled: false
|
||||
enabled: ${zfile.debug}
|
||||
datasource:
|
||||
# 初始化数据导入
|
||||
data: classpath*:db/data.sql
|
||||
sql-script-encoding: utf-8
|
||||
|
||||
initialization-mode: always
|
||||
continue-on-error: true
|
||||
|
||||
# h2 内存数据库 配置
|
||||
driver-class-name: org.h2.Driver
|
||||
url: jdbc:h2:${zfile.db.path}
|
||||
@@ -43,38 +69,15 @@ spring:
|
||||
hibernate:
|
||||
format_sql: false
|
||||
show-sql: false
|
||||
resources:
|
||||
chain:
|
||||
gzipped: true
|
||||
generate-ddl: true
|
||||
defer-datasource-initialization: true
|
||||
profiles:
|
||||
active: prod
|
||||
|
||||
zfile:
|
||||
log:
|
||||
path: ${user.home}/.zfile/logs
|
||||
db:
|
||||
path: ${user.home}/.zfile/db/zfile
|
||||
tmp:
|
||||
path: ${user.home}/.zfile/tmp
|
||||
cache:
|
||||
auto-refresh:
|
||||
interval: 1
|
||||
timeout: 1800
|
||||
constant:
|
||||
readme: readme.md
|
||||
password: password.txt
|
||||
preview:
|
||||
audio:
|
||||
maxFileSizeMb: 5
|
||||
text:
|
||||
maxFileSizeKb: 512
|
||||
onedrive:
|
||||
clientId: 09939809-c617-43c8-a220-a93c1513c5d4
|
||||
clientSecret: _l:zI-_yrW75lV8M61K@z.I2K@B/On6Q
|
||||
redirectUri: https://zfile.jun6.net/onedrive/callback
|
||||
scope: offline_access User.Read Files.ReadWrite.All
|
||||
onedrive-china:
|
||||
clientId: 4a72d927-1907-488d-9eb2-1b465c53c1c5
|
||||
clientSecret: Y9CEA=82da5n-y_]KAWAgLH3?R9xf7Uw
|
||||
redirectUri: https://zfile.jun6.net/onedrive/china-callback
|
||||
scope: offline_access User.Read Files.ReadWrite.All
|
||||
# 初始化数据导入
|
||||
sql:
|
||||
init:
|
||||
continue-on-error: true
|
||||
mode: always
|
||||
data-locations: classpath*:db/data.sql
|
||||
encoding: utf-8
|
||||
|
||||
7
src/main/resources/banner.txt
Normal file
7
src/main/resources/banner.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
________ ________ ___ ___ _______
|
||||
|\_____ \|\ _____\\ \|\ \ |\ ___ \
|
||||
\|___/ /\ \ \__/\ \ \ \ \ \ \ ___
|
||||
/ / /\ \ __\\ \ \ \ \ \ \ ___\
|
||||
/ /_/__\ \ \_| \ \ \ \ \____\ \ _____
|
||||
|\________\ \__\ \ \__\ \_______\ \_______\
|
||||
\|_______|\|__| \|__|\|_______|\|_______|
|
||||
@@ -5,9 +5,12 @@ INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (7, 'password', '管理
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (8, 'domain', '站点域名');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (11, 'customCss', '自定义 CSS');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (12, 'customJs', '自定义 JS (可用于统计代码)');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (13, 'tableSize', '表格大小');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (14, 'showOperator', '是否显示操作按钮');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (15, 'showDocument', '是否显示文档');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `value`) VALUES (13, 'tableSize', '表格大小', 'mini');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `value`) VALUES (14, 'showOperator', '是否显示操作按钮', 'true');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `value`) VALUES (15, 'showDocument', '是否显示文档', 'true');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (16, 'announcement', '网站公告');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (17, 'showAnnouncement', '是否显示网站公告');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`) VALUES (18, 'layout', '页面布局');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `value`) VALUES (17, 'showAnnouncement', '是否显示网站公告', 'true');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `value`) VALUES (18, 'layout', '页面布局', 'full');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `value`) VALUES (19, 'showLinkBtn', '是否显示生成直链按钮', 'true');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `value`) VALUES (20, 'showShortLink', '是否显示短链', 'true');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `value`) VALUES (21, 'showPathLink', '是否显示路径直链', 'true');
|
||||
1
src/main/resources/static/css/admin.da89697f.css
Normal file
1
src/main/resources/static/css/admin.da89697f.css
Normal file
@@ -0,0 +1 @@
|
||||
.zfile-admin-index[data-v-77a86734]{height:100%;overflow-y:hidden}.zfile-admin-top[data-v-77a86734]{background-color:#001529}.zfile-admin-top-logo[data-v-77a86734]{cursor:pointer;height:100%;line-height:61px;color:#fff;padding-right:20px}.zfile-admin-top-logo[data-v-77a86734]:hover{color:#1890ff}.zfile-admin-top-content[data-v-77a86734]{display:-webkit-box;display:-ms-flexbox;display:flex;margin:auto;max-width:1200px}.zfile-admin-content[data-v-77a86734]{background-color:#f0f2f5;height:100%;overflow-y:auto}.zfile-admin-content-view[data-v-77a86734]{margin:auto;max-width:1200px;margin-top:50px;margin-bottom:100px}.el-menu--horizontal>.el-menu-item.is-active[data-v-77a86734]{color:#1890ff!important}.el-menu--horizontal>.el-menu-item[data-v-77a86734],.el-menu.el-menu--horizontal[data-v-77a86734]{border:none}.zfile-admin-index-version-info[data-v-77a86734],.zfile-admin-index-version-info[data-v-77a86734] .el-link--inner{font-size:13px}.zfile-login[data-v-732157c2]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:100%;height:100%}.zfile-login-title[data-v-732157c2]{text-align:center;vertical-align:text-bottom;font-size:30px;font-weight:600;color:red;background-image:linear-gradient(-20deg,#6e45e2,#88d3ce);-webkit-text-fill-color:transparent;-webkit-background-clip:text;line-height:80px}.zfile-login-title small[data-v-732157c2]{font-size:20px}.zfile-login-form[data-v-732157c2]{width:350px;padding:30px 35px 15px;background:#fff;border:1px solid #eaeaea;-webkit-box-shadow:0 0 25px #cac6c6;box-shadow:0 0 25px #cac6c6}.el-row[data-v-506ed09e]{overflow-y:auto}#siteForm[data-v-506ed09e]{margin-top:20px}#siteForm[data-v-506ed09e] .el-select{width:70%}.markdown-body[data-v-12b3dceb]{padding:unset}.el-drive-form-col[data-v-3a30d939]{padding-left:0!important}.zfile-site-id-input-site-type-select[data-v-3a30d939]{width:100px}.zfile-cache-statistics[data-v-4d6f6841]{margin-bottom:20px}.zfile-cache-table[data-v-4d6f6841]{width:100%;overflow-y:auto}.zfile-filter-delete-btn[data-v-20b2942a]{margin-left:10px;margin-top:5px}.zfile-filter-save-btn[data-v-20b2942a]{text-align:right;margin-top:15px}.el-row[data-v-32b498fc]{padding:20px}.el-form-item[data-v-32b498fc]{margin-right:50px}.card-title[data-v-32b498fc]{color:rgba(0,0,0,.45);font-size:14px}.card-content[data-v-32b498fc]{color:rgba(0,0,0,.85);font-size:25px;line-height:30px}.card-title-button[data-v-32b498fc]{float:right;padding:3px 0}.table-search-input[data-v-32b498fc]{width:300px;float:right}#filterForm .el-row[data-v-32b498fc]{padding:0}#cacheDialog[data-v-32b498fc] .el-dialog__body{padding:20px}.table-edit-icon[data-v-32b498fc]{margin-left:5px;color:#409eff;cursor:pointer}.zfile-admin-short-form[data-v-f6484e10] .el-form-item:first-child{margin-left:10px}.zfile-admin-short-form[data-v-f6484e10] .el-form-item:not(:first-child){margin-left:20px}.el-pagination[data-v-f6484e10]{margin-top:15px}.table-edit-icon[data-v-f6484e10]{margin-left:10px;cursor:pointer}.input-with-select .el-select[data-v-3ac09bd8]{width:100px}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
src/main/resources/static/css/app.89e722fc.css
Normal file
1
src/main/resources/static/css/app.89e722fc.css
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.el-menu[data-v-23e92c9f],.el-row[data-v-23e92c9f]{height:100vh}
|
||||
@@ -1 +0,0 @@
|
||||
.el-row[data-v-333298fb]{overflow-y:auto}#siteForm[data-v-333298fb]{margin-top:20px;margin-left:20px}.zfile-word-aux[data-v-333298fb]{margin-left:20px;color:#aaa}
|
||||
@@ -1 +0,0 @@
|
||||
.login-container[data-v-5fc660a3]{width:100%;height:100%}.login-page[data-v-5fc660a3]{border-radius:5px;margin:180px auto;width:350px;padding:35px 35px 15px;background:#fff;border:1px solid #eaeaea;-webkit-box-shadow:0 0 25px #cac6c6;box-shadow:0 0 25px #cac6c6}label.el-checkbox.rememberme[data-v-5fc660a3]{margin:0 0 15px;text-align:left}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user