发布时间:2026/9/2 5:24:09
Android FTP客户端实战:从Commons Net到文件传输闭环 简介面向Android开发者的FTP功能实现资源包基于Apache commons-net-3.7.jar解决应用内文件上传、下载、远程文件管理等需求。包内含完整工程文件与可运行APK共477个文件以flat布局资源、json数据、xml配置、jar库为主另有dex、class编译产物、gradle构建脚本及png图标等压缩包总大小约10.44MB。已有421人学习浏览。资源可直接导入Android Studio展示了FTPClient连接、被动模式设置、文件流读写、completePendingCommand处理及删除重命名等操作并包含网络异常处理与基础安全考虑。适合具备一定Android基础、希望快速集成FTP功能的开发者参考。 上个月接手一个外包交付的工程包里面一个名为 AndroidFtp.rar 的压缩文件特别显眼。解压完我发现这其实是一个典型的安卓 FTP 传输模块手机扫码或者输 IP 地址就能和电脑、NAS、Linux 服务器之间的文件做互传。我猜做这个工程包的人大概率是被“局域网内多个安卓设备文件同步”这个需求逼出来的因为现在微信发文件要压缩画质网盘又绕一大圈公司内网环境下 FTP 反而成了最简单粗暴的方案。项目本身不大但“Android FTP”这个组合踩坑点非常多权限、明文流量限制、分区存储、FileProvider、中文文件名、被动模式……每一个都能让首次上手的人卡半天。这篇就把我解包、改造、调通、上线全过程中的实操思路和排查经验写清楚代码片段直接复制可用适合做 Android 开发、运维或者嵌入式调试的朋友参考。1. 这个压缩包到底在解决什么问题1.1 为什么移动端还要用 FTP很多人第一反应是都什么年代了还 FTP我一开始也这么想但真正做了几个现场项目之后发现 FTP 在移动端的使用频率远比想象中高。典型的场景有这么几类局域网内批量传输大文件。比如工控机上的日志、几 GB 的视频素材通过 HTTP 上传需要写服务端接口通过 USB 线又要物理连接FTP 只需两端在同一网段就能跑。和 NAS、Windows 共享文件夹互通。家里或办公室的 NAS 基本都带 FTP 服务手机装个客户端就能把相册备份进去。嵌入式设备调试。很多开发板、单片机模块内置了 FTP 协议栈用来做固件升级、配置下发、数据上报。Android 设备作为调试终端时用 FTP 跟它们通信比串口方便得多。服务端一键部署无需改代码。Linux 服务器上 vsftpd 装好就能用不像自制 HTTP 上传接口还要考虑鉴权、并发、超时这些事。对比一下HTTP 上传文件虽然更“现代”但服务端必须配合往往还需要 SSL 证书SMB 在安卓上兼容性参差不齐蓝牙传输慢、距离近USB 又不适合远程。FTP 胜在协议简单、实现成熟、服务端零改造成本所以在特定场景下依然是可靠选项。1.2 从压缩包结构看方案选型解开 AndroidFtp.rar 之后目录结构很清晰基本是一个标准的 Android 工程app/src/main/java/下有 MainActivity、FtpHelper、常量配置类。app/libs/里是commons-net-3.10.0.jar。res/xml/下有 FileProvider 的路径配置文件。附带一个 README写了服务器 IP、账号密码的替换位置。这种结构的选型思路值得说一说作者没有自己写 FTP 协议而是选了 Apache Commons Net 这个老牌 Java 库。原因很简单FTP 底层的命令交互、主动/被动模式、数据连接管理都是一堆容易出 bug 的细节自己实现一轮至少要一周而 Commons Net 在 Java 领域跑了很多年API 稳定社区案例多。它的核心类FTPClient用起来其实就是几行代码后面我会给完整示例。2. Android FTP 客户端落地的几个关键点2.1 权限、明文流量和 Android 9 的默认限制在 Android 上做 FTP 客户端第一关是权限和网络策略。FTP 默认是明文协议不走 TLS 加密而 Android 9API 28开始默认禁止应用使用明文网络流量。也就是说你不做任何配置直接连ftp://192.168.x.x大概率会抛Cleartext HTTP traffic not permitted的异常。处理方式有两种。第一种是在AndroidManifest.xml里的application标签直接加android:usesCleartextTraffictrue这个最简单但代价是应用内所有明文网络请求都被放行如果项目里还有其他敏感网络请求安全性会比较差。第二种是只对特定域名或 IP 放开明文权限在res/xml/network_security_config.xml里配置network-security-config domain-config cleartextTrafficPermittedtrue domain includeSubdomainstrue192.168.1.100/domain /domain-config /network-security-config然后在application中引用android:networkSecurityConfigxml/network_security_config这样做更稳妥尤其是将来要把 FTP 地址换成公网 IP 的时候白名单机制能避免把整个应用的明文流量都暴露出去。另外别忘记在AndroidManifest.xml里加网络权限uses-permission android:nameandroid.permission.INTERNET /以及如果你要把文件保存到公共存储目录还需要处理 Android 11 以上的存储权限适配这块下面单独讲。2.2 FTPClient 连接参数的正确配置权限搞定之后真正连接 FTP 服务器时会遇到一堆细节问题。拿我最常用的 Commons Net 来说一段最基础的连接代码如下import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; FTPClient client new FTPClient(); client.setConnectTimeout(10_000); client.setDataTimeout(15_000); client.connect(host, port); int reply client.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { client.disconnect(); throw new RuntimeException(连接被拒绝); } client.login(username, password); client.setControlEncoding(UTF-8); client.enterLocalPassiveMode(); client.setFileType(FTP.BINARY_FILE_TYPE);这里有几个参数必须说清楚不然很容易被坑enterLocalPassiveMode()进入被动模式。主动模式下服务器会主动连回客户端的某个端口安卓设备在 NAT 后或者用移动网络时服务器根本连不回来被动模式则是由客户端主动发起数据连接成功率明显更高。局域网内的 FTP 服务基本都推荐用被动模式。setControlEncoding(UTF-8)控制连接的编码格式。很多 FTP 服务器默认用本地编码Windows 自带的 FTP 服务尤其容易输出 GBK 编码的目录名如果不统一编码列目录时中文文件名直接乱码。setFileType(FTP.BINARY_FILE_TYPE)强制二进制传输。如果不设成 BINARY默认的 ASCII 模式会在传输过程中做换行符转换传 APK、图片、压缩包这些二进制文件时文件会被写坏。2.3 中文文件名、路径分隔符与重连文件名的坑多到让我一度想放弃 FTP——但其实是没找对方法。第一个坑是远程路径分隔符。FTP 协议里路径统一用/分隔不要因为 Android 本地路径用File.separator就把远程路径也拼成\这在 Windows FTP 服务器上还能碰巧工作在 Linux vsftpd 上直接 550 错误。第二个坑是中文文件名的 URL 编码问题。如果服务器开启了 UTF-8 特性客户端设置setControlEncoding(UTF-8)之后中文名通常没问题但如果服务器没启用 UTF-8可能需要手动转码。我的处理方式是在connect成功之后主动探测一下服务器是否支持 UTF-8if (client.hasFeature(UTF8)) { client.setControlEncoding(UTF-8); }第三个坑是连接复用。移动端的网络不稳定一次长时间 FTP 会话很容易被服务端踢掉。我的习惯是每次传输任务独立建立连接传完就logout()和disconnect()短连接重连比长时间保活靠谱得多。如果要做“ftp 监控”这种轮询任务就用 ScheduledExecutorService 定时执行重连逻辑不要尝试维护一个永久连接。3. 从零搭一个“手机 ↔ 电脑”FTP 互传闭环3.1 服务端怎么快速起一个 FTP要测客户端先得有一个服务端。Linux 上最常用的是 vsftpdCentOS 7 装起来很快yum install -y vsftpd systemctl start vsftpd systemctl enable vsftpd默认配置下匿名用户可能开着为了数据安全建议修改/etc/vsftpd/vsftpd.confanonymous_enableNO local_enableYES write_enableYES local_umask022 pasv_min_port30000 pasv_max_port31000然后创建系统用户并设置密码useradd ftpuser passwd ftpuserPassive 端口范围这里特别重要如果服务器有防火墙光放行 21 端口是不够的数据连接会随机落在 30000-31000 区间不放行就会表现为“能登录但传不了文件”。开放命令firewall-cmd --permanent --add-port21/tcp firewall-cmd --permanent --add-port30000-31000/tcp firewall-cmd --reloadWindows 上不想装第三方软件的话可以用控制面板的“启用或关闭 Windows 功能”打开“Internet Information Services”里的“FTP 服务器”然后在 IIS 管理器里建站点。不过 Windows 自带 FTP 的配置相对麻烦还是建议直接用 FileZilla Server图形界面点几下就能跑起来。3.2 Android 11 之后文件读取怎么做才对客户端把文件上传到 FTP第一步是让用户选择本地文件。很多老代码还在用file:///storage/emulated/0/...这种硬编码路径这在现代 Android 上已经行不通了。Android 10 开始强制分区存储应用不能随意访问外部存储的任意目录Android 11 更进一步连Android/data目录都不让普通应用访问了。热搜里那一串content://com.xxx.fileprovider/...的报错本质上就是 FileProvider 和分区存储碰撞后的并发症。正确的做法是用系统文件选择器SAF让用户自己授权文件访问权val intent Intent(Intent.ACTION_OPEN_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) type */* } startActivityForResult(intent, REQUEST_CODE_PICK_FILE)拿到content://的 Uri 之后不要尝试转换成 File 路径应该直接用 ContentResolver 打开输入流val inputStream contentResolver.openInputStream(uri)这个 InputStream 就能直接交给 FTPClient 上传。同理下载下来的文件如果不想放在应用私有目录建议通过MediaStore写入公共 Download 目录或者让用户选择保存位置。3.3 上传下载的完整示例下面这段代码是我改造后最终跑通的简化版上传逻辑很清晰fun upload(uri: Uri, remotePath: String): Boolean { val client FTPClient() return try { client.connect(host, port) client.login(username, password) client.enterLocalPassiveMode() client.setFileType(FTP.BINARY_FILE_TYPE) client.setControlEncoding(UTF-8) contentResolver.openInputStream(uri)?.use { input - return client.storeFile(remotePath, input) } ?: false } catch (e: Exception) { e.printStackTrace() false } finally { try { client.logout() client.disconnect() } catch (_: Exception) { } } }下载方向的代码同样是对称的只是方向反过来fun download(remotePath: String, outputStream: OutputStream): Boolean { val client FTPClient() return try { client.connect(host, port) client.login(username, password) client.enterLocalPassiveMode() client.setFileType(FTP.BINARY_FILE_TYPE) client.retrieveFile(remotePath, outputStream) } catch (e: Exception) { e.printStackTrace() false } finally { try { client.logout() client.disconnect() } catch (_: Exception) { } } }如果你要做的是“ftp 监控”即定期扫描远程目录里有没有新文件并下载可以在上面的基础上加一个client.listFiles()的轮询逻辑。最好把轮询放到 WorkManager 里指定周期任务避免每次都启动一个常驻 Service 耗电。4. 实战中我踩过的坑和排查清单4.1 连不上先按这个顺序查在多个项目里拿到过“FTP 连不上”的问题大多数不是代码的问题而是网络环境和服务端配置的问题。我整理了一个排查顺序排查点检查内容常见原因服务端进程端口是否监听服务没启动或端口被占用网络连通性手机和服务器能否相互 ping 通Wi-Fi 隔离、跨网段路由问题防火墙21 端口和数据端口是否放行安全组只放行 21没放行被动端口主动/被动模式服务器是否支持被动模式某些旧服务器只允许主动模式账号权限登录后能否列出目录目录权限不够或 chroot 限制客户端超时连接超时、数据超时设置网络慢时默认超时太短其中最坑的是“能登录但 listFiles 卡死”的问题十次里有八次是防火墙没有放行被动端口范围。你把 30000-31000 放行之后问题立刻消失。4.2 “FTP over TLS is not enabled” 警告到底要不要管你可能会在服务器日志里看到一条 warningFTP over TLS is not enabled, users cannot securely log in.这句话的意思是当前 FTP 服务没有启用 TLS 加密用户登录时的账号密码会以明文形式在网络上传输。内网环境比如家庭局域网、公司内部调试网络风险相对可控很多老项目直接忽略这条警告。但如果你的 FTP 服务器暴露在公网或者传输的是敏感数据就必须启用 FTPS。Android 客户端这边可以用 Commons Net 里的FTPSClient替换FTPClientval client FTPSClient(TLS) client.connect(host, port) client.login(username, password) client.execPBSZ(0) client.execPROT(P)注意公网环境不要用自签名证书否则客户端需要额外做证书信任处理维护成本会明显上升。能上 SFTP 的话就更好了。4.3 文件路径和 content:// 转换失败这半年里最常被问的问题就是那一串content://com.baidu.searchbox.fileprovider/...或者content://com.ss.android.uri.key/external_root/...的报错。核心原因就是有的应用去拿 FileProvider 暴露的 Uri然后试图转成绝对路径去访问结果被分区存储拦截。正确的做法是永远不要对content://调用toString()之后拼到文件路径里而是用ContentResolver.openInputStream()拿流。如果你确实需要知道文件名可以用OpenableColumns.DISPLAY_NAME查询val cursor contentResolver.query(uri, null, null, null, null) cursor?.use { if (it.moveToFirst()) { val displayName it.getString( it.getColumnIndex(OpenableColumns.DISPLAY_NAME) ) } }这个文件名可以当作 FTP 的远程文件名来用避免中文名乱码的同时也绕开了路径权限问题。最后再分享一个我实际项目里的小技巧把 FTP 的上传/下载任务封装成一个FtpTask的普通 Java/Kotlin 类用线程池并发执行每个任务内部独立连接和断开。这样既不会因为多个任务共用一个 FTPClient 导致状态错乱也方便给每个任务加独立的超时和重试机制。我试过很多次短连接虽看着效率低但稳定性远好于长连接尤其是在手机网络切 Wi-Fi 的情况下长连接几乎必断短连接反而是最省心的方案。本文还有配套的精品资源点击获取

相关新闻

2026/9/2 5:24:09

海尔KFR-35GW/E1-1U空调选购指南:一级能效与变频技术如何实现省电

这次我们来看一款在省电空调品类中讨论度颇高的产品——海尔KFR-35GW/E1-1U壁挂式空调。对于很多家庭来说,选购空调的核心痛点非常明确:既要制冷/制热效果好,又希望电费账单不那么“感人”。市面上的“省电”宣传五花八门,这款海尔…

2026/9/2 5:24:09

AI基座正规公司

在当今快速发展的教育环境中,教师的专业发展和管理效率成为了教育机构关注的焦点。安徽晓窗教育科技有限公司(简称“晓窗”)作为一家专注于K12学校、教育局及中等职业学校的管理信息化产品与服务的企业,致力于通过先进的技术手段解…

2026/9/2 5:24:09

人工智能常用英文词汇-Day33

在阅读人工智能(AI)论文时,掌握高频英文术语及其准确中文翻译,能显著提升文献理解效率。本文按主题分类整理常见词汇,附简要介绍,供学习与写作参考。一、基础概念英文中文翻译介绍Artificial Intelligence …

2026/9/2 5:39:09

开源模型胜任自动化任务?从选型到工程化落地的完整指南

“自动化任务到底能不能用开源模型?”这个问题,几乎每次技术讨论都会有人问,而回答常常分成两派。一派认为自动化任务出错代价高,必须让最聪明的闭源大模型来兜底,于是团队每个月要为 API 调用付出高额账单&#xff0c…

2026/9/2 5:39:09

C# WinForm扫码枪集成与轻量级仓库管理系统开发实战

简介:这是一套基于C# WinForm开发的轻量级货物出入库与订单管理桌面系统,面向中小型仓储、电商或零售企业的IT人员及.NET初学者,解决人工录单效率低、易出错等实际业务痛点。系统核心支持扫码枪自动识别条形码与二维码,通过正则匹…

2026/9/2 5:39:09

基于C#与.NET构建电商系统:架构设计、核心模块与实战优化

简介:本资源是一套基于.NET平台与C#语言开发的完整电子商务网站系统源码及配套设计文档,面向Web开发初学者、高校计算机专业学生及.NET技术实践者,旨在解决电商类项目从架构设计到功能落地的学习与复用难题。压缩包共1618个文件,涵…

2026/9/2 5:39:09

gprMax电磁仿真从入门到精通:FDTD方法、2D/3D建模与实战避坑指南

简介:本资源是一套面向地质探测、考古与基础设施检测领域科研人员及高校师生的GprMax2D/3D实战教程包,聚焦地面穿透雷达(GPR)正向仿真建模能力培养,解决初学者在二维/三维GPR建模、参数设置、结果解析与工程场景适配中…

2026/9/2 5:39:09

AI聚合API统一接入多模型:从OpenAI兼容原理到智能体实战

最近在折腾自己的 AI 应用时,最大的痛点倒不是模型能力跟不上,而是手头要管的 Key 太多、接入协议各不相同、每次新模型发布都要重新写一遍对接代码。后来我把多个模型的 API 统一到自建的聚合网关里,配合智能体开发框架,一套 Ope…

2026/9/1 16:02:17

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出,第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器,出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台,直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/9/1 8:27:47

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流:为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历:明明传感器本身性能很好,信号输出却一塌糊涂——噪声大、漂移明显、重复性差,怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/9/1 7:04:43

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起,其实就是嵌入式开发里最常遇到的一类需求:用一块不算贵的 MCU,同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控,主频…

2026/9/2 0:03:41

单片机毕业设计-基于单片机与蓝牙通讯的输液状态监测终端设计与开发 基于 STM32 或 51 单片机的液位‑滴速‑温度多参数输液监护装置设计(024005)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/9/2 0:03:41

DeepSeek字幕翻译实战:从API调用到批量SRT转中文的完整方案

这次我们来看一个很实用的 DeepSeek 落地场景:用 DeepSeek 把英文视频字幕自动翻译成中文。具体案例是《恶魔君》1989 年第 28 集的英转中字幕任务,标题写得很直白,但背后其实是一整套可以复用的技术流程:字幕解析、模型调用、批量…

2026/9/2 0:03:41

用Python搭建搞笑语音助手:从语音识别到语音合成全教程

当你家里摆着一台天猫精灵,却总希望语音助手偶尔“不正经”一点,不用官方腔回答问题,而是张口就接几句搞笑段子,会是什么体验?我最近动手验证了一下这个想法——没有去改装任何市面上现有的智能音箱,而是直…

2026/9/2 1:15:22

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/2 1:15:22

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/2 1:15:20

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…