SpringBoot集成Milton(webdav)应用实例(一)
    有最奇崛的峰峦 成全过你我张狂
  
海上清辉与圆月 盛进杯光
有最孤傲的雪山 静听过你我诵章
世人惊羡的桥段 不过寻常
有最奇崛的峰峦 成全过你我张狂
海上清辉与圆月 盛进杯光
有最残破的书简 记载过光阴漫长
无意拾过的片瓦 历数寒凉
有最孤傲的雪山 静听过你我诵章
世人惊羡的桥段 不过寻常
    
01
—
webdav简介
    
WebDAV (Web-based Distributed Authoring and Versioning) 一种基于 HTTP 1.1协议的通信协议。它扩展了HTTP 1.1,在GET、POST、HEAD等几个HTTP标准方法以外添加了一些新的方法,使应用程序可对Web Server直接读写,并支持写文件锁定(Locking)及解锁(Unlock),还可以支持文件的版本控制。
            
简单来说,webdav是一种协议,可以通过这种协议,对储存于云的文件直接进行传输,和平时我们用的那个百度网盘类似,可以在自己的电脑上,直接对云文件传输编辑。
            
            
            
            
            
02
                    —
                    
Milton
            
Milton.io,是一个基于Java开发的,支持webdav协议的开源框架。详情可见官网介绍:https://milton.io/
            
此外,还支持CalDav和CardDav
            
使用milton开发webdav服务,通常可以通过2种方式进行,一种是注解的方式,这种方式简单明了。另外一种是基于milton原生的方式,即实现milton的原生类,实现一些接口方法,从而实现服务,两种方式各有各的好处,对于初学者来说,注解的方式比较友好一些。milton原生方式可能稍微复杂些,因为需要了解milton每个资源类的逻辑和用法
            
            
            
03
                    —
                    
Milton整合Spring
            
这里采用注解的方式,将通过一个实际的案例,一步一步来搭建一个属于自己的webdav服务。
            
              
          
            首先引入项目依赖,milton目前高版本是到4了,由于对新版本的不是很熟悉,所以这里使用比较熟悉的版本2,项目后续会通过读取数据库的文件存储目录结构,所以引入数据库支持,目前因为有个文件管理系统,目录结构存储在数据库,文件在文件存储服务器上的。
          
            
              
          
            
              
          
              
                
                  
                
              
              
                
                  <project xmlns="http://maven.apache.org/POM/4.0.0"
                
              
              
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              
              
                         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
              
              
                    <modelVersion>4.0.0</modelVersion>
              
              
                
                  
              
              
                    <groupId>com.lgli</groupId>
              
              
                    <artifactId>webdav-server</artifactId>
              
              
                    <version>1.0</version>
              
              
                
                  
              
              
                    <properties>
              
              
                        <maven.compiler.source>8</maven.compiler.source>
              
              
                        <maven.compiler.target>8</maven.compiler.target>
              
              
                
                  
              
              
                        <milton.version>2.8.0.3</milton.version>
              
              
                        <springboot.version>2.5.11</springboot.version>
              
              
                        <mybatis.plus>3.5.3.1</mybatis.plus>
              
              
                        <mysql.connection>8.0.32</mysql.connection>
              
              
                    </properties>
              
              
                
                  
              
              
                
                  
              
              
                    <dependencies>
              
              
                        <dependency>
              
              
                            <groupId>org.springframework.boot</groupId>
              
              
                            <artifactId>spring-boot-configuration-processor</artifactId>
              
              
                            <version>${springboot.version}</version>
              
              
                            <optional>true</optional>
              
              
                        </dependency>
              
              
                
                  
              
              
                        <dependency>
              
              
                            <groupId>org.springframework.boot</groupId>
              
              
                            <artifactId>spring-boot-starter</artifactId>
              
              
                            <version>${springboot.version}</version>
              
              
                        </dependency>
              
              
                
                  
              
              
                        <dependency>
              
              
                            <groupId>org.springframework.boot</groupId>
              
              
                            <artifactId>spring-boot-starter-web</artifactId>
              
              
                            <version>${springboot.version}</version>
              
              
                        </dependency>
              
              
                
                  
              
              
                        <dependency>
              
              
                            <groupId>com.baomidou</groupId>
              
              
                            <artifactId>mybatis-plus-boot-starter</artifactId>
              
              
                            <version>${mybatis.plus}</version>
              
              
                        </dependency>
              
              
                
                  
              
              
                        <dependency>
              
              
                            <groupId>mysql</groupId>
              
              
                            <artifactId>mysql-connector-java</artifactId>
              
              
                            <version>${mysql.connection}</version>
              
              
                        </dependency>
              
              
                
                  
              
              
                
                  
              
              
                        <dependency>
              
              
                            <groupId>io.milton</groupId>
              
              
                            <artifactId>milton-server-ce</artifactId>
              
              
                            <version>${milton.version}</version>
              
              
                        </dependency>
              
              
                
                  
              
              
                
                  
              
              
                        <dependency>
              
              
                            <groupId>cn.hutool</groupId>
              
              
                            <artifactId>hutool-all</artifactId>
              
              
                            <version>5.8.16</version>
              
              
                        </dependency>
              
              
                
                  
              
              
                    </dependencies>
              
              
                
                  
              
              
                    <build>
              
              
                        <!--保证配置文件和xml通过编译-->
              
              
                        <resources>
              
              
                            <resource>
              
              
                                <directory>src/main/resources</directory>
              
              
                                <includes>
              
              
                                    <include>**/*.yaml</include>
              
              
                                    <include>**/*.properties</include>
              
              
                                    <include>**/banner.txt</include>
              
              
                                </includes>
              
              
                            </resource>
              
              
                            <resource>
              
              
                                <directory>src/main/java</directory>
              
              
                                <includes>
              
              
                                    <include>**/*.xml</include>
              
              
                                </includes>
              
              
                            </resource>
              
              
                        </resources>
              
              
                    </build>
              
              
                
                  </project>
                
              
            
          
milton的大致工作原理是,对特定的webdav协议的http请求,有一个自己的处理方法,即io.milton.http.HttpManager#process方法,所以,首先需要定义一个拦截器类,把所需要milton处理的http请求,给予milton来处理。
            
这里定义一个SpringMiltonFilter拦截器,继承org.springframework.web.filter.GenericFilterBean类,并重写doFilter方法,
            
              
          
            
            
              
          
这里的io.milton.http.HttpManager#process就是milton处理webdav请求的方法,这个HttpManager是来自于一个io.milton.config.HttpManagerBuilder通过 io.milton.config.HttpManagerBuilder #buildHttpManager方法所创建出来的,所以要获取HttpManager,首先要获取到HttpManagerBuilder
            
              
                
            
          
HttpManagerBuilder的获取,可以通过new一个实例,同时设定一些必要的参数,比如这里所用的是注解的方式,那么 需要指定milton的Resource包扫描和一个ResourceFactory(这俩是必须的) ,还有一些需要或者不需要的权限认证的设置,首先这里先不考虑权限,所以权限全部设置为false,
            
              
          
            
            
            
这里使用注解的方式,需要的ResourceFactory是一个io.milton.http.annotated.AnnotationResourceFactory
            
            即:
          
            
              
          
            
            
              
          
在构建ResourceFactory的时候,指定的安全管理器,先设置为null,就是图例显示的io.milton.http.fs.NullSecurityManager
            
最后,在指定milton的包扫描路径中(io.milton.config.HttpManagerBuilder#setControllerPackagesToScan),创建一个自己的Resource管理器,同时标注注解 ResourceController
            
              
          
            
            
写一个方法,标注注解Root,表示根文件夹,这里表示当webdav请求服务的时候,找到的根文件夹。
            
这个时候还需要从根文件夹中获取文件或者文件夹,需要另外写其他的方法,来表示获取根文件夹中的文件/文件夹,或者从文件夹中获取下一级文件夹(直到递归到最后一级文件夹),这时候只需要写一个方法就好了,标注注解ChildrenOf
            
            
            
方法传入了一个实体类,这里可以发现,这个实体类和Root返回的根文件夹是同一个类,然后,如果实体类的ID为空,视为获取根文件夹下的文件/文件夹,这里返回了2个文件夹和1个txt文件:
            
            
            
            
获取根文件的文件/文件夹都设置了有ID,于是后续调用这个方法的时候,会根据ID来获取指定文件夹下的文件/文件夹
            
              
          
            那么基于此,一个简单的webdav服务就实现了,看下效果:
          
            
            
            
            
              
          
            
              
          
这里把本次涉及到的核心代码片段贴在下面:
            
            
              package com.lgli.webdav.config;
            
            
              
                
            
            
              
                
            
            
              import io.milton.config.HttpManagerBuilder;
            
            
              import io.milton.http.HttpManager;
            
            
              import io.milton.http.Request;
            
            
              import io.milton.http.ResourceFactory;
            
            
              import io.milton.http.Response;
            
            
              import io.milton.http.annotated.AnnotationResourceFactory;
            
            
              import io.milton.http.fs.NullSecurityManager;
            
            
              import io.milton.http.http11.DefaultHttp11ResponseHandler;
            
            
              import org.springframework.beans.factory.annotation.Autowired;
            
            
              import org.springframework.context.annotation.Bean;
            
            
              import org.springframework.context.annotation.Configuration;
            
            
              import org.springframework.web.filter.GenericFilterBean;
            
            
              
                
            
            
              import javax.servlet.FilterChain;
            
            
              import javax.servlet.ServletException;
            
            
              import javax.servlet.ServletRequest;
            
            
              import javax.servlet.ServletResponse;
            
            
              import javax.servlet.http.HttpServletRequest;
            
            
              import javax.servlet.http.HttpServletResponse;
            
            
              import java.io.IOException;
            
            
              import java.util.List;
            
            
              import java.util.Optional;
            
            
              
                
            
            
              
                /**
              
            
            
              
                 * springboot整合milton过滤器
              
            
            
              
                 *
              
            
            
              
                 * @author lgli
              
            
            
              
                 */
              
            
            
              
                
            
            
              
                
              
            
            
              public class SpringMiltonFilter extends GenericFilterBean {
            
            
              
                
            
            
                  /**
            
            
              
                     * milton配置类
              
            
            
              
                     */
              
            
            
                  private MiltonProperties miltonProperties;
            
            
              
                
            
            
                  /**
            
            
              
                     * milton Http管理构建器
              
            
            
              
                     * 主要用于生成HttpManager用于处理webdav请求
              
            
            
              
                     */
              
            
            
                  private HttpManagerBuilder httpManagerBuilder;
            
            
              
                
            
            
                  /**
            
            
              
                     * milton Http处理webdav
              
            
            
              
                     */
              
            
            
                  private HttpManager httpManager;
            
            
              
                
            
            
              
                
            
            
                  
            
            
                  public void setMiltonProperties(MiltonProperties miltonProperties) {
            
            
                      this.miltonProperties = miltonProperties;
            
            
                  }
            
            
              
                
            
            
              
                
            
            
                  
            
            
                  public void setHttpManagerBuilder(HttpManagerBuilder httpManagerBuilder) {
            
            
                      this.httpManagerBuilder = httpManagerBuilder;
            
            
                  }
            
            
              
                
            
            
              
                
            
            
                  
            
            
                  HttpManagerBuilder httpManagerBuilder(@Autowired ResourceFactory resourceFactory
            
            
                          , @Autowired MiltonProperties mp) {
            
            
                      HttpManagerBuilder builder = new HttpManagerBuilder();
            
            
                      builder.setResourceFactory(resourceFactory);
            
            
                      builder.setBuffering(DefaultHttp11ResponseHandler.BUFFERING.whenNeeded);
            
            
                      builder.setEnableCompression(false);
            
            
                      builder.setEnableBasicAuth(false);
            
            
                      builder.setEnableCookieAuth(false);
            
            
                      builder.setControllerPackagesToScan(mp.getController());
            
            
                      builder.setUrlAdapter((re) -> {
            
            
                          String s = HttpManager.decodeUrl(re.getAbsolutePath());
            
            
                          List<String> httpUrl = miltonProperties.getHttpUrl();
            
            
                          for (String s1 : httpUrl) {
            
            
                              boolean isSatisfy = s.contains(s1);
            
            
                              if (isSatisfy) {
            
            
                                  s = s.replace(s1, "");
            
            
                              }
            
            
                          }
            
            
                          if( s.contains( "/DavWWWRoot")) {
            
            
                              s =  s.replace( "/DavWWWRoot", "");
            
            
                          }
            
            
                          return s;
            
            
                      });
            
            
                      return builder;
            
            
                  }
            
            
              
                
            
            
              
                
            
            
                  
            
            
                  ResourceFactory getResourceFactory() {
            
            
                      AnnotationResourceFactory factory = new AnnotationResourceFactory();
            
            
                      factory.setSecurityManager(new NullSecurityManager());
            
            
                      return factory;
            
            
                  }
            
            
              
                
            
            
              
                
            
            
              
                
            
            
              
                
            
            
              
                
            
            
              
                
            
            
              
                
            
            
                  
            
            
                  public void destroy() {
            
            
                      super.destroy();
            
            
                      Optional.of(httpManager).ifPresent(HttpManager::shutdown);
            
            
                  }
            
            
              
                
            
            
                  
            
            
                  protected void initFilterBean() throws ServletException {
            
            
                      super.initFilterBean();
            
            
                      Optional.of(httpManagerBuilder).ifPresent(hmb -> httpManager = hmb.buildHttpManager());
            
            
              
                
            
            
                  }
            
            
              
                
            
            
                  
            
            
                  public void doFilter(ServletRequest servletRequest
            
            
                          , ServletResponse servletResponse
            
            
                          , FilterChain filterChain) throws IOException, ServletException {
            
            
                      HttpServletRequest request = (HttpServletRequest) servletRequest;
            
            
                      String requestURI = request.getRequestURI();
            
            
                      //判断是否需要milton处理
            
            
                      boolean present = miltonProperties.getHttpUrl()
            
            
                              .stream()
            
            
                              .anyMatch(requestURI::startsWith);
            
            
                      if (!present) {
            
            
                          filterChain.doFilter(servletRequest, servletResponse);
            
            
                          return;
            
            
                      }
            
            
                      Request miltonReq = new io.milton.servlet
            
            
                              .ServletRequest(request, getServletContext());
            
            
                      Response miltonRes = new io.milton.servlet
            
            
                              .ServletResponse((HttpServletResponse) servletResponse);
            
            
                      httpManager.process(miltonReq, miltonRes);
            
            
                  }
            
            
              
                
            
            
              
                
            
            
              }
            
            
              
                
            
          
        
            
          
          
            
            
              package com.lgli.webdav.controller;
            
            
              
                
            
            
              import cn.hutool.core.util.StrUtil;
            
            
              import com.lgli.webdav.entity.FileEntity;
            
            
              import com.lgli.webdav.entity.FolderEntity;
            
            
              import com.lgli.webdav.entity.ResourceEntity;
            
            
              import io.milton.annotations.*;
            
            
              
                
            
            
              import java.io.InputStream;
            
            
              import java.util.ArrayList;
            
            
              import java.util.List;
            
            
              
                
            
            
              
                /**
              
            
            
              
                 * ResourceController
              
            
            
              
                 *
              
            
            
              
                 * @author lgli
              
            
            
              
                 */
              
            
            
              
                
              
            
            
              public class ResourceManageController {
            
            
              
                
            
            
                  
            
            
                  public FolderEntity getRoot() {
            
            
                      FolderEntity folderEntity = new FolderEntity();
            
            
                      folderEntity.setName("root");
            
            
                      return folderEntity;
            
            
                  }
            
            
              
                
            
            
              
                
            
            
                  
            
            
                  public List<ResourceEntity> getList(FolderEntity folder) {
            
            
                      List<ResourceEntity> list = new ArrayList<>();
            
            
                      if (StrUtil.isEmpty(folder.getId())) {
            
            
                          //根文件夹
            
            
                          FolderEntity folderEntity0 = new FolderEntity();
            
            
                          folderEntity0.setId("0");
            
            
                          folderEntity0.setName("文件夹0");
            
            
                          list.add(folderEntity0);
            
            
                          FolderEntity folderEntity1 = new FolderEntity();
            
            
                          folderEntity1.setId("1");
            
            
                          folderEntity1.setName("文件夹1");
            
            
                          list.add(folderEntity1);
            
            
                          FileEntity fileEntity0 = new FileEntity();
            
            
                          fileEntity0.setId("2");
            
            
                          fileEntity0.setName("文件1.txt");
            
            
                          list.add(fileEntity0);
            
            
                      } else if ("0".equals(folder.getId())) {
            
            
                          FolderEntity folderEntity0 = new FolderEntity();
            
            
                          folderEntity0.setId("0-1");
            
            
                          folderEntity0.setName("文件夹0-1");
            
            
                          list.add(folderEntity0);
            
            
                      } else if ("1".equals(folder.getId())) {
            
            
                          FolderEntity folderEntity0 = new FolderEntity();
            
            
                          folderEntity0.setId("1-1");
            
            
                          folderEntity0.setName("文件夹1-1");
            
            
                          list.add(folderEntity0);
            
            
                      } else if ("1-1".equals(folder.getId())) {
            
            
                          FolderEntity folderEntity0 = new FolderEntity();
            
            
                          folderEntity0.setId("1-1-1");
            
            
                          folderEntity0.setName("文件夹1-1-1");
            
            
                          list.add(folderEntity0);
            
            
                      }
            
            
                      return list;
            
            
                  }
            
            
              }
            
            
              
                
            
          
        
            
          
        
            后续将对 demo每个类和Resource类做一个详细的介绍,同时还会添加权限认证, 请持续关注,
如果你也感兴趣,麻烦多多支持。谢谢!
          
            
        
如有错误,烦请指正。
          
            
        
点击公众号获取更多
          
            
        
