尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Android FTP服务器Demo:手机秒变局域网文件中转站
简介一份演示在安卓设备上搭建FTP服务器的工程样例适合需要实现局域网文件共享、远程上传下载的移动开发人员参考。项目基于Apache FtpServer组件完成以简洁的代码结构展示了ftp服务从初始化、启动到停止的完整调用链方便快速移植。压缩包共28个文件体积仅1.72MB其中包含7个jar依赖包ftpserver-core、mina-core、slf4j等用于提供核心服务与日志能力11个xml文件承担工程配置、AndroidManifest清单和界面布局另有5张png图片、1个Java源码文件及project、properties等工程描述信息资源划分清晰便于对照学习。当前已有592人学习下载。阅读该Demo后开发者能够理解安卓进程中如何以内嵌方式运行ftp服务并掌握网络权限声明、端口监听、文件目录映射等关键设置在遇到连接不上、权限冲突等问题时也可以参考其libs目录和res目录的配置方式进行排查进而将这个轻量级服务模块嵌入自有App实现手机与电脑之间的文件直传。1. Android FTP服务器Demo手机秒变局域网文件中转站Android手机当FTP服务器用在过去几年里一直是文件共享场景里一个比较小众但不冷门的需求。所谓FTP服务器Android方案说白了就是在手机上起一个监听21端口的服务让同一局域网内的电脑、平板甚至另一台手机通过FTP客户端直接读写手机存储上的文件。这份Demo资源的核心价值在于它把服务端、客户端、权限处理、后台保活都串在了一个工程里跑起来就能用。适合这几类人——经常在手机和电脑之间来回传APK包和日志的开发者、需要在实验室局域网里做文件分发测试的工程师以及想研究嵌入式FTP服务移植思路的入门者。2. 技术选型与工作原理为什么用Apache FtpServer而不是自己写协议2.1 FTP协议在Android端的两条路线实现一个Android FTP服务器摆在面前的无非两条路一条是完全自己实现FTP协议栈从TCP监听、命令解析、数据传输通道管理到被动模式端口协商全部手写另一条是引入现成的开源FTP服务器库把精力放在Android系统适配和业务功能上。自己写协议栈的想法趁早放弃。FTP协议本身命令繁多光控制连接上的命令就有几十种外加主动模式和被动模式两种数据传输方式的差异以及ASCII与二进制传输模式的转换短时间内根本做不完。更重要的是FTP客户端遍布各种平台FileZilla、WinSCP这些工具都相对严格地遵循协议规范服务器实现稍有偏差就会产生各种诡异的问题。相比之下Apache FtpServer是Java生态里比较成熟的嵌入式FTP服务器库天然能跑在Android上。它负责了协议解析、用户认证、文件系统抽象这些底层逻辑Android开发者的工作重点就变成了把Android的存储目录映射成FTP服务器的虚拟文件系统再解决权限、网络、生命周期这些平台相关的问题。2.2 Apache FtpServer的架构与Android适配切入点Apache FtpServer的核心抽象是FtpServer接口和FtpServerFactory底层用MINA框架做网络I/O。它提供了一个叫FileSystemView的接口负责把FTP客户端看到的路径映射到真实存储——这一层正是Android适配的核心切入点。Android端一般会自己实现一个FileSystemView把客户端请求的路径解析到应用专属目录、SD卡公共目录或内部存储同时做好越权访问的边界控制。另一块关键适配是监听端口和网络接口绑定默认绑定21端口但Android的多网卡环境需要明确绑定到WLAN接口的IP。常见做法是先通过ConnectivityManager拿到当前WLAN的IPv4地址再把这个地址传给FtpServer作为绑定的服务器地址。如果直接不指定地址让服务器绑定所有接口在某些Android版本上会因为IPv6地址冲突导致客户端连接失败。2.3 项目目录结构一瞥这份Demo的工程结构大致如下app/src/main/java/ ├── ftp/ │ ├── FtpServerManager.java // 服务生命周期管理 │ ├── AndroidFileSystemView.java // 文件系统视图适配 │ └── UserManager.java // 用户与权限配置 ├── service/ │ └── FtpService.java // 后台服务跑FTP服务器 └── ui/ └── MainActivity.java // 启动/停止、IP显示、状态展示各模块职责很清晰FtpServerManager负责初始化Apache FtpServer的配置和启动停止AndroidFileSystemView是连接协议层和Android存储之间的桥梁FtpService保证FTP服务在应用退到后台时不被立即杀死。UI层则提供给用户操作入口和信息展示。3. 项目搭建与核心实现从依赖配置到可运行Demo3.1 依赖引入与权限声明拿到Demo工程之后第一步需要确认依赖和权限。在build.gradle中添加Apache FtpServer的依赖dependencies { implementation org.apache.ftpserver:ftpserver-core:1.2.0 implementation org.apache.mina:mina-core:2.0.23 }权限方面在AndroidManifest.xml中需要声明INTERNET权限用于网络通信、FOREGROUND_SERVICE权限Android 9启动前台服务需要以及网络状态访问权限用于获取WLAN IP和监听网络变化uses-permission android:nameandroid.permission.INTERNET / uses-permission android:nameandroid.permission.ACCESS_NETWORK_STATE / uses-permission android:nameandroid.permission.ACCESS_WIFI_STATE / uses-permission android:nameandroid.permission.FOREGROUND_SERVICE /这里有个容易忽视的点如果目标SDK版本在28及以上FTP服务必须以前台服务方式运行否则系统会直接禁止后台启动并且需要同时申请FOREGROUND_SERVICE权限。日志显示“RemoteServerException: Context.startForegroundService() did not then call Service.startForeground()”就是踩到了这个问题。3.2 核心启动流程FtpServerFactory配置FTP服务器的启动逻辑集中在FtpServerManager中。实现的核心代码如下public class FtpServerManager { private FtpServer ftpServer; public void startFtpServer(Context context, String username, String password) { FtpServerFactory factory new FtpServerFactory(); // 设置监听端口 ListenerFactory listenerFactory new ListenerFactory(); listenerFactory.setPort(21); // 绑定具体IP避免IPv6环境下的连接问题 String wlanIp getWlanIpAddress(context); if (wlanIp ! null) { listenerFactory.setServerAddress(wlanIp); } factory.addListener(default, listenerFactory.createListener()); // 配置用户 PropertiesUserManager userManager new PropertiesUserManager( new DefaultUserManagerFactory().createUserManager() { Override public User getUserByName(String name) { User user super.getUserByName(name); if (user ! null) return user; // 动态创建用户Home目录指向共享根目录 BaseUser baseUser new BaseUser(); baseUser.setName(username); baseUser.setPassword(password); baseUser.setHomeDirectory(context.getExternalFilesDir(null).getAbsolutePath()); baseUser.setEnabled(true); baseUser.setAuthority(new WritePermission()); return baseUser; } }); factory.setUserManager(userManager); // 文件系统视图 factory.setFileSystem(new AndroidFileSystemView(context)); // 缓冲区大小和空闲超时设置 factory.getDataConnectionConfiguration().setMaxIdleTime(300); factory.setConnectionConfig(new DefaultConnectionConfigFactory().createConnectionConfig()); ftpServer factory.createServer(); try { ftpServer.start(); } catch (FtpException e) { Log.e(FtpServer, Failed to start FTP server, e); } } private String getWlanIpAddress(Context context) { WifiManager wm (WifiManager) context.getApplicationContext() .getSystemService(Context.WIFI_SERVICE); int ipInt wm.getConnectionInfo().getIpAddress(); return String.format(%d.%d.%d.%d, (ipInt 0xff), (ipInt 8) 0xff, (ipInt 16) 0xff, (ipInt 24) 0xff); } }这段代码里有几个值得注意的参数setPort(21)是FTP标准控制端口但如果需要避免冲突或这端口被运营商封禁可以改成1024以上的高位端口比如2121客户端连接时填对应端口即可。setMaxIdleTime(300)是数据连接空闲超时时间单位是秒设太短会导致大文件传输中断时难以恢复。setServerAddress()绑定WLAN IP这一步看起来简单但在部分Android设备上如果同时开启了热点和WLAN或者手机连接的是5GHz频段而电脑在2.4GHz频段拿到的IP地址可能不在同一网段就会导致客户端完全找不到服务器。3.3 文件系统视图实现存储映射与越权防护Android的文件系统与PC不同内部存储的根路径有/storage/emulated/0这类结构还有分区存储的限制。直接暴露整个文件系统既不安全也不符合Android平台的规范。AndroidFileSystemView要解决的正是这个问题public class AndroidFileSystemView implements FileSystemView { private final File rootDirectory; private File workingDirectory; public AndroidFileSystemView(Context context) { // 使用应用专属目录避免分区存储权限问题 this.rootDirectory context.getExternalFilesDir(ftp_share); if (!rootDirectory.exists()) { rootDirectory.mkdirs(); } this.workingDirectory rootDirectory; } Override public FtpFile getHomeDirectory() { return new AndroidFtpFile(rootDirectory); } Override public FtpFile getWorkingDirectory() { return new AndroidFtpFile(workingDirectory); } Override public boolean changeWorkingDirectory(String dir) { // 防止路径穿越 File newDir new File(workingDirectory, dir); try { String rootPath rootDirectory.getCanonicalPath(); String newPath newDir.getCanonicalPath(); if (newPath.startsWith(rootPath)) { workingDirectory newDir; return true; } } catch (IOException e) { return false; } return false; } // 其他FtpFile相关方法包括获取文件列表、创建文件等 }这里采用了应用专属目录getExternalFilesDir(ftp_share)作为共享根目录是因为Android 10及以上版本对公共目录有分区存储限制如果共享的是/sdcard/Download或DCIM读写时需要额外的权限声明而且从Android 11开始应用无法再随意创建任意公共目录的文件。changeWorkingDirectory中的路径穿越防护是整个文件系统视图的关键所在。用getCanonicalPath()规范化路径后判断是否以根目录开头能拦截掉../../system/etc这类越权访问。这是我实际测试中发现必须做的一层防护否则客户端可以绕过限制任意读取手机存储。4. 多用户与安全配置从裸奔到可控4.1 动态用户配置与匿名的取舍Demo默认支持匿名访问吗大多数场景下是不建议的。手机存储往往有较多个人数据匿名访问意味着同一WiFi下任何人都能连接并下载文件。但作为调试工具的Demo匿名模式有时又是刚需——例如在演示场景下输入用户名密码比较麻烦。Apache FtpServer对这两种模式都支持配置差异只是在UserManager里是否添加匿名用户。动态创建用户的写法在3.2节的代码里已经出现过这里补充多用户的批量配置方式。可以用PropertiesUserManager配合以users.properties文件批量定义用户信息格式如下ftpserver.user.admin.homedirectory/data/root_dir ftpserver.user.admin.userpasswordadmin123 ftpserver.user.admin.enabletrue ftpserver.user.admin.writepermissiontrue ftpserver.user.admin.maxloginnumber2 ftpserver.user.admin.idletime600当用户数量不多时我一般直接用BaseUser在代码里硬编码用户多了以后从properties文件加载更便于管理。参数含义为homedirectory决定该用户登录后看到的根目录writepermission控制是否有写入权限maxloginnumber限制同一账号的并发连接数防止一台手机被大量设备同时挂载idletime是空闲断开阈值单位秒。4.2 被动模式与防火墙端口规划FTP的主动模式在Android这种NAT环境里经常出事——客户端向服务器的21端口发送PORT命令指定一个客户端IP和端口由服务器主动连接客户端。在WLAN环境下客户端往往在路由器后面服务器根本连不过去。所以Android上的FTP服务器几乎只能依赖被动模式。被动模式的原理是客户端连接到服务器的21端口后发送PASV命令服务器开启一个随机高位端口并告知客户端客户端主动连接到这个端口传输数据。Apache FtpServer默认会自动分配一个端口但为了让穿透更稳定可以手动指定被动模式的端口范围DataConnectionConfigurationFactory dataConnFactory new DataConnectionConfigurationFactory(); dataConnFactory.setPassivePorts(60000-60050); factory.setDataConnectionConfiguration(dataConnFactory.createDataConnectionConfiguration());指定范围后的实际用途有两个配置路由器端口映射时可以把60000-60050整个段映射到手机上排查问题时可以快速判断防火墙是否放行了这一段端口。还有一个参数setPassiveAddress()当手机处在多级NAT或多SSID环境时手持机会拿到错误的地址通过PASV响应——这种情况下需要显式指定被动模式的对外地址。不过Demo场景下一般不需要手动指定依赖路由器自动分配即可。**提示**别把被动端口范围设得太大每个端口都会占用一个文件描述符。范围在10到20个端口之间即可因为FTP数据连接是短连接用完后会释放。4.3 权限模型只读账号与写入账号分离工程实践中把只读和可写账号分开是比较稳妥的做法。例如用guest账号做只读分享用admin账号做文件管理。Apache FtpServer对权限的控制粒度较细最小可以控制到单个文件和目录BaseUser readOnlyUser new BaseUser(); readOnlyUser.setName(guest); readOnlyUser.setPassword(guest123); readOnlyUser.setHomeDirectory(rootPath); // 不添加WritePermission该用户就只读 // readOnlyUser.setAuthority(new WritePermission());这里注释掉setAuthority(new WritePermission())相当于FTP客户端登录后只能查看和下载文件无法上传、删除或重命名。如果项目需要在同一账号下让不同目录的权限不一样那就要实现Authority接口在authorize()方法里通过FtpRequest的路径参数做匹配。只是这种需求在手机FTP场景中相对少见。5. 避坑指南从乱码到掉线的五个典型故障5.1 中文文件名乱码FTP协议字符集与Android平台差异现象客户端看到的中文文件名是乱码甚至完全无法读取。原因Apache FtpServer 默认使用本地系统字符集编码文件名而Android的默认字符集是UTF-8但电脑端的FTP客户端特别是Windows上的FileZilla默认设置为ANSI编码在发送LIST命令时用本地编码解析两边不统一就产生了乱码。解决最直接的办法是在创建FTP服务器时强制执行UTF-8字符集System.setProperty(file.encoding, UTF-8);如果还不生效有另一个技巧FTP协议本身支持OPTS UTF-8 ON命令在新版Apache FtpServer中只要客户端发送了这个命令就会启用UTF-8编码所以问题通常出在客户端的编码设置上。解决办法是在FileZilla站点管理器中把字符集设置为“强制UTF-8”。5.2 WiFi休眠把FTP服务带崩电源管理不是协议问题现象手机放在桌上十分钟后电脑连接FTP服务器超时手机屏幕亮起后又恢复了。原因Android系统在屏幕熄灭一段时间后会自动关闭WiFi或让CPU进入深度休眠FTP服务器进程被挂起网络连接自然断开。解决需要在服务中获取WiFi锁和CPU锁。一个比较稳妥的做法是在FtpService启动时添加如下逻辑PowerManager powerManager (PowerManager) getSystemService(POWER_SERVICE); wakeLock powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, FtpServer::KeepAwake); wakeLock.acquire(); WifiManager.WifiLock wifiLock wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, FtpServer::WifiLock); wifiLock.acquire();5.3 路由器AP隔离导致“连接被拒绝”现象手机和电脑连的是同一个WiFi电脑Ping不通手机的IPFTP连接被拒绝。原因很多家用路由器默认开启了“AP隔离”或“访客网络隔离”阻断无线客户端之间的互访这并非Android端的问题。解决登录路由器管理后台关闭AP隔离功能。如果是访客网络切换到主网络。判断方法很简单在电脑上ping手机IP如果通再去连FTPPing不通问题就不在FTP服务器本身。5.4 高版本Android存储权限锁死targetSdkVersion导致共享目录不可写现象FTP客户端能连接、能列出目录但上传文件报错550查看日志发现是Permission denied。原因Android 10以上启用分区存储后应用不能随意访问公共目录的任意路径。如果项目把共享根目录指向了/sdcard/Download且targetSdkVersion为29以上就没有直接写入权限。解决把共享根目录切换到应用专属目录例如context.getExternalFilesDir(ftp_share)。该目录路径中含包名的Android/data/下在Android 11以上对应用自身仍然可读写。如果确实需要共享公共目录可以引导用户手动授权“所有文件访问权限”但这需要专门的系统设置页面跳转不适合作为一个轻量Demo默认方案。5.5 被动模式在复杂NAT环境下连接超时现象客户端控制连接正常但发送文件或列目录时一直卡住最终超时。原因路由器没有把被动模式的端口范围映射到手机上或者服务器告知客户端的被动IP是内网地址客户端无法路由到达。解决先确认路由器是否支持端口映射把60000-60050的TCP端口映射到手机内网IP。如果只是同一局域网内的连接可以将DataConnectionConfigurationFactory的被动端口范围改为0表示完全随机分配同时将setPassiveAddress()显式设置为手机WLAN IP避免服务器上报错误IP地址。6. 验证与进阶用FileZilla实测通信和数据传输服务器跑起来之后最直接的验证方法是拿真实客户端连一遍。以FileZilla为例按如下步骤走一遍就能确认Demo是否正常手机打开FTP服务在应用UI上确认IP地址和端口号。电脑上打开FileZilla主机填手机IP端口填21或自定义端口用户名密码填Demo设定的账号。连接后在本地站点区域拖拽一个文件上传到远程站点区域再在远程站点选中一个文件下载到本地。查看FileZilla底部日志面板确认TYPE、PASV、STOR等命令都返回成功。上传和下载各跑一遍之后再做一次边界测试——传一个稍大体积的文件比如超过100MB观察断点续传和连接稳定性。如果中途断开关注手机端的WiFi锁是否生效如果速度很慢关注手机是不是在节电模式。验证通过后有两个进阶技巧值得留意。第一个是网络切换自动重启——当WiFi断开重连或IP变化时FTP服务器的监听地址会失效。可以在BroadcastReceiver中监听WifiManager.NETWORK_STATE_CHANGED_ACTION重新获取IP并重启服务Override public void onReceive(Context context, Intent intent) { String action intent.getAction(); if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { NetworkInfo networkInfo intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); if (networkInfo.isConnected()) { // 延迟2秒等待IP稳定然后重启FTP服务 new Handler().postDelayed(() - FtpServerManager.getInstance().restart(), 2000); } } }第二个技巧是开机自启。在服务中注册BOOT_COMPLETED广播接收到广播后检查开关状态并拉起前台服务。不过考虑到多数Android厂商的深度定制系统对开机自启限制较多这个功能只能尽力而为不要指望在所有机型上都生效。这个Demo我从下载研究到改造成自己的工具前后花了两个周末。印象最深的教训是早期版本把共享根目录直接指向/sdcard在Android 11上怎么配都读不到文件后来改成应用专属目录才彻底解决问题。从那以后我每次在手机上跑文件服务类应用都会先把存储路径的策略想清楚再动协议层。希望这份拆解能帮你在Android FTP方案上少走冤枉路下载源码后对照着跑通一遍比自己闭门造车省时间得多。本文还有配套的精品资源点击获取
RELATED

相关推荐

基于深度学习的人脸识别系统实战:从模型选型到毕设落地

基于深度学习的人脸识别系统实战:从模型选型到毕设落地

1. 项目全貌与选题价值分析1.1 这个毕设到底在做什么人脸识别系统的毕业设计,近几年一直是计算机视觉方向的热门选题。很多人一听“人脸识别”就觉得高大上,以为要自己从零写卷积神经网络、自己造轮子训练模型。实际上,一个合格的毕设项目&am…

📅 2026/10/17 3:13:59
图像生成VR体验:低成本三维重建与全景漫游实战

图像生成VR体验:低成本三维重建与全景漫游实战

做VR内容的人都有一个共识:建模和场景搭建特别烧钱。我这两年试过不少方案,最后发现一条性价比极高的路子——用图像直接生成VR体验。说到底,图像本身就是一种带了色彩、纹理和几何线索的信息载体,把它拆开、重组,再加…

📅 2026/10/17 3:13:59
YOLOv11结合可变形注意力DAT的目标检测改进实践指南

YOLOv11结合可变形注意力DAT的目标检测改进实践指南

毕设选题表一交,很多人就开始焦虑了。目标检测方向的题年年都有,但每年都有学生栽在同一个地方:要么只是改了改网络层数、换了换激活函数,答辩老师一问"为什么这么改",只能支支吾吾说"试出来效果好&quo…

📅 2026/10/17 3:08:59
MORE NEWS

更多资讯

📰

LangGraph多智能体动态任务分配与专家调度实战

多智能体系统里最容易让人头疼的问题,不是单个智能体写不出来,而是多个智能体凑到一起之后,到底听谁的、怎么分工、任务来了往哪儿派。我自己在搭这一类系统时踩过不少坑,后来用LangGraph把能力路由这块理顺了,才算真正…

📰

AirLLM分层加载:单卡8GB显存跑70B大模型实战

1. 单卡跑大模型这件事,到底卡在哪第一次看到 airllm 这个项目标题的时候,我的直觉是:又是一个“在消费级显卡上跑大模型”的尝试。这类项目这两年层出不穷,但真正能跑通、跑稳、跑出可用速度的并不多。airllm 的核心思路很直接—…

📰

明明加了锁却还是超卖?分布式锁的边界与事务时序陷阱

生产环境凌晨一点,某电商团队的库存模块突然出现超卖。所有人第一反应是分布式锁没加好,排查了半天却发现锁的日志一切正常:同一个商品ID,加锁、释放记录完全成对出现,Redis里也没看到异常。可数据就是对不上&#xff…

📰

Spring Boot乐器培训管理系统实战:从数据库设计到部署调试

搞乐器培训管理系统这类 Spring Boot 项目,我前后做了好几版。单看需求列表,无非是学员管理、课程排期、教师排班、缴费记录、课时统计,挺常规的。但真正把这些功能串成一套能稳定跑起来的系统,里面的角色权限划分、业务状态流转、…

📰

Ranger Admin 2.0 Tar包部署指南:Hadoop权限治理实战

简介:面向大数据平台运维与开发人员的Ranger Admin 2.0控制台tar包,旨在解决官方文档多侧重安装说明、却缺乏可直接部署的编译后制品问题。Ranger作为Hadoop生态中的集中式权限管理组件,其编译过程依赖较多且耗时较长,该tar包直接…

📰

告别无标题项目:从混沌想法到可交付项目的完整流程

新建一个空白文档,系统默认给它取名叫“无标题”。你盯着那块纯白区域,脑子里好像有很多事,可真要写点什么,光标闪了半天也落不下一个字。我见过太多项目就死在这一步:保存的时候叫“无标题”,三个月后打开…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬