内容简介:在开发中遇到了需要实现文件下载的功能,起初以为只用浏览器的GET(frame、a)和POST(form)请求具有如下特点:
在开发中遇到了需要实现文件下载的功能,起初以为只用 <a>
标签就能搞定, <a>
标签确实能够搞定常见的场景。但是像 导出
或者在 header
里面添加了特殊字段的时候,使用 <a>
标签就搞不定了,又不想去使用原生 XMLHttpRequest
,因为又一堆的兼容性需求(技术能力不够ε=ε=ε=┏(゜ロ゜;)┛,有现成的兼容方案为啥要自己造轮子呢,说不定还爆胎>逃666),所以萌生基于 Axios
封装。
Ajax无法下载文件的原因
浏览器的GET(frame、a)和POST(form)请求具有如下特点:
- response会交由浏览器处理
- response内容可以为二进制文件、字符串等
Ajax请求具有如下特点:
- response会交由Javascript处理
- response内容仅可以为字符串
Ajax本身设计的目标就是用来获取 文本数据 的,而不是用来搞二进制的。
XMLHttpRequest 2.0
新增的数据类型 Blob
看张老师的文章 理解DOMString、Document、FormData、Blob、File、ArrayBuffer数据类型
有了 Blob
类型之后,JavaScript处理二进制进一步增强,可以说以后想怎样就怎样(废话)。
文件下载实现
response header前提条件
服务端返回的头部需要设置
Content-Disposition: "attachment; filename=xxxx.docx;"
<a>
标签的直接下载
// Downloader.ts import qs from 'qs'; /** * downloadByUrl * @param config - 配置参数 * @param config.url - 地址 * @param config.params - querystring参数. * @param filename 文件名称,包括扩展名部分(不一定生效) * * @description * 原理是使用<a>的href和download属性,所以filename不一定会生效, 浏览器机制问题. * * @see https://zhuanlan.zhihu.com/p/58888918 * @see https://github.com/kennethjiang/js-file-download */ export function downloadByUrl( config: { url: string; params: any; }, filename = '' ): void { var tempLink = document.createElement('a'); tempLink.style.display = 'none'; tempLink.href = config.url + qs.stringify(config.params, { addQueryPrefix: true }); tempLink.setAttribute('download', filename); if (typeof tempLink.download === 'undefined') { tempLink.setAttribute('target', '_blank'); } document.body.appendChild(tempLink); tempLink.click(); document.body.removeChild(tempLink); } 复制代码
主要是使用了 js-file-download
的代码,进行了简单的封装,而且去除了对 Blob
的依赖,主要为了兼容低版本的浏览器。同时使用了 qs
对 querystring
参数进行了简单的处理。
基于 axios
的实现
// Downloader.ts import axios, { AxiosResponse, AxiosRequestConfig } from 'axios'; import fileDownload from 'js-file-download'; import logger from 'js-logger'; /** * 提取文件名. * @param response axios的response * @description 从reponse header的content-disposition中提取文件名. */ const extractFilenameFromResponseHeader = (response: AxiosResponse): string => { // content-disposition: "attachment; filename=xxxx.docx;" const contentDisposition = response.headers['content-disposition']; const patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*'); const result = patt.exec(contentDisposition) as RegExpExecArray; let filename = ''; if (result) { filename = result.length > 0 ? result[1] : ''; } // 解码之前尝试去除空格和双引号 // content-disposition: "attachment; filename=\"xxxx.docx\";" return decodeURIComponent(filename.trim().replace(new RegExp('"', 'g'), '')); }; const axiosInstance = axios.create({/* 可以传递公共默认的axios配置,但是注意reponse interceptor中默认把reponse.data作为JSON解析的情况 */}); // https://www.zhihu.com/question/263323250 // https://github.com/axios/axios/issues/815#issuecomment-340972365 const downloadByAxios = async function ( config: AxiosRequestConfig, filename = '' ): Promise<any | AxiosResponse<any>> { let response = await axiosInstance({ ...config, responseType: 'blob', // 指定类型 }); let resBlob = response.data; // <--- store the blob if it is let respData = null; // 如果确定接口response.data是二进制,所以请求失败时是JSON. // 这里只对response.data做JSON的尝试解析 try { let respText = await new Promise((resolve, reject) => { let reader = new FileReader(); reader.addEventListener('abort', reject); reader.addEventListener('error', reject); reader.addEventListener('loadend', () => { resolve(reader.result as string); }); reader.readAsText(resBlob); }); respData = JSON.parse(respText as string); // <--- try to parse as json evantually } catch (err) { // ignore } // 如果response.data能够确定是二进制,则respData = null说明请求成功 // 否则 respData !== null说明请求失败 if (respData as ResponseData) { logger.error(respData); // 方便调用者有进一步的 then().catch()处理 return Promise.reject({ ...respData, }); } else { // 触发浏览器下载 // 如果没有传递filename尝试从Content-Disposition提取 fileDownload(resBlob, filename || extractFilenameFromResponseHeader( response )); // 方便调用者有进一步的 then().catch()处理 return Promise.resolve({ ...response, }); } }; 复制代码
代码大部分都是参考这个 issue 实现的,只有少部分的个人代码。
基于 axios
实现的功能:
-
可以使用
axios
的所有参数,不管请求是GET
或者POST
-
解决了在
header
中添加额外的参数的需求 -
可以指定
filename
,如果服务端没有设置content-disposition
的情况 -
返回
Promise
方便调用者进一步处理请求
缺点:
-
只能使用独立的
axios
实例,不能公用一个axios
本来想把 下载功能 使用
axios interceptor
拦截器实现,但是返回的response.data
是Blob
二进制,但是其它的response interceptor
默认前提都是把response.data
当作JSON
处理,导致全部出现异常,所以把下载功能独立出来,更方便维护。 -
使用独立的
axios
实例,所以项目中的axios
默认配置需要重新配置一遍
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- jsp文件下载功能实现代码
- 使用selenium实现批量文件下载
- 前端js实现字符串/图片/excel文件下载
- jsp+servlet实现文件上传与下载功能
- JSP servlet实现文件上传下载和删除
- SpringMVC+Ajax实现文件批量上传和下载功能实例代码
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
离散数学及其应用(原书第7版)
Kenneth H. Rosen / 徐六通、杨娟、吴斌 / 机械工业出版社 / 2015-1-1 / 129
《计算机科学丛书:离散数学及其应用(原书第7版)》是介绍离散数学理论和方法的经典教材,已经成为采用率最高的离散数学教材,被美国众多名校用作教材,获得了极大的成功。中文版也已被国内大学广泛采用为教材。作者参考使用教师和学生的反馈,并结合自身对教育的洞察,对第7版做了大量的改进,使其成为更有效的教学工具。《计算机科学丛书:离散数学及其应用(原书第7版)》可作为1至2个学期的离散数学课入门教材,适用于数......一起来看看 《离散数学及其应用(原书第7版)》 这本书的介绍吧!