使用HTTP POST向Python Flask服务器上传文件时文件名特殊字符丢失
我来帮你搞定这个问题!你上传的2^8.txt到Flask后端后变成了28.txt,核心问题出在Flask的secure_filename函数上,还有你之前用encodeURI()找错了方向——因为FormData里的文件名不是URL参数,所以这个编码方法根本不起作用。
问题根源
secure_filename是Werkzeug(Flask依赖的工具库)提供的安全处理函数,它会过滤掉所有它认为"不安全"的字符,其中就包括^这类符号。它的设计目的是防止恶意文件名(比如包含路径遍历的../)带来的安全风险,但同时也会误杀一些你需要的特殊字符。
三种解决方案
方案一:直接使用原始文件名(简单但需注意安全)
如果你的业务场景可以接受直接使用用户上传的原始文件名,完全可以跳过secure_filename的处理:
if request.method == 'POST': if 'file' not in request.files: flash('No file part') return jsonify(status="fail") file = request.files['file'] # 直接取原始文件名,不做过滤 filename = file.filename print(filename) # 现在会正确输出2^8.txt
⚠️ 注意:这种方式要警惕安全风险,比如用户上传../../malware.exe这类文件名,可能会导致文件被存到不该存的位置,建议额外加一层路径校验。
方案二:自定义安全文件名过滤规则
如果需要保留^这类字符,同时过滤真正危险的路径字符,可以自己实现一个过滤函数,替代secure_filename:
import re from flask import request, flash, jsonify def custom_secure_filename(filename): # 只过滤掉路径相关的危险字符(比如/、\、..等),保留^、_、-、.等安全符号 # 把所有不符合规则的字符替换成下划线 return re.sub(r'[^\w\-\.^]', '_', filename) if request.method == 'POST': if 'file' not in request.files: flash('No file part') return jsonify(status="fail") file = request.files['file'] filename = custom_secure_filename(file.filename) print(filename) # 输出2^8.txt
这个函数只移除那些可能导致路径遍历的危险字符,同时保留你需要的^符号,兼顾安全和需求。
方案三:前端编码+后端解码
如果不想改动后端的secure_filename逻辑,也可以在前端对文件名进行URL编码,后端再解码还原:
- 前端代码修改:
var fileSelect = document.getElementById('myfile'); var files = fileSelect.files; var formData = new FormData(); var file = files[0]; // 用encodeURIComponent编码文件名,而不是encodeURI var encodedFilename = encodeURIComponent(file.name); formData.append('file', file, encodedFilename);
- 后端代码修改:
from urllib.parse import unquote from flask import request, flash, jsonify if request.method == 'POST': if 'file' not in request.files: flash('No file part') return jsonify(status="fail") file = request.files['file'] # 先解码文件名,再按需处理 filename = unquote(file.filename) print(filename) # 输出2^8.txt
这里要注意用encodeURIComponent而不是encodeURI,前者会编码所有特殊字符(包括^),后端用unquote就能完美还原。
总结
最推荐的是方案二,既能保留你需要的特殊字符,又能保证基本的安全。如果追求简单,方案一也可以,但一定要做好安全校验。
内容的提问来源于stack exchange,提问作者zifan yan

