flask处理文件上传
在Flask框架中,你可以使用request.files
对象来处理文件上传。以下是一个简单的文件上传的示例:
from flask import Flask, request
from werkzeug.utils import secure_filename
import os
app = Flask(__name__)
# 定义文件上传的路径
UPLOAD_FOLDER = '/path/to/upload'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
# 检查是否有文件被上传
if 'file' not in request.files:
return '没有文件被上传'
file = request.files['file']
# 如果用户没有选择文件,浏览器可能会提交一个空文件,需要检查一下
if file.filename == '':
return '没有选择文件'
# 文件检查通过后,保存文件
if file:
# 使用 Werkzeug 的 secure_filename 来确保文件名是安全的
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return '文件已成功上传'
return '''
<!doctype html>
<title>Upload new File</title>
<h1>Upload new File</h1>
<form method=post enctype=multipart/form-data>
<input type=file name=file>
<input type=submit value=Upload>
</form>
'''
if __name__ == '__main__':
app.run(debug=True)
在上述代码中,我们首先从flask和werkzeug.utils导入所需的模块。然后我们创建了一个Flask应用实例,并设置了文件上传路径。然后我们创建了一个路由/upload,这个路由接受GET和POST请求。
处理多个文件上传:
from flask import request
from werkzeug.utils import secure_filename
@app.route('/upload', methods=['POST'])
def upload_files():
# `request.files` 是一个字典,包含了所有上传的文件
for file_key in request.files:
file = request.files[file_key]
# 使用 `secure_filename` 函数来确保文件名是安全的
filename = secure_filename(file.filename)
# 保存文件到服务器
file.save(os.path.join('uploads', filename))
return 'Files uploaded successfully'
当我们接收到POST请求时,我们首先检查是否有文件在请求中。如果没有文件,我们就返回错误消息。如果有文件,我们使用Werkzeug的secure_filename
函数来确保文件名是安全的,然后我们保存文件。
最后,当我们接收到GET请求时,我们返回一个HTML表单,用户可以通过这个表单来上传文件。
这里需要自己创建并指定文件上传的路径(在代码中为UPLOAD_FOLDER
),并确保应用有权限写入此路径。
Werkzeug的secure_filename
函数
werkzeug.utils.secure_filename
是一个安全功能,它确保用户上传的文件名不会导致安全问题。
这个函数首先检查并移除文件名中可能导致问题的特殊字符,例如路径分隔符(/
或 \
)和空格,从而防止目录遍历攻击(这种攻击通过在文件名中包含路径分隔符来试图访问不应该被访问的文件系统路径)。
例如,如果有人尝试上传一个名为"…/…/…/etc/passwd"的文件,secure_filename
函数将会清理文件名,结果可能就变成了"etc_passwd",这样就防止了可能的安全问题。
另外,它还确保文件名仅包含ASCII字符,这样就避免了由于Unicode字符可能导致的一些编码问题。
下面是一个例子:
from werkzeug.utils import secure_filename
filename = secure_filename("../../etc/passwd")
print(filename) # 输出: etc_passwd
所以,使用secure_filename
函数是一个很好的实践,能确保用户上传的文件名不会导致文件系统或安全问题。
html文件上传例子
<!DOCTYPE html>
<html>
<body>
<h2>File Upload</h2>
<form action="http://localhost:5000/upload" method="post" enctype="multipart/form-data">
Select file to upload:
<input type="file" name="file" id="file">
<input type="submit" value="Upload" name="submit">
</form>
</body>
</html>
在这个HTML代码中,我们创建了一个表单,其中包含一个文件输入元素和一个提交按钮。enctype="multipart/form-data"
属性是必需的,如果你想要在表单中包含文件上传控件。
action="http://localhost:5000/upload"
指定了表单提交后的处理URL。当你点击"Upload"按钮后,文件将被发送到该URL。请将该URL替换为你的Flask应用接受文件上传的实际URL。
在此示例中假设Flask应用正在本地运行,并监听5000端口,且你已经设置了一个名为/upload
的路由来处理文件上传。
enctype=“multipart/form-data”
在HTML中,当你创建一个表单用于提交数据到服务器时,需要决定如何将这些数据编码和打包到HTTP请求中。这是由表单的 enctype
属性决定的。
enctype
属性决定了表单数据在提交时如何编码。它有三个可能的值:
application/x-www-form-urlencoded
:这是默认值,所有的字符都会被编码(空格转换为 “+” 加号,特殊符号转换为 ASCII HEX 值)。这种编码方式适用于简单的表单,只包含文本字段。multipart/form-data
:这种编码方式不会对字符进行编码,这意味着你可以上传二进制文件。这是文件上传时必须使用的编码方式。text/plain
:这种编码方式会将空格转换为 “+” 加号,但不对特殊字符编码。这种编码方式不常用。
当你设置 enctype="multipart/form-data"
时,意味着你告诉浏览器应该使用 multipart/form-data
格式来编码表单数据。这个格式每个部分都是一个数据块,这些数据块可以包含二进制数据(例如文件内容),因此可以用来上传文件。每个部分都有自己的头部信息,描述了数据的类型、文件名等信息。这就是为什么你可以在一个表单中同时上传多个文件,每个文件作为请求主体中的一个部分。
当你使用 multipart/form-data
格式提交表单数据时,数据会被划分为多个部分或块。每一部分都代表了表单中的一个字段。例如,如果你的表单有两个文本字段和一个文件字段,那么数据就会被划分为三个部分。
每个部分都有自己的头部信息和主体内容。头部信息描述了这部分数据的类型和其他元数据。例如,头部信息可能包含Content-Disposition
字段,该字段描述了这部分数据是一个表单字段,并提供了字段的名称和(如果这是一个文件字段的话)文件名。头部信息还可能包含Content-Type
字段,该字段描述了数据的媒体类型(例如,文本、JPEG图片等)。
主体内容是这部分数据的实际内容。对于文本字段,主体内容就是用户输入的文本。对于文件字段,主体内容就是文件的二进制数据。
这是一个multipart/form-data
请求的简化示例:
POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data;boundary="boundary"
--boundary
Content-Disposition: form-data; name="text"
hello
--boundary
Content-Disposition: form-data; name="file"; filename="example.txt"
Content-Type: text/plain
Hello, World!
--boundary--
在这个示例中,数据被划分为两个部分。第一部分是一个名为"text"的文本字段,内容是"hello"。第二部分是一个名为"file"的文件字段,文件名是"example.txt",内容是"Hello, World!“。每个部分都由一个分界符(”–boundary")和自己的头部信息开始。请求以一个带有两个短划线的分界符(“–boundary–”)结束。