vue + djangorestframework實現文件下載功能

1.安裝模塊及配置及配置

先安裝django-cors-headers包

pip3 install djangorestframework django-cors-headers

 

在setting文件中註冊app

INSTALLED_APPS = [
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'api.apps.ApiConfig',
 'corsheaders', # 注册应用cors
]

 

在setting文件中註冊中間件

MIDDLEWARE = [
 'django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'corsheaders.middleware.CorsMiddleware', # 注册组件cors
]

 

配置置請求方法與請求頭部信息

CORS_ALLOW_METHODS = (
 'GET',
 'OPTIONS',
 'PATCH',
 'POST',
 'VIEW',
)

CORS_ALLOW_HEADERS = (
 'XMLHttpRequest',
 'X_FILENAME',
 'accept-encoding',
 'authorization',
 'content-type',
 'dnt',
 'origin',
 'user-agent',
 'x-csrftoken',
 'x-requested-with',
 'Pragma',
)

 

2.添加路由與方法

路由

from django.contrib import admin
from django.urls import path
from api import views

urlpatterns = [
 path('admin/', admin.site.urls),
 path('downloadFile/', views.Download.as_view()),
]

 

視圖

class Download(DispatchMethod,APIView):

    def post(self, request):
        file_name = request.data.get('file_name')
        file_path = request.data.get('file_path')
        connKey = request.data.get("connKey")

        hostname = request.data.get("hostname")
        login_key = request.data.get("login_key")
        program_id = request.data.get("program_id")
        stime = str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))

        language = getLanguage(request)
        data = checkToken(request)
        if data.get("status") > 0:
            bu = UmbResourceBll(connKey)
            data = bu.getFileObj(file_name,file_path,language)
            WriteApiLog(stime, program_id, login_key, hostname, "[method:post]", self)
            if data.get("status"):
                return JsonResponse(data, status=200)
            return data
        else:
            return JsonResponse(data, status=401)

class UmbResourceBll(object):

    def __init__(self, connkey=k_ov.local_db_id):
        self.dbConnKey = baseUtils.checkConnkey(connkey)
        self.ud = UmbResourceDal()
        self.ulang = langUtils()

    def response_param(self, response_code, response_msg, language=None, error_log=None):
        if language:
            if response_code == -99:
                sMessage = self.ulang.getMSG(response_msg, language) + str(error_log)
            else:
                sMessage = self.ulang.getMSG(response_msg, language)
        else:
            sMessage = response_msg
        return response_code, sMessagedef getFileObj(self, file_name, file_path, language):
        status = 0
        sMessage = ""
        rows = []
        try:
            # StreamingHttpResponse 流式響應,内容的迭代器形式,以内容流的方式响应
            file_obj = StreamingHttpResponse(self.file_iterator(file_path))
            file_obj['Content-Type'] = 'application/octet-stream'
            file_obj['Access-Control-Expose-Headers'] = "Content-Disposition, Content-Type"
            # escape_uri_path防止文件是中文名出現亂碼
            file_obj['Content-Disposition'] = "attachment; filename={}".format(escape_uri_path(file_name))

            # status, sMessage = self.response_param(2, 'UW_0120', language)
            return file_obj

        except Exception as e:
            status, sMessage = self.response_param(-99, 'AM_0008', language, error_log=e)
            return {"rows": rows, "status": status, "message": sMessage}

    @staticmethod
    def file_iterator(file_path, chunk_size=512):
        """
        讀出文件 以rb的形式
        文件生成器,防止文件過大,導致內存溢出
        :param file_path: 文件絕對路勁
        :param chunk_size: 每次讀出的大小
        :return:
        """
        with open(file_path, 'rb') as f:
            while True:
                content = f.read(chunk_size)
                if content:
                    yield content
                else:
                    break

 

3.前端展示及獲取邏輯

template
<template>
    <div class="right-button">
        <div class="right-button">
            <el-row style="margin-left:10px">
                 <el-button size="mini" icon="el-icon-view" type="primary" @click="showFiles">{{$t("視圖")}}</el-button>
                 <el-button size="mini" icon="el-icon-folder-opened"  @click="downloadFile" type="success">{{$t("下載")}}</el-button>
            </el-row>
        </div>
    </div>  
</template>

 

script

     // 下載指定的文件
        downloadFile(){
            // 從session中獲取被點擊的主table行數據
            let current_row = window.sessionStorage.getItem("nowRow")

            current_row = eval('('+current_row+')')
            if(!current_row.file_name){
                return
            }

            DownloadFileParames.file_name = current_row.file_name
            DownloadFileParames.file_path = current_row.file_path
            DownloadFileParames.connKey = this.$store.state.now_conn_key
            let file_type

            return new Promise((resolve, reject) =>{

                this.$http.post(apiDownloadFile,DownloadFileParames,{
                    "headers": { 'Authorization': "jwt " + window.localStorage.getItem("token") },
                    "responseType": "blob"
                }).then(

                    response => {
                        resolve(response.data)

                        // 根據文件後綴設置文件流的文件類型
                        if(DownloadFileParames.file_name.endsWith("pdf")){
                            file_type = "application/pdf"
                        }else if(DownloadFileParames.file_name.endsWith("mp4")){
                            file_type = "video/mpeg4"
                        }

                        let blob = new Blob([response.data], {
                            type:file_type
                        })

                        // 文件名
                        let fileNameEncode = response.headers['content-disposition'].split("filename=")[1]

                        // 解码 函数可把字符串作为 URI 组件进行解码
                        let fileName = decodeURIComponent(fileNameEncode)

                        //  msSaveBlob和msSaveOrOpenBlob 方法允许用户在客户端上保存文件
                        // window.navigator 对象包含有关访问者浏览器的信息
                        if (window.navigator.msSaveOrOpenBlob) {
                            navigator.msSaveBlob(blob, fileName)
                        } else {
                            // 創建a標籤
                            var link = document.createElement('a')

                            // 設置a標籤鏈接
                            link.href = window.URL.createObjectURL(blob)

                            // 將文件名賦值給a標籤的下載屬性并觸發點擊
                            link.download = fileName
                            link.click()

                            //释放内存
                            window.URL.revokeObjectURL(link.href)
                        }
                    }, err => {
                        reject(this.$message({
                            showClose: false,
                            message: res.data.message,
                            type: 'error',
                            duration: 2000    
                        }))
                    }
                )
            })
        }

 

 

posted @ 2023-03-15 17:56  仗剑煮大虾  阅读(52)  评论(0编辑  收藏  举报