【疑难杂症2025-007】马看见什么是人决定的, FTP/HTTP看见什么也是人决定的

本文由Markdown语法编辑器编辑完成。

1.背景:

最近花了两三天的时间,解决了一个医院客户,困扰了我比较两三星期的,关于ftp下载医学影像的问题。

故障大概是这样的: 某一天,线上的同事找到我说,这家医院有两个院区,医生反馈来自分院的数据,在我们的系统里面无法展示。主院区的影像则正常展示。我通过VPN远程到医院的服务器,看到了很多的报错,涉及到分院的数据时,报错都是: “Resource not found.”

这家医院是FTP的对接方式。他们的数据库里面会存放每个检查的FTP的地址。我们的服务器,会定时轮询,从数据库中读取到检查的FTP路径,然后再从FTP Server把图像下载下来,进行后续处理。

由于他们是两个院区,因此分别有两个FTP Server,一个存主院区的数据,一个存分院区的数据。出问题的是分院区的数据。

2.问题定位:

前期花了一定的时间进行摸索。

我们是作为FTP客户端,从医院的FTP服务端拉取数据。

程序主要使用了两种方式,来进行FTP的对接,对应的是两个不同的python库函数。

ftp也可以利用浏览器直接访问服务端。当我在客户的浏览器,登录ftp的服务器后,并且根据数据库中定义的,文件夹在ftp服务器的路径,是可以一层一层定位到图像的。

在这里插入图片描述
于是我也利用python自带的ftplip进行测试。

测试代码如下:

2.1 python内置的ftplip

关于python内置的ftplip的相关介绍,可以参考链接:
https://docs.python.org/zh-cn/3/library/ftplib.html
https://dataxujing.github.io/ftplib/


# 定义FTP服务器的连接常量:
_FTP_SERVER_IP = "192.168.0.11"
_FTP_SERVER_PORT = 21
_FTP_SERVER_ROOT = "/pacs"
_FTP_SERVER_USERNAME = "dicomreader"
_FTP_SERVER_PASSWORD = "abc123"

from ftplib import FTP

if __name__ == "__main__":

    # 连接 FTP
    ftp_jn = FTP()
    ftp_jn.connect(_FTP_SERVER_IP, _FTP_SERVER_PORT)  # 主机和端口,默认端口是 21
    ftp_jn.login(_FTP_SERVER_USERNAME, _FTP_SERVER_PASSWORD)  # 登录

    # 列出当前目录下的文件
    ftp_jn.cwd("./CT/2025/10/11")

    file_list = ftp_jn.nlst()
    print(f"file count is: {len(file_list)}")
    # ftp_jn.cwd("CT/2025/10/11/C5F33DB3571A36D43372E429D14F6628")
    # 或者使用 dir 查看详细信息
    # dir_list = ftp_jn.dir()
    # if "C5F33DB3571A36D43372E429D14F6628" in dir_list:
    #     print("11111")
    # print(ftp_jn.dir())

    # 退出
    ftp_jn.quit()

运行上述的测试代码后,当运行: ftp_jn.cwd(“./CT/2025/10/11”)后,即说明,将当前目录cd到指定的目录。

然后再运行: ftp_jn.nlst(), 即输出该目录下面的所有文件夹和文件的名称。

运行这些测试脚本时,都可以正常地运行,而且看到的目录,和在浏览器上面看到的,也是完全相同的。

但为什么真正下载的时候,却还是失败呢?

2.2 python第三方库fs

后来,我又用python的一个第三方库fs. 它将python与ftp, mount等服务器的常见的操作,均进行了封装,并提供了接口来进行交互。属于比python自带的ftplip, 更高级的一个库。

该库的github的链接如下: https://github.com/PyFilesystem/pyfilesystem2

在这里插入图片描述

正如它的简介所示, “Python’s Filesystem abstraction layer”, 它是一个对于python文件系统的一个抽象层. 当然ftp, mount等,都属于文件系统的一部分,因此都是支持的。

当我用fs, 来测试该分院服务器的数据时,奇怪的事情发生了.

from pathlib import Path
from fs.ftpfs import FTPFS

_FTP_SERVER_IP = "192.168.0.11"
_FTP_SERVER_PORT = 21
_FTP_SERVER_ROOT = "/pacs"
_FTP_SERVER_USERNAME = "dicomreader"
_FTP_SERVER_PASSWORD = "abc123"


_root_fs_ftp = FTPFS(
    host=_FTP_SERVER_IP,
    port=_FTP_SERVER_PORT,
    user=_FTP_SERVER_USERNAME,
    passwd=_FTP_SERVER_PASSWORD,
)


def _recursion_get_file_list(remote_rel_path: str):
    """
    递归的返回远端文件列表
    :param remote_rel_path: 相对于remote_path_root的相对地址
    """
    print(f"root_fs_ftp的对象类型为: {type(_root_fs_ftp)}")
    print(f"fsftp pwd: {_root_fs_ftp.ftp.pwd()}, list dir: {_root_fs_ftp.listdir('/')}")

    _remote_root_fs = _root_fs_ftp.opendir(_FTP_SERVER_ROOT)
    print(f"remote_root_fs的类型为: {type(_remote_root_fs)}")
    for entry in _remote_root_fs.filterdir(".", exclude_files=True, exclude_dirs=False):
        print(f"路径下面包含的文件夹名称为: {entry.name}")

    for x in _remote_root_fs.scandir(remote_rel_path):
        path = (Path(remote_rel_path) / x.name).as_posix()
        if x.is_dir:
            yield from _recursion_get_file_list(path)
        elif x.is_file:
            yield path


def get_file_list(remote_rel_path: str):
    return list(_recursion_get_file_list(remote_rel_path))


if __name__ == "__main__":
    remote_rel_path = "/CT/2025/10/11"
    print(get_file_list(remote_rel_path))

由于原来的代码是可以正常运行的。我其实比较纳闷,之前定义的:

 _FTP_SERVER_ROOT = '/pacs'

到底有啥用? 因为不管是实际下载,还是在浏览器里面查看图像的路径时,我都没有看到这个,带有"pacs"字符串的目录层级。但是之前一段时间,这里的确是定义了它,而且可以成功下载图像。

于是我在用fs内置的: fs.ftpfs里面的FTPFS, 应用初始化参数构建后,就想看看目录的目录结构是什么。

于是采用了: _root_fs_ftp.ftp.pwd(), 来输出当前连接ftp后,所处的服务器的路径, 以及ftp服务器的根路径,都有哪些文件夹。
结果这时输出的ftp的当前路径是:

/pacs2025

而ftp服务器所处的根路径下面,有一个pacs和一个pacs2025的文件夹。
在这里插入图片描述
之后我试图,从/pacs2025的当前路径,先cd到上一级目录,再进入pacs路径,想一探究竟,看看为什么pacs路径里面,没有我要下载的影像。

以下是我在调试的过程中,和chatgpt探讨如何解决问题的一些关键截图:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

经过和AI的反复探讨和考证,最后发现,之所以我们的服务,突然无法下载医院分院的数据。是因为医院信息科,调整了他们的FTP Server的根路径,也就是使用账号和密码登录后,默认进入的根路径。

调整前,是/pacs; 调整后,是/pacs2025.

因此,医院服务端返回给我们的图像的路径,其实已经是相对于/pacs2025, 这个根路径下面的相对路径了。而我们在下载时,还是先opendir()了/pacs, 然后再从/pacs下面找图, 当然就所有的图像,都会报错: Resource not found了。

3. 结论:

其实不只是FTP Server.

平时我们在用python的http-server服务的时候,也是这样的。我随便切换到某个磁盘目录下面,然后运行:

python3 -m http.server 9001(可以任意选择一个未被占用的端口即可)

那么我们在浏览器上面访问时,输入启动http-server服务器的ip:9001目录,便可以看到这个目录下面的所有文件了。但是我们其实并不知道,服务器上面还有哪些其他目录。我们能够看到的文件,只是服务端允许我们看到的那部分文件罢了。

就像《封神榜第一部, 朝歌风云》中,殷商说的那句话,”马看到什么,是人决定的。“

我也就模仿着来一句,”FTP/HTTP客户端看到什么,是服务端(也是人)决定的 ! “

(完)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

inter_peng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值