美文网首页django-rest-framework开发
[Python] Django + React的文件上传操作

[Python] Django + React的文件上传操作

作者: 敲代码的密斯想 | 来源:发表于2020-04-08 16:27 被阅读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())
    

    大功告成!

    相关文章

      网友评论

        本文标题:[Python] Django + React的文件上传操作

        本文链接:https://www.haomeiwen.com/subject/qubtmhtx.html