Flask+vue+axios实现流式zip包传输

Flask+vue+axios实现流式zip包传输

1. Flask部分

1.1 创建压缩包

class DirPackage:

    @staticmethod
    def pack_zip(from_path, to_path):
        """打包指定路径到某目录下面
            :param from_path: 打包路径
            :param to_path: .zip结尾的文件存放位置
        """
        z = zipfile.ZipFile(to_path, 'w', zipfile.ZIP_DEFLATED)
        for path, dirnames, filenames in os.walk(from_path):
            fpath = path.replace(from_path, '')
            for filename in filenames:
                encode_name = filename.encode('utf-8').decode('utf-8')
                z.write(os.path.join(path, filename),
                        os.path.join(fpath, encode_name))
        z.close()
        (file_path, file_name) = os.path.split(to_path)
        return file_name

1.2 封装流式传输部分

class DownloadService:
    @staticmethod
    def download_zip_file(saver: DownloadDirFactory, user_name: str, token: str, **kwargs):
        zip_file = os.path.join(saver.NORMAL_0, '{}_{}.zip'.format(user_name, token))
        file_name = DirPackage.pack_zip(saver.NORMAL_1, zip_file)

        def send():
            with open(zip_file, 'rb') as file:
                while True:
                    data = file.read(20 * 1024 * 1024)  # 20M
                    if not data:
                        break
                    yield data

        response = Response(send(), content_type='application/octet-stream')
        response.headers["Access-Control-Expose-Headers"] = "Content-Disposition"
        response.headers["Content-Disposition"] = 'attachment; filename={}'.format(file_name.encode('utf-8').decode('ISO-8859-1'))
        return response
  • response需要设置Access-Control-Expose-Headers否则前台axios,post请求无法获取对应的文件名

1.3 路由接口

@staticmethod
    @app.route("/app/multidownload", methods=["POST"])
    def multi_batch_download():
        uName, token = MultiBatchController.ParserTool.parser_name_token_from_request(request)
        download_saver = MultiBatchController.MultiBatchTools.saver_copy_and_init(
            MultiBatchController.DownloadSaveService, request, basepath)
        return MultiBatchController.DownloadTools.download_zip_file(download_saver, uName, token)

2. vue前台

2.1 template

<template>
  <div class="app-container">
    <el-row>
      <el-col :span="20" :xs="24">
        <el-form ref="queryForm" size="small" :inline="true" label-width="68px">
          <el-form-item label="用户ip" prop="userName">
            <el-input
              placeholder="ip"
              clearable
              v-model="user_info.user_name"
              style="width: 240px"
            />
          </el-form-item>
          <el-form-item>
            <el-button type="primary" icon="el-icon-search" size="mini" @click="image_get">下载</el-button>
          </el-form-item>
        </el-form>
      </el-col>
    </el-row>
  </div>
</template>

2.2 JS部分

<script>
import axios from 'axios'

export default {
  data () {
    return {
      user_info: {
        user_name: '',
        token: '12345'
      },
      url: 'http://192.168.31.200:50001/app/multidownload'
    }
  },
  mounted () {

  },
  methods: {
    // 获得处理好的东西
    image_get () {
      let param = new FormData()
      param.append('user_name', 'tacom')
      param.append('token', '12345')

      let config = {
        headers: { 'Content-Type': 'application/x-www-form-urlencoded; application/octet-stream' },
        responseType: 'blob'
      }
      axios.post(this.url,
        param, config
      ).then((res) => {
        debugger
        const blob = new Blob([res.data], { type: "application/zip" })
        // let decode_data = Base64.decode(res.data)
        let dispositon = decodeURI(res.headers['content-disposition'])
        let fileName = dispositon.substring(dispositon.indexOf('filename=')+9, dispositon.length)

        const elink = document.createElement('a')
        elink.download = fileName
        elink.style.display = 'none'
        elink.href = URL.createObjectURL(blob)
        document.body.appendChild(elink)
        elink.click()
        URL.revokeObjectURL(elink.href)
        document.body.removeChild(elink)

      })
    }
  }
}
</script>

  • 首先需要设置responseType为blob
  • 同时还在在headers中指明协议
  • 最后在创建blob对象的时候指名类别

3. Axios解码异常/压缩包损坏

  • 我的异常是Mock拦截了blob数据,并强制转码为了str数据
  • 在这里插入图片描述所以如果压缩包损坏/数据量不同,需要注意返回的类型是否正确
  • 上图注释掉mock模块前,responseType是空,request是MockXMLHttpRequest
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值