Apache FtpServer在Windows上使用以及SpringBoot中集成apache ftpserver实现Ftp 服务端搭建
场景
Apache Ftpserver
Apache FtpServer是100%纯Java FTP服务器。它被设计为基于当前可用的开放协议的完整且
可移植的FTP服务器引擎解决方案。FtpServer可以作为Windows服务或Unix / Linux守护程序独立运行,
也可以嵌入Java应用程序中。我们还提供对Spring应用程序内集成的支持,
并以OSGi捆绑软件的形式提供我们的发行版。默认的网络支持基于高性能异步IO库Apache MINA。
使用MINA,FtpServer可以扩展到大量并发用户。
特性
1、100%纯Java,免费的开源可恢复FTP服务器
2、多平台支持和多线程设计。
3、用户虚拟目录,写入权限,空闲超时和上传/下载带宽限制支持。
4、匿名登录支持。
5、上传和下载文件都是可恢复的。
6、处理ASCII和二进制数据传输。
7、支持IP限制以禁止IP。
8、数据库和文件可用于存储用户数据。
9、所有FTP消息都是可定制的。
10、隐式/显式SSL / TLS支持。
11、MDTM支持-您的用户可以更改文件的日期时间戳。
12、“模式Z”支持更快地上传/下载数据。
13、可以轻松添加自定义用户管理器,IP限制器,记录器。
14、可以添加用户事件通知(Ftplet)。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
实现
Apache FtpServer在Windows上下载安装与使用
1、Apache Ftp Server下载
进入apache官网
https://mina.apache.org/

点击FtpServer选项

选择自己需要下载的版本,比如这里选择1.1.1

然后再进入dist目录,选择zip点击下载

下载之后解压即可。
2、修改用户配置文件
找到res/conf下的users.properties
编辑该文件,是配置ftp用户相关信息的配置文件
# Password is "admin" ftpserver.user.admin.userpassword=123456 ftpserver.user.admin.homedirectory=D:\\ftp ftpserver.user.admin.enableflag=true ftpserver.user.admin.writepermission=true ftpserver.user.admin.maxloginnumber=0 ftpserver.user.admin.maxloginperip=0 ftpserver.user.admin.idletime=0 ftpserver.user.admin.uploadrate=0 ftpserver.user.admin.downloadrate=0
修改为上面示例配置,即配置admin账户的密码,文件路径等相关配置。
其他参数为0表示不进行限制,不然可以根据自己需要搜索或者官方源码api中说明进行配置。
3、修改ftpd全局配置文件
找到res/ftpd-typical.xml,并编辑如下两个地方

修改
<file-user-manager file="./res/conf/users.properties" encrypt-passwords="clear"/>
取消密码加密
不然会提示
RECEIVED: PASS *****
导致登录失败。

再注释掉ssl认证
不然会提示
javax.net.ssl.SSLHandshakeException: SSL handshake failed.
at
org.apache.mina.filter.ssl.SslFilter.messageReceived(SslFilter.java:519)
Caused by: javax.net.ssl.SSLHandshakeException: no cipher suites in common

3、启动ftpserver
进入到bin目录下,打开cmd输入
.\ftpd.bat res/conf/ftpd-typical.xml
通过走配置文件的方式启动

启动成功之后浏览器访问本地ip的2121端口

输入上面配置的用户名admin,密码

登录成功之后如上所示。
Ftp 客户端FileZilla使用
FileZilla Ftp客户端工具
https://www.filezilla.cn/download/client

下载对应版本之后,解压即可用

进行文件传输测试可用。
SpringBoot中集成Apache FtpServer实现Ftp服务端
1、新建SpringBoot项目,添加所需依赖
<!-- https://mvnrepository.com/artifact/org.apache.ftpserver/ftpserver-core --> <dependency> <groupId>org.apache.ftpserver</groupId> <artifactId>ftpserver-core</artifactId> <version>1.1.1</version> </dependency>
2、新建FTP配置类
import org.apache.ftpserver.DataConnectionConfigurationFactory; import org.apache.ftpserver.FtpServer; import org.apache.ftpserver.FtpServerFactory; import org.apache.ftpserver.ftplet.Ftplet; import org.apache.ftpserver.listener.ListenerFactory; import org.apache.ftpserver.usermanager.ClearTextPasswordEncryptor; import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import java.util.HashMap; import java.util.Map; /** * 配置类 */ @Configuration public class FtpConfig extends CachingConfigurerSupport { @Value("${ftp.port}") private Integer ftpPort; @Value("${ftp.activePort}") private Integer ftpActivePort; @Value("${ftp.passivePorts}") private String ftpPassivePorts; @Bean public FtpServer createFtpServer(){ FtpServerFactory serverFactory = new FtpServerFactory(); ListenerFactory factory = new ListenerFactory(); //设置连接端口 factory.setPort(ftpPort); DataConnectionConfigurationFactory dataConnectionConfigurationFactory=new DataConnectionConfigurationFactory(); //设置多少时间后关闭一个闲置的链接,单位是秒,0代表不设置 dataConnectionConfigurationFactory.setIdleTime(60*60*24); //设置主动模式端口 dataConnectionConfigurationFactory.setActiveLocalPort(ftpActivePort); dataConnectionConfigurationFactory.setPassiveIpCheck(true); //设置被动模式端口 dataConnectionConfigurationFactory.setPassivePorts(ftpPassivePorts); factory.setDataConnectionConfiguration(dataConnectionConfigurationFactory.createDataConnectionConfiguration()); //替换默认监听器 serverFactory.addListener("default", factory.createListener()); //读取用户配置文件 PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory(); try { ClassPathResource classPathResource = new ClassPathResource("users.properties"); userManagerFactory.setUrl(classPathResource.getURL()); } catch (Exception e){ throw new RuntimeException("配置文件users.properties不存在"); } userManagerFactory.setPasswordEncryptor(new ClearTextPasswordEncryptor()); serverFactory.setUserManager(userManagerFactory.createUserManager()); //配置自定义用户事件 Map<String, Ftplet> myFtplet = new HashMap<String, Ftplet>(); myFtplet.put("miaFtplet", new MyFtplet()); serverFactory.setFtplets(myFtplet); //创建FtpServer FtpServer server = serverFactory.createServer(); return server; } }
2、这里的ftp连接端口、ftp主动模式端口、ftp被动模式端口范围从配置文件读取,所以在application.yml中添加配置
ftp: port: 200 #ftp连接端口 activePort: 250 #ftp主动模式端口 passivePorts: "50021-60021" #被动连接数据传输端口
3、上面配置类中获取用户配置,读取resources下的文件,所以在resources下新建users.properties
#表示test的密码是123456 以下都是admin的参数设置,可以多个 ftpserver.user.test.userpassword=123456 ftpserver.user.test.homedirectory=C:\\ftpFile ftpserver.user.test.enableflag=true ftpserver.user.test.writepermission=true ftpserver.user.test.maxloginnumber=0 ftpserver.user.test.maxloginperip=0 ftpserver.user.test.idletime=0 ftpserver.user.test.uploadrate=0 ftpserver.user.test.downloadrate=0
4、配置类中还对用户事件进行自定义
新建MyFtplet并继承DefaultFtplet,对会话连接、断连、登录、上传文件等事件进行自定义处理事件
import org.apache.ftpserver.ftplet.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.util.UUID; public class MyFtplet extends DefaultFtplet { public static final Logger log= LoggerFactory.getLogger(MyFtplet.class); @Override public FtpletResult onLogin(FtpSession session, FtpRequest request) throws FtpException, IOException { try { User user = session.getUser(); String requestLine = request.getRequestLine(); String name = user.getName(); log.info("用户:'{}'登录成功,requestLine:'{}'", name, requestLine); }catch (Exception exception){ log.error("用户:'{}'登录异常:{}", session.toString(),exception.toString()); } return super.onLogin(session, request); } @Override public FtpletResult onConnect(FtpSession session) throws FtpException, IOException { try { UUID sessionId = session.getSessionId(); InetSocketAddress clientAddress = session.getClientAddress(); String hostString = clientAddress.getHostString(); log.info("用户:'{}',hostString:'{}',建立连接", sessionId, hostString); }catch (Exception exception){ log.error("用户:'{}',建立连接异常:{}", session.toString(),exception.toString()); } return super.onConnect(session); } @Override public FtpletResult onDisconnect(FtpSession session) throws FtpException, IOException { try{ String name = session.getUser().getName(); String hostString = session.getClientAddress().getHostString(); log.info("用户:'{}',hostString:'{}',断开连接", name, hostString); }catch (Exception exception){ log.error("用户:'{}',断开连接异常:{}", session.toString(),exception.toString()); } return super.onDisconnect(session); } /** * * 开始上传 * Override this method to intercept uploads * @param session The current {@link FtpSession} * @param request The current {@link FtpRequest} * @return The action for the container to take * @throws FtpException * @throws IOException */ @Override public FtpletResult onUploadStart(FtpSession session, FtpRequest request) throws FtpException, IOException { try { //获取上传文件的上传路径 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); }catch (Exception exception){ log.error("用户:'{}',上传文件异常:{}", session.toString(),exception.toString()); } return super.onUploadStart(session, request); } /** * 上传完成 * Override this method to handle uploads after completion * @param session The current {@link FtpSession} * @param request The current {@link FtpRequest} * @return The action for the container to take * @throws FtpException * @throws IOException */ @Override public FtpletResult onUploadEnd(FtpSession session, FtpRequest request) throws FtpException, IOException { try { //获取上传文件的上传路径 String path = session.getUser().getHomeDirectory(); //获取上传用户 String name = session.getUser().getName(); //获取上传文件名 String filename = request.getArgument(); File file=new File(path+"/"+filename); if (file.exists()){ System.out.println(file); } log.info("用户:'{}',上传文件到目录:'{}',文件名称为:'{},状态:成功!'", name, path, filename); }catch (Exception exception){ log.error("用户:'{}',上传文件结束异常:{}", session.toString(),exception.toString()); } return super.onUploadEnd(session, request); } }
5、启动应用,并连接和上传文件测试
连接并登录成功进入回调

文件上传成功进入回调

6、配置类相关配置含义可以查询官方源码以及demo等查看
官网下载源码版压缩包

查看api说明
