杰瑞科技汇

Django文件上传如何实现?

目录

  1. 核心概念:Django 处理文件上传的两个关键部分
  2. 上传文件到服务器本地
    • 步骤 1:修改 settings.py
    • 步骤 2:创建 forms.py(推荐方式)
    • 步骤 3:创建视图函数
    • 步骤 4:创建模板(HTML)
    • 步骤 5:配置 URL
    • 完整示例与演示
  3. 上传文件到云存储(以 AWS S3 为例)
    • 为什么使用云存储?
    • 准备工作:安装 boto3 和配置 AWS
    • 修改 settings.py
    • 修改视图函数
    • 完整示例与演示
  4. 重要注意事项
    • 安全性
    • 性能
    • 文件命名

核心概念

Django 通过两个核心组件来处理文件上传:

  1. request.FILES:这是一个类似于 request.POST 的字典对象,包含了所有通过 HTTP POST 请求上传的文件,在视图中,你可以通过 request.FILES['your_file_input_name'] 来访问上传的文件。
  2. FileField / ImageField:这是 Django 模型中用于存储文件路径的字段类型,当你将一个文件对象(来自 request.FILES)保存到模型的 FileField 时,Django 会自动处理文件在服务器上的存储,并将相对路径保存到数据库中。

实践一:上传文件到服务器本地

这是最基础的方式,文件直接保存在你的项目服务器上。

步骤 1:修改 settings.py

你需要告诉 Django 在哪里存储上传的文件,以及如何通过 URL 访问它们。

# myproject/settings.py
# 1. 定义上传文件的根目录
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
# 解释:
# MEDIA_URL: 浏览器访问上传文件的 URL 前缀。
# MEDIA_ROOT: 上传文件在服务器上的实际存储路径。
# BASE_DIR 是你的项目根目录,/path/to/your/project。
# 这会在你的项目根目录下创建一个名为 'media' 的文件夹。

步骤 2:创建 forms.py(推荐方式)

创建一个表单来处理文件上传,这样可以使视图更简洁。

# myapp/forms.py
from django import forms
class UploadFileForm(forms.Form):= forms.CharField(max_length=50)
    file = forms.FileField()

步骤 3:创建视图函数

这是处理上传逻辑的核心部分。

# myapp/views.py
from django.shortcuts import render, redirect
from .forms import UploadFileForm
def upload_file(request):
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            # 从表单中获取文件对象
            file = form.cleaned_data['file']
            # 获取文件名
            filename = file.name
            # 将文件保存到指定的 MEDIA_ROOT 路径下
            # Django 的 FileField 会自动处理路径,但这里我们手动演示
            # 注意:直接使用 file.save() 是更 Django 的方式,但为了清晰,我们先展示手动保存
            with open(f'media/{filename}', 'wb+') as destination:
                for chunk in file.chunks():
                    destination.write(chunk)
            # 保存到数据库(更推荐的方式)
            # from .models import MyModel
            # instance = MyModel(title=form.cleaned_data['title'], file=file)
            # instance.save() # 这会自动处理文件保存
            return redirect('success') # 上传成功后重定向
    else:
        form = UploadFileForm()
    return render(request, 'upload.html', {'form': form})
def success(request):
    return render(request, 'success.html')

更推荐的 Django 式做法: 如果你有一个模型,你应该将 request.FILES 中的文件直接赋值给模型的 FileField,然后调用 save(),Django 会自动处理文件保存和路径记录。

# myapp/models.py
from django.db import models
class Document(models.Model):= models.CharField(max_length=100)
    upload = models.FileField(upload_to='documents/') # 文件会保存在 MEDIA_ROOT/documents/ 目录下
# myapp/views.py (更新版)
from .models import Document
def upload_file_django_way(request):
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            # 直接创建模型实例并保存
            document = Document(title=form.cleaned_data['title'], upload=request.FILES['file'])
            document.save() # Django 会自动处理文件!
            return redirect('success')
    else:
        form = UploadFileForm()
    return render(request, 'upload.html', {'form': form})

upload_to='documents/' 指定了文件在 MEDIA_ROOT 下的子目录。

步骤 4:创建模板(HTML)

创建一个简单的模板来显示上传表单。

<!-- templates/upload.html -->
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">上传文件</title>
</head>
<body>
    <h2>上传文件</h2>
    <form method="post" enctype="multipart/form-data">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">上传</button>
    </form>
</body>
</html>

关键点<form> 标签必须有 enctype="multipart/form-data" 属性,否则文件数据无法正确发送。

<!-- templates/success.html -->
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">上传成功</title>
</head>
<body>
    <h2>文件上传成功!</h2>
    <a href="{% url 'upload_file' %}">返回上传页面</a>
</body>
</html>

步骤 5:配置 URL

配置 URL 路径来连接视图和模板。

# myproject/urls.py
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('upload/', include('myapp.urls')), # 假设应用名为 myapp
]
# 开发环境下,让 Django 提供媒体文件
# 这仅在开发时使用,生产环境应使用 Nginx 或其他 Web 服务器
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# myapp/urls.py
from django.urls import path
from . import views
urlpatterns = [
    path('file/', views.upload_file, name='upload_file'),
    path('success/', views.success, name='success'),
]

运行 python manage.py runserver,访问 http://127.0.0.1:8000/upload/file/ 就可以测试上传了,上传的文件会保存在 myproject/media/ 目录下。


实践二:上传文件到云存储(以 AWS S3 为例)

在生产环境中,直接将文件上传到应用服务器是不推荐的(扩展性差、备份麻烦),更好的方式是使用云存储服务,如 Amazon S3

为什么使用云存储?

  • 高可用性和持久性:S3 提供了极高的数据持久性。
  • 可扩展性:无需担心服务器磁盘空间不足。
  • 分离关注点:应用服务器和文件存储分离,架构更清晰。
  • 成本效益:按需付费,通常比自建存储更便宜。

准备工作

  1. 拥有一个 AWS 账户。
  2. 创建一个 S3 bucket。
  3. 创建一个具有 s3:PutObject 权限的 IAM 用户,并获取其 Access Key IDSecret Access Key

步骤 1:安装 boto3django-storages

boto3 是 AWS 的官方 SDK,django-storages 是一个 Django 后端,可以无缝地将 Django 的 FileField 连接到 S3。

pip install boto3 django-storages

步骤 2:修改 settings.py

添加 storages 配置。

# myproject/settings.py
INSTALLED_APPS = [
    # ...
    'storages', # 添加这一行
    # ...
]
# --- AWS S3 配置 ---
AWS_ACCESS_KEY_ID = 'YOUR_AWS_ACCESS_KEY_ID'
AWS_SECRET_ACCESS_KEY = 'YOUR_AWS_SECRET_ACCESS_KEY'
AWS_STORAGE_BUCKET_NAME = 'your-s3-bucket-name'
AWS_S3_REGION_NAME = 'your-bucket-region' # 'us-east-1'
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'
# 配置默认的文件和图片存储后端为 S3
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
# (可选) 为媒体文件和静态文件使用不同的 S3 前缀
# MEDIAFILES_LOCATION = 'media'
# STATICFILES_LOCATION = 'static'
# STATIC_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/static/'
# MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/media/'

步骤 3:修改视图函数和模型

你的代码几乎不需要改变!django-storages 会自动接管 FileField 的存储逻辑。

模型:确保你的模型有 FileField 并且设置了 upload_to

# myapp/models.py
from django.db import models
class Document(models.Model):= models.CharField(max_length=100)
    # 文件将直接上传到 S3 的 'documents/' 目录下
    upload = models.FileField(upload_to='documents/')

视图:视图代码与本地存储的 推荐做法 完全一样!你不需要做任何修改。

# myapp/views.py (与之前完全相同)
from .models import Document
from .forms import UploadFileForm
def upload_file(request):
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            document = Document(title=form.cleaned_data['title'], upload=request.FILES['file'])
            document.save() # 文件会自动上传到 S3!
            return redirect('success')
    else:
        form = UploadFileForm()
    return render(request, 'upload.html', {'form': form})

模板和 URL:也完全不需要修改。

当你保存模型实例时,Django 会通过 django-storages 后端,将文件流式传输到你的 S3 bucket 中,而不是本地服务器。


重要注意事项

安全性

  • 验证文件类型:永远不要信任客户端传来的文件扩展名,检查文件的真实内容(MIME 类型)。

    from django.core.exceptions import ValidationError
    from django.utils import timezone
    import os
    def validate_file_extension(value):
        ext = os.path.splitext(value.name)[1]
        valid_extensions = ['.pdf', '.docx', '.jpg', '.png']
        if not ext.lower() in valid_extensions:
            raise ValidationError('不支持的文件类型。')

    然后在你的 FileField 中使用它:upload = models.FileField(upload_to='documents/', validators=[validate_file_extension])

  • 限制文件大小:在 settings.py 中设置 DATA_UPLOAD_MAX_MEMORY_SIZEFILE_UPLOAD_MAX_MEMORY_SIZE

  • 病毒扫描:对于上传的文件,特别是用户可执行文件,应进行病毒扫描。

性能

  • 流式上传:对于大文件,使用 file.chunks() 进行分块写入,而不是一次性读取整个文件到内存,Django 的 FileField 默认会这样做,但如果你手动处理文件,请务必使用 chunks()
  • 异步上传:对于非常大的文件,可以考虑使用 JavaScript 客户端库(如 Uppy, Dropzone.js)进行分片上传,这可以提供更好的用户体验,并且可以绕过服务器的请求超时限制。

文件命名

  • 避免文件名冲突:如果多个用户上传同名文件,后一个会覆盖前一个,使用 uuid 或时间戳来生成唯一的文件名是一个好习惯。

    import uuid
    from django.utils import timezone
    def get_file_path(instance, filename):
        # instance 是模型实例
        # filename 是原始文件名
        # 返回一个唯一的路径, documents/2025/10/27/filename_uuid.ext
        ext = filename.split('.')[-1]
        filename = f"{filename.split('.')[0]}_{uuid.uuid4().hex}.{ext}"
        return os.path.join('documents', timezone.now().strftime('%Y/%m/%d'), filename)
    class Document(models.Model):
        upload = models.FileField(upload_to=get_file_path)

希望这份详细的指南能帮助你完全掌握 Django 的文件上传功能!

分享:
扫描分享到社交APP
上一篇
下一篇