Python 处理中文文件名的3个坑(附 Flask 上传解决函数)

最近在做一个 Flask 文件上传工具,用户上传了一个叫 产品图片.jpg 的文件,结果服务器上保存出来变成了空文件名,直接报错。排查下来发现是一个"老熟人"的锅------secure_filename()。


坑一:secure_filename() 会吃掉中文

用 Flask 做文件上传,官方示例几乎都会这么写:

python 复制代码
from werkzeug.utils import secure_filename

filename = secure_filename("产品图片.jpg")
print(filename)  # 输出:.jpg  ← 文件名没了!

secure_filename() 的设计目标是过滤掉不安全字符,但它的"不安全"定义里包含了所有非 ASCII 字符,中文直接被扔掉。

如果文件名全是中文,返回的就是一个光秃秃的 .jpg,后续 open() 直接报错。


坑二:os.path 在 Windows 上的编码陷阱

在 Windows 环境下,如果脚本编码和系统编码不一致,os.path.exists()、os.rename() 等操作中文路径时可能抛出:

复制代码
UnicodeEncodeError: 'gbk' codec can't encode character...

解决方式是确保脚本头部声明编码,并统一使用 pathlib.Path 替代 os.path:

python 复制代码
# -*- coding: utf-8 -*-
from pathlib import Path

p = Path("D:/上传文件/产品图片.jpg")
print(p.exists())  # 正常工作

坑三:open() 写文件默认编码不是 UTF-8

python 复制代码
# 危险写法(Windows 下默认 GBK)
with open("结果.txt", "w") as f:
    f.write("中文内容")

# 安全写法
with open("结果.txt", "w", encoding="utf-8") as f:
    f.write("中文内容")

养成习惯,凡是涉及中文内容的文件读写,显式指定 encoding="utf-8"。


解决函数:safe_chinese_filename()

针对坑一,自己封一个替代 secure_filename() 的函数,有需要的可以直接拿去用。保留中文的同时过滤真正危险的字符:

python 复制代码
import re
import uuid

def safe_chinese_filename(filename: str) -> str:
    """
    安全处理文件名,支持中文。
    - 保留中文、英文、数字、点、下划线、连字符
    - 过滤路径穿越字符(/ \\ .. 等)
    - 文件名为空时自动生成 UUID 文件名
    """
    # 分离文件名和扩展名
    if "." in filename:
        name, ext = filename.rsplit(".", 1)
        ext = "." + re.sub(r"[^\w]", "", ext)  # 扩展名只保留字母数字
    else:
        name, ext = filename, ""

    # 只保留中文、字母、数字、下划线、连字符
    name = re.sub(r"[^\w\u4e00-\u9fff\-]", "_", name)
    name = name.strip("_")

    # 如果处理后为空,用 UUID 兜底
    if not name:
        name = uuid.uuid4().hex[:8]

    return name + ext


# 测试
print(safe_chinese_filename("产品图片.jpg"))        # 产品图片.jpg
print(safe_chinese_filename("../../../etc/passwd")) # ______etc_passwd
print(safe_chinese_filename(".jpg"))                # a1b2c3d4.jpg(UUID兜底)
print(safe_chinese_filename("hello world.png"))     # hello_world.png

Flask 中替换使用:

python 复制代码
# 原来
filename = secure_filename(file.filename)

# 替换为
filename = safe_chinese_filename(file.filename)

总结这些坑,并填上

问题 原因 解决方案
中文文件名上传后消失 secure_filename() 过滤非 ASCII 使用自定义 safe_chinese_filename()
中文路径报编码错误 Windows GBK 与 UTF-8 不一致 改用 pathlib.Path
中文写入文件乱码 open() 默认编码非 UTF-8 显式声明 encoding="utf-8"

这三个坑我都在同一个项目里踩过,整理出来希望能帮你少走弯路。如果你也在用 Flask 做文件上传,safe_chinese_filename() 这个函数直接拿去用就行。


有问题欢迎评论区交流 👇

相关推荐
2401_8858850412 分钟前
芬兰语音通知接口怎样对接?语音提醒接入流程
android·前端·python·php·语音识别
大风起兮云飞扬丶32 分钟前
Java——枚举
java·开发语言
小灰灰搞电子36 分钟前
Rust+Slint 实现高级动态消息提示框源码分享
开发语言·rust
Dev _39 分钟前
Python解释器安装在系统级目录导致命令行用户‌没有权限‌向该目录写入文件
开发语言·python
qq_406981031 小时前
大白话rust系列08 当变量遮蔽遇上引用,探究变量遮蔽的本质
开发语言·后端·rust
喵了几个咪1 小时前
GoWind ShortPlay 拆解:不用模型的推荐闭环——埋点回流、三路召回与已看去重
开发语言·后端·微服务·golang·gowind
谙弆悕博士1 小时前
用 C 写了个 Windows 聊天室,踩了几个多线程和控制台的坑
c语言·开发语言·c++·windows·网络编程·聊天室
Dev _2 小时前
PyCharm旧进程仍在后台运行,导致新实例无法启动或连接
ide·python·pycharm
Canmag 兴隆磁性2 小时前
回复磁导率 μ_R:永磁体在动态磁路中的关键参数
开发语言·学习·r语言·永磁材料·充磁·磁导率
CRMEB系统商城2 小时前
鲜花零售的预售与积分营销
开发语言·人工智能·科技·小程序·零售