前端文件下载+js监听文件下载完成功能

值得注意

  • 下载文件,清除缓存策略(最好后端接口处理,前端处理有些浏览器不识别)
    后端: 接口再头部添加清除缓存标记
    前端: 请求连接后面添加时间戳或者随机数

  • a标签下载文件,base64 url文件过大无法下载问题;可以将文件存入内存createObjectURL中,用虚拟内存地址指向

总结

  • 所有情况通用的方式: 后端设置下载请求的响应头 Content-Disposition: attachment;
    • filename="filename.jpg"
    • attachment 表示让浏览器强制下载
    • filename 用于设置下载弹出框里预填的文件名
  • 非跨域情况下 给a标签加上 download 属性,如 <a href="url" download="xxx.png"></a>
    • download 里写文件名 注意后缀 (值非必填)
  • 通过请求解决跨域问题 动态创建a标签通过blob形式下载 具体看下面解析

  • 文件下载通常有以下3种方式
1、<a href="http://localhost:8080/upload/user.png">下载</a>  a标签访问文件地址
2、window.open('http://localhost:8080/upload/user.png') 打开文件地址
3、后端提供一个接口 /api/download 通过接口返回文件流

浏览器通过请求头Content-Type中的MIME类型(媒体类型,通常称为 Multipurpose
Internet Mail Extensions 或 MIME 类型,如 :image/jpeg application/pdf)识别数据类型,对相应的数据做出相应处理,对于图像文本等浏览器可以直接打开的文件,默认处理方式就是打开,为了避免浏览器直接打开文件我们需要做一些处理;

此方式最为简易,只需要知道文件在服务器上的地址,就可以通过a标签实现下载
<a href="https://.../158ac1e6917445a4aa384a2a7209445a.xlsx" download="test">下载文件</a>
<a href="https://.../6d0e6934246c4ba9ba1a43c6992836ca.png" download="test">下载图片</a>
已知文件的地址,可以通过上面的方式将地址放入href属性内,download属性存放下载文件的名称,此属性为必须。


若文件地址为异步获取,即点击下载/导出按钮时才会从接口拿,则可以通过js插入a标签来实现。demo如下:

异步获取文件路径之后执行以下代码即可自动下载

*以下方式都可以避免浏览器直接打开文件

一、无监听下载实现

1、使用a标签下载 (单个下载)

须知: 当url是同源(同域名、同协议、同端口号)时,这种情况用 a标签加download属性的方式即可,download属性指示浏览器该下载而不是打开该文件,同时该属性值即下载时的文件名

 <el-button type="text" @click.stop="downModel">下载模板</el-button>
// 下载模板
        downModel() {
            let script = document.createElement('a');
            script.setAttribute('href',           
           baseUrl+"/highStarHotel/download/importEmployeeTemplates/v1? 
           token="+Cookie.get("HOTEL-MA-TOKEN"));
            script.setAttribute('target', '_blank');
            script.setAttribute("download", "批量导入酒店员工信息模块.xlsx");
            document.body.appendChild(script);
            script.click();
            document.body.removeChild(script);
        },

2、使用iframe 标签下载(批量下载下载)

downModel() {
//  this.downUrlMenu   批量下载链接
   _.each(this.downUrlMenu, (col) => {
        let iframe = document.createElement("iframe");
        iframe.setAttribute('style','display:none;height:0')
        iframe.setAttribute('src',col)
        document.body.appendChild(iframe);
        setTimeout(()=>{
          document.body.removeChild(iframe);
        },2000)
      })
}

3、使用form表单下载

/**
 * 导出到本地
 *  
 */ 
createSelectedForm(url,params) {
  let form = $("<form/>")
  form.attr("target", "_blank").attr("method", "get").attr("action", url)
  _.each(params, (value, key) => {
    value = value ? value : ""
    form.append("<input type='hidden' value='"+value+"' name='"+key+"' />")
  })
  form.appendTo(document.body)
  form.submit()
  form.remove()
}

二、 监听下载完成实现

实现原理:通过XMLHttpRequest发送请求

  • 解决不同浏览器的下载触发navigator.msSaveBlob
  • 利用FileReader来读取文件内容,关键是设置a标签,将blob的文件内容转换为base64并放入a标签的href中,模拟点击来进行下载。
  • 对于非常小的文件,比如小图标等可以用base64作为url地址,对于其他的大文件,可以将文件存入内存中createObjectURL,用虚拟内存地址指向。
    URL.createObjectURL 将blob形式的文件存入并返回一个url以供下载。
    而URL.revokeObjectURL则将该内存释放。
  • 至此,可以看到大文件根本不能使用base64,直接使用blob是最好的
  • Content-Type 需与下载指定类型一致,否则乱码;https://www.runoob.com/http/http-content-type.html
 exportExcel(type) {
    let _that = this,
    downWord =''公区查验(复验)报告.docx',
    url = "/ydyf_stat/v1/fb/ci_type_stat/export_issue_report/",
      params = {
        type:type
      },
    xhr = new XMLHttpRequest();
    xhr.open('POST', url, true); 
    xhr.setRequestHeader("Content-Type"
        , "application/x-www-form-urlencoded");  // 下载文件类型 .docx
    xhr.responseType = "blob";  // 返回类型blob

    // 定义请求完成的处理函数
    xhr.onload = function () {
      // 请求完成 // 返回200
      if (this.status === 200) {        
          let blob = this.response;
            try {
                 let jsonData = JSON.parse(blob);
                 if (jsonData.code) {
                     // 说明是普通对象数据,后台转换失败
                     // to do something
                 }
               } catch (err) {
                 // 解析成对象失败,说明是正常的文件流
                let fileName = decodeURIComponent(downWord);
                if (navigator.msSaveBlob == null) {
                    let a = document.createElement('a');
                    a.download = fileName;
                    a.href = URL.createObjectURL(blob);
                    $("body").append(a);    // 修复firefox中无法触发click
                    a.click();
                    URL.revokeObjectURL(a.href);
                    $(a).remove();
                } else {
                  navigator.msSaveBlob(blob, fileName);
                }
            }
        }
    };
    // 发送ajax请求
    xhr.send(qs.stringify(params))
  },


注意

  • axios设置responseType=blob导出文件和失败返回json处理

axios设置instance.defaults.responseType = 'blob’请求下载导出一个文件,请求成功时返回的是一个流形式的文件,正常导出文件。但是请求失败的时候返回的是json ,不会处理错误信息,而是直接导出包含错误信息的文件。
可以通过返回的blob数据type类型进行区分,如果type是文件类型,导出文件,如果type是json则把blob数据转为string,处理错误信息。

三、 后端设置

  • 后端设置下载请求的响应头 Content-Disposition 强制下载
    这是最通用的一种方式 不受跨域和请求方式的影响

    Content-Disposition: attachment; filename="filename.jpg"

    想使用window.open实现强制下载的可以用这种方式

在常规的 HTTP 应答中,该响应头的值表示对响应内容的展现形式
  • inline 表示将响应内容作为页面的一部分进行展示
  • attachment 表示将响应内容作为附件下载,大多数浏览器会呈现一个“保存为”的对话框
  • filename(可选) 指定为保存框中预填的文件名
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    X先生_未知数的X阅读 15,967评论 3 119
  • 又是一次旅行,在旅行中随手的用用APP提升一下旅行的品质十分必要。虽然说我用的也不是什么特别小众的应用,在这里记录...
    ouzar阅读 350评论 0 4
  • 新买的不粘煎锅到了。(在大姐家看到了麦饭石不粘锅,于是就买了回来。) 很期待明天的到来,用它来做早餐。 做什么呢,...
    杂乱有章阅读 147评论 0 0
  • 今天晚上我到哥哥家玩123木头人。玩的可开心啦!吃饭的时候,妈妈过来叫我们吃饭。蒜黄,肉,白菜,大饼,馒头,我吃了...
    平凡一生123阅读 5,441评论 0 1