[Python] Django + React的文件上传操作
2020-04-08 本文已影响0人
敲代码的密斯想
1. 目的
想在发送邮件的过程中带上附件,查了公司邮件open API的文档,发现只需在调用接口时带上attachmentList参数,内容是文件id即可; 文件id可以通过现成接口(称为A)拿到,具体步骤就是调用接口A,上传文件(文件流的方式),即可在返回值中获取文件id,再将文件id放入邮件open API接口中即可。
把需求拆解一下,分为以下几块:1. [前端] 做一个上传文件的组件,需要 实现文件上传+获取文件上传后的返回值 的功能
2. [后端] 包一个文件上传接口,需要实现 接收文件+调用接口A上传文件+获取接口A返回值+返回结果 的功能
。
2. 前端代码
首先展示一下前端代码,这里用的是备受吐槽的飞冰UI组件+React框架。
这里用到了飞冰的上传文件组件
# render部分
<Upload.Dragger
listType='text'
action={uploadApi}
beforeUpload={this.handleAttachValidate}
onSuccess={this.handleUploadSuccess}
onRemove={this.handleRemoveFile}
onError={this.handleUploadError}
/>
# 代码实现
# 验证上传文件的格式以及文件个数
handleAttachValidate = (file) => {
let fileName = file.name
let returnFlag = true
let attachmentList = this.state.form.attachmentList
if (file.size >= 1048576) {
Message.error({
title: '文件大小不能超过1M',
align: 'cc cc'
});
returnFlag = false
}
attachmentList.forEach(function (item) {
if (item.name == fileName) {
Message.error({
title: '文件名有重复',
align: 'cc cc'
});
returnFlag = false
}
})
if (attachmentList.length >= 5) {
Message.error({
title: '附件个数不能大于5个',
align: 'cc cc'
});
returnFlag = false
}
return returnFlag
}
# 删除已上传的文件 (只需将文件id删除即可)
handleRemoveFile = (file) => {
let fileName = file.name
if (file.response != null && file.response.success && file.response.data.uploadRes) {
let attachmentPath = file.response.data.Attachment.AttachmentPath
let form= this.state.form
let index = -1
for (var i = 0; i < form.attachmentList.length; i++) {
if (form.attachmentList[i].name == fileName && form.attachmentList[i].AttachmentPath == attachmentPath) {
index = i
break
}
}
if (index > -1) {
form.attachmentList.splice(index, 1)
}
this.setState({ form: form})
}
}
# 上传失败的提示
handleUploadError = (file) => {
if (file.response != null && file.response.error != null) {
Message.error({
title: file.response.error.toString(),
align: 'cc cc'
})
}
}
# 上传成功后,将文件id填入表单
handleUploadSuccess = (file, value) => {
let res = file.response
let form= this.state.form
if (res.success && res.data.uploadRes) {
form.attachmentList.push({ name: file.name, AttachmentName: res.data.Attachment.AttachmentName, AttachmentPath: res.data.Attachment.AttachmentPath })
} else {
Message.error({
title: file.name.toString() + '上传失败',
align: 'cc cc'
});
}
this.setState({ form: form})
}
3. 后端代码
后端主要用的是Django,直接上代码吧
- controller层
@request_fixture #自己写的error controller装饰器
def post_attachment_upload(request):
"""
上传邮件附件
"""
if request.method != 'POST': # 规定必须是POST方法
return error_wrapper(type=ErrorCode.REQ_METHOD_ERROR)
else:
files = request.FILES.getlist('file', None) # Django获取文件的方式
if not files:
return error_wrapper(error='无上传文件')
else:
res = post_attachment_upload_impl(files)
if res.get('uploadRes'):
return success_wrapper(data=res)
else:
return error_wrapper(error=res.get('desc'))
- 业务代码
#!/usr/bin/python env
# -*- coding: utf-8 -*-
import os
import requests
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from app.common_app.constants import API
from utils.file_utils import file_to_bytes
def post_attachment_upload_impl(files):
file_name = str(files[0])
file_path = default_storage.save(file_name, ContentFile(files[0].read())) # 暂时存下文件,并获取文件路径
file_bytes = file_to_bytes(file_path) # 文件转码
os.remove(file_path) # 删除文件
body = dict(AttachmentList=[dict(AttachmentName=file_name, AttachmentContent=file_bytes)])
res = requests.post(url=API.MAIL_UPLOAD_ATTACHMENT, json=body).json()
if res.get('ResultCode') == 1:
if res.get('AttachmentList') and len(res.get('AttachmentList')) > 0:
return dict(uploadRes=True, Attachment=res.get('AttachmentList')[0], desc='上传成功')
else:
return dict(uploadRes=False, desc='上传失败')
else:
return dict(uploadRes=False, desc=res.get('ResultMsg'))
def file_to_bytes(file): # 文件转码成base64
with open(file, 'rb') as f:
s = f.read()
res = base64.b64encode(s)
return str(res.decode())
大功告成!