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

1. 目的

想在發(fā)送郵件的過(guò)程中帶上附件,查了公司郵件open API的文檔,發(fā)現(xiàn)只需在調(diào)用接口時(shí)帶上attachmentList參數(shù),內(nèi)容是文件id即可; 文件id可以通過(guò)現(xiàn)成接口(稱(chēng)為A)拿到,具體步驟就是調(diào)用接口A,上傳文件(文件流的方式),即可在返回值中獲取文件id,再將文件id放入郵件open API接口中即可。
把需求拆解一下,分為以下幾塊:1. [前端] 做一個(gè)上傳文件的組件,需要 實(shí)現(xiàn)文件上傳+獲取文件上傳后的返回值 的功能 2. [后端] 包一個(gè)文件上傳接口,需要實(shí)現(xiàn) 接收文件+調(diào)用接口A上傳文件+獲取接口A返回值+返回結(jié)果 的功能。

2. 前端代碼

首先展示一下前端代碼,這里用的是備受吐槽的飛冰UI組件+React框架。
這里用到了飛冰的上傳文件組件

# render部分
        <Upload.Dragger
                 listType='text'
                 action={uploadApi}
                 beforeUpload={this.handleAttachValidate}
                 onSuccess={this.handleUploadSuccess}
                 onRemove={this.handleRemoveFile}
                 onError={this.handleUploadError}
            />
# 代碼實(shí)現(xiàn)
    # 驗(yàn)證上傳文件的格式以及文件個(gè)數(shù)
    handleAttachValidate = (file) => {
        let fileName = file.name
        let returnFlag = true
        let attachmentList = this.state.form.attachmentList
        if (file.size >= 1048576) {
            Message.error({
                title: '文件大小不能超過(guò)1M',
                align: 'cc cc'
            });
            returnFlag = false
        }
        attachmentList.forEach(function (item) {
            if (item.name == fileName) {
                Message.error({
                    title: '文件名有重復(fù)',
                    align: 'cc cc'
                });
                returnFlag = false
            }
        })
        if (attachmentList.length >= 5) {
            Message.error({
                title: '附件個(gè)數(shù)不能大于5個(gè)',
                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  #自己寫(xiě)的error controller裝飾器
def post_attachment_upload(request):
    """
    上傳郵件附件
    """
    if request.method != 'POST':                 # 規(guī)定必須是POST方法
        return error_wrapper(type=ErrorCode.REQ_METHOD_ERROR)
    else:
        files = request.FILES.getlist('file', None)     # Django獲取文件的方式
        if not files:
            return error_wrapper(error='無(wú)上傳文件')
        else:
            res = post_attachment_upload_impl(files)
            if res.get('uploadRes'):
                return success_wrapper(data=res)
            else:
                return error_wrapper(error=res.get('desc'))
  • 業(yè)務(wù)代碼
#!/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()))    # 暫時(shí)存下文件,并獲取文件路徑

    file_bytes = file_to_bytes(file_path)        # 文件轉(zhuǎn)碼
    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):   # 文件轉(zhuǎn)碼成base64
    with open(file, 'rb') as f:
        s = f.read()
        res = base64.b64encode(s)
    return str(res.decode())

大功告成!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容