通过fetch发送 post 请求下载文件基础知识入门_fetch基础入门

背景最近遇到一个下载的需求,由于 url 参数太长(常用的下载方法 a 标签或者 location.href 的方法都是 get 请求,get 请求参数长度有限制),无法下载,考虑了好几种方案,最终还是觉得通过 ajax 的 POST 方法进行下载,比较容易实现,下面记录实现过程以及遇到的问题。但是由于 AJAX 并不会唤起浏览器的下载窗口, AJAX 设计

通过fetch发送 post 请求下载文件基础知识入门

背景

最近遇到一个下载的需求,由于 url 参数太长(常用的下载方法 a 标签或者 location.href 的方法都是 get 请求,get 请求参数长度有限制),无法下载,考虑了好几种方案,最终还是觉得通过 ajax 的 POST 方法进行下载,比较容易实现,下面记录实现过程以及遇到的问题。

通过fetch发送 post 请求下载文件基础知识入门_fetch基础入门

但是由于 AJAX 并不会唤起浏览器的下载窗口, AJAX 设计的初衷就是用来实现 异步刷新 的,用以改善原始的 form 表单提交刷新页面的问题,那么如何来解决呢?

POST 方法下载实现原理

通过 fetch 请求获取文件,然后利用 Blob 对象来接收处理,在接收到后端返回的文件后,把其转化一下,放入 a标签 的 href 中,并触发下载行为。

实现的代码如下:

fetch(url, {
  method: 'POST',
  body: JSON.stringify(params),
  header: {
     'Content-Type': 'application/json;charset=UTF-8'
  }
}).then(function(response) {
  return response.blob();
}).then(function(blob) {
  const link = document.createElement('a')
  link.style.display = 'none'
  link.href = URL.createObjectURL(blob)
  document.body.appendChild(link)
  link.click()
  // 释放的 URL 对象以及移除 a 标签
  URL.revokeObjectURL(link.href)
  document.body.removeChild(link)
});

这里需要注意的是要记得要调用 response 的 blob 方法,这样才会返回一个 blob,如果你没用过 blob 的话,可能你以前只知道 json 和 text,其实 response 的 body 还可以转化为 arrayBuffer 和 formData 。

如何拿到文件名

可以下载文件了只是第一步,但是你会发现还有一个问题,下载下来的文件名是你看不懂的名字,类似这样:

通过fetch发送 post 请求下载文件基础知识入门_fetch基础入门

我这边的方案是把文件名放在 response 的 headers 里,放在 content-disposition 字段里,有个 fileName 字段,用来存放文件名。

我感觉在下载文件的时候 content-disposition 字段对于他们后端来说感觉是都会加的,因为最开始我用 get 下载的时候就已经有这个字段了,如果你们后端没有设置这个 header ,可以设置一下,当然也可以设置到其他字段里。

一个小插曲

当我把 fetch 后的 res 打印出来看 Response 的时候,发现 headers 里是空对象,如下:

通过fetch发送 post 请求下载文件基础知识入门_fetch基础入门

然后我再通过 res.headers 直接去拿 headers,发现还是一个 Headers 的空对象。

header{}

我还以为 headers 里面没有东西,但是当我直接通过 res.headers.get(‘content-disposition’) 去拿的时候,竟然拿到了,数据像这样:

attachment;fileName=%E7%9B%B4%E6%92%AD%E6%97%B6%E9%95%BF%E4%B8%BB%E6%92%AD%E6%98%8E%E7%BB%86.xls

然后你就可以通过多种方式将文件名给提取出来,我这里采用的是通过 split 方法来提取的。

res.headers.get('content-disposition').split(';')[1].split('=')[1]

最终的实现

准备工作都做好了,然后就写出了这样的代码:

fetch(url, {
  method: 'POST',
  body: JSON.stringify(params),
  header: {
     'Content-Type': 'application/json;charset=UTF-8'
  }
}).then(function(response) {
  const filename = res.headers.get('content-disposition').split(';')[1].split('=')[1]
  return {
    filename,
    blob: response.blob()
  }
}).then(function(obj) {
  const link = document.createElement('a')
  link.style.display = 'none'
  // a 标签的 download 属性就是下载下来的文件名
  link.download = obj.filename
  link.href = URL.createObjectURL(obj.blob)
  document.body.appendChild(link)
  link.click()
  // 释放的 URL 对象以及移除 a 标签
  URL.revokeObjectURL(link.href)
  document.body.removeChild(link)
});

本以为就可以了,但是下载下来打开 excel 发现内容是 Promise,然后才发现原来 response.blob() 返回的是一个 promise。

所以改进的实现方案如下:

fetch(url, {
  method: 'POST',
  body: JSON.stringify(params),
  header: {
     'Content-Type': 'application/json;charset=UTF-8'
  }
}).then(function(response) {
  const filename = res.headers.get('content-disposition').split(';')[1].split('=')[1]
  
  response.blob().then(blob => {
    const link = document.createElement('a')
    link.style.display = 'none'
    // a 标签的 download 属性就是下载下来的文件名
    link.download = filename
    link.href = URL.createObjectURL(blob)
    document.body.appendChild(link)
    link.click()
    // 释放的 URL 对象以及移除 a 标签
    URL.revokeObjectURL(link.href)
    document.body.removeChild(link)
   })
})

不过这种 then 里面又套了 then ,看着有点不好看,所以用 async/await 重新写了一版:

async function postDownload(url, params) {
   const request = {
     body: JSON.stringify(params),
     method: 'POST',
     headers: {
       'Content-Type': 'application/json;charset=UTF-8'
     }
   }

   const response = await fetch(url, request)
   const filename = response.headers.get('content-disposition').split(';')[1].split('=')[1]
   const blob = await response.blob()

   const link = document.createElement('a')
   link.download = decodeURIComponent(filename)
   link.style.display = 'none'
   link.href = URL.createObjectURL(blob)
   document.body.appendChild(link)
   link.click()
   URL.revokeObjectURL(link.href)
   document.body.removeChild(link)j
}

这个函数里没有写任何的错误处理,那也不是这篇文章要讲的,不过自己在实现的时候应该加上 try/catch,不然如果有问题,不报错还是很难受的。

海计划公众号
(0)
上一篇 2020/03/23 08:59
下一篇 2020/03/23 08:59

您可能感兴趣的内容

  • ES6入门之Promise对象基础教程_Promise基础教程

    1. Promise 的含义Promise 是异步编程的一种解决方案,比传统的解决方案–回调函数和事件更合理、更强大。 1.1 什么是Promise简单来说就是一个容器,里面保存着某个未来才会结束的事件(也就是异步操作)的结果。从语法上来讲,Promise是一个对象,从它可以获取异步操作的消息,它提供统一的API,各种异步操作都可以用同样的方法进行处理。

    2020/03/24
  • Vue组件系统基础知识教程_组件指南攻略

    vue.js既然是框架,那就不能只是简单的完成数据模板引擎的任务,它还提供了页面布局的功能。本文详细介绍使用vue.js进行页面布局的强大工具,vue.js组件系统。每一个新技术的诞生,都是为了解决特定的问题。组件的出现就是为了解决页面布局等等一系列问题。vue中的组件分为两种,全局组件和局部组件。一、全局组件的注册通过Vue.component()创建一个

    2020/03/26
  • 版本管理Git和SVN的介绍及其优缺点入门百科_svn小白常识

    版本管理概念:版本管理是软件配置管理的基础,它管理并保护开发者的软件资源。好处:可以保留我们的历史版本,在代码开发到一半的时候,不至于无故丢失,还可以查看BUG的来龙去脉。版本管理种类:集中式的版本管理和分布式的版本管理。实现的控制软件分别有SVN和Git。 了解了版本管理的概念那就进入主题,讲讲SVN和Git SVN概念:SVN是Subversion的简

    2020/03/24
  • e2email入门百科_一个Chrome 扩展将 OpenPGP 集成到 Gmail 中的方法

    e2email入门百科 GitHub:https://github.com/e2email-org/e2email 简介描述:一个Chrome 扩展将 OpenPGP 集成到 Gm…

    2020/03/10
  • Vue 中 render 函数有点意思基础知识教程_render菜鸟知识

    我们知道 Vue 模板是非常强大的,基本可以完成我们日常开发的所有任务。但是,有一些用例,如基于输入或插槽值创建动态组件方式,render 函数会比模板完成的更好也更出色。用过 React 开发的人对 render 函数应该非常熟悉,因为React组件通过 JSX和 render 函数来构建的。 尽管Vue render 函数也可以用JSX编写,但在这里我们

    2020/03/20
  • js的抖动,解决方法 : 防抖&节流 新手入门_防抖指南教程

    js的抖动在 js 中 改变窗口大小 & 上下滚动滚动条 & 反复向输入框中输入内容 … , 如果绑定了相应的事件 , 这些事件的触发频率非常高, 严重影响用户体验和服务器的性能 , 这种问题 在js中 就叫 js 的抖动 .解决方法 : 防抖 & 节流 js的防抖就是在 触发事件 中设置一个定时器来延迟 绑定事件 的生效 , 并且每次在 触发事件 中

    2020/03/24
  • Web开发展望入门教程_发展攻略教程

    摘要JavaScript 和排名前 3 名的 UI 框架将继续继续火热;Svelte 将会继续发展云计算,服务器端编程和 JAMStack 会不断增长;预处理和性能优化才是未来;WASM 为Web 带来了很多运算能力;aterial Design、圆角、渐变和深色主题模式是当前的设计趋势。JavaScript有人喜欢,也有人讨厌它,但是作为第一集团的成员,我

    2020/03/22
  • js事件委托入门基础_事件入门百科

    js事件委托入门基础 今天呢,咱们来说说事件委托,有的相关资料叫事件代理.首先呢,先来讲讲事件委托的起源:由于事件处理程序可以为web应用提供交互能力,因此很多开发人员会不分青红皂…

    2020/03/20
  • 怎么使用pdf.js,html5在浏览器直接打开pdf文档菜鸟教程_pdf使用帮助

    pdf.js是什么?pdf.js是一款开源的pdf文档读取解析插件,据说在HTML5下诞生的,对于主流的浏览器基本都支持。pdf.js框架的魅力所在无需任何本地支持,浏览器支持HTML5就能支持pdf.js(不过对于低版本的IE,就只能节哀了!)。pdf.js主要包含两个库文件,一个pdf.js和一个pdf.worker.js,,一个负责API解析,一个负责

    2020/04/06
  • ES6中的Symbol小白基础_symbol入门知识

    1 概述保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因在es6之前,JavaScript数据类型分为Number、Boolean、String、Null、Undefined、Objectes6中,新增了一个基本数据类型 SymbolES6 引入了一种新的原始数据类型Symbol,表示独一无二的值

    2020/04/03
  • nginx负载均衡配置小白攻略_负载均衡小白教程

    本章给大家带来nginx负载均衡的相关配置讲解,首先大家先看下面的示意图:如上图所示;当客户端发起http请求时,先经过nginx服务器处理并重新分发请求;并下发给不同的服务器,从而实现nginx的负载均衡的简单应用。nginx配置 upstream www.xyqmw.com {server 111.231.197.74:8080 weight=

    2020/03/24
  • bootstrap基础入门教程_最流行的HTML,CSS和JavaScript框架,用于开发响应式,移动端先行的web项目

    bootstrap基础入门 官方网址:http://getbootstrap.com/ GitHub:https://github.com/twbs/bootstrap 简介描述:…

    2020/03/05
  • React setState 这样用,开发直呼内行!小白指南_react使用帮助

    众所周知, React 是通过管理状态来实现对组件的管理,而setState是用于改变状态的最基本的一个方法,虽然基础,但是其实并不容易掌握,本文将结合部分源码对这个方法做一个相对深入的解析。基本用法首先官方文档给出的,它的基本API:// 接受2个参数,updater 和 callback
    setState(updater[, callback])// 第

    2020/03/22
  • Ajax异步请求入门指南_ajax入门指南

    其实我理解前端如何通过url从后端获取数据,对于异步请求Ajax一直表示有点迷惑,所谓不尝试和不探索,光靠看概念你是永远不能理解代码的魅力的,正好公司的项目里用到了最经典的Ajax,正好作为一个案例来学习如何用json的Data数据与前端进行异步请求并显示数据.首先你要知道什么是Ajax技术,我记得猫和老鼠里面倒是有Ajax,那还是我第一次听说有这么个难听的

    2020/03/26
  • 如何使你的开源项目成功?基础知识_开源菜鸟教程下载

    你已经为一个有趣的问题工作了几个月,现在决定启动一个开源项目。你在 README.md 中编写了一些说明,并发布了1.0版。几周后,人们对这个项目仍然没有什么兴趣。你做了大量的工作,付出了最大的努力,但是最后,仍然没有谁对它感兴趣。怎么会这样?更重要的是,怎样才能使你的开源项目成功?我创建了一个开源库 vocajs.com,经过努力,这个库成为了 GitHu

    2020/03/23
  • apexcharts.js小白入门_一个现代化 JavaScript 图表库

    apexcharts.js小白入门 官方网址:https://apexcharts.com GitHub:https://github.com/apexcharts/apexcha…

    2020/03/06