Django 框架的数据模型(models 类)中定义了 ImageField 和 FileField 等类型的字段,可以用来存储图片或者文件对象。ImageField 和 FileField 针对文件对象的属性和行为封装了易于使用的 API,配合 Django REST framework 提供的一系列组件,可以在编写很少量代码的情况下完成初步的文件上传功能。
各组件代码Models
from django.db import modelsclass FileModel(models.Model):name = models.CharField(max_length=50)file = models.FileField(upload_to='upload')Serializers
from .models import FileModelfrom rest_framework import serializersclass FileSerializer(serializers.ModelSerializer):class Meta:model = FileModelfields = '__all__'Views
from rest_framework import viewsetsfrom .models import FileModelfrom .serializers import FileSerializerclass FileViewSet(viewsets.ModelViewSet):queryset = FileModel.objects.all()serializer_class = FileSerializerUrls
from django.urls import path, includefrom .views import FileViewSetfrom rest_framework import routersrouter = routers.DefaultRouter()router.register(r'upload', FileViewSet)urlpatterns = [path('', include(router.urls)),]功能测试使用 HTTPie 工具利用 POST 方法以 Form 表单的形式(-f)提交上传的文件:
$ http -f POST http://127.0.0.1:8000/upload/ name="test" file@test.txtHTTP/1.1 201 Created{"file": "http://127.0.0.1:8000/upload/upload/test.txt","id": 4,"name": "test"}同时也可以直接访问 http://127.0.0.1:8000/upload/ ,通过 Django REST framework 提供的前端界面手动上传文件。
Django REST framework或者也可以自定义前端界面,HTML 上传页面示例代码:
Title二、FileField对于 FileField 类型的文件字段,后台的视图代码可以通过 requests.FILES 获取上传的文件数据。如 requests.FILES['file']。只有当请求方法为 POST 且前端的 带有属性 enctype="multipart/form-data" 时,request.FILES 才能接收到数据,否则为空。
FileField 字段的 upload_to 属性用于指定上传文件的保存位置,以 settings.py 中定义的 MEDIA_ROOT 为路径前缀。upload_to 属性可以接收包含 strftime() 格式的日期字符串(/%Y/%m/%d),用来定义类似 /year/month/day 格式的路径。如:
# 文件上传至类似 MEDIA_ROOT/uploads/2019/12/20 的路径下upload = models.FileField(upload_to='uploads/%Y/%m/%d/')可以使用 models 提供的查询接口获取已上传文件的相关信息:
$ python manage.py shell(InteractiveConsole)>>> from .models import FileModel>>> test = FileModel.objects.get(name='test') # 获取某一条数据记录>>> test.file # 数据记录关联的文件对象>>> test.file.name # 文件名'upload/test.txt'>>> test.file.url # 文件 URL'upload/test.txt'>>> test.file.size # 文件大小22>>> test.file.path # 文件路径'/home/starky/program/python/web/django/filestorage/media/upload/test.txt'通过数据模型检索到的文件(test.file)为 FieldFile 对象。FieldFile 类封装了一些便捷的 API 可以用来操作关联的底层文件,以下是一些简单的示例。
读取文件内容
>>> test = FileModel.objects.get(name='test')>>> test.file>>> with test.file.open('rb') as f:... f.read()...b'sdfsdfsdfweofssdnvdvs\n'写入新的内容
>>> with test.file.open('wb') as f:... f.write(b'Hello, World')...12>>> test.file.open().read()b'Hello, World'删除关联的底层文件
>>> test.file.delete()>>> test.file新建文件语法格式为 FieldFile.save(name, content, save=True)其中 content 参数必须接收 django.core.files.File 类或者其子类的实例,比如 ContentFile,不能直接使用 Python 内置的 file 对象。
不管是删除(FieldFile.delete())还是新建(FieldFile.save())文件,save 参数默认都为 True。即自动调用模型实例的 save() 方法提交对数据库的改动。
>>> testnew = FileModel(name='testnew', file=None)>>> testnew.file>>> from django.core.files.base import ContentFile>>> content = ContentFile('Hello, Django!')>>> testnew.file.save('testnew.txt', content)>>> testnew.file>>> testnew.file.open().read()b'Hello, Django!'三、FileUploadParserDjango REST framework 提供了 parsers.FileUploadParser 类,可以用来处理原始格式的文件内容的上传。后端获取到的 request.data 为字典结构,其中包含的 'file' 键对应的值即为上传的文件对象。
如果 FileUploadParser 类被包含 filename 参数的 URL 调用,则该参数会作为文件保存到服务端后的文件名。若 URL 中不包含 filename 参数,则客户端发起的请求必须包含 Content-Disposition 请求头及 filename 参数。如 Content-Disposition: attachment; filename=upload.jpg。
示例代码# views.pyfrom rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework.parsers import FileUploadParserclass FileUploadView(APIView):parser_classes = [FileUploadParser, ]def put(self, request, filename, format=None):file_obj = request.data['file']with open(filename, 'wb') as f:for chunk in file_obj.chunks():f.write(chunk)return Response(f'{filename} uploaded',status=204)# urls.pyfrom django.urls import re_pathurlpatterns = [re_path(r'^files/(?P[^/]+)$', views.FileUploadView.as_view()),]上传接口的 URL 为 http://xx.xx.xx.xx/files/ ,其中 用于指定上传成功后在服务器端的文件名。客户端使用 PUT 请求上传文件。
使用 postman 测试文件上传,截图如下:
postman前端上传代码示例如下(使用 jQuery,有可能出现跨域问题,可参考网上资料解决):
文件上传上传文件:
function doUpload() {$.ajax({url: 'http://xx.xx.xx.xx:8000/files/test.jpg',type: 'PUT',data: $('#files')[0].files[0],cache: false,processData: false,contentType: false,async: false}).done(function (res) {alert("上传成功")}).fail(function (res) {alert("上传失败:" + res)});}参考资料Managing filesmodels.FileFieldparsers