一、基于 Django models
Django 框架的数据模型(models
类)中定义了 ImageField
和 FileField
等类型的字段,可以用来存储图片或者文件对象。ImageField
和 FileField
针对文件对象的属性和行为封装了易于使用的 API,配合 Django REST framework 提供的一系列组件,可以在编写很少量代码的情况下完成初步的文件上传功能。
各组件代码
Models1
2
3
4
5from django.db import models
class FileModel(models.Model):
name = models.CharField(max_length=50)
file = models.FileField(upload_to='upload')
Serializers1
2
3
4
5
6
7
8from .models import FileModel
from rest_framework import serializers
class FileSerializer(serializers.ModelSerializer):
class Meta:
model = FileModel
fields = '__all__'
Views1
2
3
4
5
6
7from rest_framework import viewsets
from .models import FileModel
from .serializers import FileSerializer
class FileViewSet(viewsets.ModelViewSet):
queryset = FileModel.objects.all()
serializer_class = FileSerializer
Urls1
2
3
4
5
6
7
8
9
10from django.urls import path, include
from <appname>.views import FileViewSet
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'upload', FileViewSet)
urlpatterns = [
path('', include(router.urls)),
]
功能测试
使用 HTTPie 工具利用 POST 方法以 Form 表单的形式(-f
)提交上传的文件:1
2
3
4
5
6
7
8$ http -f POST http://127.0.0.1:8000/upload/ name="test" file@test.txt
HTTP/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 提供的前端界面手动上传文件。
或者也可以自定义前端界面,HTML 上传页面示例代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://xx.xx.xx.xx:8000/upload/" method="post" enctype="multipart/form-data">
<input type="text" name="name" placeholder="name"><br>
<input type="file" name="file"><br>
<input type="submit" value="submit">
</form>
</body>
</html>
二、FileField
对于 FileField
类型的文件字段,后台的视图代码可以通过 requests.FILES
获取上传的文件数据。如 requests.FILES['file']
。
只有当请求方法为 POST 且前端的 <form>
带有属性 enctype="multipart/form-data"
时,request.FILES
才能接收到数据,否则为空。
FileField
字段的 upload_to
属性用于指定上传文件的保存位置,以 settings.py
中定义的 MEDIA_ROOT
为路径前缀。upload_to
属性可以接收包含 strftime()
格式的日期字符串(/%Y/%m/%d
),用来定义类似 /year/month/day
格式的路径。如:1
2# 文件上传至类似 MEDIA_ROOT/uploads/2019/12/20 的路径下
upload = models.FileField(upload_to='uploads/%Y/%m/%d/')
可以使用 models 提供的查询接口获取已上传文件的相关信息:1
2
3
4
5
6
7
8
9
10
11
12
13
14$ python manage.py shell
(InteractiveConsole)
from <appname>.models import FileModel
'test') # 获取某一条数据记录 test = FileModel.objects.get(name=
# 数据记录关联的文件对象 test.file
<FieldFile: upload/test.txt>
# 文件名 test.file.name
'upload/test.txt'
# 文件 URL test.file.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 可以用来操作关联的底层文件,以下是一些简单的示例。
读取文件内容1
2
3
4
5
6
7'test') test = FileModel.objects.get(name=
test.file
<FieldFile: upload/test.txt>
with test.file.open('rb') as f:
f.read()
...
b'sdfsdfsdfweofssdnvdvs\n'
写入新的内容1
2
3
4
5
6with test.file.open('wb') as f:
b'Hello, World') f.write(
...
12
test.file.open().read()
b'Hello, World'
删除关联的底层文件1
2
3 test.file.delete()
test.file
<FieldFile: None>
新建文件
语法格式为 FieldFile.save(name, content, save=True)
其中 content
参数必须接收 django.core.files.File
类或者其子类的实例,比如 ContentFile
,不能直接使用 Python 内置的 file 对象。
不管是删除(FieldFile.delete()
)还是新建(FieldFile.save()
)文件,save
参数默认都为 True
。即自动调用模型实例的 save()
方法提交对数据库的改动。
1 | 'testnew', file=None) testnew = FileModel(name= |
三、FileUploadParser
Django REST framework 提供了 parsers.FileUploadParser
类,可以用来处理原始格式的文件内容的上传。后端获取到的 request.data
为字典结构,其中包含的 'file'
键对应的值即为上传的文件对象。
如果 FileUploadParser
类被包含 filename
参数的 URL 调用,则该参数会作为文件保存到服务端后的文件名。若 URL 中不包含 filename
参数,则客户端发起的请求必须包含 Content-Disposition
请求头及 filename
参数。如 Content-Disposition: attachment; filename=upload.jpg
。
示例代码
1 | # views.py |
1 | # urls.py |
上传接口的 URL 为 http://xx.xx.xx.xx/files/<filename>
,其中 <filenmae>
用于指定上传成功后在服务器端的文件名。客户端使用 PUT
请求上传文件。
使用 postman 测试文件上传,截图如下:
前端上传代码示例如下(使用 jQuery,有可能出现跨域问题,可参考网上资料解决):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<html>
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form>
<p>上传文件: <input type="file" name="files" id='files' /></p>
<input type="button" value="上传" onclick="doUpload()" />
</form>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script type="text/javascript">
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)
});
}
</script>
</body>
</html>