springboot整合apache ftpserver详细教程(推荐)
一、Apacheftpserver相关简介
ApacheFtpServer是100%纯JavaFTP服务器。它被设计为基于当前可用的开放协议的完整且可移植的FTP服务器引擎解决方案。FtpServer可以作为Windows服务或Unix/Linux守护程序独立运行,也可以嵌入Java应用程序中。我们还提供对Spring应用程序内集成的支持,并以OSGi捆绑软件的形式提供我们的发行版。默认的网络支持基于高性能异步IO库ApacheMINA。使用MINA,FtpServer可以扩展到大量并发用户。
二、Apacheftpserver相关特性
- 100%纯Java,免费的开源可恢复FTP服务器
- 多平台支持和多线程设计。
- 用户虚拟目录,写入权限,空闲超时和上载/下载带宽限制支持。
- 匿名登录支持。
- 上传和下载文件都是可恢复的。
- 处理ASCII和二进制数据传输。
- 支持IP限制以禁止IP。
- 数据库和文件可用于存储用户数据。
- 所有FTP消息都是可定制的。
- 隐式/显式SSL/TLS支持。
- MDTM支持-您的用户可以更改文件的日期时间戳。
- “模式Z”支持更快地上传/下载数据。
- 可以轻松添加自定义用户管理器,IP限制器,记录器。
- 可以添加用户事件通知(Ftplet)。
三、Apacheftpserver简单部署使用(基于windows下,linux大同小异)
1、根据需要下载对应版本的部署包:https://mina.apache.org/ftpserver-project/downloads.html
2、解压部署包并调整.\res\conf\users.properties和.\res\conf\ftpd-typical.xml配置文件
users.properties文件配置
例如配置一个bxl用户: #密码配置新的用户 ftpserver.user.bxl.userpassword=123456 #主目录,这里可以自定义自己的主目录 ftpserver.user.bxl.homedirectory=./res/bxl-home #当前用户可用 ftpserver.user.bxl.enableflag=true #具有上传权限 ftpserver.user.bxl.writepermission=true #最大登陆用户数为20 ftpserver.user.bxl.maxloginnumber=20 #同IP登陆用户数为2 ftpserver.user.bxl.maxloginperip=2 #空闲时间为300秒 ftpserver.user.bxl.idletime=300 #上传速率限制为480000字节每秒 ftpserver.user.bxl.uploadrate=48000000 #下载速率限制为480000字节每秒 ftpserver.user.bxl.downloadrate=48000000
ftpd-typical.xml文件配置
127.0.0.1
3、启动并访问
首先启动服务,打开cmd并cd到bin路径执行.\ftpd.batres/conf/ftpd-typical.xml,看到如下状态说明启动成功
测试访问,打开浏览器输入:ftp://localhost:2121/就会看到你的文件目录了,如果没有配置匿名用户,则会要求你输入用户名密码,正是你在user.properties中配置的
四、Springboot整合Apacheftpserver(重点)
方式一:独立部署ftpserver服务
这种方式比较简单,只要把服务部署好即可,然后通过FtpClien来完成相关操作,同jedis访问redis服务一个道理,没啥可说的。主要注意一下ftpserver的访问模式,如果要支持外网连接,需要使用被动模式passive。
方式二:将ftpserver服务内嵌到springboot服务中
这种方式需要和springboot整合在一起,相对比较复杂,但这种方式下ftpserver会随着springboot服务启动或关闭而开启或销毁。具体使用哪种方式就看自己的业务需求了。
简单说一下我的实现的方案,ftpserver支持配置文件和db两种方式来保存账号信息和其它相关配置,如果我们的业务系统需要将用户信息和ftp的账号信息打通,并且还有相关的业务统计,比如统计系统中每个人上传文件的时间、个数等等,那么使用数据库来保存ftp账号信息还是比较方便灵活的。我这里就选择使用mysql了。
开始整合
1、项目添加依赖
//这些只是apacheftpserver相关的依赖,springboot项目本身的依赖大家自己添加即可org.slf4j slf4j-log4j12 1.7.25 org.apache.ftpserver ftpserver-core 1.1.1 org.apache.ftpserver ftplet-api 1.1.1 org.apache.mina mina-core 2.0.16
2、数据库建表用来保存相关的账户信息(大家可以手动添加几条用来测试),具体字段意思参考users.properties文件配置(可以想象一下以后我们的系统每注册一个用户都可以为其添加一条ftp_user信息,用来指定保存用户的上传数据等等)
CREATETABLEFTP_USER( useridVARCHAR(64)NOTNULLPRIMARYKEY, userpasswordVARCHAR(64), homedirectoryVARCHAR(128)NOTNULL, enableflagBOOLEANDEFAULTTRUE, writepermissionBOOLEANDEFAULTFALSE, idletimeINTDEFAULT0, uploadrateINTDEFAULT0, downloadrateINTDEFAULT0, maxloginnumberINTDEFAULT0, maxloginperipINTDEFAULT0 );
3、配置ftpserver,提供ftpserver的init()、start()、stop()方法
importcom.mysql.cj.jdbc.MysqlDataSource;
importcom.talkingdata.tds.ftpserver.plets.MyFtpPlet;
importorg.apache.commons.io.IOUtils;
importorg.apache.ftpserver.DataConnectionConfigurationFactory;
importorg.apache.ftpserver.FtpServer;
importorg.apache.ftpserver.FtpServerFactory;
importorg.apache.ftpserver.ftplet.FtpException;
importorg.apache.ftpserver.ftplet.Ftplet;
importorg.apache.ftpserver.listener.Listener;
importorg.apache.ftpserver.listener.ListenerFactory;
importorg.apache.ftpserver.ssl.SslConfigurationFactory;
importorg.apache.ftpserver.usermanager.ClearTextPasswordEncryptor;
importorg.apache.ftpserver.usermanager.DbUserManagerFactory;
importorg.apache.ftpserver.usermanager.PropertiesUserManagerFactory;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.core.io.ClassPathResource;
importorg.springframework.stereotype.Component;
importjavax.sql.DataSource;
importjava.io.File;
importjava.io.FileOutputStream;
importjava.io.IOException;
importjava.util.HashMap;
importjava.util.Map;
/**
*注意:被@Configuration标记的类会被加入ioc容器中,而且类中所有带@Bean注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。
*ftp服务访问地址:
*ftp://localhost:3131/
*/
@Configuration("MyFtp")
publicclassMyFtpServer{
privatestaticfinalLoggerlogger=LoggerFactory.getLogger(MyFtpServer.class);
//springboot配置好数据源直接注入即可
@Autowired
privateDataSourcedataSource;
protectedFtpServerserver;
//我们这里利用spring加载@Configuration的特性来完成ftpserver的初始化
publicMyFtpServer(DataSourcedataSource){
this.dataSource=dataSource;
initFtp();
logger.info("Apacheftpserverisalreadyinstantiationcomplete!");
}
/**
*ftpserverinit
*@throwsIOException
*/
publicvoidinitFtp(){
FtpServerFactoryserverFactory=newFtpServerFactory();
ListenerFactorylistenerFactory=newListenerFactory();
//1、设置服务端口
listenerFactory.setPort(3131);
//2、设置被动模式数据上传的接口范围,云服务器需要开放对应区间的端口给客户端
DataConnectionConfigurationFactorydataConnectionConfFactory=newDataConnectionConfigurationFactory();
dataConnectionConfFactory.setPassivePorts("10000-10500");
listenerFactory.setDataConnectionConfiguration(dataConnectionConfFactory.createDataConnectionConfiguration());
//3、增加SSL安全配置
//SslConfigurationFactoryssl=newSslConfigurationFactory();
//ssl.setKeystoreFile(newFile("src/main/resources/ftpserver.jks"));
//ssl.setKeystorePassword("password");
//ssl.setSslProtocol("SSL");
//settheSSLconfigurationforthelistener
//listenerFactory.setSslConfiguration(ssl.createSslConfiguration());
//listenerFactory.setImplicitSsl(true);
//4、替换默认的监听器
Listenerlistener=listenerFactory.createListener();
serverFactory.addListener("default",listener);
//5、配置自定义用户事件
MapftpLets=newHashMap();
ftpLets.put("ftpService",newMyFtpPlet());
serverFactory.setFtplets(ftpLets);
//6、读取用户的配置信息
//注意:配置文件位于resources目录下,如果项目使用内置容器打成jar包发布,FTPServer无法直接直接读取Jar包中的配置文件。
//解决办法:将文件复制到指定目录(本文指定到根目录)下然后FTPServer才能读取到。
//PropertiesUserManagerFactoryuserManagerFactory=newPropertiesUserManagerFactory();
//StringtempPath=System.getProperty("java.io.tmpdir")+System.currentTimeMillis()+".properties";
//FiletempConfig=newFile(tempPath);
//ClassPathResourceresource=newClassPathResource("users.properties");
//IOUtils.copy(resource.getInputStream(),newFileOutputStream(tempConfig));
//userManagerFactory.setFile(tempConfig);
//userManagerFactory.setPasswordEncryptor(newClearTextPasswordEncryptor());//密码以明文的方式
//serverFactory.setUserManager(userManagerFactory.createUserManager());
//6.2、基于数据库来存储用户实例
DbUserManagerFactorydbUserManagerFactory=newDbUserManagerFactory();
//todo....
dbUserManagerFactory.setDataSource(dataSource);
dbUserManagerFactory.setAdminName("admin");
dbUserManagerFactory.setSqlUserAdmin("SELECTuseridFROMFTP_USERWHEREuserid='{userid}'ANDuserid='admin'");
dbUserManagerFactory.setSqlUserInsert("INSERTINTOFTP_USER(userid,userpassword,homedirectory,"+
"enableflag,writepermission,idletime,uploadrate,downloadrate)VALUES"+
"('{userid}','{userpassword}','{homedirectory}',{enableflag},"+
"{writepermission},{idletime},uploadrate},{downloadrate})");
dbUserManagerFactory.setSqlUserDelete("DELETEFROMFTP_USERWHEREuserid='{userid}'");
dbUserManagerFactory.setSqlUserUpdate("UPDATEFTP_USERSETuserpassword='{userpassword}',homedirectory='{homedirectory}',enableflag={enableflag},writepermission={writepermission},idletime={idletime},uploadrate={uploadrate},downloadrate={downloadrate},maxloginnumber={maxloginnumber},maxloginperip={maxloginperip}WHEREuserid='{userid}'");
dbUserManagerFactory.setSqlUserSelect("SELECT*FROMFTP_USERWHEREuserid='{userid}'");
dbUserManagerFactory.setSqlUserSelectAll("SELECTuseridFROMFTP_USERORDERBYuserid");
dbUserManagerFactory.setSqlUserAuthenticate("SELECTuserid,userpasswordFROMFTP_USERWHEREuserid='{userid}'");
dbUserManagerFactory.setPasswordEncryptor(newClearTextPasswordEncryptor());
serverFactory.setUserManager(dbUserManagerFactory.createUserManager());
//7、实例化FTPServer
server=serverFactory.createServer();
}
/**
*ftpserverstart
*/
publicvoidstart(){
try{
server.start();
logger.info("ApacheFtpserverisstarting!");
}catch(FtpExceptione){
e.printStackTrace();
}
}
/**
*ftpserverstop
*/
publicvoidstop(){
server.stop();
logger.info("ApacheFtpserverisstoping!");
}
}
4、配置监听器,使spring容器启动时启动ftpserver,在spring容器销毁时停止ftpserver
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.web.context.WebApplicationContext;
importorg.springframework.web.context.support.WebApplicationContextUtils;
importjavax.servlet.ServletContextEvent;
importjavax.servlet.ServletContextListener;
importjavax.servlet.annotation.WebListener;
@WebListener
publicclassFtpServerListenerimplementsServletContextListener{
privatestaticfinalLoggerlogger=LoggerFactory.getLogger(MyFtpServer.class);
privatestaticfinalStringSERVER_NAME="FTP-SERVER";
@Autowired
privateMyFtpServerserver;
//容器关闭时调用方法stopftpServer
publicvoidcontextDestroyed(ServletContextEventsce){
//WebApplicationContextctx=WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
//MyFtpServerserver=(MyFtpServer)ctx.getServletContext().getAttribute(SERVER_NAME);
server.stop();
sce.getServletContext().removeAttribute(SERVER_NAME);
logger.info("ApacheFtpserverisstoped!");
}
//容器初始化调用方法startftpServer
publicvoidcontextInitialized(ServletContextEventsce){
//WebApplicationContextctx=WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
//MyFtpServerserver=(MyFtpServer)ctx.getBean("MyFtp");
sce.getServletContext().setAttribute(SERVER_NAME,server);
try{
//项目启动时已经加载好了
server.start();
logger.info("ApacheFtpserverisstarted!");
}catch(Exceptione){
e.printStackTrace();
thrownewRuntimeException("ApacheFtpserverstartfailed!",e);
}
}
}
5、通过继承DefaultFtplet抽象类来实现一些自定义用户事件(我这里只是举例)
importorg.apache.ftpserver.ftplet.*;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importjava.io.IOException;
publicclassMyFtpPletextendsDefaultFtplet{
privatestaticfinalLoggerlogger=LoggerFactory.getLogger(MyFtpPlet.class);
@Override
publicFtpletResultonUploadStart(FtpSessionsession,FtpRequestrequest)
throwsFtpException,IOException{
//获取上传文件的上传路径
Stringpath=session.getUser().getHomeDirectory();
//获取上传用户
Stringname=session.getUser().getName();
//获取上传文件名
Stringfilename=request.getArgument();
logger.info("用户:'{}',上传文件到目录:'{}',文件名称为:'{}',状态:开始上传~",name,path,filename);
returnsuper.onUploadStart(session,request);
}
@Override
publicFtpletResultonUploadEnd(FtpSessionsession,FtpRequestrequest)
throwsFtpException,IOException{
//获取上传文件的上传路径
Stringpath=session.getUser().getHomeDirectory();
//获取上传用户
Stringname=session.getUser().getName();
//获取上传文件名
Stringfilename=request.getArgument();
logger.info("用户:'{}',上传文件到目录:'{}',文件名称为:'{},状态:成功!'",name,path,filename);
returnsuper.onUploadEnd(session,request);
}
@Override
publicFtpletResultonDownloadStart(FtpSessionsession,FtpRequestrequest)throwsFtpException,IOException{
//todoservies...
returnsuper.onDownloadStart(session,request);
}
@Override
publicFtpletResultonDownloadEnd(FtpSessionsession,FtpRequestrequest)throwsFtpException,IOException{
//todoservies...
returnsuper.onDownloadEnd(session,request);
}
}
6、配置springboot静态资源的访问
importorg.springframework.context.annotation.Configuration;
importorg.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
publicclassFtpConfigimplementsWebMvcConfigurer{
@Override
publicvoidaddResourceHandlers(ResourceHandlerRegistryregistry){
//可以通过os来判断
Stringos=System.getProperty("os.name");
//linux设置
//registry.addResourceHandler("/ftp/**").addResourceLocations("file:/home/pic/");
//windows设置
//第一个方法设置访问路径前缀,第二个方法设置资源路径,既可以指定项目classpath路径,也可以指定其它非项目路径
registry.addResourceHandler("/ftp/**").addResourceLocations("file:D:\\apache-ftpserver-1.1.1\\res\\bxl-home\\");
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
}
7、以上6步已经完成ftpserver的配置,随着springboot项目的启动就会开启ftpserver服务,下面在给大家贴一下客户端的访问的util,大家可以自行封装一下即可。
importorg.apache.commons.net.ftp.FTPClient;
importorg.apache.commons.net.ftp.FTPFile;
importorg.apache.commons.net.ftp.FTPReply;
importorg.apache.commons.net.ftp.FTPSClient;
importjava.io.*;
publicclassFtpClientUtil{
//ftp服务器ip地址
privatestaticStringFTP_ADDRESS="localhost";
//端口号
privatestaticintFTP_PORT=3131;
//用户名
privatestaticStringFTP_USERNAME="bxl";
//密码
privatestaticStringFTP_PASSWORD="123456";
//相对路径
privatestaticStringFTP_BASEPATH="";
publicstaticbooleanuploadFile(StringremoteFileName,InputStreaminput){
booleanflag=false;
FTPClientftp=newFTPClient();
ftp.setControlEncoding("UTF-8");
try{
intreply;
ftp.connect(FTP_ADDRESS,FTP_PORT);//连接FTP服务器
ftp.login(FTP_USERNAME,FTP_PASSWORD);//登录
reply=ftp.getReplyCode();
System.out.println("登录ftp服务返回状态码为:"+reply);
if(!FTPReply.isPositiveCompletion(reply)){
ftp.disconnect();
returnflag;
}
ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
//设置为被动模式
ftp.enterLocalPassiveMode();
ftp.makeDirectory(FTP_BASEPATH);
ftp.changeWorkingDirectory(FTP_BASEPATH);
//originFilePath就是上传文件的文件名,建议使用生成的唯一命名,中文命名最好做转码
booleana=ftp.storeFile(remoteFileName,input);
//booleana=ftp.storeFile(newString(remoteFileName.getBytes(),"iso-8859-1"),input);
System.out.println("要上传的原始文件名为:"+remoteFileName+",上传结果:"+a);
input.close();
ftp.logout();
flag=true;
}catch(IOExceptione){
e.printStackTrace();
}finally{
if(ftp.isConnected()){
try{
ftp.disconnect();
}catch(IOExceptionioe){
}
}
}
returnflag;
}
//publicstaticBooleanuploadFile(StringremoteFileName,InputStreaminputStream,StringftpAddress,intftpPort,
//StringftpName,StringftpPassWord,StringftpBasePath){
//FTP_ADDRESS=ftpAddress;
//FTP_PORT=ftpPort;
//FTP_USERNAME=ftpName;
//FTP_PASSWORD=ftpPassWord;
//FTP_BASEPATH=ftpBasePath;
//uploadFile(remoteFileName,inputStream);
//returntrue;
//}
publicstaticbooleandeleteFile(Stringfilename){
booleanflag=false;
FTPClientftpClient=newFTPClient();
try{
//连接FTP服务器
ftpClient.connect(FTP_ADDRESS,FTP_PORT);
//登录FTP服务器
ftpClient.login(FTP_USERNAME,FTP_PASSWORD);
//验证FTP服务器是否登录成功
intreplyCode=ftpClient.getReplyCode();
if(!FTPReply.isPositiveCompletion(replyCode)){
returnflag;
}
//切换FTP目录
ftpClient.changeWorkingDirectory(FTP_BASEPATH);
ftpClient.dele(filename);
ftpClient.logout();
flag=true;
}catch(Exceptione){
e.printStackTrace();
}finally{
if(ftpClient.isConnected()){
try{
ftpClient.logout();
}catch(IOExceptione){
}
}
}
returnflag;
}
publicstaticbooleandownloadFile(Stringfilename,StringlocalPath){
booleanflag=false;
//FTPSClientftpClient=newFTPSClient("TLS",true);
FTPClientftpClient=newFTPClient();
try{
//连接FTP服务器
ftpClient.connect(FTP_ADDRESS,FTP_PORT);
//登录FTP服务器
ftpClient.login(FTP_USERNAME,FTP_PASSWORD);
//验证FTP服务器是否登录成功
intreplyCode=ftpClient.getReplyCode();
if(!FTPReply.isPositiveCompletion(replyCode)){
returnflag;
}
//切换FTP目录
ftpClient.changeWorkingDirectory(FTP_BASEPATH);
//此处为demo方法,正常应该到数据库中查询fileName
FTPFile[]ftpFiles=ftpClient.listFiles();
for(FTPFilefile:ftpFiles){
if(filename.equalsIgnoreCase(file.getName())){
FilelocalFile=newFile(localPath+"/"+file.getName());
OutputStreamos=newFileOutputStream(localFile);
ftpClient.retrieveFile(file.getName(),os);
os.close();
}
}
ftpClient.logout();
flag=true;
System.out.println("文件下载完成!!!");
}catch(Exceptione){
e.printStackTrace();
}finally{
if(ftpClient.isConnected()){
try{
ftpClient.logout();
}catch(IOExceptione){
}
}
}
returnflag;
}
}
五、总结
到此,所有的配置已经完成,我们的业务系统也同时也承担了一个角色,那就是ftp服务器,整个配置是没有加入SSL/TLS安全机制的,大家如果感兴趣可以自行研究下。我代码中注释那那部分,只是注意下通过客户端访问时,需要使用FtpsCliet,而非FtpCliet。当然还需要配置你自己的ftpserver.jks文件,也就是javakeystore。百度下一下如何生成,很简单哦!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。