MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Python使用Flask与Django进行文件上传

2022-05-296.5k 阅读

Flask 文件上传

在 Flask 应用中实现文件上传,我们需要了解几个关键部分:HTML 表单的设置、Flask 路由处理函数的编写以及文件的保存逻辑。

1. HTML 表单设置

首先,我们需要创建一个 HTML 表单来让用户选择并上传文件。在 HTML 中,表单的 enctype 属性需要设置为 multipart/form-data,这是因为我们要上传文件,默认的 application/x-www-form-urlencoded 不能处理文件数据。

<!DOCTYPE html>
<html>

<head>
    <title>File Upload</title>
</head>

<body>
    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="file">
        <input type="submit" value="Upload">
    </form>
</body>

</html>

在上述表单中,input 标签的 typefilename 属性的值为 file,这个 name 值在 Flask 后端接收文件时会用到。表单的 action 属性指定了文件上传的目标路由为 /uploadmethodpost,因为上传文件通常使用 POST 方法。

2. Flask 路由处理函数

接下来,我们编写 Flask 应用的路由处理函数来接收并保存上传的文件。

from flask import Flask, request, render_template
import os

app = Flask(__name__)


@app.route('/')
def index():
    return render_template('upload.html')


@app.route('/upload', methods=['POST'])
def upload():
    file = request.files['file']
    if file:
        filename = os.path.join('uploads', file.filename)
        file.save(filename)
        return 'File uploaded successfully'
    else:
        return 'No file part'


if __name__ == '__main__':
    if not os.path.exists('uploads'):
        os.makedirs('uploads')
    app.run(debug=True)

在上述代码中:

  • 首先,我们导入了 Flaskrequestrender_templateFlask 是核心应用类,request 用于处理 HTTP 请求,render_template 用于渲染 HTML 模板。
  • 创建了 Flask 应用实例 app
  • 定义了根路由 /,它返回渲染的 upload.html 模板,这个模板就是我们上面创建的包含文件上传表单的 HTML。
  • 定义了 /upload 路由,它只接受 POST 方法。在处理函数中,通过 request.files['file'] 获取上传的文件,这里的 'file' 要与 HTML 表单中 input 标签的 name 属性值一致。
  • 检查文件是否存在,如果存在,我们使用 os.path.join 来构建文件保存路径,将文件保存到 uploads 目录下,目录名 uploads 可以根据需求自定义。如果目录 uploads 不存在,在程序启动时创建它。
  • 如果文件不存在,返回 'No file part'

3. 处理文件类型限制

有时候,我们可能需要限制用户上传的文件类型。可以通过检查文件扩展名来实现这一点。

from flask import Flask, request, render_template
import os

app = Flask(__name__)
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}


def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route('/')
def index():
    return render_template('upload.html')


@app.route('/upload', methods=['POST'])
def upload():
    file = request.files['file']
    if file and allowed_file(file.filename):
        filename = os.path.join('uploads', file.filename)
        file.save(filename)
        return 'File uploaded successfully'
    else:
        return 'Invalid file type'


if __name__ == '__main__':
    if not os.path.exists('uploads'):
        os.makedirs('uploads')
    app.run(debug=True)

在上述代码中,我们定义了 ALLOWED_EXTENSIONS 集合,包含允许的文件扩展名。allowed_file 函数用于检查文件名是否符合要求。它通过检查文件名中是否包含 . 以及扩展名是否在允许的集合中来判断。在 upload 处理函数中,调用 allowed_file 函数来验证文件类型,如果文件类型不允许,返回 'Invalid file type'

4. 处理大文件上传

默认情况下,Flask 对上传文件的大小有限制。如果要处理大文件上传,我们需要设置 MAX_CONTENT_LENGTH

from flask import Flask, request, render_template
import os

app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 16MB
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}


def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route('/')
def index():
    return render_template('upload.html')


@app.route('/upload', methods=['POST'])
def upload():
    file = request.files['file']
    if file and allowed_file(file.filename):
        filename = os.path.join('uploads', file.filename)
        file.save(filename)
        return 'File uploaded successfully'
    else:
        return 'Invalid file type or file too large'


if __name__ == '__main__':
    if not os.path.exists('uploads'):
        os.makedirs('uploads')
    app.run(debug=True)

在上述代码中,通过 app.config['MAX_CONTENT_LENGTH'] 设置了允许上传的最大文件大小为 16MB。如果上传的文件超过这个大小,Flask 会返回一个 413 Payload Too Large 错误。我们可以在 upload 处理函数中捕获这个错误并返回合适的提示信息,这里简单地在文件类型检查的 else 分支中增加了文件过大的提示。

Django 文件上传

在 Django 项目中实现文件上传同样涉及多个方面,包括模型定义、表单创建、视图处理以及模板设置。

1. 模型定义

首先,我们需要在 Django 的模型中定义文件字段。假设我们有一个 Article 模型,每个文章可以上传一个相关的文件。

from django.db import models


class Article(models.Model):
    title = models.CharField(max_length=100)
    file = models.FileField(upload_to='article_files')


在上述模型中,FileField 用于定义文件字段,upload_to 参数指定了文件上传后保存的目录,这里设置为 article_files,这个目录会在项目的媒体根目录下创建。

2. 表单创建

接下来,我们创建一个表单来处理文件上传。

from django import forms
from.models import Article


class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ['title', 'file']


在上述代码中,我们使用 ModelForm 来创建表单,它会根据 Article 模型自动生成表单字段。Meta 类指定了关联的模型为 Article,并指定了要包含在表单中的字段 titlefile

3. 视图处理

然后,我们编写视图函数来处理文件上传。

from django.shortcuts import render, redirect
from.forms import ArticleForm


def article_upload(request):
    if request.method == 'POST':
        form = ArticleForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return redirect('article_list')
    else:
        form = ArticleForm()
    return render(request, 'article_upload.html', {'form': form})


在上述视图函数中:

  • 首先检查请求方法是否为 POST,如果是,表示用户提交了表单。
  • 创建 ArticleForm 实例,传入 request.POST 数据和 request.FILES 文件数据。
  • 检查表单是否有效,如果有效,调用 form.save() 方法将数据保存到数据库,这里包括文件的保存,然后重定向到 article_list 视图(这里假设存在这个视图来展示文章列表)。
  • 如果请求方法不是 POST,创建一个空的表单实例,并将表单传递给模板 article_upload.html 进行渲染。

4. 模板设置

最后,我们创建模板来显示表单。

<!DOCTYPE html>
<html>

<head>
    <title>Article File Upload</title>
</head>

<body>
    <form method="post" enctype="multipart/form-data">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="Upload">
    </form>
</body>

</html>

在上述模板中:

  • 表单的 enctype 属性设置为 multipart/form-data,以支持文件上传。
  • {% csrf_token %} 用于防止跨站请求伪造攻击,这是 Django 表单必须的。
  • {{ form.as_p }} 将表单以段落形式渲染,展示出 titlefile 字段。

5. 配置媒体文件

为了让 Django 能够正确处理上传的文件,我们还需要在项目的配置文件 settings.py 中进行一些设置。

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')

在上述设置中:

  • MEDIA_URL 定义了媒体文件在浏览器中访问的 URL 前缀,这里设置为 /media/
  • MEDIA_ROOT 定义了媒体文件在服务器上保存的根目录,这里使用 os.path.join 结合项目的基础目录 BASE_DIR 创建了 media 目录。

此外,还需要在项目的主 urls.py 中添加媒体文件的 URL 映射。

from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('your_app.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

在上述代码中,通过 static 函数将媒体文件的 URL 映射添加到 urlpatterns 中,这样 Django 就可以正确处理媒体文件的请求。

6. 处理文件类型限制

与 Flask 类似,我们也可以在 Django 中限制上传文件的类型。可以通过在表单的 clean 方法中进行验证。

from django import forms
from.models import Article


class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ['title', 'file']

    def clean_file(self):
        file = self.cleaned_data.get('file')
        if file:
            allowed_extensions = ['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']
            ext = file.name.split('.')[-1].lower()
            if ext not in allowed_extensions:
                raise forms.ValidationError('Invalid file type')
        return file


在上述代码中,clean_file 方法用于验证 file 字段。它获取表单中上传的文件,检查文件扩展名是否在允许的列表中,如果不在,抛出 ValidationError 错误,提示文件类型无效。

7. 处理大文件上传

Django 也有对上传文件大小的限制。默认情况下,DATA_UPLOAD_MAX_MEMORY_SIZE 设置为 2.5MB。如果要处理更大的文件,可以在 settings.py 中增加或修改这个设置。

DATA_UPLOAD_MAX_MEMORY_SIZE = 16 * 1024 * 1024  # 16MB

上述设置将允许上传的最大文件大小设置为 16MB。如果上传文件超过这个大小,Django 会抛出 RequestDataTooBig 异常,我们可以在视图函数中捕获这个异常并进行相应处理,比如返回错误提示信息给用户。

通过以上步骤,我们详细介绍了在 Flask 和 Django 中实现文件上传的方法,包括基本的文件上传、文件类型限制以及大文件上传的处理。无论是 Flask 的轻量级灵活架构,还是 Django 的功能丰富的大型项目框架,都能很好地满足文件上传的需求,开发者可以根据项目的具体情况选择合适的框架来实现文件上传功能。