mirror of
https://github.com/zfile-dev/zfile.git
synced 2025-04-19 05:34:52 +00:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
803b8cdf71 | ||
|
|
eca5f7e48b | ||
|
|
b70c37f3f0 | ||
|
|
5cb2844141 | ||
|
|
4dd6cdb4b3 | ||
|
|
c6127c029f | ||
|
|
e149039ecb | ||
|
|
37688d83cf | ||
|
|
6692016642 | ||
|
|
3f41aeda9a | ||
|
|
de86d5c47d | ||
|
|
c89e072005 | ||
|
|
b533b5e959 | ||
|
|
d35cf27d47 | ||
|
|
796c4c1fb0 | ||
|
|
1033d6c1a9 | ||
|
|
e09c6b4e58 | ||
|
|
168b0b08f3 | ||
|
|
60a6a5348c | ||
|
|
2ec8a5df1f | ||
|
|
47b5f6ac12 | ||
|
|
c64c8465f2 | ||
|
|
f636681dd8 | ||
|
|
eadd2434e0 | ||
|
|
b84c0bff42 | ||
|
|
4cb5b84bfe | ||
|
|
0351b4401c | ||
|
|
48cb14be8a | ||
|
|
eea2ff11f9 | ||
|
|
325ec1a348 | ||
|
|
93205266d3 | ||
|
|
6849a4347f | ||
|
|
cb5c6a5945 | ||
|
|
5ed45c3bb3 | ||
|
|
4442ec3165 | ||
|
|
b268a24333 | ||
|
|
84f9354d4e | ||
|
|
8dfc4f8004 | ||
|
|
1158f5c2b9 | ||
|
|
c5f0e15207 | ||
|
|
68a842ce75 |
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 列表中有关的信息
|
||||
- 我已经清理过浏览器缓存并重试
|
||||
-->
|
||||
|
||||
43
README.md
43
README.md
@@ -19,9 +19,11 @@
|
||||
|
||||
## 相关地址
|
||||
|
||||
预览地址: [https://zfile.jun6.net](https://zfile.jun6.net)
|
||||
预览地址: [https://zfile.vip](https://zfile.vip)
|
||||
|
||||
文档地址: [http://docs.zhaojun.im/zfile](http://docs.zhaojun.im/zfile)
|
||||
文档地址: [https://docs.zfile.vip](https://docs.zfile.vip)
|
||||
|
||||
社区地址: [https://bbs.zfile.vip](https://bbs.zfile.vip)
|
||||
|
||||
项目源码: [https://github.com/zhaojun1998/zfile](https://github.com/zhaojun1998/zfile)
|
||||
|
||||
@@ -69,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`。
|
||||
@@ -98,7 +100,7 @@ chmod +x zfile/bin/*.sh
|
||||
~/zfile/bin/start.sh
|
||||
```
|
||||
|
||||
篇幅有限, 更详细的安装教程及介绍请参考: [ZFile 文档](http://docs.zhaojun.im/zfile)
|
||||
篇幅有限, 更详细的安装教程及介绍请参考: [ZFile 文档](https://docs.zfile.vip)
|
||||
|
||||
访问地址:
|
||||
|
||||
@@ -120,29 +122,6 @@ chmod +x zfile/bin/*.sh
|
||||

|
||||

|
||||
|
||||
## 开发计划
|
||||
|
||||
- [x] API 支持 [点击查看文档](https://github.com/zhaojun1998/zfile/blob/master/API.md)
|
||||
- [x] 更方便的部署方式
|
||||
- [x] 布局优化 - 自定义操作按钮 (现为右键实现)
|
||||
- [x] 后台优化 - 设置按照其功能进行分离
|
||||
- [x] 体验优化 - 支持前后端分离部署
|
||||
- [x] 体验优化 - 文本预览更换 vscode 同款编辑器 monaco editor
|
||||
- [x] 架构调整 - 支持多存储策略
|
||||
- [x] 体验优化 - 忽略文件列表 (正则表达式)
|
||||
- [x] 新功能 - Docker 支持
|
||||
- [x] 新功能 - 图片模式
|
||||
- [x] 新功能 - 直链/短链管理
|
||||
- [ ] ~~新功能 - 后台支持上传、编辑、删除等操作 (不再支持)~~
|
||||
- [ ] 体验优化 - 自定义支持预览的文件后缀 (正则表达式)
|
||||
- [ ] 体验优化 - 一键安装脚本
|
||||
- [ ] 新功能 - 分享功能,支持分享密码,文件夹分享
|
||||
- [ ] 新功能 - 直链支持 Referer 防盗链
|
||||
- [ ] 体验优化 - 视频列表支持
|
||||
- [ ] 新功能 - 单独页面打开文件预览
|
||||
- [ ] 新功能 - 在线查看日志功能
|
||||
- [ ] 部署优化 - Docker Compose 支持
|
||||
|
||||
## 支持作者
|
||||
|
||||
如果本项目对你有帮助,请作者喝杯咖啡吧。
|
||||
@@ -153,10 +132,6 @@ chmod +x zfile/bin/*.sh
|
||||
|
||||
[](https://starchart.cc/zhaojun1998/zfile.svg)
|
||||
|
||||
## 服务器赞助
|
||||
|
||||
<a href="https://kuline.cn"><img src="https://cdn.jun6.net/2021/05/14/1f6a4f0ad09ce.png" width="100px"></a>
|
||||
|
||||
## 开发工具赞助
|
||||
|
||||
<a href="https://www.jetbrains.com/?from=zfile"><img src="https://cdn.jun6.net/2021/04/21/26e410d60b0b0.png?1=1" width="100px"></a>
|
||||
|
||||
35
pom.xml
35
pom.xml
@@ -12,13 +12,14 @@
|
||||
|
||||
<groupId>im.zhaojun</groupId>
|
||||
<artifactId>zfile</artifactId>
|
||||
<version>3.2</version>
|
||||
<version>3.2.5</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>
|
||||
@@ -56,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>
|
||||
|
||||
<!-- 工具类 -->
|
||||
@@ -84,7 +87,6 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
@@ -92,6 +94,31 @@
|
||||
<version>3.6</version>
|
||||
</dependency>
|
||||
|
||||
<!-- WebDav -->
|
||||
<dependency>
|
||||
<groupId>io.milton</groupId>
|
||||
<artifactId>milton-server-ce</artifactId>
|
||||
<version>3.1.1.413</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>json</artifactId>
|
||||
<groupId>org.json</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- webdav client -->
|
||||
<dependency>
|
||||
<groupId>com.github.lookfirst</groupId>
|
||||
<artifactId>sardine</artifactId>
|
||||
<version>5.10</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 其他工具类 -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
@@ -108,7 +135,7 @@
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.61</version>
|
||||
<version>1.2.69</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -149,4 +176,4 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
@@ -0,0 +1,36 @@
|
||||
package im.zhaojun.zfile.config;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 应用上下文配置
|
||||
*
|
||||
* @author me
|
||||
* @date 2022/4/9
|
||||
*/
|
||||
@Configuration
|
||||
public class ApplicationContextConfigure implements ApplicationContextAware {
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
ApplicationContextConfigure.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* bean名称获取对象
|
||||
*/
|
||||
public static Object getBean(String name) throws BeansException {
|
||||
return applicationContext.getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* bean类型获取对象
|
||||
*/
|
||||
public static <T> T getBean(Class<T> clazz) throws BeansException {
|
||||
return applicationContext.getBean(clazz);
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
package im.zhaojun.zfile.config;
|
||||
package im.zhaojun.zfile.config;
|
||||
@@ -42,8 +42,10 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,12 +39,4 @@ public class ZFileConfiguration {
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean filterRegist() {
|
||||
FilterRegistrationBean frBean = new FilterRegistrationBean();
|
||||
frBean.setFilter(new CorsFilter());
|
||||
frBean.addUrlPatterns("/*");
|
||||
return frBean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package im.zhaojun.zfile.config.webdav;
|
||||
|
||||
import im.zhaojun.zfile.config.webdav.adapter.WebDavUrlAdapterImpl;
|
||||
import im.zhaojun.zfile.config.webdav.auth.SystemConfigSecurityManager;
|
||||
import im.zhaojun.zfile.config.webdav.resolver.WebDavRedirectViewResolver;
|
||||
import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import im.zhaojun.zfile.model.dto.SystemConfigDTO;
|
||||
import im.zhaojun.zfile.service.SystemConfigService;
|
||||
import io.milton.http.ResourceFactory;
|
||||
import io.milton.http.SecurityManager;
|
||||
import io.milton.http.annotated.AnnotationResourceFactory;
|
||||
import io.milton.http.fs.NullSecurityManager;
|
||||
import io.milton.servlet.DefaultMiltonConfigurator;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* Milton(webDav)配置
|
||||
*
|
||||
* @author me
|
||||
* @date 2022/4/9
|
||||
*/
|
||||
@Configuration
|
||||
public class MiltonConfiguration extends DefaultMiltonConfigurator implements InitializingBean {
|
||||
/**
|
||||
* 安全管理器
|
||||
*/
|
||||
private static SecurityManager securityManager = new NullSecurityManager();
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
/**
|
||||
* 构建milton初始化配置
|
||||
*/
|
||||
@Override
|
||||
protected void build() {
|
||||
builder.setSecurityManager(securityManager);
|
||||
builder.setContextPath(ZFileConstant.WEB_DAV_PREFIX);
|
||||
builder.setUrlAdapter(new WebDavUrlAdapterImpl());
|
||||
final ResourceFactory resourceFactory = builder.getResourceFactory();
|
||||
if (resourceFactory instanceof AnnotationResourceFactory) {
|
||||
((AnnotationResourceFactory) resourceFactory).setViewResolver(new WebDavRedirectViewResolver());
|
||||
}
|
||||
super.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 属性初始化完成后,更新安全管理器,使用系统配置鉴权
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
final SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();
|
||||
if (systemConfig != null) {
|
||||
securityManager = new SystemConfigSecurityManager(systemConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package im.zhaojun.zfile.config.webdav;
|
||||
|
||||
import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import io.milton.http.annotated.AnnotationResourceFactory;
|
||||
import io.milton.servlet.MiltonFilter;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* WebDav配置
|
||||
*
|
||||
* @author me
|
||||
* @date 2022/4/9
|
||||
*/
|
||||
@Configuration
|
||||
public class WebDavConfiguration {
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean miltonFilter() {
|
||||
FilterRegistrationBean registration = new FilterRegistrationBean();
|
||||
registration.setFilter(new MiltonFilter());
|
||||
registration.setName("miltonFilter");
|
||||
registration.addUrlPatterns(ZFileConstant.WEB_DAV_PREFIX + "/*");
|
||||
registration.addInitParameter("resource.factory.class", AnnotationResourceFactory.class.getName());
|
||||
registration.addInitParameter("milton.configurator", MiltonConfiguration.class.getName());
|
||||
registration.addInitParameter("controllerPackagesToScan", "im.zhaojun.zfile.controller.home");
|
||||
registration.setOrder(1);
|
||||
return registration;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
package im.zhaojun.zfile.config.webdav.adapter;
|
||||
|
||||
import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import im.zhaojun.zfile.util.RegexMatchUtils;
|
||||
import io.milton.http.HttpManager;
|
||||
import io.milton.http.Request;
|
||||
import io.milton.http.UrlAdapter;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
/**
|
||||
* WebDav路径适配器实现
|
||||
*
|
||||
* @author me
|
||||
* @date 2022/4/10
|
||||
*/
|
||||
public class WebDavUrlAdapterImpl implements UrlAdapter {
|
||||
|
||||
/**
|
||||
* 获取url
|
||||
* eg: domain.com/{webdavPrefix}/{driveId}/{folders}
|
||||
*
|
||||
* @param request 请求
|
||||
* @return {@link String}
|
||||
*/
|
||||
@Override
|
||||
public String getUrl(Request request) {
|
||||
// 匹配url前缀和驱动器ID
|
||||
Matcher matcher = RegexMatchUtils.match("^" + ZFileConstant.WEB_DAV_PREFIX + "/(\\d+)(.*)",
|
||||
HttpManager.decodeUrl(request.getAbsolutePath()));
|
||||
final String driveId = RegexMatchUtils.getIndexResult(matcher, 1);
|
||||
if (driveId == null) {
|
||||
return "";
|
||||
}
|
||||
// 获取摘除前缀和驱动器ID后的文件路径
|
||||
final String realPath = RegexMatchUtils.getIndexResult(matcher, 2);
|
||||
return realPath != null ? realPath : "";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
|
||||
package im.zhaojun.zfile.config.webdav.auth;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import im.zhaojun.zfile.model.dto.SystemConfigDTO;
|
||||
import io.milton.http.Auth;
|
||||
import io.milton.http.Request;
|
||||
import io.milton.http.Request.Method;
|
||||
import io.milton.http.http11.auth.DigestResponse;
|
||||
import io.milton.resource.Resource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 基于当前系统配置的WebDav鉴权管理器
|
||||
*
|
||||
* @author me
|
||||
* @date 2022/4/10
|
||||
* @see io.milton.http.fs.SimpleSecurityManager
|
||||
*/
|
||||
public class SystemConfigSecurityManager implements io.milton.http.SecurityManager {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SystemConfigSecurityManager.class);
|
||||
|
||||
private String realm = "SystemConfig";
|
||||
private Map<String, String> nameAndPasswords;
|
||||
|
||||
/**
|
||||
* 根据系统配置创建安全管理器
|
||||
*
|
||||
* @param systemConfig 系统配置DTO
|
||||
*/
|
||||
public SystemConfigSecurityManager(SystemConfigDTO systemConfig) {
|
||||
if (systemConfig != null) {
|
||||
this.nameAndPasswords = MapUtil.of(systemConfig.getUsername(), systemConfig.getPassword());
|
||||
}
|
||||
}
|
||||
|
||||
public Object getUserByName(String name) {
|
||||
String actualPassword = nameAndPasswords.get(name);
|
||||
if (actualPassword != null) {
|
||||
return name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户名+密码身份验证
|
||||
*
|
||||
* @param user 用户
|
||||
* @param password 密码
|
||||
* @return {@link Object}
|
||||
*/
|
||||
@Override
|
||||
public Object authenticate(String user, String password) {
|
||||
if (user.contains("@")) {
|
||||
user = user.substring(0, user.indexOf("@"));
|
||||
}
|
||||
String actualPassword = nameAndPasswords.get(user);
|
||||
if (actualPassword == null) {
|
||||
log.debug("user not found: " + user);
|
||||
return null;
|
||||
} else {
|
||||
//比对密码MD5摘要
|
||||
return (actualPassword.equals(SecureUtil.md5(password))) ? user : null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求摘要身份验证(不进行换算)
|
||||
*
|
||||
* @param digestRequest 消化的请求
|
||||
* @return {@link Object}
|
||||
*/
|
||||
@Override
|
||||
public Object authenticate(DigestResponse digestRequest) {
|
||||
String serverResponse = nameAndPasswords.get(digestRequest.getUser());
|
||||
String clientResponse = digestRequest.getResponseDigest();
|
||||
//比对密码MD5摘要
|
||||
if (serverResponse.equals(SecureUtil.md5(clientResponse))) {
|
||||
return "ok";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean authorise(Request request, Method method, Auth auth, Resource resource) {
|
||||
if (auth == null) {
|
||||
log.trace("authorise: declining because there is no auth object");
|
||||
return false;
|
||||
} else {
|
||||
if (auth.getTag() == null) {
|
||||
log.trace("authorise: declining because there is no auth.getTag() object");
|
||||
return false;
|
||||
} else {
|
||||
log.trace("authorise: permitting because there is an authenticated user associated with this request");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRealm(String host) {
|
||||
return realm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param realm the realm to set
|
||||
*/
|
||||
public void setRealm(String realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public void setNameAndPasswords(Map<String, String> nameAndPasswords) {
|
||||
this.nameAndPasswords = nameAndPasswords;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isDigestAllowed() {
|
||||
// 关闭请求摘要换算,client端请求时若换算为摘要,则无法和系统设置中获取的密码MD5比对
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
|
||||
package im.zhaojun.zfile.config.webdav.resolver;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import im.zhaojun.zfile.model.entity.webdav.WebDavFile;
|
||||
import io.milton.common.View;
|
||||
import io.milton.http.template.TemplateProcessor;
|
||||
import io.milton.http.template.ViewResolver;
|
||||
import io.milton.servlet.OutputStreamWrappingHttpServletResponse;
|
||||
import io.milton.servlet.ServletResponse;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* WebDav重定向视图处理器
|
||||
* Get注解handler返回字符串时,将使用本ViewResolver处理
|
||||
*
|
||||
* @author me
|
||||
* @date 2022/4/9
|
||||
*/
|
||||
public class WebDavRedirectViewResolver implements ViewResolver {
|
||||
|
||||
@Override
|
||||
public TemplateProcessor resolveView(View view) {
|
||||
return new RedirectTemplateProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重定向模板处理程序
|
||||
*
|
||||
* @author me
|
||||
* @date 2022/04/10
|
||||
*/
|
||||
public static class RedirectTemplateProcessor implements TemplateProcessor {
|
||||
|
||||
@Override
|
||||
public void execute(Map<String, Object> model, OutputStream out) {
|
||||
try {
|
||||
// 获取要下载的资源文件
|
||||
final Object resource = model.get("resource");
|
||||
if (!(resource instanceof WebDavFile)) {
|
||||
throw new RuntimeException("couldn't get direct url.");
|
||||
}
|
||||
final WebDavFile file = (WebDavFile) resource;
|
||||
// 构造文件直链的路径
|
||||
final String redirectPath = String.format("/%s/%s%s", ZFileConstant.DIRECT_LINK_PREFIX, file.getDriveId(), file.getFullPath());
|
||||
// 重定向到直链
|
||||
HttpServletResponse resp = new OutputStreamWrappingHttpServletResponse(ServletResponse.getResponse(), out);
|
||||
resp.setStatus(301);
|
||||
resp.setHeader("Location", URLUtil.encode(redirectPath));
|
||||
resp.setHeader("Connection", "close");
|
||||
resp.flushBuffer();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
package im.zhaojun.zfile.controller.admin;
|
||||
package im.zhaojun.zfile.controller.admin;
|
||||
@@ -0,0 +1,159 @@
|
||||
package im.zhaojun.zfile.controller.home;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import im.zhaojun.zfile.config.ApplicationContextConfigure;
|
||||
import im.zhaojun.zfile.context.DriveContext;
|
||||
import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import im.zhaojun.zfile.model.dto.FileItemDTO;
|
||||
import im.zhaojun.zfile.model.entity.webdav.WebDavEntity;
|
||||
import im.zhaojun.zfile.model.entity.webdav.WebDavFile;
|
||||
import im.zhaojun.zfile.model.entity.webdav.WebDavFolder;
|
||||
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
|
||||
import im.zhaojun.zfile.util.RegexMatchUtils;
|
||||
import io.milton.annotations.*;
|
||||
import io.milton.http.HttpManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* WebDav控制器
|
||||
*
|
||||
* @author me
|
||||
* @date 2022/4/9
|
||||
*/
|
||||
@Slf4j
|
||||
@ResourceController
|
||||
@ConditionalOnProperty(value = "webdav.enable", havingValue = "true")
|
||||
public class WebDavController {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(WebDavController.class);
|
||||
|
||||
|
||||
/**
|
||||
* 获取根目录文件夹
|
||||
*
|
||||
* @return {@link WebDavFolder} WebDav文件夹
|
||||
*/
|
||||
@Root
|
||||
public WebDavFolder getRootFolder() {
|
||||
return new WebDavFolder(ZFileConstant.PATH_SEPARATOR, getDriveId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取根目录子文件/文件夹(控制器)
|
||||
*
|
||||
* @param rootFolder 根文件夹
|
||||
* @return {@link WebDavController} 根控制器
|
||||
*/
|
||||
@ChildrenOf
|
||||
public WebDavController getChildren(WebDavController rootFolder) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子文件/文件夹
|
||||
*
|
||||
* @param parent 父文件夹
|
||||
* @return {@link List}<{@link WebDavEntity}> WebDav实体
|
||||
*/
|
||||
@ChildrenOf
|
||||
public List<WebDavEntity> getChildren(WebDavFolder parent) {
|
||||
if (parent == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
// 获取驱动器文件服务
|
||||
AbstractBaseFileService fileService = ApplicationContextConfigure.getBean(DriveContext.class).get(parent.getDriveId());
|
||||
if (fileService == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 获取文件列表
|
||||
List<FileItemDTO> fileItemList = fileService.fileList(parent.getFullPath());
|
||||
// 转换FileItemDTO为WebDavEntity
|
||||
return WebDavEntity.convertFromFileItemDTO(fileItemList, parent);
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("get webDav children failed,parent:{},msg:{}", JSON.toJSONString(parent), e.getMessage(), e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子文件内容
|
||||
*
|
||||
* @param webDavFile WebDav文件
|
||||
* @return {@link String} ViewResolver模板名称
|
||||
*/
|
||||
@Get
|
||||
public String getChild(WebDavFile webDavFile) {
|
||||
return JSON.toJSONString(webDavFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取WebDav实体文件名
|
||||
*/
|
||||
@Name
|
||||
public String getWebDavFile(WebDavEntity webDavEntity) {
|
||||
return webDavEntity.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取WebDav实体展示名称
|
||||
*/
|
||||
@DisplayName
|
||||
public String getDisplayName(WebDavEntity webDavEntity) {
|
||||
return webDavEntity.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取WebDav实体唯一id
|
||||
*/
|
||||
@UniqueId
|
||||
public String getUniqueId(WebDavEntity entity) {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取WebDav实体修改日期
|
||||
*/
|
||||
@ModifiedDate
|
||||
public Date getModifiedDate(WebDavEntity webDavEntity) {
|
||||
return webDavEntity.getModifiedDate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取WebDav实体创建日期
|
||||
*/
|
||||
@CreatedDate
|
||||
public Date getCreatedDate(WebDavEntity webDavEntity) {
|
||||
return webDavEntity.getCreatedDate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取WebDav实体大小
|
||||
*/
|
||||
@ContentLength
|
||||
public Long getContentLength(WebDavEntity entity) {
|
||||
if (entity instanceof WebDavFile) {
|
||||
return ((WebDavFile) entity).getSize();
|
||||
}
|
||||
// 性能考虑,文件夹暂不进行大小统计
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取驱动器id
|
||||
*
|
||||
* @return {@link Integer}
|
||||
*/
|
||||
private Integer getDriveId() {
|
||||
String requestUrl = HttpManager.decodeUrl(HttpManager.request().getAbsolutePath());
|
||||
final String driveId = RegexMatchUtils.matchByIndex("^" + ZFileConstant.WEB_DAV_PREFIX + "/(\\d+)(.*)", requestUrl, 1);
|
||||
return driveId != null ? Integer.valueOf(driveId) : null;
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public class InstallController {
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/install")
|
||||
@PostMapping("/doInstall")
|
||||
public ResultBean install(SystemConfigDTO systemConfigDTO) {
|
||||
if (!StringUtils.isEmpty(systemConfigService.getAdminUsername())) {
|
||||
return ResultBean.error("请勿重复初始化.");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
package im.zhaojun.zfile.filter;
|
||||
@@ -23,6 +23,12 @@ public class StorageConfigConstant {
|
||||
|
||||
public static final String PASSWORD_KEY = "password";
|
||||
|
||||
public static final String WEBDAV_USERNAME = "webdavUsername";
|
||||
|
||||
public static final String WEBDAV_PASSWORD = "webdavPassword";
|
||||
|
||||
public static final String WEBDAV_URL = "webdavUrl";
|
||||
|
||||
public static final String HOST_KEY = "host";
|
||||
|
||||
public static final String PORT_KEY = "port";
|
||||
|
||||
@@ -21,6 +21,11 @@ public class ZFileConstant {
|
||||
*/
|
||||
public static String DIRECT_LINK_PREFIX = "directlink";
|
||||
|
||||
/**
|
||||
* WebDav前缀
|
||||
*/
|
||||
public static String WEB_DAV_PREFIX = "/webdav";
|
||||
|
||||
/**
|
||||
* 系统产生的临时文件路径
|
||||
*/
|
||||
|
||||
@@ -48,4 +48,12 @@ public class StorageStrategyConfig {
|
||||
|
||||
private String proxyDomain;
|
||||
|
||||
private String region;
|
||||
|
||||
private String webdavUsername;
|
||||
|
||||
private String webdavPassword;
|
||||
|
||||
private String webdavUrl;
|
||||
|
||||
}
|
||||
@@ -2,10 +2,7 @@ package im.zhaojun.zfile.model.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.*;
|
||||
import java.util.Date;
|
||||
|
||||
@Entity(name = "SHORT_LINK")
|
||||
@@ -22,4 +19,4 @@ public class ShortLinkConfig {
|
||||
|
||||
private Date createDate;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package im.zhaojun.zfile.model.entity.webdav;
|
||||
|
||||
import im.zhaojun.zfile.model.constant.ZFileConstant;
|
||||
import im.zhaojun.zfile.model.dto.FileItemDTO;
|
||||
import im.zhaojun.zfile.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.zfile.util.StringUtils;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* WebDav实体
|
||||
*
|
||||
* @author me
|
||||
* @date 2022/4/9
|
||||
*/
|
||||
@Data
|
||||
public class WebDavEntity {
|
||||
/**
|
||||
* 唯一ID
|
||||
*/
|
||||
private UUID id;
|
||||
/**
|
||||
* 驱动器ID
|
||||
*/
|
||||
private Integer driveId;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createdDate;
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
private Date modifiedDate;
|
||||
/**
|
||||
* 是否是目录
|
||||
*/
|
||||
private boolean isDirectory;
|
||||
/**
|
||||
* 父文件夹
|
||||
*/
|
||||
private WebDavFolder parent;
|
||||
|
||||
public WebDavEntity() {
|
||||
}
|
||||
|
||||
public WebDavEntity(String name, WebDavFolder parent) {
|
||||
this.id = UUID.randomUUID();
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
this.createdDate = new Date();
|
||||
this.modifiedDate = new Date();
|
||||
this.isDirectory = true;
|
||||
}
|
||||
|
||||
public WebDavEntity(UUID id, String name, Date createdDate, Date modifiedDate,
|
||||
WebDavFolder parent) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
this.createdDate = createdDate;
|
||||
this.modifiedDate = modifiedDate;
|
||||
this.isDirectory = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全路径
|
||||
*/
|
||||
public String getFullPath() {
|
||||
if (this.getParent() != null) {
|
||||
final String parentFullPath = this.getParent().getFullPath();
|
||||
return StringUtils.removeDuplicateSeparator(parentFullPath + ZFileConstant.PATH_SEPARATOR + this.getName());
|
||||
} else {
|
||||
return ZFileConstant.PATH_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<WebDavEntity> convertFromFileItemDTO(List<FileItemDTO> fileItemList, WebDavFolder parent) {
|
||||
List<WebDavEntity> result = new ArrayList<>();
|
||||
if (fileItemList == null || fileItemList.size() == 0) {
|
||||
return result;
|
||||
}
|
||||
for (FileItemDTO each : fileItemList) {
|
||||
WebDavEntity entity = convertFromFileItemDTO(each, parent);
|
||||
result.add(entity);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static WebDavEntity convertFromFileItemDTO(FileItemDTO fileItemDTO, WebDavFolder parent) {
|
||||
if (fileItemDTO == null) {
|
||||
return null;
|
||||
}
|
||||
WebDavEntity entity;
|
||||
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
|
||||
entity = new WebDavFolder(fileItemDTO.getName(), parent);
|
||||
} else {
|
||||
entity = new WebDavFile(fileItemDTO.getName(), fileItemDTO.getSize(), parent);
|
||||
}
|
||||
entity.setModifiedDate(fileItemDTO.getTime());
|
||||
entity.setDriveId(parent.getDriveId());
|
||||
return entity;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package im.zhaojun.zfile.model.entity.webdav;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* WebDav文件实体
|
||||
*
|
||||
* @author me
|
||||
* @date 2022/4/9
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class WebDavFile extends WebDavEntity {
|
||||
/**
|
||||
* 大小
|
||||
*/
|
||||
private Long size;
|
||||
/**
|
||||
* 内容类型
|
||||
*/
|
||||
private String contentType;
|
||||
|
||||
public WebDavFile(String fileName, Long size, WebDavFolder parent) {
|
||||
super(fileName, parent);
|
||||
this.setSize(size);
|
||||
this.setDirectory(false);
|
||||
}
|
||||
|
||||
public WebDavFile(UUID id, String name, Date createdDate, Date modifiedDate, WebDavFolder parent) {
|
||||
super(id, name, createdDate, modifiedDate, parent);
|
||||
this.setDirectory(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package im.zhaojun.zfile.model.entity.webdav;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* WebDav文件夹实体
|
||||
*
|
||||
* @author me
|
||||
* @date 2022/4/9
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class WebDavFolder extends WebDavEntity {
|
||||
public WebDavFolder(String folderName, WebDavFolder parent) {
|
||||
super(folderName, parent);
|
||||
}
|
||||
|
||||
public WebDavFolder(String folderName, Integer driveId) {
|
||||
super(folderName, null);
|
||||
setDriveId(driveId);
|
||||
}
|
||||
|
||||
public WebDavFolder(UUID id, String name, Date createdDate, Date modifiedDate, WebDavFolder parent) {
|
||||
super(id, name, createdDate, modifiedDate, parent);
|
||||
}
|
||||
|
||||
public WebDavFile addFile(String fileName, Long size) {
|
||||
WebDavFile file = new WebDavFile(fileName, size, this);
|
||||
file.setDirectory(false);
|
||||
return file;
|
||||
}
|
||||
|
||||
public WebDavFolder addFolder(String folderName) {
|
||||
return new WebDavFolder(folderName, this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@ public enum StorageTypeEnum {
|
||||
LOCAL("local", "本地存储"),
|
||||
ALIYUN("aliyun", "阿里云 OSS"),
|
||||
TENCENT("tencent", "腾讯云 COS"),
|
||||
WebDAV("webdav", "WebDAV"),
|
||||
UPYUN("upyun", "又拍云 USS"),
|
||||
FTP("ftp", "FTP"),
|
||||
UFILE("ufile", "UFile"),
|
||||
|
||||
@@ -40,6 +40,9 @@ 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 -> {
|
||||
try {
|
||||
|
||||
@@ -216,7 +216,11 @@ public abstract class MicrosoftDriveServiceBase extends AbstractBaseFileService
|
||||
fileItemDTO.setTime(fileItem.getDate("lastModifiedDateTime"));
|
||||
|
||||
if (fileItem.containsKey(ONE_DRIVE_FILE_FLAG)) {
|
||||
fileItemDTO.setUrl(fileItem.getString("@microsoft.graph.downloadUrl"));
|
||||
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);
|
||||
|
||||
@@ -2,6 +2,7 @@ package im.zhaojun.zfile.service.impl;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.extra.ftp.Ftp;
|
||||
import cn.hutool.extra.ftp.FtpMode;
|
||||
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.zfile.model.dto.FileItemDTO;
|
||||
import im.zhaojun.zfile.model.entity.StorageConfig;
|
||||
@@ -22,6 +23,7 @@ import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -66,6 +68,7 @@ public class FtpServiceImpl extends AbstractBaseFileService implements BaseFileS
|
||||
} else {
|
||||
ftp = new Ftp(host, Integer.parseInt(port), username, password, StandardCharsets.UTF_8);
|
||||
ftp.getClient().type(FTP.BINARY_FILE_TYPE);
|
||||
ftp.setMode(FtpMode.Passive);
|
||||
testConnection();
|
||||
isInitialized = true;
|
||||
}
|
||||
@@ -88,6 +91,10 @@ public class FtpServiceImpl extends AbstractBaseFileService implements BaseFileS
|
||||
|
||||
for (FTPFile ftpFile : ftpFiles) {
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
// 跳过 ftp 的本目录和上级目录
|
||||
if (Arrays.asList(".", "..").contains(ftpFile.getName())) {
|
||||
continue;
|
||||
}
|
||||
fileItemDTO.setName(ftpFile.getName());
|
||||
fileItemDTO.setSize(ftpFile.getSize());
|
||||
fileItemDTO.setTime(ftpFile.getTimestamp().getTime());
|
||||
|
||||
@@ -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;
|
||||
@@ -72,6 +74,9 @@ public class LocalServiceImpl extends AbstractBaseFileService implements BaseFil
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) throws FileNotFoundException {
|
||||
if (StrUtil.startWith(path, "..") || StrUtil.startWith(path, "/..")) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<FileItemDTO> fileItemList = new ArrayList<>();
|
||||
|
||||
String fullPath = StringUtils.removeDuplicateSeparator(filePath + path);
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
@@ -121,7 +122,7 @@ public class UpYunServiceImpl extends AbstractBaseFileService implements BaseFil
|
||||
int lastDelimiterIndex = path.lastIndexOf("/");
|
||||
String name = path.substring(lastDelimiterIndex + 1);
|
||||
|
||||
Map<String, String> fileInfo = upYun.getFileInfo(StringUtils.removeDuplicateSeparator(basePath + ZFileConstant.PATH_SEPARATOR + path));
|
||||
Map<String, String> fileInfo = upYun.getFileInfo(URLUtil.encode(StringUtils.removeDuplicateSeparator(basePath + ZFileConstant.PATH_SEPARATOR + path), StandardCharsets.UTF_8));
|
||||
|
||||
if (fileInfo == null) {
|
||||
throw new NotExistFileException();
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
package im.zhaojun.zfile.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.github.sardine.DavResource;
|
||||
import com.github.sardine.Sardine;
|
||||
import com.github.sardine.SardineFactory;
|
||||
import im.zhaojun.zfile.exception.NotExistFileException;
|
||||
import im.zhaojun.zfile.model.constant.StorageConfigConstant;
|
||||
import im.zhaojun.zfile.model.dto.FileItemDTO;
|
||||
import im.zhaojun.zfile.model.entity.StorageConfig;
|
||||
import im.zhaojun.zfile.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.zfile.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.zfile.service.StorageConfigService;
|
||||
import im.zhaojun.zfile.service.base.AbstractBaseFileService;
|
||||
import im.zhaojun.zfile.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
||||
public class WebdavServiceImpl extends AbstractBaseFileService {
|
||||
|
||||
private Sardine sardine;
|
||||
|
||||
private String url;
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
@Override
|
||||
public void init(Integer driveId) {
|
||||
this.driveId = driveId;
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByDriveId(driveId);
|
||||
this.mergeStrategyConfig(stringStorageConfigMap);
|
||||
String username = stringStorageConfigMap.get(StorageConfigConstant.WEBDAV_USERNAME).getValue();
|
||||
String password = stringStorageConfigMap.get(StorageConfigConstant.WEBDAV_PASSWORD).getValue();
|
||||
url = stringStorageConfigMap.get(StorageConfigConstant.WEBDAV_URL).getValue();
|
||||
|
||||
if (Objects.isNull(url)) {
|
||||
log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription());
|
||||
isInitialized = false;
|
||||
} else {
|
||||
// 如果用户名和密码为空,则使用默认用户名和密码
|
||||
if (StrUtil.isNotEmpty(username) && StrUtil.isNotEmpty(password)) {
|
||||
sardine = SardineFactory.begin("admin", "YgSBFCbH");
|
||||
} else {
|
||||
sardine = SardineFactory.begin();
|
||||
}
|
||||
testConnection();
|
||||
isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) throws Exception {
|
||||
List<FileItemDTO> resultList = new ArrayList<>();
|
||||
|
||||
String requestPath = StringUtils.removeDuplicateSeparator(url + "/" + path);
|
||||
|
||||
List<DavResource> resources = sardine.list(requestPath);
|
||||
|
||||
Integer index = 0;
|
||||
|
||||
for (DavResource res : resources) {
|
||||
// 如果不是根目录, 则跳过第一个, 因为第一个是当前目录
|
||||
if (!StrUtil.equals(path, "/") && index++ == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FileItemDTO fileItemResult = new FileItemDTO();
|
||||
fileItemResult.setName(res.getName());
|
||||
fileItemResult.setTime(res.getModified());
|
||||
fileItemResult.setSize(res.getContentLength());
|
||||
fileItemResult.setType(res.isDirectory() ? FileTypeEnum.FOLDER : FileTypeEnum.FILE);
|
||||
fileItemResult.setPath(path);
|
||||
fileItemResult.setUrl(getDownloadUrl(path + res.getName()));
|
||||
resultList.add(fileItemResult);
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) {
|
||||
return StringUtils.concatPath(url, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.WebDAV;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StorageConfig> storageStrategyConfigList() {
|
||||
return new ArrayList<StorageConfig>() {{
|
||||
add(new StorageConfig("webdavUsername", "用户名"));
|
||||
add(new StorageConfig("webdavPassword", "密码"));
|
||||
add(new StorageConfig("webdavUrl", "WebDav 链接"));
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileItemDTO getFileItem(String path) {
|
||||
List<FileItemDTO> list;
|
||||
try {
|
||||
int end = path.lastIndexOf("/");
|
||||
list = fileList(path.substring(0, end + 1));
|
||||
} catch (Exception e) {
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
|
||||
for (FileItemDTO fileItemDTO : list) {
|
||||
String fullPath = StringUtils.concatUrl(fileItemDTO.getPath(), fileItemDTO.getName());
|
||||
if (Objects.equals(fullPath, path)) {
|
||||
return fileItemDTO;
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotExistFileException();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,16 @@
|
||||
package im.zhaojun.zfile.util;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
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 com.mpatric.mp3agic.ID3v1;
|
||||
import com.mpatric.mp3agic.ID3v2;
|
||||
import com.mpatric.mp3agic.InvalidDataException;
|
||||
@@ -29,27 +35,36 @@ public class AudioUtil {
|
||||
private static final Logger log = LoggerFactory.getLogger(AudioUtil.class);
|
||||
|
||||
public static AudioInfoDTO getAudioInfo(String url) throws Exception {
|
||||
String query = new URL(URLUtil.decode(url)).getQuery();
|
||||
AudioInfoDTO audioInfoDTO;
|
||||
try {
|
||||
String query = new URL(URLUtil.decode(url)).getQuery();
|
||||
|
||||
if (query != null) {
|
||||
url = url.replace(query, URLUtil.encode(query));
|
||||
if (query != null) {
|
||||
url = url.replace(query, URLUtil.encode(query));
|
||||
}
|
||||
|
||||
// 如果音乐文件大小超出 5M, 则不解析此音乐
|
||||
if (im.zhaojun.zfile.util.HttpUtil.getRemoteFileSize(url)
|
||||
> (1024 * 1024 * ZFileConstant.AUDIO_MAX_FILE_SIZE_MB)) {
|
||||
return AudioInfoDTO.buildDefaultAudioInfoDTO();
|
||||
}
|
||||
|
||||
String fullFilePath = StringUtils.removeDuplicateSeparator(ZFileConstant.TMP_FILE_PATH + ZFileConstant.PATH_SEPARATOR + UUID.fastUUID());
|
||||
|
||||
File file = new File(fullFilePath);
|
||||
FileUtil.mkParentDirs(file);
|
||||
|
||||
final HttpResponse response = HttpRequest.get(url).setFollowRedirects(true).timeout(-1).executeAsync();
|
||||
response.writeBody(file);
|
||||
|
||||
audioInfoDTO = parseAudioInfo(file);
|
||||
audioInfoDTO.setSrc(url);
|
||||
file.deleteOnExit();
|
||||
return audioInfoDTO;
|
||||
} catch (Exception e) {
|
||||
log.error("获取音频文件信息失败.", e);
|
||||
}
|
||||
|
||||
// 如果音乐文件大小超出 5M, 则不解析此音乐
|
||||
if (im.zhaojun.zfile.util.HttpUtil.getRemoteFileSize(url)
|
||||
> (1024 * 1024 * ZFileConstant.AUDIO_MAX_FILE_SIZE_MB)) {
|
||||
return AudioInfoDTO.buildDefaultAudioInfoDTO();
|
||||
}
|
||||
|
||||
String fullFilePath = StringUtils.removeDuplicateSeparator(ZFileConstant.TMP_FILE_PATH + ZFileConstant.PATH_SEPARATOR + UUID.fastUUID());
|
||||
|
||||
File file = new File(fullFilePath);
|
||||
FileUtil.mkParentDirs(file);
|
||||
HttpUtil.downloadFile(url, file);
|
||||
AudioInfoDTO audioInfoDTO = parseAudioInfo(file);
|
||||
audioInfoDTO.setSrc(url);
|
||||
file.deleteOnExit();
|
||||
return audioInfoDTO;
|
||||
return AudioInfoDTO.buildDefaultAudioInfoDTO();
|
||||
}
|
||||
|
||||
private static AudioInfoDTO parseAudioInfo(File file) throws IOException, UnsupportedTagException {
|
||||
@@ -86,4 +101,4 @@ public class AudioUtil {
|
||||
|
||||
return audioInfoDTO;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,16 +137,18 @@ public class FileUtil {
|
||||
long contentLength = endByte - startByte + 1;
|
||||
//文件类型
|
||||
String contentType = request.getServletContext().getMimeType(fileName);
|
||||
|
||||
if (Objects.equals(type, LocalFileResponseTypeConstant.DOWNLOAD) || StrUtil.isEmpty(contentType)) {
|
||||
contentType = "attachment";
|
||||
if (StrUtil.isEmpty(contentType)) {
|
||||
contentType = "application/octet-stream";
|
||||
}
|
||||
|
||||
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));
|
||||
if (Objects.equals(type, LocalFileResponseTypeConstant.DOWNLOAD) || StrUtil.isEmpty(contentType)) {
|
||||
String contentDisposition = "attachment;filename=" + URLUtil.encode(fileName);
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition);
|
||||
}
|
||||
response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(contentLength));
|
||||
// [要下载的开始位置]-[结束位置]/[文件总大小]
|
||||
response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + startByte + rangeSeparator + endByte + "/" + file.length());
|
||||
@@ -193,4 +195,4 @@ public class FileUtil {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
61
src/main/java/im/zhaojun/zfile/util/RegexMatchUtils.java
Normal file
61
src/main/java/im/zhaojun/zfile/util/RegexMatchUtils.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package im.zhaojun.zfile.util;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 正则匹配工具类
|
||||
*
|
||||
* @author me
|
||||
* @date 2022/4/9
|
||||
*/
|
||||
public class RegexMatchUtils {
|
||||
|
||||
/**
|
||||
* 正则匹配分组序号值
|
||||
*
|
||||
* @param regex 正则表达式
|
||||
* @param str 待匹配字符串
|
||||
* @param index 分组序号,从1开始
|
||||
* @return {@link String} 不存在/匹配失败返回null
|
||||
*/
|
||||
public static String matchByIndex(String regex, String str, Integer index) {
|
||||
Matcher matcher = match(regex, str);
|
||||
if (matcher == null) {
|
||||
return null;
|
||||
}
|
||||
return getIndexResult(matcher, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 匹配字符串
|
||||
*
|
||||
* @param regex 正则表达式
|
||||
* @param str 待匹配字符串
|
||||
* @return {@link Matcher}
|
||||
*/
|
||||
public static Matcher match(String regex, String str) {
|
||||
if (str == null || "".equals(str)) {
|
||||
return null;
|
||||
}
|
||||
Matcher matcher = Pattern.compile(regex).matcher(str);
|
||||
if (!matcher.lookingAt()) {
|
||||
return null;
|
||||
}
|
||||
return matcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定分组序号的匹配结果
|
||||
*
|
||||
* @param matcher {@link Matcher}
|
||||
* @param index 分组序号,从1开始
|
||||
* @return {@link String} 不存在/匹配失败返回null
|
||||
*/
|
||||
public static String getIndexResult(Matcher matcher, Integer index) {
|
||||
if (matcher == null || index == null || index < 0 || index > matcher.groupCount()) {
|
||||
return null;
|
||||
}
|
||||
return matcher.group(index);
|
||||
}
|
||||
}
|
||||
@@ -97,6 +97,11 @@
|
||||
"type": "java.lang.String",
|
||||
"defaultValue": "directlink",
|
||||
"description": "直链前缀名称, 默认为 directlink"
|
||||
},
|
||||
{
|
||||
"name": "zfile.webdav",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "是否开启 webdav 文件管理."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
zfile:
|
||||
debug: false
|
||||
webdav: false
|
||||
directLinkPrefix: directlink
|
||||
log:
|
||||
path: ${user.home}/.zfile/logs
|
||||
@@ -35,7 +36,8 @@ server:
|
||||
servlet:
|
||||
context-path: ''
|
||||
tomcat:
|
||||
max-threads: 20
|
||||
threads:
|
||||
max: 20
|
||||
compression:
|
||||
enabled: true
|
||||
|
||||
@@ -47,13 +49,6 @@ spring:
|
||||
path: /h2-console
|
||||
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}
|
||||
@@ -75,5 +70,15 @@ spring:
|
||||
hibernate:
|
||||
format_sql: false
|
||||
show-sql: false
|
||||
generate-ddl: true
|
||||
defer-datasource-initialization: true
|
||||
profiles:
|
||||
active: prod
|
||||
active: prod
|
||||
|
||||
# 初始化数据导入
|
||||
sql:
|
||||
init:
|
||||
continue-on-error: true
|
||||
mode: always
|
||||
data-locations: classpath*:db/data.sql
|
||||
encoding: utf-8
|
||||
@@ -13,4 +13,5 @@ INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `value`) VALUES (17, 'showAnnoun
|
||||
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');
|
||||
INSERT INTO SYSTEM_CONFIG (`ID`, `k`, `REMARK`, `value`) VALUES (21, 'showPathLink', '是否显示路径直链', 'true');
|
||||
alter table short_link change "key" key varchar(255) null;
|
||||
@@ -76,6 +76,9 @@
|
||||
true: 表示当前logger的appender-ref和rootLogger的appender-ref都有效
|
||||
-->
|
||||
|
||||
<!-- milton webdav -->
|
||||
<logger name="io.milton" additivity="false" level="info"/>
|
||||
|
||||
<!-- jetCache logger -->
|
||||
<logger name="com.alicp" additivity="false" level="debug"/>
|
||||
|
||||
|
||||
@@ -1 +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-834c542c]{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-834c542c]{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-834c542c]{font-size:20px}.zfile-login-form[data-v-834c542c]{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}
|
||||
.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}
|
||||
@@ -1 +1 @@
|
||||
.zfile-install[data-v-89486d62]{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-install-form[data-v-89486d62]{width:450px;padding:30px 35px 15px;background:#fff;border:1px solid #eaeaea;-webkit-box-shadow:0 0 15px #cac6c6;box-shadow:0 0 15px #cac6c6}.zfile-install-title[data-v-89486d62]{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-install-enter[data-v-89486d62]{text-align:right;margin-bottom:0}.zfile-install-title small[data-v-89486d62]{font-size:20px}
|
||||
.zfile-install[data-v-c4d9c6f8]{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-install-form[data-v-c4d9c6f8]{width:450px;padding:30px 35px 15px;background:#fff;border:1px solid #eaeaea;-webkit-box-shadow:0 0 15px #cac6c6;box-shadow:0 0 15px #cac6c6}.zfile-install-title[data-v-c4d9c6f8]{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-install-enter[data-v-c4d9c6f8]{text-align:right;margin-bottom:0}.zfile-install-title small[data-v-c4d9c6f8]{font-size:20px}
|
||||
4
src/main/resources/static/js/admin.min.js
vendored
4
src/main/resources/static/js/admin.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
src/main/resources/static/js/install.min.js
vendored
2
src/main/resources/static/js/install.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,187 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>404 ERROR</title>
|
||||
<style type="text/css">
|
||||
body, div, h3, h4, li, ol {
|
||||
margin: 0;
|
||||
padding: 0
|
||||
}
|
||||
|
||||
body {
|
||||
font: 14px/1.5 'Microsoft YaHei', '微软雅黑', Helvetica, Sans-serif;
|
||||
min-width: 1200px;
|
||||
background: #f0f1f3;
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline: 0
|
||||
}
|
||||
|
||||
h3, h4, strong {
|
||||
font-weight: 700
|
||||
}
|
||||
|
||||
a {
|
||||
color: #428bca;
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline
|
||||
}
|
||||
|
||||
.error-page {
|
||||
background: #f0f1f3;
|
||||
padding: 80px 0 180px
|
||||
}
|
||||
|
||||
.error-page-container {
|
||||
position: relative;
|
||||
z-index: 1
|
||||
}
|
||||
|
||||
.error-page-main {
|
||||
position: relative;
|
||||
background: #f9f9f9;
|
||||
margin: 0 auto;
|
||||
width: 617px;
|
||||
-ms-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 50px 50px 70px
|
||||
}
|
||||
|
||||
.error-page-main:before {
|
||||
content: '';
|
||||
display: block;
|
||||
background: url(img/errorPageBorder.png?1427783409637);
|
||||
height: 7px;
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
width: 100%;
|
||||
left: 0
|
||||
}
|
||||
|
||||
.error-page-main h3 {
|
||||
font-size: 24px;
|
||||
font-weight: 400;
|
||||
border-bottom: 1px solid #d0d0d0
|
||||
}
|
||||
|
||||
.error-page-main h3 strong {
|
||||
font-size: 54px;
|
||||
font-weight: 400;
|
||||
margin-right: 20px
|
||||
}
|
||||
|
||||
.error-page-main h4 {
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
color: #333
|
||||
}
|
||||
|
||||
.error-page-actions {
|
||||
font-size: 0;
|
||||
z-index: 100
|
||||
}
|
||||
|
||||
.error-page-actions div {
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
padding: 30px 0 0 10px;
|
||||
width: 50%;
|
||||
-ms-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: #838383
|
||||
}
|
||||
|
||||
.error-page-actions ol {
|
||||
list-style: decimal;
|
||||
padding-left: 20px
|
||||
}
|
||||
|
||||
.error-page-actions li {
|
||||
line-height: 2.5em
|
||||
}
|
||||
|
||||
.error-page-actions:before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
bottom: 17px;
|
||||
left: 50px;
|
||||
width: 200px;
|
||||
height: 10px;
|
||||
-moz-box-shadow: 4px 5px 31px 11px #999;
|
||||
-webkit-box-shadow: 4px 5px 31px 11px #999;
|
||||
box-shadow: 4px 5px 31px 11px #999;
|
||||
-moz-transform: rotate(-4deg);
|
||||
-webkit-transform: rotate(-4deg);
|
||||
-ms-transform: rotate(-4deg);
|
||||
-o-transform: rotate(-4deg);
|
||||
transform: rotate(-4deg)
|
||||
}
|
||||
|
||||
.error-page-actions:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
bottom: 17px;
|
||||
right: 50px;
|
||||
width: 200px;
|
||||
height: 10px;
|
||||
-moz-box-shadow: 4px 5px 31px 11px #999;
|
||||
-webkit-box-shadow: 4px 5px 31px 11px #999;
|
||||
box-shadow: 4px 5px 31px 11px #999;
|
||||
-moz-transform: rotate(4deg);
|
||||
-webkit-transform: rotate(4deg);
|
||||
-ms-transform: rotate(4deg);
|
||||
-o-transform: rotate(4deg);
|
||||
transform: rotate(4deg)
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="error-page">
|
||||
<div class="error-page-container">
|
||||
<div class="error-page-main">
|
||||
<h3>
|
||||
<strong>404</strong>很抱歉,您要访问的文件/页面不存在!
|
||||
</h3>
|
||||
<div class="error-page-actions">
|
||||
<div>
|
||||
<h4>可能原因:</h4>
|
||||
<ol>
|
||||
<li>网络信号差不稳定</li>
|
||||
<li>找不到请求的页面</li>
|
||||
<li>输入的网址不正确</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div>
|
||||
<h4>可以尝试:</h4>
|
||||
<ol>
|
||||
<li><a href="#" onclick="backHomePage()">返回首页</a></li>
|
||||
<li><a href="https://github.com/zhaojun1998/zfile/issues" target="_blank">留言反馈</a></li>
|
||||
<li><a href="#">联系站长</a></li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function backHomePage() {
|
||||
window.location.href = window.location.origin;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user