企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] >[success] # 下载文件功能 <br/> [参考文档:关于前端实现文件下载功能](https://segmentfault.com/a/1190000019359452#item-1) [参考文档:前端文件下载的正确打开方式](https://mp.weixin.qq.com/s/eHeQI-nJ2tNq6AmTmhLLnw) 文件下载方式有`3种方式`和`批量下载并且打包`: 1. window.open() 2. 通过a标签打开新页面下载 3. 通过文件流(blob)的方式下载 4. 如何实现批量下载打包 <br/> >[success] ## window.open()打开新页面下载文件 <br/> ~~~ 使用场景:下载'excel'文件,后端提供接口,接口返回的是'文件流',可以直接使用'window.open()',最简单 的方式。 优点:最简洁。 弊端:当'参数错误'时,或其它原因导致接口'请求失败',这时'无法监听到接口返回的错误信息',需要保证请求 必须是正确的且能正确返回'数据流',不然打开新页面会'直接输出接口返回的错误信息',体验不好。 ~~~ <br/> 1. index.html ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>下载文件</title> <script> window.onload = function () { document.querySelector('button').onclick = function () { const URL = 'download.io' // 接口 window.open(URL, '文件名') } } </script> </head> <body> <button>下载文件</button> </body> </html> ~~~ <br/> >[success] ## a标签打开新页面下载文件 <br/> ~~~ 通过'a标签'下载的方式,同'window.open()'是一样的,唯一的优点是可以'自定义下载后的文件名','a标签' 里有'download属性可以自定义文件名',如果不指定,那么下载的文件名就会根据请求内容的 'Content-Disposition' 来确定,如果没有 'Content-Disposition' ,那么就会使用请求的 'URL' 的 '最后一部分作为文件名'。 弊端:同window.open()方式一样,无法监听错误信息。 问题:以上'两种方式',当在下载'.mp3格式',或者'视频文件'时,浏览器会'直接播放该文件',而达不到直接 '下载'的功能,此时,'当下载音视频文件时无法使用这两种方式'。 ~~~ <br/> 1. index.html ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>下载文件</title> <script> window.onload = function () { document.querySelector('button').onclick = function () { const URL = 'download.io' // 接口 /** * a标签下载文件方法 * @param { String } url 接口url * @param { String } option 文件要更改的名称 */ const exportFile = (url, fileName) => { // 创建a标签 const link = document.createElement('a') // 获取body元素 const body = document.querySelector('body') // 设置url下载链接(接口) link.href = URL // 重命名文件名称(这个属于html5新特性,有些浏览器暂时不支持,而且跨域重命名会失效) link.download = fileName // 隐藏标签 link.style.display = 'none' // 把标签添加到body元素最后面 body.appendChild(link) // 点击a标签 link.click() // 销毁a标签 body.removeChild(link) } // 执行下载文件方法 exportFile(URL,'奥利给') } } </script> </head> <body> <button>下载文件</button> </body> </html> ~~~ <br/> >[success] ## 通过文件流的方式下载 <br/> ~~~ 为了方便'重复使用下载功能',进行了一个简单的封装,思路: 1. 调用'下载接口',指定返回'数据类型blob' 2. 在'ajax拦截器'时候进行'类型判断'如果为'媒体类型'就执行保存文件方法。 ~~~ <br/> 1. responseType的几种值类型 <br/> ![](https://img.kancloud.cn/2e/2e/2e2ea00a71c6752c868afb9f263c09ab_1032x553.png) <br/> 2. index.html ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <script src="test.js"></script> <title>下载文件</title> <script> window.onload = function () { document.querySelector('button').onclick = function () { // 下载接口 const URL = 'download.io' // 调用下载接口 ajax({ url: URL, type: 'get', responseType: 'blob' // 设置返回的数据类型blob、 }) } } </script> </head> <body> <button>下载文件</button> </body> </html> ~~~ <br/> 3. test.js ~~~ /** * 保存文件方法 * @param { Object } blob blob二进制大对象 * @param { Object } filename 文件名称 */ const saveAs = (blob, filename) => { if (window.navigator.msSaveOrOpenBlob) { // Navigator.msSaveBlob()方法将“ File或” 保存Blob到磁盘。 navigator.msSaveBlob(blob, decodeURI(filename)) } else { // 创建a标签 const link = document.createElement('a') // 获取body const body = document.querySelector('body') // 创建对象url link.href = window.URL.createObjectURL(blob) // 重命名文件名称 link.download = decodeURI(filename) // 隐藏a标签 link.style.display = 'none' // 把a标签添加到body后面 body.appendChild(link) // 模拟点击a标签 link.click() // 删除a标签 body.removeChild(link) // 用来释放一个之前已经存在的、通过调用 URL.createObjectURL() 创建的 URL 对象 window.URL.revokeObjectURL(link.href) } } /** * ajax工具函数 * @param { Object } option 该参数为对象,以下为参数说明 * @param { String } data 接口参数 * @param { String } type 请求类型 post/get * @param { String } url 接口url * @param { String } responseType 数据返回类型blob、arrayBuffer等等 * @param { Function } success 数据成功的回调函数 */ function ajax (option) { var xhr = new XMLHttpRequest(); // 如果是get,并且有参数才设置 if (option.type === 'get' && option.data) { option.url += "?"; option.url += option.data; option.data = null; // 这样最后直接send data即可 } xhr.open(option.type, option.url); // 返回指定数据类型blob、arrayBuffer等等 xhr.responseType = option.responseType || '' // 这里是设置请求头的授权密钥 xhr.setRequestHeader('authorization', 'xxxxxxxxxxxx') // 这里根据项目需求而定是否添加 // 如果是post,并且有参数才设置 if (option.type == 'post' && option.data) { xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded'); } xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { var type = xhr.getResponseHeader("Content-Type"); // 获取服务器在header上的信息 if (type.indexOf('json') != -1) { // 判断为json格式信息 option.success(JSON.parse(xhr.responseText)) } else if (type.indexOf('xml') != -1) { // 判断为xml格式信息 option.success(xhr.responseXML) } else if(type.indexOf('octet-stream') != -1){ // 判断为媒体类型格式 // 获取文件名称 let [,filename] = xhr.getResponseHeader('Content-Disposition').split('='); // 执行保存文件方法 saveAs(xhr.response, filename) } else { // 普通字符串 option.success(xhr.responseText) } } } xhr.send(option.data); } ~~~ <br/> >[success] ## 如何实现批量下载,且打包文件 <br/> 1. index.html ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <!-- jszip --> <script src="https://cdn.bootcss.com/jszip/3.2.2/jszip.min.js"></script> <!-- FileSaver.js --> <script src="https://cdn.bootcss.com/FileSaver.js/1.3.8/FileSaver.min.js"></script> <!-- ajax.js --> <script src="ajax.js"></script> <title>下载文件</title> <script> window.onload = function () { document.querySelector('button').onclick = function () { // 点击下载按钮 let urlArr = [ // 批量选中的文件Url数组 { url: 'download.io', fileName: '文件1.xlsx' }, { url: 'download.io', fileName: '文件2.xlsx' } ] /** * 批量下载并且打成zip包 * @param urlArr Array [{url: 下载文件的路径, fileName: 下载文件名称}] * @param filename zip文件名 */ const download = (urlArr, filename = '默认zip文件名') => { // urlArr没有值就返回 if (!urlArr.length) return // new一个压缩构造函数 const zip = new JSZip() // 储存多个请求 const promisesArr = [] // 调用批量下载接口 Promise.all(urlArr.map(item => ajax({ url: item.url, type: 'get', responseType: 'blob' }))).then((res) => { for(let i=0;i<urlArr.length;i++){ zip.file(urlArr[i].fileName, res[i], { binary: true }) // 逐个添加文件 } zip.generateAsync({ type: 'blob' }).then((content) => { // 生成二进制流 saveAs(content, `${filename}.zip`) // 利用file-saver保存文件 }) }) } // 执行批量下载并且打压缩包 download(urlArr, '我要打个压缩包') } } </script> </head> <body> <button>下载文件</button> </body> </html> ~~~ 2. ajax.js ~~~ /** * ajax工具函数 * @param { Object } option 该参数为对象,以下为参数说明 * @param { String } data 接口参数 * @param { String } type 请求类型 post/get * @param { String } url 接口url * @param { String } responseType 数据返回类型blob、arrayBuffer等等 * @param { Function } success 数据成功的回调函数 */ function ajax (option) { return new Promise((resolve, reject) => { var xhr = new XMLHttpRequest(); // 如果是get,并且有参数才设置 if (option.type === 'get' && option.data) { option.url += "?"; option.url += option.data; option.data = null; // 这样最后直接send data即可 } xhr.open(option.type, option.url); // 返回指定数据类型blob、arrayBuffer等等 xhr.responseType = option.responseType || '' // 这里是设置请求头的授权密钥 xhr.setRequestHeader('authorization', 'xxxxxxx') // 这里根据需求而定是否添加 // 如果是post,并且有参数才设置 if (option.type == 'post' && option.data) { xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded'); } xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { var type = xhr.getResponseHeader("Content-Type"); // 获取服务器在header上的信息 if (type.indexOf('json') != -1) { // 判断为json格式信息 option.success(JSON.parse(xhr.responseText)) } else if (type.indexOf('xml') != -1) { // 判断为xml格式信息 option.success(xhr.responseXML) } else if(type.indexOf('octet-stream') != -1){ // 判断为媒体类型格式 resolve(xhr.response) } else { // 普通字符串 option.success(xhr.responseText) } } } xhr.send(option.data); }) } ~~~