欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Flask使用 secure_filename 获取文件名不完整问题

程序员文章站 2024-03-16 14:22:22
...

使用secure_filename获取文件名时,中文以及其他特殊字符会被省略。

原因:secure_filename()函数只返回ASCII字符,非ASCII字符会被过滤掉。

1 查看函数原代码

文件路径如下:

Flask使用 secure_filename 获取文件名不完整问题

源码如下:找到secure_filename(filename)函数。

Flask使用 secure_filename 获取文件名不完整问题

代码中有一个变量:_filename_ascii_strip_re

Flask使用 secure_filename 获取文件名不完整问题

代码解释:

根据文件名中的空字符,包括空格、换行(\n)、制表符(\t)等,把文件名分割成列表,然后使用下划线“_”进行连接,再过滤掉正则之外的字符,最后去掉字符串两头的“._”字符,最终生成新的文件名

2 解决文件名问题

2.1 修改源码

建议修改之前对源码进行备份

将secure_filename(filename)函数修改为下方代码:

def secure_filename(filename):
    r"""Pass it a filename and it will return a secure version of it.  This
    filename can then safely be stored on a regular file system and passed
    to :func:`os.path.join`.  The filename returned is an ASCII only string
    for maximum portability.

    On windows systems the function also makes sure that the file is not
    named after one of the special device files.

    >>> secure_filename("My cool movie.mov")
    'My_cool_movie.mov'
    >>> secure_filename("../../../etc/passwd")
    'etc_passwd'
    >>> secure_filename(u'i contain cool \xfcml\xe4uts.txt')
    'i_contain_cool_umlauts.txt'

    The function might return an empty filename.  It's your responsibility
    to ensure that the filename is unique and that you generate random
    filename if the function returned an empty one.

    .. versionadded:: 0.5

    :param filename: the filename to secure
    """
    if isinstance(filename, text_type):
        from unicodedata import normalize
        filename = normalize('NFKD', filename).encode('utf-8', 'ignore')  # 转码
        if not PY2:
            filename = filename.decode('utf-8')  # 解码
    for sep in os.path.sep, os.path.altsep:
        if sep:
            filename = filename.replace(sep, ' ')

    # 正则增加对汉字的过滤	\u4E00-\u9FBF	中文
    # 自定义构建新正则
    _filename_ascii_add_strip_re = re.compile(r'[^A-Za-z0-9_\u4E00-\u9FBF.-]')

    # 使用正则
    # 根据文件名中的空字符,包括空格、换行(\n)、制表符(\t)等,把文件名分割成列表,然后使用下划线“_”进行连接,再过滤掉正则之外的字符,最后去掉字符串两头的“._”字符,最终生成新的文件名
    filename = str(_filename_ascii_add_strip_re.sub('', '_'.join(filename.split()))).strip('._')

    return filename

2.2 使用第三方库(pypinyin),将中文名转换成拼音

pypinyin第三方库安装命令:

pip install pypinyin
import pypinyin

filename = secure_filename(''.join(pypinyin.lazy_pinyin(file.filename)))

2.3 使用uuid模块重命名文件名

python的uuid模块提供UUID类和函数uuid1(), uuid3(), uuid4(), uuid5() 来生成1, 3, 4, 5各个版本的UUID ( 需要注意的是: python中没有uuid2()这个函数)。

import uuid

uuid.uuid1([node[, clock_seq]])  # 基于时间戳
# 使用主机ID, ***, 和当前时间来生成UUID, 可保证全球范围的唯一性. 但由于使用该方法生成的UUID中包含有主机的MAC地址, 因此可能危及隐私.
# 该函数有两个参数, 如果 node 参数未指定, 系统将会自动调用 getnode() 函数来获取主机的硬件地址.
# 如果 clock_seq  参数未指定系统会使用一个随机产生的14位***来代替.
uuid.uuid3(namespace, name)  # 基于名字的MD5散列值
# 通过计算命名空间和名字的MD5散列值来生成UUID, 可以保证同一命名空间中不同名字的唯一性和不同命名空间的唯一性, 但同一命名空间的同一名字生成的UUID相同.
uuid.uuid4()  # 基于随机数
# 通过随机数来生成UUID. 使用的是伪随机数有一定的重复概率.
uuid.uuid5(namespace, name)  # 基于名字的SHA-1散列值
# 通过计算命名空间和名字的SHA-1散列值来生成UUID, 算法与 uuid.uuid3() 相同.