Original is not easy, if you need to reprint, please indicate the source https://www.cnblogs.com/baixianlong/p/12192425.html Otherwise, the legal responsibility will be investigated!!!
1, About Apache ftpserver
Apache FTP server is a 100% pure Java FTP server. It is designed as a complete and portable FTP server engine solution based on the currently available open protocol. FtpServer can run independently as a Windows service or Unix / Linux daemons, or it can be embedded in Java applications. We also provide support for integration within Spring applications and provide our distribution in the form of OSGi bundles. The default network support is based on Apache MINA, a high-performance asynchronous IO library. With MINA, FtpServer can be extended to a large number of concurrent users.
2, Apache ftpserver related features
- 100% pure Java, free open source recoverable FTP server
- Multi platform support and multi thread design.
- User virtual directory, write permission, idle timeout and upload / download bandwidth limit support.
- Anonymous login support.
- Both upload and download files are recoverable.
- Handle ASCII and binary data transfer.
- Supports IP restriction to disable IP.
- data base And files can be used to store user data.
- All FTP messages are customizable.
- Implicit / explicit SSL / TLS support.
- MDTM support - your user can change the date and time stamp of the file.
- Mode Z supports faster data upload / download.
- Can easily add custom user manager, IP limiter, recorder.
- You can add user event notifications (ftlets).
3, Simple deployment and use of Apache ftpserver (based on windows, linux is the same)
- 1. Download the corresponding version of the deployment package as required: https://mina.apache.org/ftpserver-project/downloads.html
- 2. Extract the deployment package and adjust the configuration files. \ res\conf\users.properties and. \ res\conf\ftpd-typical.xml
users.properties file configuration
For example, configure a bxl User: #Password configuration new user ftpserver.user.bxl.userpassword=123456 #Home directory, where you can customize your own home directory ftpserver.user.bxl.homedirectory=./res/bxl-home #Current user available ftpserver.user.bxl.enableflag=true #Have upload permission ftpserver.user.bxl.writepermission=true #The maximum number of login users is 20 ftpserver.user.bxl.maxloginnumber=20 #The same number of IP login users is 2 ftpserver.user.bxl.maxloginperip=2 #300 seconds idle ftpserver.user.bxl.idletime=300 #Upload rate limited to 480000 bytes per second ftpserver.user.bxl.uploadrate=48000000 #Download rate limited to 480000 bytes per second ftpserver.user.bxl.downloadrate=48000000
ftpd-typical.xml file configuration
<server xmlns="http://mina.apache.org/ftpserver/spring/v1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://mina.apache.org/ftpserver/spring/v1 http://mina.apache.org/ftpserver/ftpserver-1.0.xsd" id="myServer"> <listeners> <nio-listener name="default" port="2121"> <ssl> <keystore file="./res/ftpserver.jks" password="password" /> </ssl> <!--Be careful:If you want to support Internet connection, you need to use passive mode passive,Active mode on by default--> <data-connection idle-timeout="60"> <active enabled="true" ip-check="true" /> <!-- <passive ports="2000-2222" address="0.0.0.0" external-address="xxx.xxx.xxx.xxx" /> --> </data-connection> <!--Add to ip Blacklist--> <blacklist>127.0.0.1</blacklist> </nio-listener> </listeners> <!--Add here encrypt-passwords="clear",Remove password encryption--> <file-user-manager file="./res/conf/users.properties" encrypt-passwords="clear" /> </server>
- 3. Launch and access
-
First, start the service, open cmd and cd to bin path to execute. \ ftpd.bat res/conf/ftpd-typical.xml. See the following status to indicate that the startup is successful
-
Test access, open the browser and input: ftp://localhost:2121 /, you will see your file directory. If anonymous users are not configured, you will be asked to input the user name and password, which is exactly what you configured in user.properties
-
4, Spring boot integrates Apache ftpserver (key)
Mode 1: independently deploy ftpserver service
This method is relatively simple, as long as the service is deployed, and then the relevant operations are completed through ftpclean, which is the same as jedis access redis There's nothing to say about service. Pay attention to the access mode of ftpserver. If you want to support external network connection, you need to use passive mode.
Mode 2: embed the ftpserver service into the springboot service
This method needs to be integrated with spring boot, which is relatively complex. But in this way, ftpserver will be opened or destroyed as spring boot service is started or shut down. Which way to use depends on your business needs.
To briefly describe my implementation, ftpserver supports two ways: configuration file and db to save account information and other related configurations. If our business system needs to get through user information and ftp account information, as well as related business statistics, such as the time and number of files uploaded by each person in the system, then use database to save ft P account information is more convenient and flexible. I choose to use mysql here.
Start integration
-
1. Project add dependency
//These are only the dependencies related to apache ftpserver. You can add the dependencies of the springboot project <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25 </version> </dependency> <dependency> <groupId>org.apache.ftpserver</groupId> <artifactId>ftpserver-core</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>org.apache.ftpserver</groupId> <artifactId>ftplet-api</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>org.apache.mina</groupId> <artifactId>mina-core</artifactId> <version>2.0.16</version> </dependency>
-
2. Database tables are used to save related account information (you can add several manually for testing). For specific field meanings, please refer to the users.properties file configuration (you can imagine that in the future, every user registered in our system can add an FTP user information for it, which is used to specify and save the user's upload data, etc.)
CREATE TABLE FTP_USER ( userid VARCHAR(64) NOT NULL PRIMARY KEY, userpassword VARCHAR(64), homedirectory VARCHAR(128) NOT NULL, enableflag BOOLEAN DEFAULT TRUE, writepermission BOOLEAN DEFAULT FALSE, idletime INT DEFAULT 0, uploadrate INT DEFAULT 0, downloadrate INT DEFAULT 0, maxloginnumber INT DEFAULT 0, maxloginperip INT DEFAULT 0 );
-
3. Configure ftpserver and provide init(), start(), stop() methods of ftpserver
import com.mysql.cj.jdbc.MysqlDataSource; import com.talkingdata.tds.ftpserver.plets.MyFtpPlet; import org.apache.commons.io.IOUtils; import org.apache.ftpserver.DataConnectionConfigurationFactory; import org.apache.ftpserver.FtpServer; import org.apache.ftpserver.FtpServerFactory; import org.apache.ftpserver.ftplet.FtpException; import org.apache.ftpserver.ftplet.Ftplet; import org.apache.ftpserver.listener.Listener; import org.apache.ftpserver.listener.ListenerFactory; import org.apache.ftpserver.ssl.SslConfigurationFactory; import org.apache.ftpserver.usermanager.ClearTextPasswordEncryptor; import org.apache.ftpserver.usermanager.DbUserManagerFactory; import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Component; import javax.sql.DataSource; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * Note: the class marked with @ Configuration will be added to the ioc container, and all the @ Bean annotated methods in the class will be dynamically proxied, so the same instance will be returned when calling this method. * ftp Service access address: * ftp://localhost:3131/ */ @Configuration("MyFtp") public class MyFtpServer { private static final Logger logger = LoggerFactory.getLogger(MyFtpServer.class); //Spring boot is configured to inject data directly @Autowired private DataSource dataSource; protected FtpServer server; //We use spring to load @ Configuration to initialize ftp server public MyFtpServer(DataSource dataSource) { this.dataSource = dataSource; initFtp(); logger.info("Apache ftp server is already instantiation complete!"); } /** * ftp server init * @throws IOException */ public void initFtp() { FtpServerFactory serverFactory = new FtpServerFactory(); ListenerFactory listenerFactory = new ListenerFactory(); //1. Set service port listenerFactory.setPort(3131); //2. Set the interface range of passive mode data upload, and the ECS needs to open the corresponding port to the client DataConnectionConfigurationFactory dataConnectionConfFactory = new DataConnectionConfigurationFactory(); dataConnectionConfFactory.setPassivePorts("10000-10500"); listenerFactory.setDataConnectionConfiguration(dataConnectionConfFactory.createDataConnectionConfiguration()); //3. Add SSL security configuration // SslConfigurationFactory ssl = new SslConfigurationFactory(); // ssl.setKeystoreFile(new File("src/main/resources/ftpserver.jks")); // ssl.setKeystorePassword("password"); //ssl.setSslProtocol("SSL"); // set the SSL configuration for the listener // listenerFactory.setSslConfiguration(ssl.createSslConfiguration()); // listenerFactory.setImplicitSsl(true); //4. Replace the default listener Listener listener = listenerFactory.createListener(); serverFactory.addListener("default", listener); //5. Configure custom user events Map<String, Ftplet> ftpLets = new HashMap(); ftpLets.put("ftpService", new MyFtpPlet()); serverFactory.setFtplets(ftpLets); //6. Read user's configuration information //Note: the configuration file is located in the resources directory. If the project is published as a jar package using the built-in container, FTPServer cannot directly read the configuration file in the jar package. //Solution: copy the file to the specified directory (specified in this article to the root directory) before FTPServer can read it. // PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory(); // String tempPath = System.getProperty("java.io.tmpdir") + System.currentTimeMillis() + ".properties"; // File tempConfig = new File(tempPath); // ClassPathResource resource = new ClassPathResource("users.properties"); // IOUtils.copy(resource.getInputStream(), new FileOutputStream(tempConfig)); // userManagerFactory.setFile(tempConfig); // userManagerFactory.setPasswordEncryptor(new ClearTextPasswordEncryptor()); / / password in clear text // serverFactory.setUserManager(userManagerFactory.createUserManager()); //6.2. Store user instance based on Database DbUserManagerFactory dbUserManagerFactory = new DbUserManagerFactory(); //todo.... dbUserManagerFactory.setDataSource(dataSource); dbUserManagerFactory.setAdminName("admin"); dbUserManagerFactory.setSqlUserAdmin("SELECT userid FROM FTP_USER WHERE userid='{userid}' AND userid='admin'"); dbUserManagerFactory.setSqlUserInsert("INSERT INTO FTP_USER (userid, userpassword, homedirectory, " + "enableflag, writepermission, idletime, uploadrate, downloadrate) VALUES " + "('{userid}', '{userpassword}', '{homedirectory}', {enableflag}, " + "{writepermission}, {idletime}, uploadrate}, {downloadrate})"); dbUserManagerFactory.setSqlUserDelete("DELETE FROM FTP_USER WHERE userid = '{userid}'"); dbUserManagerFactory.setSqlUserUpdate("UPDATE FTP_USER SET userpassword='{userpassword}',homedirectory='{homedirectory}',enableflag={enableflag},writepermission={writepermission},idletime={idletime},uploadrate={uploadrate},downloadrate={downloadrate},maxloginnumber={maxloginnumber}, maxloginperip={maxloginperip} WHERE userid='{userid}'"); dbUserManagerFactory.setSqlUserSelect("SELECT * FROM FTP_USER WHERE userid = '{userid}'"); dbUserManagerFactory.setSqlUserSelectAll("SELECT userid FROM FTP_USER ORDER BY userid"); dbUserManagerFactory.setSqlUserAuthenticate("SELECT userid, userpassword FROM FTP_USER WHERE userid='{userid}'"); dbUserManagerFactory.setPasswordEncryptor(new ClearTextPasswordEncryptor()); serverFactory.setUserManager(dbUserManagerFactory.createUserManager()); //7. Instantiate FTP Server server = serverFactory.createServer(); } /** * ftp server start */ public void start(){ try { server.start(); logger.info("Apache Ftp server is starting!"); }catch(FtpException e) { e.printStackTrace(); } } /** * ftp server stop */ public void stop() { server.stop(); logger.info("Apache Ftp server is stoping!"); } }
-
4. Configure the listener to start ftpserver when the spring container starts and stop ftpserver when the spring container is destroyed
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class FtpServerListener implements ServletContextListener { private static final Logger logger = LoggerFactory.getLogger(MyFtpServer.class); private static final String SERVER_NAME="FTP-SERVER"; @Autowired private MyFtpServer server; //Call method stop FTP server when container is closed public void contextDestroyed(ServletContextEvent sce) { // WebApplicationContext ctx= WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext()); // MyFtpServer server=(MyFtpServer)ctx.getServletContext().getAttribute(SERVER_NAME); server.stop(); sce.getServletContext().removeAttribute(SERVER_NAME); logger.info("Apache Ftp server is stoped!"); } //Container initialization calls start ftpServer public void contextInitialized(ServletContextEvent sce) { // WebApplicationContext ctx= WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext()); // MyFtpServer server=(MyFtpServer) ctx.getBean("MyFtp"); sce.getServletContext().setAttribute(SERVER_NAME,server); try { //The project was loaded when it started server.start(); logger.info("Apache Ftp server is started!"); } catch (Exception e){ e.printStackTrace(); throw new RuntimeException("Apache Ftp server start failed!", e); } } }
-
5, Implement some custom user events by inheriting the DefaultFtplet abstract class (I'll just give you an example here)
import org.apache.ftpserver.ftplet.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; public class MyFtpPlet extends DefaultFtplet { private static final Logger logger = LoggerFactory.getLogger(MyFtpPlet.class); @Override public FtpletResult onUploadStart(FtpSession session, FtpRequest request) throws FtpException, IOException { //Get the upload path of the uploaded file String path = session.getUser().getHomeDirectory(); //Get upload users String name = session.getUser().getName(); //Get upload file name String filename = request.getArgument(); logger.info("user:'{}',Upload file to directory:'{}',The file name is:'{}',Status: start upload~", name, path, filename); return super.onUploadStart(session, request); } @Override public FtpletResult onUploadEnd(FtpSession session, FtpRequest request) throws FtpException, IOException { //Get the upload path of the uploaded file String path = session.getUser().getHomeDirectory(); //Get upload users String name = session.getUser().getName(); //Get upload file name String filename = request.getArgument(); logger.info("user:'{}',Upload file to directory:'{}',The file name is:'{},Status: successful!'", name, path, filename); return super.onUploadEnd(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); } }
-
6. Configure access to spring boot static resources
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class FtpConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { //Can be judged by os String os = System.getProperty("os.name"); //linux settings // registry.addResourceHandler("/ftp/**").addResourceLocations("file:/home/pic/"); //windows settings //The first method sets the access path prefix, and the second method sets the resource path. You can specify either the project classpath path or other non project paths registry.addResourceHandler("/ftp/**").addResourceLocations("file:D:\\apache-ftpserver-1.1.1\\res\\bxl-home\\"); registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); } }
-
7. The above 6 steps have completed the configuration of ftpserver. With the start of springboot project, the ftpserver service will be started. Next, we will paste the util accessed by the client, and you can encapsulate it yourself.
import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPReply; import org.apache.commons.net.ftp.FTPSClient; import java.io.*; public class FtpClientUtil { // ip address of ftp server private static String FTP_ADDRESS = "localhost"; // Port number private static int FTP_PORT = 3131; // User name private static String FTP_USERNAME = "bxl"; // Password private static String FTP_PASSWORD = "123456"; // Relative path private static String FTP_BASEPATH = ""; public static boolean uploadFile(String remoteFileName, InputStream input) { boolean flag = false; FTPClient ftp = new FTPClient(); ftp.setControlEncoding("UTF-8"); try { int reply; ftp.connect(FTP_ADDRESS, FTP_PORT);// Connect to FTP server ftp.login(FTP_USERNAME, FTP_PASSWORD);// Sign in reply = ftp.getReplyCode(); System.out.println("Sign in ftp The service return status code is:" + reply); if (!FTPReply.isPositiveCompletion(reply)) { ftp.disconnect(); return flag; } ftp.setFileType(FTPClient.BINARY_FILE_TYPE); //Set to passive mode ftp.enterLocalPassiveMode(); ftp.makeDirectory(FTP_BASEPATH); ftp.changeWorkingDirectory(FTP_BASEPATH); //originFilePath is the file name of the uploaded file. It is recommended to use the generated unique name. It is better to transcode the Chinese name boolean a = ftp.storeFile(remoteFileName, input); // boolean a = ftp.storeFile(new String(remoteFileName.getBytes(),"iso-8859-1"),input); System.out.println("The original file name to upload is:" + remoteFileName + ", Upload results:" + a); input.close(); ftp.logout(); flag = true; } catch (IOException e) { e.printStackTrace(); } finally { if (ftp.isConnected()) { try { ftp.disconnect(); } catch (IOException ioe) { } } } return flag; } // public static Boolean uploadFile(String remoteFileName, InputStream inputStream, String ftpAddress, int ftpPort, // String ftpName, String ftpPassWord, String ftpBasePath) { // FTP_ADDRESS = ftpAddress; // FTP_PORT = ftpPort; // FTP_USERNAME = ftpName; // FTP_PASSWORD = ftpPassWord; // FTP_BASEPATH = ftpBasePath; // uploadFile(remoteFileName,inputStream); // return true; // } public static boolean deleteFile(String filename) { boolean flag = false; FTPClient ftpClient = new FTPClient(); try { // Connect to FTP server ftpClient.connect(FTP_ADDRESS, FTP_PORT); // Log in to FTP server ftpClient.login(FTP_USERNAME, FTP_PASSWORD); // Verify FTP server login success int replyCode = ftpClient.getReplyCode(); if (!FTPReply.isPositiveCompletion(replyCode)) { return flag; } // Switch FTP directory ftpClient.changeWorkingDirectory(FTP_BASEPATH); ftpClient.dele(filename); ftpClient.logout(); flag = true; } catch (Exception e) { e.printStackTrace(); } finally { if (ftpClient.isConnected()) { try { ftpClient.logout(); } catch (IOException e) { } } } return flag; } public static boolean downloadFile(String filename, String localPath) { boolean flag = false; // FTPSClient ftpClient = new FTPSClient("TLS", true); FTPClient ftpClient = new FTPClient(); try { // Connect to FTP server ftpClient.connect(FTP_ADDRESS, FTP_PORT); // Log in to FTP server ftpClient.login(FTP_USERNAME, FTP_PASSWORD); // Verify FTP server login success int replyCode = ftpClient.getReplyCode(); if (!FTPReply.isPositiveCompletion(replyCode)) { return flag; } // Switch FTP directory ftpClient.changeWorkingDirectory(FTP_BASEPATH); //This is the demo method. Normally, you should query the fileName in the database FTPFile[] ftpFiles = ftpClient.listFiles(); for (FTPFile file : ftpFiles) { if (filename.equalsIgnoreCase(file.getName())) { File localFile = new File(localPath + "/" + file.getName()); OutputStream os = new FileOutputStream(localFile); ftpClient.retrieveFile(file.getName(), os); os.close(); } } ftpClient.logout(); flag = true; System.out.println("File download completed!!!"); } catch (Exception e) { e.printStackTrace(); } finally { if (ftpClient.isConnected()) { try { ftpClient.logout(); } catch (IOException e) { } } } return flag; } }
Five, summary
So far, all the configurations have been completed, and our business system also plays a role, that is, ftp server. The whole configuration has not joined SSL/TLS security mechanism. If you are interested, you can study it yourself. I annotate that part of my code, just notice that when accessing through the client, you need to use ftp scliet instead of ftp Cliet. Of course, you also need to configure your own ftpserver.jks file, which is java key store. Baidu next how to generate, very simple Oh!
Personal blog address:
cnblogs: https://www.cnblogs.com/baixianlong/
github: https://github.com/xianlongbai