核心原理
一个 FTP 服务器在 Android 上的工作流程如下:

- 启动服务:你的 Android App 启动一个后台服务,这个服务会持续监听网络上的 FTP 请求。
- 创建服务器实例:服务内部使用一个 FTP 服务器库(如
NanoFTPd或Apache FTPServer)来创建一个服务器实例。 - 绑定端口:服务器实例会绑定到一个指定的网络端口(通常是 21)。
- 处理请求:当网络上的另一台设备(客户端)尝试连接这个 IP 地址和端口时,服务器库会接收并处理这些 FTP 命令(如
USER,PASS,LIST,RETR等)。 - 访问文件:服务器库通过 Android 的文件系统 API 来读取、写入、列出或删除你 App 沙盒目录或共享存储(SD卡)中的文件。
- 返回响应:服务器将操作结果(成功、失败、文件列表、文件内容等)返回给客户端。
关键点:
- 权限:App 需要网络访问权限,以及读取/写入外部存储的权限。
- IP 地址:你需要知道你手机的局域网 IP 地址,客户端才能连接,通常在 Wi-Fi 设置中可以找到。
- 安全性:默认的 FTP 是不加密的,所有数据(包括用户名和密码)都以明文传输,在公网或对安全性要求高的环境中,应考虑使用 FTPS (FTP over SSL/TLS) 或 SFTP (SSH File Transfer Protocol),对于本地局域网使用,普通 FTP 通常足够。
推荐库选择
虽然你可以自己用 Socket 编写一个 FTP 服务器,但这非常复杂且容易出错,强烈建议使用成熟的第三方库。
-
NanoFTPd (推荐)
- 优点:轻量级、专门为 Android 设计、API 简单、支持 FTP 和 FTPS,它被许多知名 App(如 Solid Explorer)使用,非常稳定。
- 缺点:文档相对较少,但代码示例清晰。
- GitHub 地址:https://github.com/iNPUTmice/NanoFTPd
-
Apache FTPServer
(图片来源网络,侵删)- 优点:功能非常强大、稳定、历史悠久,是一个成熟的 FTP 服务器框架。
- 缺点:相对较重,集成和配置可能比 NanoFTPd 稍微复杂一些。
- GitHub 地址:https://github.com/apache/ftpserver
本教程将以 NanoFTPd 为例,因为它更简单直接,非常适合快速上手。
详细搭建步骤 (使用 NanoFTPd)
步骤 1:在 Android Studio 中创建新项目
- 打开 Android Studio,创建一个新的 Empty Activity 项目。
- 选择你喜欢的语言 (Kotlin/Java) 和最低 SDK 版本。
步骤 2:添加依赖
打开你的 app/build.gradle.kts (或 build.gradle) 文件,在 dependencies 代码块中添加 NanoFTPd 的依赖。
// build.gradle (Groovy)
dependencies {
// ... 其他依赖
implementation 'com.github.iNPUTmice:NanoFTPd:1.0.0' // 请检查 GitHub 获取最新版本
}
// build.gradle.kts (Kotlin)
dependencies {
// ... 其他依赖
implementation("com.github.iNPUTmice:NanoFTPd:1.0.0") // 请检查 GitHub 获取最新版本
}
点击 "Sync Now" 同步项目。
步骤 3:添加网络和存储权限
打开 app/src/main/AndroidManifest.xml 文件,添加以下权限。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 读取外部存储权限 (Android 13+ 需要 READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO) -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 写入外部存储权限 (Android 13+ 需要 MANAGE_EXTERNAL_STORAGE 或特定媒体权限) -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Android 13+ 需要请求通知权限来显示服务器状态 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
... >
<activity ...>
...
</activity>
<!-- 声明一个 Service,用于在后台运行 FTP 服务器 -->
<service
android:name=".FtpServerService"
android:enabled="true"
android:exported="false" />
</application>
</manifest>
权限说明:
- 对于 Android 13 (API 33) 及以上,
READ_EXTERNAL_STORAGE被拆分为READ_MEDIA_IMAGES等权限,如果你的服务器需要访问所有文件,MANAGE_EXTERNAL_STORAGE是最简单的,但需要用户手动在设置中授权,且有严格限制,对于演示,可以先用READ_EXTERNAL_STORAGE并在运行时请求。 WRITE_EXTERNAL_STORAGE同样受版本影响。
步骤 4:创建 FTP 服务器服务
创建一个新的 Kotlin/Java 类 FtpServerService.kt,继承自 Service。
// FtpServerService.kt
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.os.Build
import android.os.IBinder
import android.util.Log
import com.github.pedrovgs.lynx.LynxConfig
import com.github.pedrovgs.lynx.LynxService
import java.io.File
class FtpServerService : Service() {
private lateinit var ftpServer: NanoFtpd
private val TAG = "FtpServerService"
private val NOTIFICATION_CHANNEL_ID = "FtpServerChannel"
override fun onCreate() {
super.onCreate()
createNotificationChannel()
startForeground(1, createNotification())
startFtpServer()
}
override fun onBind(intent: Intent): IBinder? {
return null // 我们不提供绑定
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "Stopping FTP server...")
ftpServer.stop()
}
private fun startFtpServer() {
try {
// 1. 设置根目录,这里使用外部存储的根目录。
// 注意:你需要确保应用有权限访问这个目录。
val rootDir = File(getExternalFilesDir(null)?.absolutePath ?: "/sdcard/ftp_root")
if (!rootDir.exists()) {
rootDir.mkdirs()
}
Log.d(TAG, "FTP Root Directory: ${rootDir.absolutePath}")
// 2. 创建服务器配置
val config = NanoFtpdConfig.Builder()
.port(2121) // 使用非标准端口避免冲突,也可以用 21
.rootDirectory(rootDir)
.anonymousEnabled(false) // 禁用匿名登录
.user("user") // 用户名
.password("password") // 密码
.build()
// 3. 创建 NanoFtpd 实例
ftpServer = NanoFtpd(config)
// 4. 启动服务器
ftpServer.start()
Log.d(TAG, "FTP server started on port ${config.port}")
} catch (e: Exception) {
Log.e(TAG, "Failed to start FTP server", e)
}
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val serviceChannel = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
"FTP Server Service",
NotificationManager.IMPORTANCE_DEFAULT
)
val manager = getSystemService(NotificationManager::class.java)
manager.createNotificationChannel(serviceChannel)
}
}
private fun createNotification(): Notification {
return Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.setContentTitle("FTP Server")
.setContentText("Server is running. Check logs for IP address.")
.setSmallIcon(android.R.drawable.ic_dialog_info)
.build()
}
}
步骤 5:启动和管理服务
你可以在一个 Activity 中通过按钮来启动和停止这个服务。
// MainActivity.kt
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val startButton: Button = findViewById(R.id.start_button)
val stopButton: Button = findViewById(R.id.stop_button)
startButton.setOnClickListener {
// 启动 FTP 服务器服务
val serviceIntent = Intent(this, FtpServerService::class.java)
startService(serviceIntent)
startButton.isEnabled = false
stopButton.isEnabled = true
}
stopButton.setOnClickListener {
// 停止 FTP 服务器服务
val serviceIntent = Intent(this, FtpServerService::class.java)
stopService(serviceIntent)
startButton.isEnabled = true
stopButton.isEnabled = false
}
}
}
步骤 6:运行并获取 IP 地址
- 将你的手机和电脑连接到同一个 Wi-Fi 网络。
- 在 Android Studio 中运行你的 App。
- 点击 "Start" 按钮。
- 打开手机的 Wi-Fi 设置,找到当前连接的网络的 IP 地址,它通常是一个以
168.x.x或0.x.x开头的地址。- 路径:
设置->WLAN->当前网络->IP 地址
- 路径:
- 你可以在 Logcat 中搜索
FtpServerService,应该能看到类似 "FTP server started on port 2121" 和 "FTP Root Directory: ..." 的日志。
如何连接和使用
你可以从任何 FTP 客户端连接到你的手机了。
从 Windows 连接
- 在 Windows 的文件资源管理器地址栏中,输入
ftp://你的手机IP地址:端口号。ftp://192.168.1.105:2121
- 系统会提示输入用户名和密码。
- 用户名:
user - 密码:
password
- 用户名:
- 连接成功后,你就可以像操作普通文件夹一样,拖放或复制文件了。
从 macOS 连接
- 打开 "访达" (Finder)。
- 点击菜单栏的 "前往" -> "连接服务器" (或使用快捷键
Cmd + K)。 - 在服务器地址栏输入
ftp://你的手机IP地址:端口号,然后点击 "连接"。 - 输入用户名和密码。
从手机连接 (使用 File Manager App)
- 在你的另一台手机上安装一个支持 FTP 的文件管理器(如 Solid Explorer, X-plore 等)。
- 打开该 App,找到 "网络" 或 "LAN" 功能。
- 添加一个新的 FTP 服务器,填入你的手机 IP 地址、端口、用户名和密码。
- 连接后即可访问和传输文件。
重要注意事项和进阶
-
权限问题:
- Android 13+:上面的
READ_EXTERNAL_STORAGE在 Android 13 上可能不够用,如果你遇到 "权限拒绝" 错误,请确保你已经在运行时正确请求了所有必要的权限,对于完全访问,可能需要引导用户去设置->应用->[你的App]->权限->所有文件访问权限中手动开启MANAGE_EXTERNAL_STORAGE。 - 作用域存储:现代 Android 推荐使用作用域存储,如果你的 FTP 服务器只需要访问特定类型的文件(如图片),可以考虑使用
MediaStoreAPI 来访问,而不是直接访问文件路径。
- Android 13+:上面的
-
安全性:
- 不要在公网上开放:普通 FTP 极不安全,仅限在可信的局域网内使用。
- 使用强密码:设置一个复杂的密码。
- 考虑 FTPS:如果需要更高安全性,可以研究
NanoFtpd的 FTPS 配置,这需要配置 SSL/TLS 证书,过程会更复杂。
-
电池和性能:
- FTP 服务器是一个持续运行的服务,会消耗一定的电量,当你不需要时,务必通过 App 停止服务。
- 对于大文件传输,确保你的手机有足够的性能和稳定的网络连接。
-
文件共享根目录:
- 示例代码中,FTP 的根目录是
App.getExternalFilesDir(null),这个目录是 App 私有的,其他 App 无法直接访问,但通过 FTP 服务暴露给了网络。 - 如果你希望共享的是 SD 卡的公共目录(如
Download),路径应该是Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),但这样需要更高级的权限管理。
- 示例代码中,FTP 的根目录是
通过以上步骤,你就可以成功地在你的 Android 设备上搭建一个功能完备的 FTP 服务器了。
