前端很多项目中,都有文件下载的需求,特别是JS生成文件内容,然后让浏览器执行下载操作(例如在线图片编辑、在线代码编辑、iPresst等)。但受限于浏览器,很多情况下我们都只能给出个链接,让用户点击打开-》另存为。如下面这个链接: <a href=”file.js”>file.js</a> 用户点击这个链接的时候,浏览器会打开并显示链接指向的文件内容,显然,这并没有实现我们的需求。HTML5中给a标签增加了一个download属性,只要有这个属性,点击这个链接时浏览器就不在打开链接指向的文件,而是改为下载(目前只有chrome、firefox和opera支持)。下载时会直接使用链接的名字来作为文件名,但是是可以改的,只要给download加上想要的文件名即可,如:download=“not-a-file.js”。Not enough!但是这样还不够,以上的方法只适合用在文件是在服务器上的情况。如果在浏览器端js生成的内容,想让浏览器进行下载要如何办到呢?其实还是有办法办到的,相信很多人都多少听过了DataURI这个词,比较常见的就是图片的src,如: <img src=”data:image/gif;base64,R0lGOXXXXX"> DataURI的解释可以移步这里,本人就不在解释了。那么,现在要将js生成的内容进行下载就有法可依了。封装成一个方法如下:JavaScript function downloadFile(aLink, fileName, content){aLink.download = fileName;aLink.href = "data:text/plain," + content; } 调用downloadFile之后,用户点击链接,就能触发浏览器下载。Not enough!但是,还不够,上面的办法有两个硬伤,会导致流失很多懒人美眉:下载的文件类型限制死了,美眉要下载处理后的果照怎么办?下载还要再点击一下,太麻烦啦。要解决文件类型的问题,可以用浏览器的新API(URL.createObjectURL)来解决问题,URL.createObjectURL通常都是用来创建图片的DataURI用来显示图片,这里用来下载文件,让浏览器来帮我们设定好文件类型。URL.createObjectURL的参数是File对象或者Blob对象,File对象也就是通过input[type=file]选择的文件,Blob对象是二进制大对象,详细说明可参考这里。现在,我们只要用content创建一个ObjectURL并赋值给aLink即可解决文件类型的限制问题。文件的自动下载也挺好办,自己构建一个UI点击事件,再自动触发下,就能实现自动下载啦。现在来看看最终代码:JavaScript/*let url = new URL('https://example.com?foo=1&bar=2');console.log(url);URL {href:"https://example.com/?foo=1&bar=2", origin: "https://example.com", protocol: "https:", username: "", password: "", …}hash:""host:"example.com"hostname:"example.com"href:"https://example.com/?foo=1&bar=2"origin:"https://example.com"password:""pathname:"/"port:""protocol:"https:"search:"?foo=1&bar=2"searchParams:URLSearchParams {}username:""}/*arrayObject.slice(start,end)返回一个新的数组,包含从start到end(不包括该元素)的 arrayObject 中的元素。 */ /*去掉?,剩下let params = new URLSearchParams(url.search.slice(1));//添加第二个foo搜索参数。params.append('foo', 4);//查询字符串变成: 'foo=1&bar=2&foo=4' *///下载功能实现:用iframes,其实就是通过iframes给后台发请求,让后台实现具体下载 import axios from 'axios'; export const baseURL = '/service'; export function getFileName(headers) {//headers['content-disposition']的属性值中的'attachment; filename='用空格代替,拿到file的值return headers['content-disposition'].replace('attachment; filename=', ''); } export function downloadData(url,params) {//URLSearchParams 接口定义了一些实用的方法来处理 URL 的查询字符串var searchParams = new URLSearchParams();url = baseURL + url;for (let key of Object.keys(params)) {searchParams.append(key, encodeURIComponent(params[key]));}document.querySelector('#downloadIframe').setAttribute('src', url + '?' + searchParams) }//下载功能的实现:用a.download, 后台返回的 res.data 必须是 blob 对象 export function download(res) {var a = document.createElement('a');//URL.createObjectURL通常都是用来创建图片的DataURI用来显示图片,这里用来下载文件,让浏览器来帮我们设定好文件类型var url = window.URL.createObjectURL(res.data);var filename = getFileName(res.headers);a.href = url;//HTML5中给a标签增加了一个download属性,只要有这个属性,点击这个链接时浏览器就不在打开链接指向的文件,而是改为下载a.download = filename;a.click();//URL.revokeObjectURL()静态方法用来释放一个之前通过调用 URL.createObjectURL() 创建的已经存在的 URL 对象。//当你结束使用某个 URL 对象时,应该通过调用这个方法来让浏览器知道不再需要保持这个文件的引用了。window.URL.revokeObjectURL(url); } function filterResponse(res) {if (isDownload(res.headers)) {download(res);} } function errorResponse(err) {return Promise.reject(err); } httpLayer.interceptors.request.use(filterResponse, errorResponse);header中Content-Disposition的作用与使用方法:Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。Content-disposition其实可以控制用户请求所得的内容存为一个文件的时候提供一个默认的文件名,文件直接在浏览器上显示或者在访问时弹出文件下载对话框。 格式说明: content-disposition = "Content-Disposition" ":" disposition-type *( ";" disposition-parm ) 字段说明: Content-Disposition为属性名 disposition-type是以什么方式下载,如attachment为以附件方式下载 disposition-parm为默认保存时的文件名 服务端向客户端游览器发送文件时,如果是浏览器支持的文件类型,一般会默认使用浏览器打开,比如txt、jpg等,会直接在浏览器中显示,如果需要提示用户保存,就要利用Content-Disposition进行一下处理,关键在于一定要加上attachment: 复制代码 代码如下: Response.AppendHeader("Content-Disposition","attachment;filename=FileName.txt"); 备注:这样浏览器会提示保存还是打开,即使选择打开,也会使用相关联的程序比如记事本打开,而不是IE直接打开了。 Content-Disposition就是当用户想把请求所得的内容存为一个文件的时候提供一个默认的文件名。具体的定义如下: 复制代码 代码如下: content-disposition = "Content-Disposition" ":"disposition-type *( ";" disposition-parm ) disposition-type = "attachment" | disp-extension-token disposition-parm = filename-parm | disp-extension-parm filename-parm = "filename" "=" quoted-string disp-extension-token = token disp-extension-parm = token "=" ( token | quoted-string ) 那么由上可知具体的例子: Content-Disposition: attachment; filename="filename.xls" 当然filename参数可以包含路径信息,但User-Agnet会忽略掉这些信息,只会把路径信息的最后一部分做为文件名。 当你在响应类型为application/octet- stream情况下使用了这个头信息的话,那就意味着你不想直接显示内容, 而是弹出一个"文件下载"的对话框,接下来就是由你来决定"打开"还是"保存" 了。 注意事项: 1.当代码里面使用Content-Disposition来确保浏览器弹出下载对话框的时候。 response.addHeader("Content-Disposition","attachment");一定要确保没有做过关于禁止浏览器缓存的操作。如下: 复制代码 代码如下:response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "No-cache"); response.setDateHeader("Expires", 0);