FTP 服务器本身是一个独立的应用程序,它监听网络端口,处理 FTP 协议命令,而 "Java Web" 通常指的是一个部署在 Servlet 容器(如 Tomcat)中的 Web 应用,它通过 HTTP/HTTPS 协议为浏览器提供服务。

将 FTP 服务器直接“嵌入”到一个标准的 Java Web 应用(WAR 文件)中并使其随 Tomcat 启动,并不是一个常见的做法,原因如下:
- 协议不同:FTP 使用 21 端口(控制连接)和动态数据端口,而 Web 应用使用 80/443 端口,它们是两个不同的服务。
- 运行方式不同:Servlet 容器(如 Tomcat)是为处理 HTTP 请求而设计的,它不是一个通用的服务器托管环境。
- 资源管理:将一个需要长期运行、监听网络套接字的服务放在 Web 应用中,可能会导致类加载、生命周期管理等问题。
推荐的实践方式是:将 FTP 服务器作为一个独立的应用来运行,而 Java Web 应用则作为其客户端,或者提供一个管理界面来控制这个独立的 FTP 服务器。
下面我将分步介绍几种可行的方案。
使用成熟的 Java FTP 服务器库(推荐)
这是最简单、最可靠的方法,我们选择一个成熟的库来快速搭建 FTP 服务器,然后将其作为独立服务运行。

步骤 1:选择库
有几个优秀的开源库可供选择:
- Apache Mina FTP Server: 一个功能强大、高度可定制的 FTP 服务器,基于 Apache Mina NIO 框架,性能很好。
- FtpServer (from Apache): 一个专门为 FTP 和 SFTP 设计的服务器,非常成熟稳定。
- Jetty FTP Server: 如果你已经在使用 Jetty 服务器,这个库可以很方便地集成。
这里我们以 Apache Mina FTP Server 为例,因为它配置灵活,文档清晰。
步骤 2:创建一个独立的 Java 应用(不是 Web 项目)
创建一个新的 Maven 或 Gradle 项目,类型可以是 jar。
添加 Maven 依赖

<dependencies>
<!-- Apache Mina Core -->
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
<version>2.1.6</version>
</dependency>
<!-- Apache Mina FTP Server -->
<dependency>
<groupId>org.apache.ftpserver</groupId>
<artifactId>ftpserver-core</artifactId>
<version>1.2.0</version>
</dependency>
<!-- 为了方便日志,我们使用 SLF4J + Logback -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.8</version>
</dependency>
</dependencies>
编写 FTP 服务器代码
创建一个主类 FtpServerStarter.java。
import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.usermanager.ClearTextPasswordEncryptor;
import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory;
import org.apache.ftpserver.usermanager.impl.BaseUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
public class FtpServerStarter {
private static final Logger logger = LoggerFactory.getLogger(FtpServerStarter.class);
public static void main(String[] args) {
// 1. 创建 FtpServerFactory
FtpServerFactory serverFactory = new FtpServerFactory();
// 2. 配置用户管理器
// 使用 properties 文件来存储用户信息
File file = new File("users.properties");
// 如果文件不存在,则创建一个默认的
if (!file.exists()) {
logger.info("Creating default users.properties file.");
// 创建一个默认用户
BaseUser user = new BaseUser();
user.setName("admin");
user.setPassword("admin123");
user.setHomeDirectory("./ftp_root"); // 设置用户主目录
PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory();
userManagerFactory.setFile(file);
userManagerFactory.setPasswordEncryptor(new ClearTextPasswordEncryptor());
serverFactory.setUserManager(userManagerFactory.createUserManager());
// 将默认用户保存到文件
userManagerFactory.getUserManager().save(user);
} else {
PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory();
userManagerFactory.setFile(file);
userManagerFactory.setPasswordEncryptor(new ClearTextPasswordEncryptor());
serverFactory.setUserManager(userManagerFactory.createUserManager());
}
// 3. 配置监听器
ListenerFactory listenerFactory = new ListenerFactory();
// 设置监听端口
listenerFactory.setPort(2121); // 避免与默认的 21 冲突
// 设置被动模式端口范围
listenerFactory.setPassivePorts("2000-2100");
// 将监听器添加到服务器工厂
serverFactory.addListener("default", listenerFactory.createListener());
// 4. 创建并启动 FTP 服务器
FtpServer ftpServer = serverFactory.createServer();
try {
ftpServer.start();
logger.info("FTP Server started on port 2121. Root directory is ./ftp_root");
System.out.println("FTP Server is running. Press any key to stop.");
// 阻塞主线程,防止服务器退出
System.in.read();
} catch (Exception e) {
logger.error("Failed to start FTP server.", e);
} finally {
ftpServer.stop();
logger.info("FTP Server stopped.");
}
}
}
创建 users.properties 文件
在项目根目录下创建 users.properties 文件,格式如下:
# user.username=password
user.admin=admin123
user.test=test123
运行
运行 FtpServerStarter 的 main 方法,你的 FTP 服务器就启动了,监听在 2121 端口,你可以使用任何 FTP 客户端(如 FileZilla, WinSCP)连接,用户名 admin,密码 admin123。
将 FTP 服务器作为 Web 应用的“后台服务”
如果你确实希望 FTP 服务器能由你的 Web 应用来“控制”(通过一个管理页面来启动、停止、添加用户),你可以这样做:
- 将方案一中的 FTP 服务器代码封装成一个可启动和停止的服务类。
- 在 Web 应用中,通过 Servlet 或 JSP 提供管理界面。
- 管理界面的操作(如点击“启动服务器”)会触发后台的 Java 代码来启动这个服务。
示例:
创建一个服务管理类 FtpServiceManager.java
import org.apache.ftpserver.FtpServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FtpServiceManager {
private static final Logger logger = LoggerFactory.getLogger(FtpServiceManager.class);
private FtpServer ftpServer;
public void start() {
if (ftpServer != null && !ftpServer.isStopped()) {
logger.warn("FTP Server is already running.");
return;
}
// 这里直接调用方案一中的启动逻辑
// 为了简化,我们假设启动逻辑已经封装好
FtpServerStarter starter = new FtpServerStarter(); // 假设这个类只负责启动,不阻塞
// 注意:这里需要修改 FtpServerStarter,使其不阻塞,而是返回 FtpServer 实例
// ftpServer = starter.start(); // 假设是这样启动的
logger.info("FTP Server start command received.");
}
public void stop() {
if (ftpServer != null && !ftpServer.isStopped()) {
ftpServer.stop();
logger.info("FTP Server stopped.");
}
}
public boolean isRunning() {
return ftpServer != null && !ftpServer.isStopped();
}
}
在 Web 应用中创建一个管理 Servlet
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ftp-admin")
public class FtpAdminServlet extends HttpServlet {
private final FtpServiceManager ftpServiceManager = new FtpServiceManager();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("isRunning", ftpServiceManager.isRunning());
req.getRequestDispatcher("/WEB-INF/views/ftp-admin.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getParameter("action");
if ("start".equals(action)) {
ftpServiceManager.start();
} else if ("stop".equals(action)) {
ftpServiceManager.stop();
}
resp.sendRedirect(req.getContextPath() + "/ftp-admin");
}
}
创建 JSP 页面 (ftp-admin.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>FTP Server Admin</title>
</head>
<body>
<h1>FTP Server Control Panel</h1>
<p>Current Status: ${isRunning ? 'Running' : 'Stopped'}</p>
<form action="${pageContext.request.contextPath}/ftp-admin" method="post">
<input type="hidden" name="action" value="${isRunning ? 'stop' : 'start'}">
<button type="submit">${isRunning ? 'Stop Server' : 'Start Server'}</button>
</form>
</body>
</html>
这样,你就可以通过访问 http://localhost:8080/your-app/ftp-admin 来管理你的 FTP 服务器了。注意:这种方案下,FTP 服务器进程的生命周期与 Web 容器(如 Tomcat)的生命周期可能不完全一致,需要仔细处理启动和关闭的时机。
在 Web 容器中嵌入运行(不推荐,但技术上可行)
有些 FTP 服务器库提供了与 Servlet 容器集成的能力,Jetty FTP Server,但这通常意味着你需要将整个 FTP 服务器逻辑作为 Web 应用的一部分来初始化,并且要处理线程、端口冲突等复杂问题,这违背了“关注点分离”的原则,除非有特殊需求,否则应避免。
总结与建议
| 方案 | 描述 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 独立服务 | 将 FTP 服务器作为单独的 Java 应用运行。 | 最佳实践、架构清晰、稳定、易于维护和部署。 | 需要额外管理一个服务进程。 | 绝大多数场景,特别是生产环境。 |
| Web 控制 | Web 应用通过后台代码控制一个独立的 FTP 服务进程。 | 提供了用户友好的管理界面,可以动态控制服务。 | 架构稍复杂,需要处理进程间通信或服务生命周期管理。 | 需要一个 Web 界面来动态管理 FTP 服务器的场景。 |
| Web 嵌入 | 将 FTP 服务器逻辑直接嵌入到 Web 应用中运行。 | 部署简单(只有一个 WAR 文件)。 | 不推荐、架构混乱、可能引发类加载问题、难以监控和管理。 | 极少数特殊需求,如快速原型开发或教学演示。 |
最终建议:
对于任何正式的项目,请选择 方案一,将 FTP 服务器作为独立的、长期运行的服务,你的 Java Web 应用可以扮演以下角色:
- 客户端:Web 应用使用 Apache Commons Net 等库连接到这个 FTP 服务器,进行文件的上传下载,实现业务功能。
- 管理端:Web 应用提供一个管理界面,用来配置、监控这个独立的 FTP 服务器(查看日志、管理用户等),这可以通过调用 FTP 服务器的管理 API(如果支持)或执行脚本实现。
这种方式是健壮、可扩展且符合软件工程最佳实践的。
