diff --git a/pom.xml b/pom.xml
index e2cd149..2edd9fa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -94,6 +94,23 @@
3.6
+
+
+ io.milton
+ milton-server-ce
+ 3.1.1.413
+
+
+ commons-logging
+ commons-logging
+
+
+ json
+ org.json
+
+
+
+
org.projectlombok
diff --git a/src/main/java/im/zhaojun/zfile/config/ApplicationContextConfigure.java b/src/main/java/im/zhaojun/zfile/config/ApplicationContextConfigure.java
new file mode 100644
index 0000000..b60f65d
--- /dev/null
+++ b/src/main/java/im/zhaojun/zfile/config/ApplicationContextConfigure.java
@@ -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 getBean(Class clazz) throws BeansException {
+ return applicationContext.getBean(clazz);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/im/zhaojun/zfile/config/webdav/MiltonConfiguration.java b/src/main/java/im/zhaojun/zfile/config/webdav/MiltonConfiguration.java
new file mode 100644
index 0000000..309c670
--- /dev/null
+++ b/src/main/java/im/zhaojun/zfile/config/webdav/MiltonConfiguration.java
@@ -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);
+ }
+ }
+}
diff --git a/src/main/java/im/zhaojun/zfile/config/webdav/WebDavConfiguration.java b/src/main/java/im/zhaojun/zfile/config/webdav/WebDavConfiguration.java
new file mode 100644
index 0000000..3887f23
--- /dev/null
+++ b/src/main/java/im/zhaojun/zfile/config/webdav/WebDavConfiguration.java
@@ -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;
+ }
+
+
+}
diff --git a/src/main/java/im/zhaojun/zfile/config/webdav/adapter/WebDavUrlAdapterImpl.java b/src/main/java/im/zhaojun/zfile/config/webdav/adapter/WebDavUrlAdapterImpl.java
new file mode 100644
index 0000000..bc81d67
--- /dev/null
+++ b/src/main/java/im/zhaojun/zfile/config/webdav/adapter/WebDavUrlAdapterImpl.java
@@ -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 : "";
+ }
+
+}
diff --git a/src/main/java/im/zhaojun/zfile/config/webdav/auth/SystemConfigSecurityManager.java b/src/main/java/im/zhaojun/zfile/config/webdav/auth/SystemConfigSecurityManager.java
new file mode 100644
index 0000000..5e528fc
--- /dev/null
+++ b/src/main/java/im/zhaojun/zfile/config/webdav/auth/SystemConfigSecurityManager.java
@@ -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 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 nameAndPasswords) {
+ this.nameAndPasswords = nameAndPasswords;
+ }
+
+
+ @Override
+ public boolean isDigestAllowed() {
+ // 关闭请求摘要换算,client端请求时若换算为摘要,则无法和系统设置中获取的密码MD5比对
+ return false;
+ }
+
+}
+
diff --git a/src/main/java/im/zhaojun/zfile/config/webdav/resolver/WebDavRedirectViewResolver.java b/src/main/java/im/zhaojun/zfile/config/webdav/resolver/WebDavRedirectViewResolver.java
new file mode 100644
index 0000000..a849791
--- /dev/null
+++ b/src/main/java/im/zhaojun/zfile/config/webdav/resolver/WebDavRedirectViewResolver.java
@@ -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 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);
+ }
+ }
+ }
+}
diff --git a/src/main/java/im/zhaojun/zfile/controller/home/WebDavController.java b/src/main/java/im/zhaojun/zfile/controller/home/WebDavController.java
new file mode 100644
index 0000000..8ec1a1a
--- /dev/null
+++ b/src/main/java/im/zhaojun/zfile/controller/home/WebDavController.java
@@ -0,0 +1,157 @@
+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 java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * WebDav控制器
+ *
+ * @author me
+ * @date 2022/4/9
+ */
+@Slf4j
+@ResourceController
+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 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 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;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/im/zhaojun/zfile/model/constant/ZFileConstant.java b/src/main/java/im/zhaojun/zfile/model/constant/ZFileConstant.java
index 7045b33..6f63446 100644
--- a/src/main/java/im/zhaojun/zfile/model/constant/ZFileConstant.java
+++ b/src/main/java/im/zhaojun/zfile/model/constant/ZFileConstant.java
@@ -21,6 +21,11 @@ public class ZFileConstant {
*/
public static String DIRECT_LINK_PREFIX = "directlink";
+ /**
+ * WebDav前缀
+ */
+ public static String WEB_DAV_PREFIX = "/webdav";
+
/**
* 系统产生的临时文件路径
*/
diff --git a/src/main/java/im/zhaojun/zfile/model/entity/webdav/WebDavEntity.java b/src/main/java/im/zhaojun/zfile/model/entity/webdav/WebDavEntity.java
new file mode 100644
index 0000000..94491a1
--- /dev/null
+++ b/src/main/java/im/zhaojun/zfile/model/entity/webdav/WebDavEntity.java
@@ -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 convertFromFileItemDTO(List fileItemList, WebDavFolder parent) {
+ List 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;
+ }
+
+}
diff --git a/src/main/java/im/zhaojun/zfile/model/entity/webdav/WebDavFile.java b/src/main/java/im/zhaojun/zfile/model/entity/webdav/WebDavFile.java
new file mode 100644
index 0000000..dcaba60
--- /dev/null
+++ b/src/main/java/im/zhaojun/zfile/model/entity/webdav/WebDavFile.java
@@ -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);
+ }
+}
diff --git a/src/main/java/im/zhaojun/zfile/model/entity/webdav/WebDavFolder.java b/src/main/java/im/zhaojun/zfile/model/entity/webdav/WebDavFolder.java
new file mode 100644
index 0000000..5513216
--- /dev/null
+++ b/src/main/java/im/zhaojun/zfile/model/entity/webdav/WebDavFolder.java
@@ -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);
+ }
+
+}
diff --git a/src/main/java/im/zhaojun/zfile/util/RegexMatchUtils.java b/src/main/java/im/zhaojun/zfile/util/RegexMatchUtils.java
new file mode 100644
index 0000000..6e3b79f
--- /dev/null
+++ b/src/main/java/im/zhaojun/zfile/util/RegexMatchUtils.java
@@ -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);
+ }
+}