• 解决python fs(filesystem)库在连接FTP服务器时无法显示文件目录的问题


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

    1. 问题提出:

    目前有一个需求是:医院的影像数据是存储在FTP服务器上的,医院提供了连接该FTP服务器的host, user, password等参数.(注:该ftp服务是部署在windows操作系统的IIR服务上)
    采用的python库是fs(filesystem2)的第三方库.fs==2.0.23版本.
    在根据提供的参数,可以正常的连接到该ftp服务器,但是在显示该ftp服务器下的文件目录时,却显示为空.但是,该ftp服务器下的确是有文件夹的.

    之前在连接别的医院的ftp服务器时,是可以正常显示ftp服务器下的文件目录的.

    通过跟踪代码,发现是由于fs库的内部的一个函数实现,在解析服务器返回的字符串时,返回为空,而导致后续无法检索到文件夹.

    定位到的函数是:fs.ftpfs下的_ftp_parse()函数.
    IIR服务器返回的lines是:

    [
      '01-20-19 04:23AM < DIR > 104794_CT',
      '01-20-19 04:22AM < DIR > 104794_SR',
       '01-19-19 07:45AM < DIR > 104795_CT',
       '01-19-19 07:43AM < DIR > 104795_SR',
       '01-15-19 07:49AM < DIR > 104796_CT',
       '01-14-19 07:47AM < DIR > 104796_SR',
       '01-13-19 08:44AM < DIR > 104797_CT'
     ]
    

    在运行:

    _list = [Info(raw_info) for raw_info in ftp_parse.parse(lines)]
    

    后,_list为[], 因此后续无法检索到图像.

    2. 问题分析:

    要想解决这个问题,需要对比之前可以正常显示ftp服务器下的文件目录和这里的区别.
    通过debug代码发现,当ftp服务是部署在linux服务器时,程序运行到上面的位置时,lines的值的格式为:

    [
      u'drwxr-xr-x    2 ftp      ftp         69632 Jan 30 03:51 3177498', 
      u'drwxr-xr-x    2 ftp      ftp         73728 Jan 30 03:51 3177512', 
      u'drwxr-xr-x    2 ftp      ftp         77824 Jan 30 03:52 3177517', 
      u'drwxr-xr-x    2 ftp      ftp         73728 Jan 30 03:52 3177529'
    ]
    

    而fs中解析这个lines的代码为_ftp_parse.py:

    re_linux = re.compile(
        """
        ^
        ([ldrwx-]{10})
        s+?
        (d+)
        s+?
        ([w-]+)
        s+?
        ([w-]+)
        s+?
        (d+)
        s+?
        (w{3}s+d{1,2}s+[w:]+)
        s+
        (.*?)
        $
        """,
        re.VERBOSE
    )
    
    def get_decoders():
        decoders = [
            (re_linux, decode_linux)
        ]
        return decoders
    
    
    def parse(lines):
        info = []
        for line in lines:
            if not line.strip():
                continue
            raw_info = parse_line(line)
            if raw_info is not None:
                info.append(raw_info)
        return info
    
    
    def parse_line(line):
        for line_re, decode_callable in get_decoders():
            match = line_re.match(line)
            if match is not None:
                return decode_callable(line, match)
        return None
    
    def decode_linux(line, match):
        perms, links, uid, gid, size, mtime, name = match.groups()
        is_link = perms.startswith('l')
        is_dir = perms.startswith('d') or is_link
        if is_link:
            name, _, _link_name = name.partition('->')
            name = name.strip()
            _link_name = _link_name.strip()
        permissions = Permissions.parse(perms[1:])
    
        mtime_epoch = _parse_time(mtime)
    
        name = unicodedata.normalize('NFC', name)
    
        raw_info = {
            "basic": {
                "name": name,
                "is_dir": is_dir
            },
            "details": {
                "size": int(size),
                "type": int(
                    ResourceType.directory
                    if is_dir else
                    ResourceType.file
                )
            },
            "access": {
                "permissions": permissions.dump()
            },
            "ftp": {
                "ls": line
            }
        }
        access = raw_info['access']
        details = raw_info['details']
        if mtime_epoch is not None:
            details['modified'] = mtime_epoch
    
        access['user'] = uid
        access['group'] = gid
    
        return raw_info
    

    从fs的源码中可以看出,_ftp_parse.py中的get_decoders()函数中,decoders这个变量数组中,只提供了一组解码器,即内部的 (re_linux, decode_linux). 因此,当ftp服务器不是部署在linux操作系统上,而是部署在windows等其他操作系统上时,ftp返回的字符串格式,就不再符合解码器中的正则表达式.

    因此,要修改这个问题,就需要根据当前IIR服务器返回的字符串的格式,撰写相应的正则表达式来解码,然后加入到_ftp_parse.py中的decoders数组中.

    要解决这个问题,就需要对python中的正则表达式有一定的了解.相关的教程可以阅读:正则表达式.
    https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386832260566c26442c671fa489ebc6fe85badda25cd000

    3. 解决方案:

    在了解了正则表达式的规范后,模拟re_linux和decode_linux的函数,可以增加re_windows和decode_windows这两个函数.增加后的函数如下:

    re_windows = re.compile(
        """
        ^
        ([w-]{8}s+[w:]+)
        s+?
        (<DIR>)
        s+?
        (.*?)
        $
        """,
        re.VERBOSE
    )
    
    def decode_windows(line, match):
        mtime, dir, name = match.groups()
        name = name.strip()
        raw_info = {
            "basic": {
                "name": name,
                "is_dir": True
            },
            "ftp": {
                "ls": line
            }
        }
        return raw_info
    
    def get_decoders():
        decoders = [
            (re_linux, decode_linux),
            #这个解码组合是新增的,针对windows操作系统的解码器.
            (re_windows, decode_windows)
        ]
        return decoders
    

    当然,这里的正则表达式是根据之前返回的字符串来撰写的.如果返回的字符串和文章中开头的字符串样式不同,还需要另外修改正则表达式.

  • 相关阅读:
    数据库MySQL技术-基础知识
    NC文件的处理【netcdf】
    设计模式-策略模式---Strategy(对象行为型)
    final修饰的变量是引用不能改变,还是引用的对象不能改变
    Spring之配置文件加载方式
    hibernate的基础学习--多表关联数据查询
    hibernate的基础学习--多对多关联
    hibernate的基础学习--一对一关联
    hibernate的基础学习--一对多关联
    hibernate的基础学习
  • 原文地址:https://www.cnblogs.com/stephen2014/p/10373930.html
Copyright © 2020-2023  润新知