django-rest-framework开发

[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,直接上代码吧

@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())

大功告成!

上一篇下一篇

猜你喜欢

热点阅读