社保系统还在用FTP传文件——不是落后,是银行就认这个

社保系统还在用FTP传文件——不是落后,是银行就认这个

一、银行和社保之间,唯一稳定的通道

做社保系统有一个绕不开的环节:和银行打交道。

养老金的发放、医保费用的代扣、社保费的代缴——这些钱的流动,社保系统算好金额,生成批量文件,传给银行执行。银行执行完了,再生成回盘文件,社保系统读回来更新状态。

这事怎么传?REST API?那是互联网公司的做法。银行的要求很简单:FTP传文件

十几年前是这个,现在还是这个。不是社保系统不想换,是银行的报文交换系统——特别是代发代扣系统——底层就是基于文件交换的。各家银行的"银企直连"接口,最底层的通道要么是Socket报文,要么是FTP文件。FTP反而是最通用、最稳定的一种。

所以社保系统里一定有一个FTP工具类。我们的叫 FtpUtil

二、目录约定就是接口

银行端的FTP目录结构是约定好的,两边协商好之后就固定了:

FTP根目录/
├── hnftpbank/         社保→银行的根目录
│   └── tobank/        社保上传文件给银行
├── tocsi/             银行→社保的根目录(银行上传回盘文件)

社保往 tobank 里放文件,银行从里面取;银行往 tocsi 里放回盘文件,社保轮询读取。

这个命名看起来很朴素,但它解决了两个问题:

  1. 不需要额外的通知机制。银行处理完一个文件,往 tocsi 里放回盘——社保程序每隔几分钟去扫一次,有文件就取回来处理
  2. 天然实现了异步解耦。社保不需要等银行处理完,银行也不需要等社保来取。文件在目录里躺着,谁需要谁去取

这不是微服务,不是消息队列,但解耦的效果是一样的。

三、文件名的日期编码

FTP上的文件名通常带日期,比如 20260521.txt。这不是随便取的——银行文件名有严格的编码规范,通常包含日期、批次号、业务类型代码。

社保这边在上传时也按同样的规则生成文件名,银行扫描时按规则解析。文件名本身就是协议的一部分——它告诉对方"这是什么类型的数据、哪一天、第几批"。

四、编码:中文目录名在FTP上的处理

有一个看似不起眼但踩过坑的细节:FTP服务器上可能有中文目录名。

我们的 CreateDirecroty 方法里有这样一行:

String subDirectory = new String(remote.substring(start, end).getBytes("GBK"), "ISO-8859-1");

FTP协议默认用ASCII或ISO-8859-1传输路径信息。中文目录名必须先用GBK编码成字节,再以ISO-8859-1透传,否则FTP服务器不认识这个目录。

为什么会有中文目录?有的银行FTP服务器是Windows搭建的,他们自己建目录时用了中文名。社保这边就得适配。这不是技术问题,是对接现实问题的妥协

五、文件上传的三步标准动作

ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);  // ①设二进制模式
CreateDirecroty(ftpClient, pathname);               // ②确保目录存在
ftpClient.storeFile(fileName, inputStream);          // ③写入文件

三步走,每一步都有坑。

设二进制模式:如果不设,FTP默认是ASCII模式,对于含中文或二进制的文件,内容会被篡改。批量报盘文件里每一行都有金额字段,差一个字节就是财务事故。

确保目录存在:银行FTP的目录不一定是事先建好的。有时银行换了运维人员、迁移了服务器,目录就不见了。CreateDirecroty 会逐层检查并创建——这个防御性检查在生产环境里救了不止一次。

写入文件:最需要注意的不是写入本身,而是写入完成后的校验。文件传上去了不等于银行能正确解析。我们的做法是:上传后,调用银行的校验服务(Verifier)确认文件的格式通过银行的规则校验。

主动模式 vs 被动模式——被防火墙决定的参数

FTP有两种数据传输模式,区别在于"谁连谁":

主动模式(PORT)被动模式(PASV)
谁发起数据连接服务器主动连客户端客户端主动连服务器
防火墙友好度差——服务器要穿透客户端防火墙好——客户端出站连接通常放行
典型场景双方都在内网、无防火墙跨网段、有防火墙/NAT

社保和银行之间通常不在同一个网络里。银行FTP服务器在银行内网,社保系统在政务网。中间隔着银行端的防火墙、政务网的防火墙。主动模式下,银行服务器要反向连社保客户端的某个随机端口——这个端口大概率被政务网防火墙拦了。所以社保对接银行的FTP,几乎只能用被动模式

反过来,如果社保这边自己搭FTP服务器让医院上传文件——社保的服务器在内网,医院在外网——也是被动模式。因为医院端不知道社保防火墙开了哪些端口。

代码里这一行看似不起眼,但在跨网段场景下决定了能不能连通

ftpClient.enterLocalPassiveMode();  // 被动模式,让客户端主动发起数据连接

没这一行,防火墙一拦,连接超时、文件传一半断开、debug 时只看到 Read timed out——根本想不到是模式不对。

六、回盘文件的轮询模式

银行处理完代发代扣后,生成回盘文件放到 tocsi 目录。社保系统的做法:

启动定时任务 → 每10分钟扫 FTP/tocsi/ → 发现新文件 → 下载到本地 → 解析入库

定时任务只做一件事:

ftpClient.changeWorkingDirectory("/tocsi");
FTPFile[] files = ftpClient.listFiles();
for (FTPFile f : files) {
    ftp.downloadFile(ftpClient, "/tocsi", f.getName(), localpath);
}

简单到什么程度?没有消息队列,没有回调接口,就是扫目录 + 下载 + 处理。但这套机制在十多年的运行中从来没出过问题——它简单到你没什么地方可以出错。

七、为什么不换掉FTP

这个问题被反复问过。答案分两层:

技术层:可以换。把 FtpUtil 换成 SftpUtil(走SSH),或者换成HTTP文件上传。封装一下,上层业务代码不用改。

业务层:不能换。因为银行端的接口是固定的。社保和银行的对接协议(银企直连接口)在合同中写死了FTP的IP、端口、目录结构、文件名规则。要换就得和银行重新谈合同——这个成本远超技术替换本身。

所以FTP不是选出来的最优解,是在现有约束下唯一不需要重新谈判的方案。这个"方案",运行了十几年。

八、一个容易被忽略的连接细节

FTP连接不频繁——每天几次到几十次——但每次连接都很关键。代码里做了这些事:

ftpClient.setControlEncoding("UTF-8");        // 控制通道编码
ftpClient.connect(host, port);
ftpClient.login(username, password);
int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
    throw new utilException("connect failed...");
}

检查replyCode这一步最容易漏。很多FTP工具类连上了就直接操作,不检查登录是否成功。如果FTP服务器配置变了、密码过期了、网络不通——没有这个检查,程序会直接走到下一步,然后抛出一个莫名其妙的异常。加上replyCode检查,问题定位从"怎么文件传不上去"变成了"FTP登录失败"。

九、结语

FTP在2026年看是古老的。但银行和社保之间的文件交换,十几年来一直是这个模式。

技术的"先进"和"落后"在政务系统里不是先后关系,是谁说了算的关系。银行说FTP,社保就FTP。社保说要用Socket报文,银行就Socket。不是选择最好的技术,是选择对方接受的技术。

FTP的"落后"恰恰是它的优势:足够简单、足够稳定、所有银行都支持。一条社保基金的发放链路,从18年前跑到今天,中间换过数据库、换过应用服务器、换过前端框架——但FTP那部分,从来没动过。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值