python Flask当文件名包含中文时使用secure_filename,中文会被忽略的解决办法

在修改自己项目的文件上传功能时,发现文件是成功上传到服务器端了,但是服务器端上的文件名确是'xlsx',前面的中文全都不见了,最后发现是secure_filename的问题,这里把解决方法整理一下

原因

查看源码可以发现secure_filename函数只返回ASCII字符,非ASCII字符会被其中的正则表达式过滤掉。

def secure_filename(filename):
    if isinstance(filename, text_type):
        from unicodedata import normalize

        filename = normalize("NFKD", filename).encode("ascii", "ignore")
        if not PY2:
            filename = filename.decode("ascii")
    for sep in os.path.sep, os.path.altsep:
        if sep:
            filename = filename.replace(sep, " ")
    filename = str(_filename_ascii_strip_re.sub("", "_".join(filename.split()))).strip(
        "._"
    )

    # on nt a couple of special files are present in each folder.  We
    # have to ensure that the target file is not such a filename.  In
    # this case we prepend an underline
    if (
        os.name == "nt"
        and filename
        and filename.split(".")[0].upper() in _windows_device_files
    ):
        filename = "_" + filename

    return filename

解决方法

修改源码

def secure_filename(filename):
    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

使用pypinyin

pip install pypinyin
pypinyin是汉字拼音转换模块,可以将汉字转为拼音,但可能存在拼音有误的问题,具体可以查看后面的地址。https://pypi.org/project/pypinyin/

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

使用uuid模块重命名文件名

https://blog.csdn.net/qq_36390239/article/details/98847888

a) uuid.uuid1([node[, clock_seq]])  : 基于时间戳

  使用主机ID, 序列号, 和当前时间来生成UUID, 可保证全球范围的唯一性. 但由于使用该方法生成的UUID中包含有主机的网络地址, 因此可能危及隐私. 该函数有两个参数, 如果 node 参数未指定, 系统将会自动调用 getnode() 函数来获取主机的硬件地址. 如果 clock_seq  参数未指定系统会使用一个随机产生的14位序列号来代替. 

b) uuid.uuid3(namespace, name) : 基于名字的MD5散列值

  通过计算命名空间和名字的MD5散列值来生成UUID, 可以保证同一命名空间中不同名字的唯一性和不同命名空间的唯一性, 但同一命名空间的同一名字生成的UUID相同.

c) uuid.uuid4() : 基于随机数

  通过随机数来生成UUID. 使用的是伪随机数有一定的重复概率. 

d) uuid.uuid5(namespace, name) : 基于名字的SHA-1散列值

  通过计算命名空间和名字的SHA-1散列值来生成UUID, 算法与 uuid.uuid3() 相同.
posted @ 2021-06-24 10:43  晓天的BigWorld  阅读(1143)  评论(0编辑  收藏  举报