文章首发于个人网站

背景

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

但是由于 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 还可以转化为 arrayBufferformData

具体 Response 可以见 MDN

如何拿到文件名

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

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

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

一个小插曲

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

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

我还以为 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.filenamelink.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 = filenamelink.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,不然如果有问题,不报错还是很难受的。

有兴趣可以关注我的微信公众号「前端桃园」

post 请求下载文件相关推荐

  1. php post请求 下载文件,POST请求 下载文件

    通过axios发送POST请求 下载文件. 这时候的 Content-Type: application/json;charset=UTF-8 先贴axios({ method: 'post', ur ...

  2. jquery ajax post请求下载文件

    第一次用ajax post请求下载文件 ,之前一直用vue的写,今天用同样方式发现是乱码,走了不少弯路,特此记录下方法 var outData = getData() //传参数据var url = ...

  3. post请求下载文件(兼容IE)

    post请求下载文件(兼容IE) 有时候由于下载文件的url携带的参数过多,导致整个url长度超过get请求的上限,这个时候需要更换为post请求,具体实现如下 function downLoadFi ...

  4. 【post请求下载文件流】如何使用post请求下载文件流 blob

    最近有个需求,做文件的下载.其实下载没什么要特别说的,之前都是用的get请求,这次不同,后台用的post请求,返回的是数据流,这种post请求下载文件的方式倒是没怎么弄过.记录一下. 接口 接口地址为 ...

  5. js post请求下载文件

    js 下载post请求的文件 /* content 文件二进制内容 fileName 文件名 */ downloadFile(content, fileName) {(fileName &&a ...

  6. ajax请求文件下载 php,使用Ajax请求下载文件

    要使浏览器下载一个文件,您需要这样做:function downloadFile(urlToSend) { var req = new XMLHttpRequest(); req.open(" ...

  7. 实战PyQt5: 130-使用HTTP请求下载文件

    在桌面应用中,往往需要从网络获取一些数据,比如下某个图像或者文本文件,查询城市天气预报,加载网络地图等等.在Qt中,提供一些网络处理类,可以很方便地实现上面列出的需求.核心类QNetworkAcces ...

  8. vue中使用post请求下载文件

    fetch(url+(携带的参数), { method: "POST", headers: { 'Content-Type':'application/x-www-form-url ...

  9. Java 前端请求下载文件

  10. http协议请求部首详解以及用c++写socket下载文件

    本篇博文将介绍几个我认为比较有作用的请求部首 Connection ,这个部首常见的值是keep-alive 和close.close的意思是说,我发一次请求,你回复给我消息,那么咱俩就立刻断开,等我 ...

最新文章

  1. python GIL :全局解释器
  2. 【计算机网络】传输层 : TCP 可靠传输 ( 可靠传输机制 | 快速重传机制 )
  3. 三星a7支持html吗,三星A7怎么样 三星A7特点介绍
  4. jQuery工具和方法(二)
  5. u3d文件上传至服务器,unity 上传图片到云服务器
  6. 用python玩转数据慕课答案第三周_大学慕课用Python玩转数据答案公众号
  7. C语言第七讲,函数入门.
  8. VMware三种工作模式
  9. Python map/reduce/filter/sorted函数以及匿名函数
  10. 测试软件测显卡有啸叫,完美解决 显卡电流声!显卡啸叫!吱吱的电流声!附解决方案!...
  11. Mybatis提高查询效率的方式
  12. Ubuntu 图标主题 Nitrux 升级
  13. 055 集体照 (25 分)
  14. 三星 c5 html,三星GALAXY C5/C7参数配置介绍 均支持Samsung Pay
  15. SM2 SM3 SM4 国密版本,基于bouncycastle 实现
  16. 大学毕业生参考信函提示
  17. 探索AI助手ChatGPT实际应用场景
  18. mysql 删除重复_MySQL查询和删除重复记录
  19. Maximum Profit(C++最大利益简单算法)
  20. java中使用tika_【Tika基础教程之中的一个】Tika基础教程

热门文章

  1. 杭电ACM2000题
  2. 了解开源图表组件FusionChartsFree
  3. 在计算机英语中 input的意思是,计算机英语词汇解释
  4. ios刺客信条一直显示连接服务器,刺客信条本色iOS进不去怎么办
  5. 未来生活进行时: 畅想未来新兴技术40年——百大趋势性技术汇总(上)
  6. 决策支持系统 (Decision-making Support System, DSS) (人机智能系统)
  7. 计算机网络技术毕业生实习报告_计算机网络专业毕业实习报告
  8. Java多线程编程-停止线程 暂停线程
  9. php怎么把多个pdf拼接成一个,两张pdf合并成一页 怎么将pdf文件中的页面进行拼接?两页或多页pdf拼接...
  10. 批处理批量卸载微软补丁