三十七、实战演练之接口自动化平台的文件上传
上传文件功能
上传文件功能主要针对需要测试上传文件的接口。原理是,把要测试上传的文件先上传到测试平台,然后把路径写入 用例中,后台真正测试时再将其进行上传。
一、上传文件模型
在testplans/models.py 模块中编写如下模型:
class UploadFile(models.Model):
"""文件上传"""
file = models.FileField(help_text='文件', verbose_name='文件')
info = models.JSONField(help_text='数据', verbose_name='数据', default=list,blank=True)
def str (self):
return self.file.name
class Meta:
db_table = 'upload_file'
verbose_name = ' 文 件 上 传 '
verbose_name_plural = verbose_name
django框架中上传文件可以使用FileField 字段,它保存的是上传文件的路径。
二、文件存储设置
默认情况下,文件上传后保存在MEDIA_ROOT 配置下的路径中。在配置文件中添加如下配置:
MEDIA_ROOT = BASE_DIR / 'upload_files'
然后,在项目根目录创建目录upload_files 。
三、上传文件接口设计
(1)文件上传
接口名称: /upload/
请求方式: POST
参数格式: form表单
请求参数:
参数 | 变量名 | 类型 | 说明 | 是否必传 |
文件 | file | 文件 | 上传文件 | 是 |
请求示例:
form格式参数
------WebKitFormBoundaryuB0zvJWw5yrFtxuU
Content-Disposition: form-data; name="file"; filename="hahaha.sql"
Content-Type: application/octet-stream
------WebKitFormBoundaryuB0zvJWw5yrFtxuU--
返回示例
响应状态码:201 响应数据:
{
"id":7,
"info": ["hahaha.sql","D:\\project\\backend\\upload_files\\hahaha.sql","application/octet-stream"]
}
(2)文件删除
接口名称: /upload/pk/
请求方式: DELETE
参数格式: 路径参数
请求参数: 无
返回示例
响应状态码:204 响应数据:无
(3)查看文件列表
接口名称: /upload/
请求方式: GET
参数格式: 无
请求参数:
参数 | 变量名 | 类型 | 说明 | 是否必传 |
项目id | project | 整数 | 项目id | 否 |
返回示例
响应状态码:200 响应数据:
[{
"id": 7,
"info": [
"hahaha.sql", "D:\\project\\backend\\upload_files\\hahaha.sql", "application/octet-stream"
]},
{
"id": 6,
"info": [
"hahaha.sql", "D:\\project\\backend\\upload_files\\hahaha.sql", "application/octet-stream"
]},
{
"id": 5,
"info": [
"hahaha.sql", "D:\\project\\backend\\upload_files\\hahaha.sql", "application/octet-stream"
]},
]
(4)查看文件
接口名称: /upload/pk/
请求方式: GET
参数格式: 路径参数
请求参数: 无
返回示例
响应状态码:200
响应数据:
{
"id": 7,
"info": [
"hahaha.sql", "D:\\project\\backend\\upload_files\\hahaha.sql", "application/octet-stream"
]
}
四、后端代码
(1)序列化器
class UploadFileSerializer(serializers.ModelSerializer):
"""文件上传序列化器"""
class Meta:
model = UploadFile
fields = '__all__'
extra_kwargs = {'file': {'write_only': True}, 'info': {'read_only': True}}
(2)视图
① 限制上传文件的大小、限制重复上传
复写 perform_create,获取文件的大小size和名字name。
前端会把我们这个文件传过来,传过来之后,我们后端通过self.request.data['file'] 就能拿到这个文件,然后下面有个属性size就能拿到他的大小,name拿到他的文件名字。
我们打个断点看下到底是咋实现的。
http://127.0.0.1:8000/upload/ 上传文件
可以看到,他是一个在内存里的uploadfile 一个上传文件,大小是57505,可以看到他的name和size、content_type属性等等。
判断文件大小,还要判断文件名是否存在(settings.MEDIA_ROOT 这个就是配置文件中的)。
因为上传文件的时候不需要输入参数info,因为你上传的时候还不知道参数info -文件具体参数是啥。所以在序列化器的时候把info设置为read_only = True。
② 删除本地保存的文件
先看下父类的 perform_destroy 是咋实现的。
调用instance.delete()之前先去删除本地保存的文件
具体代码附上:
class UploadFileViewSet(ModelViewSet):
"""文件上传视图"""
queryset = UploadFile.objects.all()
serializer_class = UploadFileSerializer
permission_classes = [IsAuthenticated]
def perform_create(self, serializer):
# 限制文件大小,文件重复
# 生成info数据
size = self.request.data['file'].size
name = self.request.data['file'].name
if size > 1024 * 300:
raise ValidationError(detail='上传的文件大小不可超过300KB')
if os.path.isfile(settings.MEDIA_ROOT / name):
raise ValidationError(detail=f'文件【{name}】已存在')
file_type = self.request.data['file'].content_type
file_path = str(settings.MEDIA_ROOT / name)
info = [name, file_path, file_type]
serializer.save(info=info)
def perform_destroy(self, instance):
"""文件删除"""
# 删除本地保存的文件
os.remove(instance.file.path)
instance.delete()
(3)路由
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter()
router.register('upload', views.UploadFileViewSet)
urlpatterns = router.urls