Web文件下载总结
创始人
2025-05-29 02:50:37

文章目录

    • location.href 或 window.open
    • 标签 或 点击事件
      • a标签的download
      • 通过点击事件下载
    • 利用Blob对象
    • 利用base64
    • 关于文件名
      • Content-Disposition
      • 自定义header
      • 重命名

web文件下载是业务常见的需求场景

location.href 或 window.open

function download(path){window.open(path)
}

将新开一个窗口页面进行下载,下载完毕,页面会自动关闭。会有一瞬间的白屏显示及消失。

function download(path){location.href = path
}

将在当前页面进行下载。触发浏览器的下载弹框,交互友好。常规下载推荐这种方式。

这两种方式有如下优缺点:

优点

  • 简单方便直接。
  • 将下载任务转交给浏览器进行处理,可看到下载进度。

缺点

  • 会出现 url 长度限制问题。
  • 需要注意 url 编码问题,可对编码进行decodeURIComponent处理。
  • 浏览器可直接浏览的文件类型是不提供下载的,如 txt、png、jpg、gif 等。
  • 不能添加 header,也就不能进行鉴权。

标签 或 点击事件

a标签的download

我们知道,a 标签可以访问下载文件的地址,浏览器帮助进行下载。但是对于浏览器支持直接浏览的txt、png、jpg、gif等文件,是不提供直接下载(可右击从菜单里另存为)。为了解决这个问题,可以利用download属性。

要知道浏览器是否支持download属性,简单的一句代码即可区分

const isSupport = 'download' in document.createElement('a');

此属性指示浏览器下载 URL 而不是导航到它,因此将提示用户将其保存为本地文件。如果属性有一个值,那么此值将在下载保存过程中作为预填充的文件名(如果用户需要,仍然可以更改文件名)。此属性对允许的值没有限制,但是 / 和 \ 会被转换为下划线。大多数文件系统限制了文件名中的标点符号,故此,浏览器将相应地调整建议的文件名。

  • 此属性仅适用于同源 URL。
  • 尽管 HTTP URL 需要位于同一源中,但是可以使用 blob: URLdata: URL ,以方便用户下载使用 JavaScript 生成的内容(例如使用在线绘图 Web 应用程序创建的照片)。
  • 如果 HTTP 头中的 Content-Disposition 属性赋予了一个不同于此属性的文件名,HTTP 头属性优先于此属性。
  • 如果 HTTP 头属性 Content-Disposition 被设置为 inline(即 Content-Disposition='inline'),那么 Firefox 优先考虑 HTTP 头 Content-Disposition download 属性

基于上面描述,如果你尝试下载跨域链接,那么其实download的效果就会没了,跟不设置download表现一致。即浏览器能预览的还是会预览,而不是下载。

简单用法:

点击下载

可以带上属性值,指定下载的文件名称,即重命名下载文件。不设置的话默认是文件原本名。

点击下载

还可以带上文件名及后缀名,对文件名称及文件MIME类型进行覆盖。不过一般不会对文件类型进行处理,除非下载无法自动处理该文件类型。

点击下载

如上,会下载了一个名叫test.png的图片

对于在跨域下不能下载浏览器可预览的文件,其实可以跟后端协商好,在后端层做多一层转发,最终返回给前端的文件链接跟下载页同域就好了。

优点

  • 能下载浏览器可浏览的文件

缺点

  • 不能下载跨域下的浏览器可浏览的文件
  • 不能进行鉴权

通过点击事件下载

function download(path, fileName){const a = document.createElement('a')a.href = patha.download = fileNamea.click()
}

利用Blob对象

该方法较上面的直接使用a标签download这种方法的优势在于,它除了能利用已知文件地址路径进行下载外,还能通过发送ajax请求api获取文件流进行下载。毕竟有些时候,后端不会直接提供一个下载地址给你直接访问,而是要调取api。

利用Blob对象可以将文件流转化成Blob二进制对象。该对象兼容性良好,需要注意的是

Safari has a serious issue with blobs that are of the type application/octet-stream

进行下载的思路很简单:发请求获取二进制数据,转化为Blob对象,利用URL.createObjectUrl生成url地址,赋值在a标签的href属性上,结合download进行下载。

/*** 下载文件* @param {String} path - 请求地址* @param {String} name - 文件名字*/
function downloadFile (path, name) {const xhr = new XMLHttpRequest();xhr.open('get', path);xhr.responseType = 'blob';xhr.send();xhr.onload = function () {if (this.status === 200) {let resBlob = this.response// 当未设置responseType为'blob'时,则将此注释放开// resBlob = new Blob([resBlob], { type: xhr.getResponseHeader('Content-Type') });const url = URL.createObjectURL(resBlob);const a = document.createElement('a');a.href = url;a.download = name;a.click();URL.revokeObjectURL(url);}};
}

因为发请求时已设置返回数据类型为Blob类型(xhr.responseType = ‘blob’),所以target.response就是一个Blob对象,打印出来会看到两个属性size和type。虽然type属性已指定了文件的类型,但是为了稳妥起见,还是在download属性值里指定后缀名,如Firefox不指定下载下来的文件就会不识别类型。

如果发送请求时不设置xhr.responseType = ‘blob’,默认ajax请求会返回DOMString类型的数据,即字符串。这时就需要注释的代码了,对返回的文本转化为Blob对象,然后创建blob url。

优点

  • 能下载浏览器可浏览的文件。
  • 可设置header,也就可添加鉴权信息。

利用base64

这里的用法跟上面用Blob大同小异,基本上思路是一样的,唯一不同的是,上面是利用Blob对象生成Blob URL,而这里则是生成Data URL,所谓Data URL,就是base64编码后的url形式。

/*** 下载文件* @param {String} path - 请求地址* @param {String} name - 文件名字*/
function downloadFile (path, name) {const xhr = new XMLHttpRequest();xhr.open('get', path);xhr.responseType = 'blob';xhr.send();xhr.onload = function () {if (this.status === 200) {const fileReader = new FileReader();fileReader.readAsDataURL(this.response);fileReader.onload = function () {const a = document.createElement('a');a.href = this.result;a.download = name;a.click();};}};
}

关于文件名

有时候我们在发送下载请求之前,并不知道文件名,或者文件名是后端提供的,我们就要想办法获取。

Content-Disposition

当返回文件流的时候,我们在浏览器上观察接口返回的信息,会看到有这么一个header:Content-Disposition

Content-disposition: attachment; filename=20200323151823_190342.xlsx; filename*=UTF-8''CMCoWork_%E6%B5%8B%E8%AF%95

上面的值是例子。

其中包含了文件名,我们可以想办法获取其中的文件名。我们看到,有filename=和filename*=,后者不一定有,在旧版浏览器中或个别浏览器中,会不支持这种形式,filename*采用了RFC 5987中规定的编码方式。

所以你要获取文件名,就变成,截取这段字符串中的这两个字段值了。

看上面的例子大家可能发现,怎么值怪怪的。是的,如果名字是英文,那好办, 如果是有中文或者其他特殊符号,是需要处理好编码的

  • filename,需要后端处理好编码形式,但是就算后端处理好了,也会应每个浏览器的不同,解析的情况也不同。是个比较难处理好的家伙,所以才有后面的filename*
  • filename*,是个现代浏览器支持的,为了解决filename的不足,一般是UTF-8,我们用decodeURIComponent就能解码了,能还原成原本的样子。当然,解码前你要把值中的UTF-8’'这种部分给去掉。

所以,在我们实现之前,我们就要明白,取Content-Disposition的内容,并不是百分百能符合你预期的,除非你的文件名全是英文数字。

我们提取文件名值:

// xhr是XMLHttpRequest对象
const content = xhr.getResponseHeader('Content-disposition');
if (content) {let name1 = content.match(/filename=(.*);/)[1]; // 获取filename的值let name2 = content.match(/filename\*=(.*)/)[1]; // 获取filename*的值name1 = decodeURIComponent(name1);name2 = decodeURIComponent(name2.substring(7)); // 这个下标7就是UTF-8''
}

上面我们获得了两个文件名name1,name2,如果两个都存在,那么我们优先取name2的,因为这个更靠谱,name1如果包含中文或特殊符号,就有风险还原不了真正的文件名。

缺陷

  • 非全数字英文的文件名,如果浏览器只支持filename,获取的文件名编码可能会有问题。

自定义header

本质上跟上述的Content-Disposition差不多,只是我们这里不使用默认的header,我们自己自定义一个response header,跟后端决定好编码方式返回,前端直接获取这个自定义header,然后使用对应的解码即可,如使用decodeURIComponent

但是我们都要知道,在跨域的情况下,前端获取到的header只有默认的6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma

所以你想要获取到别的header,需要后端配合,设置

Access-Control-Expose-Headers: Content-disposition, custom-header

这样,前端就能获取到对应暴露的header字段,需要注意的是,Content-disposition也是需要暴露的。

重命名

这里提供个方法来获取文件后缀名

function getFileType (name) {const index = name.lastIndexOf('.');return name.substring(index + 1);
}

上一篇:VS2017使用Eigen库

下一篇:3.15日报

相关内容

热门资讯

python:DIY字符画的程... 目录开发环境要求运行方法具体的操作步骤如下:代码示例源码及运行程序下载地址 开发环境要...
特朗普被判后,上诉!他还觉得冤... 三名法官中,除了一名是奥巴马时期由民主党政府任命以外,另两位法官都是共和党政府时期任命。而就这样的搭...
增程已老、纯电难料,理想的现实... 理想汽车于北京时间 5 月 29 日晚间港股盘后、美股盘前发布了 2024 年第一季度财报。一句话,...
Launching nodes... 文章目录背景介绍运行一个启动文件(可选)控制Turtlesim节点 参考...
金融测试项目 最近测试项目很多很忙,已经多到了996的地步,但我依然认为阶段性的整理永...
特朗普2019年来首次约见鲍威... 周四,美联储在一份声明中表示,美联储主席鲍威尔5月29日周四应美国总统特朗普的邀请,在白宫与其会面。...
DOGE效应显现?华盛顿等“深... 特朗普政策给美国劳动力市场带来不确信行,上周美国失业救济续领人数大幅增加。美国劳工部周四发布的最新数...
贷款利率太高惹的祸!美国4月成... 周四,根据美国房地产经纪人协会(NAR)的数据,美国4月成屋签约销售指数环比大跌6.3%,为自202...
巨子生物胶原蛋白“缺量罗生门”... 重组胶原蛋白龙头企业巨子生物(2367.HK)正卷入成分添加“缺斤短两”的争议中。5月24日,美妆行...
黄金投资崩塌!大案,涉数亿资金... 5月15日,林明像往常一样,熟练地打开永坤黄金的线上平台,点击“黄金提现”按钮。按以往三年的操作,资...
CentOS jdk-8u36... 系统自带JDK卸载: java路径查询: whereis java查找 ...
马斯克“副将”据称届满离任政府... 财联社5月30日讯(编辑 赵昊)综合多家媒体报道,知情人士透露,曾经是埃隆·马斯克“政府效率部”中实...
一度涨超6%,市值重回全球第一... 科技股“遇冷”之际,英伟达这份财报来得可谓及时。当地时间5月28日,英伟达披露了2026财年一季度财...
特朗普与哈佛的冲突:3亿美国人... 特朗普与哈佛的冲突反映了当下美国社会意识形态的严重分裂。哈佛作为美国顶尖学府,代表着传统的精英教育和...
宇树科技回应更名:是公司运营方... 【宇树科技回应更名:是公司运营方面的常规变更】29日,宇树科技向合作伙伴表示,因公司发展需要,杭州宇...
前端资源共享方案对比-笔记:i... 前端页面资源如何分享,常见的有iframe,其次是js-sdk。这两类的在地图类工具经...
PTA:L1-040 最佳情侣... L1-040 最佳情侣身高差 问题描述:         专家通过多组情侣研究数据发现...
LeetCode 热题 HOT... 介绍 对于算法题,按题型类别刷题才会更有成效,因此我这里在网上搜索并参考...
会过日子的人都在用,超八成人能... “618”进程过半。近日,新浪金融研究院的一份《分期消费习惯调研》显示,“能赚会算”、“能花会省”的...
股市必读:奥特维(688516... 截至2025年5月29日收盘,奥特维(688516)报收于33.41元,上涨2.14%,换手率0.9...
丧钟已敲响,新能源汽车金融泡沫... 某宇宙第一大车企是谁?大家心里都有数,名字我就不说了。 这家车企, 2020年负债1366亿,到 2...
指针进阶(上) 内容小复习🐱: 字符指针:存放字符的数组 char arr1[10];...
基于OpenCv的传统视觉应用... 图像生成 OpenCv是计算机视觉中经典的专用库,具备支持多语言、跨平台的优点...
经纬早班车|美国一季度经济环比...   【隔夜重磅】  美股收高,英伟达涨逾3%  美东时间周四,美股三大指数集体收涨。道指比前一交易日...
深入剖析Linux——进程信号 致前行的人:                 要努力,但不着急ÿ...
未来超额收益仍在于规模化扩张的...   当前,地缘博弈加剧、技术封锁频现,全球宏观环境正经历深度重构,而AI(人工智能)引发的“算力革命...
玄戒芯片亮相,雷军离超越乔布斯... 近日,小米玄戒O1芯片引发全球科技界关注。雷军在接受央视《面对面》节目专访中曾提到:他创业的源头是在...
【CSS 知识总结】第二篇 -... 一,前言 上一篇,简单介绍了 html 标签和使用语义化的好处ÿ...
学习28个案例总结 学习前         对于之前遇到的问题没有及时总结,导致做什么事情都是新的一样。没...
美股震荡收高,美上诉法院恢复特... *英伟达财报提振科技股*美国上诉法院恢复实施特朗普政府关税政策*美国一季度经济环比萎缩0.2%当地时...