目录
- 核心概念:Django 处理文件上传的两个关键部分
- 上传文件到服务器本地
- 步骤 1:修改
settings.py - 步骤 2:创建
forms.py(推荐方式) - 步骤 3:创建视图函数
- 步骤 4:创建模板(HTML)
- 步骤 5:配置 URL
- 完整示例与演示
- 步骤 1:修改
- 上传文件到云存储(以 AWS S3 为例)
- 为什么使用云存储?
- 准备工作:安装
boto3和配置 AWS - 修改
settings.py - 修改视图函数
- 完整示例与演示
- 重要注意事项
- 安全性
- 性能
- 文件命名
核心概念
Django 通过两个核心组件来处理文件上传:
request.FILES:这是一个类似于request.POST的字典对象,包含了所有通过 HTTP POST 请求上传的文件,在视图中,你可以通过request.FILES['your_file_input_name']来访问上传的文件。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 提供了极高的数据持久性。
- 可扩展性:无需担心服务器磁盘空间不足。
- 分离关注点:应用服务器和文件存储分离,架构更清晰。
- 成本效益:按需付费,通常比自建存储更便宜。
准备工作
- 拥有一个 AWS 账户。
- 创建一个 S3 bucket。
- 创建一个具有
s3:PutObject权限的 IAM 用户,并获取其 Access Key ID 和 Secret Access Key。
步骤 1:安装 boto3 和 django-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_SIZE和FILE_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 的文件上传功能!
