java ftp服务器 多用户、 多文件夹监听文件上传 spring boot整合Ftp

引言

  1. 这里介绍 java创建 Ftp服务器, 并且监测客户端上传文件后,读取文件的操作。使用 hutool 创建Ftp客户端,使用 ftplet-api 下的 DefaultFtplet 进行文件监听。
  2. 上传的文件夹通过不同用户添加,设置不同的用户名、密码、上传文件夹,测试时通过连接不同的用户,上传文件到指定文件夹。
  3. 这里有个小小的问题 DefaultFtplet 监听器是 ftp 依赖下的类,测试使用 Hutool 下的 Ftp 类,所以无法通过 hutoolFtp类 测试指定文件夹上传,只能上传 创建 Ftp 服务器 时 指定的文件夹,即创建 Ftp服务器时 添加 不同用户 的文件夹

sql 数据表

CREATE TABLE `ftp_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ftp_code` varchar(255) NOT NULL COMMENT 'ftp编码',
  `username` varchar(255) NOT NULL COMMENT '用户名',
  `password` varchar(255) NOT NULL COMMENT '密码',
  `upload_folder` varchar(255) NOT NULL COMMENT '上传文件夹',
  `status` int(11) NOT NULL DEFAULT '0' COMMENT '是否使用 1使用 0 未用',
  PRIMARY KEY (`id`),
  UNIQUE KEY `user` (`username`,`password`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;

数据表实体类

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class FtpInfo {

    private Integer id;

    private String ftpCode;

    private String username;

    private String password;

    private String uploadFolder;

    private Integer status;
}

添加依赖



   org.projectlombok
   lombok



    org.apache.ftpserver
    ftpserver-core
    1.2.0



    org.apache.ftpserver
    ftplet-api
    1.2.0



    org.apache.mina
    mina-core
    2.0.16



    cn.hutool
    hutool-all
    5.8.18

yml配置文件 及配置类

这里提供一种,引入 ftp服务器 端口的方式,也可以通过方式引入

yml配置文件 定义账号 密码 端口号

ftp-server:
  port: 27    # 端口号

配置类

package com.esframework.project.kdzt.ftp.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

/**
 * @author Administrator
 */
@Data
@Component
@Configuration
@ConfigurationProperties(prefix = "ftp-server")
public class FtpProperties {

    private Integer port;
}

利用hutool代码 整合spring boot 创建 ftp服务端

配置监听器

package com.example.ftp.ftp2;

import org.apache.ftpserver.ftplet.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

/**
 * ftp服务器侦听器
 *
 * @date 2023/06/29
 */
public class FtpServerListener extends DefaultFtplet {
    public static final Logger log = LoggerFactory.getLogger(FtpServerListener.class);


    /**
     * 在连接* 开始连接@param session 会话
     *
     * @return {@link FtpletResult}
     * @throws FtpException ftp例外
     * @throws IOException  ioexception
     */
    @Override
    public FtpletResult onConnect(FtpSession session) throws FtpException, IOException {
        UUID sessionId = session.getSessionId();
        if (sessionId != null) {
            log.info("{}尝试登录ftpserver", sessionId);
        }
        User user = session.getUser();
        if (user != null && user.getName() != null) {
            assert sessionId != null;
            log.info("{}尝试使用用户名:{},密码:{}登录ftpserver.", sessionId, user.getName(), user.getPassword());
        }
        return super.onConnect(session);
    }

    /**
     * 在断开* 关闭连接@param session 会话
     *
     * @return {@link FtpletResult}
     * @throws FtpException ftp例外
     * @throws IOException  ioexception
     */
    @Override
    public FtpletResult onDisconnect(FtpSession session) throws FtpException, IOException {
        UUID sessionId = session.getSessionId();
        User user = session.getUser();
        if (sessionId != null) {
            log.info("{}关闭ftpserver连接", sessionId);
        }
        if (user != null && user.getName() != null) {
            assert sessionId != null;
            log.info("{}用户名:{}关闭ftpserver连接.", sessionId, user.getName());
        }
        return super.onDisconnect(session);
    }

    /**
     * 上传开始* 开始上传@param session 会话
     *
     * @param request 请求
     * @return {@link FtpletResult}
     * @throws FtpException ftp例外
     * @throws IOException  ioexception
     */
    @Override
    public FtpletResult onUploadStart(FtpSession session, FtpRequest request) throws FtpException, IOException {
        //获取上传文件的上传路径
        String path = session.getUser().getHomeDirectory();
        //自动创建上传路径
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
        //获取上传用户
        String name = session.getUser().getName();
        // 获取上传文件名
        String filename = request.getArgument();
        log.info("用户:'{}',上传文件到目录:'{}',文件名称为:'{}',状态:开始上传~", name, path, filename);

        return super.onUploadEnd(session, request);
    }

    /**
     * 在上传* 上传完成@param session 会话
     *
     * @param request 请求
     * @return {@link FtpletResult}
     * @throws FtpException ftp例外
     * @throws IOException  ioexception
     */
    @Override
    public FtpletResult onUploadEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
        //获取上传文件的上传路径
         String path = session.getUser().getHomeDirectory();
        // 获取上传用户
        String name = session.getUser().getName();
        // 获取上传文件名
        String fileName = request.getArgument();
        File file = new File(path + "/" + fileName);
        if (file.exists()) {
            // 读取文件
            String filePath = String.format("%s\\%s", path, fileName);
            log.info("用户:'{}',上传文件到目录:'{}',文件名称为:'{},状态:成功!'", name, path, fileName);
        }
        return super.onUploadStart(session, request);
    }

    @Override
    public FtpletResult onDownloadStart(FtpSession session, FtpRequest request) throws FtpException, IOException {
        return super.onDownloadStart(session, request);
    }

    @Override
    public FtpletResult onDownloadEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
        return super.onDownloadEnd(session, request);
    }
}

使用 ApplicationRunner 启动服务时,创建 ftp服务

package com.example.ftp.ftp2;

import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.ftp.SimpleFtpServer;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ftpserver.ftplet.Authority;
import org.apache.ftpserver.usermanager.impl.BaseUser;
import org.apache.ftpserver.usermanager.impl.WritePermission;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.List;

/**
 * ftp服务器
 *
 * @author Administrator
 * @date 2023/06/30
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class FtpServer implements ApplicationRunner {

    private final FtpProperties ftpProperties;

    @Override
    public void run(ApplicationArguments args) throws Exception {
		// 这里演示写了两个用户  实际业务中 可以查询数据库添加
        BaseUser user = new BaseUser();
        user.setName("user1");
        user.setPassword("user1");
        // 配置上传的文件夹 没有就新建
        File folder = FileUtil.file("D:\\ftp1\\user1");
        if(!folder.exists()) {
            boolean mkdir = folder.mkdir();
        }
        user.setHomeDirectory("D:\\ftp1\\user1");
        user.setMaxIdleTime(0);
        user.setEnabled(true);
        //设置用户权限 不设置权限 上传文件会被拒绝 导致上传失败
        List authorities = Lists.newArrayList();
        authorities.add(new WritePermission());
        user.setAuthorities(authorities);


        BaseUser user1 = new BaseUser();
        user1.setName("user2");
        user1.setPassword("user2");
        // 配置上传的文件夹 没有就新建
        File folder1 = FileUtil.file("D:\\ftp1\\user2");
        if(!folder.exists()) {
            boolean mkdir = folder1.mkdir();
        }
        user1.setHomeDirectory("D:\\ftp1\\user2");
        user1.setMaxIdleTime(0);
        user1.setEnabled(true);
        //设置用户权限
        List authorities1 = Lists.newArrayList();
        authorities1.add(new WritePermission());
        user1.setAuthorities(authorities1);

        SimpleFtpServer simpleFtpServer = SimpleFtpServer.create()
        // 添加用户
        .addUser(user).addUser(user1)
        .setPort(ftpProperties.getPort());
        // 配置监听器
        simpleFtpServer.addFtplet("folder", new FtpServerListener());
        simpleFtpServer.start();
        log.info("Ftp 开启=====》");
    }
}

测试

使用 hutool 进行测试

注意
下面使用 public boolean upload(String destPath, File file) 这个方法时 destPath 会根据 Ftp Server 创建时 user的 setHomeDirectory 参数变化,

  1. 不设置 setHomeDirectory 参数时,destPath 写全部路径,但是监听器无法获取 上传文件的全路径。
  2. 设置 setHomeDirectory 参数时, destPath 填写 / 就是 setHomeDirectory 定义的文件夹
package com.example.ftp.ftp;

import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.ftp.Ftp;

import java.io.IOException;

public class FtpTest {

    public static void main(String[] args) {
       // 输入不同的用户名、密码 测试上传不通的文件夹
        Ftp ftp1 = new Ftp("ip", port, "username", "password");
        try {
            ftp1.upload("/", FileUtil.file("D:\\FlFile\\1234.txt"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
               ftp1.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}


总结

  1. 最近遇到了上传Ftp到服务器后,读取文件保存到数据库的功能,然后查找相关代码,最初查到了,使用hutool 工具类创建ftp服务器, 使用 hutool 的 文件监听器 WatchMonitor 然后监听 指定目录,监听到新建文件,然后开始读取文件,但是如果项目中发生异常,监听会出问题。
  2. 然后又找到了Ftp 自己带的文件监听 DefaultFtplet, 可以监听到文件上传成功,然后操作。
  3. 最后就是如何多用户多文件夹上传的问题,测试了几遍,发现在创建Ftp服务的时候,可以添加多个用户,每个用户不同的用户名、密码、指定的上传文件夹,在客户端上传时,可以准确监听到文件上传,这样 多用户 多文件夹上传功能终于实现了。

几个要点

用户指定用户名、密码、文件夹

  BaseUser user1 = new BaseUser();
  user1.setName("user2");
  user1.setPassword("user2");
  // 配置上传的文件夹 没有就新建
  File folder1 = FileUtil.file("D:\\ftp1\\user2");
  if(!folder.exists()) {
      boolean mkdir = folder1.mkdir();
  }
  user1.setHomeDirectory("D:\\ftp1\\user2");
  user1.setMaxIdleTime(0);
  user1.setEnabled(true);

设置用户权限、不设置用户权限 上传文件会被拒绝,导致上传失败

  //设置用户权限
  List authorities1 = Lists.newArrayList();
  authorities1.add(new WritePermission());
  user1.setAuthorities(authorities1);

hutool 文件监听示例

ftp 文件监听,失败的迭代示例

package com.example.ftp.ftp;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.watch.WatchMonitor;
import cn.hutool.core.io.watch.Watcher;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.CharsetUtil;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.util.List;

@Component
public class FtpUpdate implements ApplicationRunner {


    public void update() {
        File file = FileUtil.file("D:\\ftp");
        //这里只监听文件或目录的修改事件
        WatchMonitor watchMonitor = WatchMonitor.create(file, WatchMonitor.ENTRY_CREATE);
        watchMonitor.setWatcher(new Watcher() {
            @Override
            public void onCreate(WatchEvent event, Path currentPath) {
                Object obj = event.context();
                Console.log("创建:{}-> {}", currentPath, obj);
                String filePath = String.format("%s\\%s", currentPath, obj.toString());
                String destPath = String.format("%s\\%s", "D:\\OfFile", obj);

                // 读取文件
                List readLines = FileUtil.readLines(destPath, CharsetUtil.UTF_8);
                readLines.forEach(System.out::println);
            }

            @Override
            public void onModify(WatchEvent event, Path currentPath) {
                Object obj = event.context();
                Console.log("修改:{}-> {}", currentPath, obj);
            }

            @Override
            public void onDelete(WatchEvent event, Path currentPath) {
                Object obj = event.context();
                Console.log("删除:{}-> {}", currentPath, obj);
            }

            @Override
            public void onOverflow(WatchEvent event, Path currentPath) {
                Object obj = event.context();
                Console.log("Overflow:{}-> {}", currentPath, obj);
            }
        });
        watchMonitor.setMaxDepth(3);

        //设置监听目录的最大深入,目录层级大于制定层级的变更将不被监听,默认只监听当前层级目录
        //启动监听
        watchMonitor.start();
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        update();
    }
}

你可能感兴趣的:(java,服务器,开发语言)