springboot中实现内嵌ftp服务器,
InitFtpServer
实现CommandLineRunner接口,重写run方法,
自动装配了一个FtpServer类型的bean。run方法的作用是启动一个FTP服务器(server.start())
当Spring Boot应用启动并初始化完成后,它会自动执行run方法
CommandLineRunner 是Spring Boot中的一个接口,用于指示当Spring应用程序的上下文加载完成后应该执行的代码。实现了CommandLineRunner接口的组件可以在所有Spring Beans都已被初始化之后运行一些特定的代码。
package com.ruoyi.web.controller.tool.ftp;
import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.ftplet.FtpException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
/**
* springboot启动时初始化ftpserver
*/
@Component
public class InitFtpServer implements CommandLineRunner {
public static final Logger log = LoggerFactory.getLogger(FtpServer.class);
@Autowired
private FtpServer server;
@Override
public void run(String... args) throws Exception {
try {
server.start();
log.info(">>>>>>>ftp start success ");
} catch (FtpException e) {
e.printStackTrace();
log.error(">>>>>>>ftp start error {}", e);
}
}
}
FtpConfig
自动配置类,配置创建ftp服务器时的配置
定义一个bean,Spring将自动调用这个方法并注册返回的FtpServer实例,使其可以在上面init时自动注入和使用
@Bean
public FtpServer createFtpServer() { ... }
其中FtpServerFactory用于创建和配置FTP服务器实例,而ListenerFactory用于定义FTP服务器的网络监听设置,包括端口号、被动模式端口范围等。
package com.ruoyi.web.controller.tool.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 {
@Value("${ftp.port}")
private Integer ftpPort;
@Bean
public FtpServer createFtpServer(){
FtpServerFactory serverFactory = new FtpServerFactory();
ListenerFactory factory = new ListenerFactory();
// ftp端口
factory.setPort(ftpPort);
/**
* 被动模式
*/
DataConnectionConfigurationFactory dataConnectionConfigurationFactory=new DataConnectionConfigurationFactory();
dataConnectionConfigurationFactory.setActiveEnabled(true);
dataConnectionConfigurationFactory.setIdleTime(60);
dataConnectionConfigurationFactory.setActiveLocalPort(2121);
dataConnectionConfigurationFactory.setPassiveAddress("0.0.0.0");
dataConnectionConfigurationFactory.setPassivePorts("2000-2222");
dataConnectionConfigurationFactory.setPassiveExternalAddress("127.0.0.1");
factory.setDataConnectionConfiguration(dataConnectionConfigurationFactory.createDataConnectionConfiguration());
// replace the default listener
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> m = new HashMap<String, Ftplet>();
m.put("miaFtplet", new UploadListener());
serverFactory.setFtplets(m);
// start the server
FtpServer server = serverFactory.createServer();
return server;
}
}
UploadListener
继承DefaultFtplet类,用于监听和响应FTP服务器的各种事件,例如文件上传、下载等
重写onUploadStart方法,上传时的处理,记录上传日志,并确保路径存在,不存在则创建
重写onUploadEnd 方法,上传结束后的处理,监听器无法通过autowirte注入bean,写一个bean工厂,从spring的上下文WebApplicationContext 中获取FileRecordService(文件处理服务),对ftp上传的文件进行重命名、记录等。(这里的FileRecordService是我自己项目中的service,可以删掉)
package com.ruoyi.web.controller.tool.ftp;
import org.apache.ftpserver.ftplet.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
public class UploadListener extends DefaultFtplet {
public static final Logger log= LoggerFactory.getLogger(UploadListener.class);
/**
*
* 开始上传
* 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 {
//获取上传文件的上传路径
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);
}
/**
* 上传完成
* 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 {
//获取上传文件的上传路径
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);
return super.onUploadStart(session, request);
}
@Override
public FtpletResult onDownloadStart(FtpSession session, FtpRequest request) throws FtpException, IOException {
//todo servies...
return super.onDownloadStart(session, request);
}
@Override
public FtpletResult onDownloadEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
//todo servies...
return super.onDownloadEnd(session, request);
}
}
附上yml中的配置
ftp:
port: 3131 #ftp连接端口
passivePorts: 20 #被动连接数据传输端口
passiveExternalAddress: 127.0.0.1 #部署的服务器ip地址
users.propeties配置文件
例如配置一个admin用户:
#密码 配置新的用户
ftpserver.user.admin.userpassword=123456
#主目录,这里可以自定义自己的主目录
ftpserver.user.admin.homedirectory=./res/admin-home
#当前用户可用
ftpserver.user.admin.enableflag=true
#具有上传权限
ftpserver.user.admin.writepermission=true
#最大登陆用户数为20
ftpserver.user.admin.maxloginnumber=20
#同IP登陆用户数为2
ftpserver.user.admin.maxloginperip=2
#空闲时间为300秒
ftpserver.user.admin.idletime=300
#上传速率限制为480000字节每秒
ftpserver.user.admin.uploadrate=48000000
#下载速率限制为480000字节每秒
ftpserver.user.admin.downloadrate=48000000