vue+django实现下载文件

发布时间:2021-03-02 09:33:28编辑:admin阅读(3027)

    一、概述

    在项目中,点击下载按钮,就可以下载文件。

    传统的下载链接一般是get方式,这种链接是公开的,可以任意下载。

    在实际项目,某些下载链接,是私密的。必须使用post方式,传递正确的参数,才能下载。

     

    二、django项目

    本环境使用django 3.1.5,新建项目download_demo

    1.png

     

    安装模块

    pip3 install djangorestframework django-cors-headers

     

     修改文件download_demo/settings.py

    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
    ]

     

    注册中间件

    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_CREDENTIALS = True
    CORS_ORIGIN_ALLOW_ALL = True
    
    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',
    )

     

    修改download_demo/urls.py

    from django.contrib import admin
    from django.urls import path
    from api import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('download/excel/', views.ExcelFileDownload.as_view()),
    ]

     

    修改api/views.py

    from django.shortcuts import render,HttpResponse
    from download_demo import settings
    from django.utils.encoding import escape_uri_path
    from django.http import StreamingHttpResponse
    from django.http import JsonResponse
    from rest_framework.views import APIView
    from rest_framework import status
    import os
    
    class ExcelFileDownload(APIView):
        def post(self,request):
            print(request.data)
            # filename = "大江大河.xlsx"
            filename = request.data.get("filename")
            download_file_path = os.path.join(settings.BASE_DIR, "upload",filename)
            print("download_file_path",download_file_path)
    
            response = self.big_file_download(download_file_path, filename)
            if response:
                return response
    
            return JsonResponse({'status': 'HttpResponse', 'msg': 'Excel下载失败'})
    
        def file_iterator(self,file_path, chunk_size=512):
            """
            文件生成器,防止文件过大,导致内存溢出
            :param file_path: 文件绝对路径
            :param chunk_size: 块大小
            :return: 生成器
            """
            with open(file_path, mode='rb') as f:
                while True:
                    c = f.read(chunk_size)
                    if c:
                        yield c
                    else:
                        break
    
        def big_file_download(self,download_file_path, filename):
            try:
                response = StreamingHttpResponse(self.file_iterator(download_file_path))
                # 增加headers
                response['Content-Type'] = 'application/octet-stream'
                response['Access-Control-Expose-Headers'] = "Content-Disposition, Content-Type"
                response['Content-Disposition'] = "attachment; filename={}".format(escape_uri_path(filename))
                return response
            except Exception:
                return JsonResponse({'status': status.HTTP_400_BAD_REQUEST, 'msg': 'Excel下载失败'},
                                    status=status.HTTP_400_BAD_REQUEST)


     

    在项目根目录创建upload文件

    1.png

     

     里面放一个excel文件,比如:大江大河.xlsx

     

    三、vue项目

    新建一个vue项目,安装ElementUI 模块即可。

    新建test.vue

    <template>
      <div style="width: 70%;margin-left: 30px;margin-top: 30px;">
        <el-button class="filter-item" type="success" icon="el-icon-download" @click="downFile()">下载</el-button>
      </div>
    </template>
    
    <script>
      import axios from 'axios'
    
      export default {
        data() {
          return {
          }
        },
        mounted: function() {
    
        },
        methods: {
          downloadFile(url, options = {}){
            return new Promise((resolve, reject) => {
              // console.log(`${url} 请求数据,参数=>`, JSON.stringify(options))
              // axios.defaults.headers['content-type'] = 'application/json;charset=UTF-8'
              axios({
                method: 'post',
                url: url, // 请求地址
                data: options, // 参数
                responseType: 'blob' // 表明返回服务器返回的数据类型
              }).then(
                response => {
                  // console.log("下载响应",response)
                  resolve(response.data)
                  let blob = new Blob([response.data], {
                    type: 'application/vnd.ms-excel'
                  })
                  // console.log(blob)
                  // let fileName = Date.parse(new Date()) + '.xlsx'
                  // 切割出文件名
                  let fileNameEncode = response.headers['content-disposition'].split("filename=")[1];
                  // 解码
                  let fileName = decodeURIComponent(fileNameEncode)
                  // console.log("fileName",fileName)
                  if (window.navigator.msSaveOrOpenBlob) {
                    // console.log(2)
                    navigator.msSaveBlob(blob, fileName)
                  } else {
                    // console.log(3)
                    var link = document.createElement('a')
                    link.href = window.URL.createObjectURL(blob)
                    link.download = fileName
                    link.click()
                    //释放内存
                    window.URL.revokeObjectURL(link.href)
                  }
                },
                err => {
                  reject(err)
                }
              )
            })
          },
          // 下载文件
          downFile(){
            let postUrl= "http://127.0.0.1:8000/download/excel/"
            let params = {
              filename: "大江大河.xlsx",
            }
            // console.log("下载参数",params)
            this.downloadFile(postUrl,params)
          },
        }
      }
    </script>
    
    <style>
    </style>


    注意:这里使用post请求,并将filename传输给api,用来下载指定的文件。 

     

    访问测试页面,点击下载按钮

    1.png

     

     就会自动下载

    1.png

     

    打开工具栏,查看响应信息

    1.png

     

    这里,就是django返回的文件名,浏览器下载保存的文件名,也是这个。

    遇到中文,会进行URLcode编码。

    所以在vue代码中,对Content-Disposition做了切割,得到了文件名。


关键字