a标签/js下载文件(2020)

article/2025/9/28 12:32:54

a标签/js 下载服务器文件

  • 一、二进制式下载
    • 1、responseType(请求)
    • 2、Content-Type(响应)判断是普通数据还是文件流(可选)
    • 3、Content-Disposition(响应)和文件名(可选)
    • 4、文件下载
  • 二、URL下载
    • 1、a标签的href属性
    • 2、a标签的download属性
    • 3、下载文件
    • 4、download属性的限制
    • 5、跨域文件下载解决方案-后端
    • 6、跨域文件下载解决方案-前端(鸡肋)
    • 7、跨域文件下载解决方案-前端(有效)

主要参考【前端:下载文件实现方式及跨域下载(详解) https://blog.csdn.net/qq_43471802/article/details/103436595】
加上自己遇到的问题,记录并分享,如有错误,请指正

下载文件根据后端返回的是文件流还是URL下载url地址,主要分两种:

  • 二进制式下载
  • URL下载

一、二进制式下载

如果后端返回二进制文件流,前端需要使用Blob接收

1、responseType(请求)

首先在前端发送请求时就应在请求头中,用responseType告知服务器需要返回的数据类型,responseType默认是“json”,这里我们请求的是文件流:“blob”

不同的请求插件设置header的方式不同,用axios来说,axios.post(url, data, config),responseType是在config里设置的(这些设置应该是在底层赋给请求头):

export function download(url, data) {return axiosInstance.post(url,data,{responseType: 'blob'});
}

如果这里不定义responseType,下载下来的文件内容会乱码

2、Content-Type(响应)判断是普通数据还是文件流(可选)

服务器返回不同数据,我们会做不同的处理,json我们直接取用,文件流数据需处理后下载。

在axios项目中,一般为了给所有的请求做一些统一处理,比如baseURL、请求带token,回包错误码提示,在底层封装一个axios实例,所有的请求都调用该实例的方法。

这种情况下,文件的请求就有可能和普通数据的请求调用的是同一个实例。直接在总响应拦截器里判断出文件流并执行下载,就不用在每一文件请求协议回调里各自再写一遍执行下载的代码。如何区分响应数据的是文件流还是json数据就很有必要了。

头部Content-Type表示服务端发送的类型及采用的编码方式,一般为application/json.而回包是文件,则Content-Type 一般为“octets/stream”,我们就以此判断是返回的是文件还是普通数据。

//axios响应拦截器里
if(res.headers &&(res.headers['content-type'].indexOf('application/x-msdownload') != -1 ||res.headers['content-type'].indexOf('octets/stream') != -1 ||res.headers['content-type'].indexOf('application/octet-stream') != -1)){//执行下载方法
}

3、Content-Disposition(响应)和文件名(可选)

还是针对第2节所描述的情况:判断出来什么时候是文件数据,在回包里拿到整个文件,而文件名就需要从响应头里的Content-Disposition属性获取
【官方文档:Content-Disposition】

Content-Disposition可以出现在消息主体中, 也可以出现在multipart/form-data类型的应答消息体中。Content-Disposition在不同的地方有不同的作用和意义,而文件下载属于前者,下面我们也只说第一种。

在常规的HTTP应答中,Content-Disposition在响应头,有两个参数。第一个参数用于指示回复的内容该以何种形式展示:

  • inline — 默认值,内联形式。表示回复中的消息体会以页面的一部分或者整个页面的形式展示)
  • attachment附件形式。意味着消息体应该被下载到本地,大多数浏览器会自动触发一个“保存为”的对话框,将filename的值预填为下载后的文件名,假如它存在的话
    当第一个参数为attachment 时才有第二个参数——filename

这里Content-Disposition应为attachment,文件名就在第二个参数里:
在这里插入图片描述
我们可以从参数里分离出文件名:

if(res.headers["content-disposition"] && res.headers["content-disposition"] &&  res.headers["content-disposition"].split(";").length > 1 &&res.headers["content-disposition"].split(";")[1].split("filename=").length > 1){filename = res.headers["content-disposition"].split(";")[1].split("filename=")[1];filename = Base64.decode(filename, "utf-8");
}

可能遇到的坑:js代码里无法获取响应header的Content-Disposition字段。这个问题会在我的另一篇文章里做记录【js无法获取响应header的Content-Disposition字段(2020)】

4、文件下载

上面第2、3节,在响应拦截器里判断是文件下载并拿到文件名,都不是必须的步骤,只因为我的项目用的axios,并且对axios实例有封装,请求文件的协议也需要和其他普通协议一样带一些参数和配置,才有区分判断的需要

下面就是正式处理响应数据(文件流)并下载文件的方法。

服务返回文件流数据(blob对象),需要用JS对象Blob构造函数来接收并储存,然后用URL.createObjectURL生成一个可使用的URL地址,之后把这个URL地址赋给一个临时创建的a标签,用a标签HTML5新属性download实现本地储存,以达到实现下载需求:

/*** 下载文件* @param data  二进制文件流数据* @param filename*/
const downloadByFile= function (data, filename) {if (!data) returnlet url = window.URL.createObjectURL(new Blob([data]))let link = document.createElement('a')link.style.display = 'none'link.href = urllink.setAttribute('download', filename)document.body.appendChild(link)link.click()document.body.removeChild(link);
}

二、URL下载

服务器只是返回文件的url和name(如下):
在这里插入图片描述

1、a标签的href属性

这种情况还是借助a标签进行下载,a标签的href属性包含超链接指向的 URL 或 URL 片段。
对于大多数文件,只要用href指向文件url,点击a标签,就会下载文件:

<a href="${fileUrl}">下载文件</a>

然而,对于一些浏览器可以识别的文件格式,比如.txt、.png、.jpg 、.mp4等,这样写只会直接在浏览器打开该文件,无法下载。针对这种情况,H5新增了download属性

2、a标签的download属性

download属性可以指示浏览器下载 URL 而不是导航到它。

如果download属性有一个值,那么此值将在下载保存过程中作为预填充的文件名。

所以只要加上download属性,就可以正常下载文件了。

3、下载文件

使用a标签下载文件,有两种实现:

  • 可以直接在html里写个a标签:
<a href="${fileUrl}" download>下载文件</a>
  • 对于动态数据,可在js里用api创建a标签:
/*** 下载文件* @param url* @param filename*/
function downloadFile(url,filename) {if (!url) returnlet link = document.createElement('a') //创建a标签link.style.display = 'none'  //使其隐藏link.href = url //赋予文件下载地址link.setAttribute('download',filename) //设置下载属性 以及文件名document.body.appendChild(link) //a标签插至页面中link.click() //强制触发a标签事件document.body.removeChild(link);
}

注意!!!!:这里注意a元素的href直接是url字符串,而上面【一、二进制式下载】下载方法里,a元素的href是blob对象对象通过createObjectURL转化得到的可用url。

4、download属性的限制

如果加上download属性,文件还是直接打开,无法正常下载,这有可能是download属性失效造成的。

download属性也受同源策略的影响,即非同一端口下不能直接下载第三方文件,所以这里download失效之后做的仅仅是跳转功能:
官方文档

所以上面的下载文件方法并不适用于下载跨域文件。

5、跨域文件下载解决方案-后端

针对跨域文件下载问题,可以前端仍是采用上面的方法,后端 oss批量设置HTTP头,设置HTTP请求头为Content-Disposition
为 attachment即可,访问的时候就是直接下载而不是浏览!

这种方法是在参考的文章里提到的,我没测过,不知道可行性,因为url是文件服务器的地址,后端反馈说发送协议可以设置HTTP请求头,但如果没发协议无法对文件服务器如此设置。所以如果有谁知道这种方案,可以交流下

6、跨域文件下载解决方案-前端(鸡肋)

可以对文件类型判断,如果不是图片、文本文件,上面的方法不用加download属性就是有效的;
如果是图片,可以试试下面的方法:

export function downloadIamge(url,name){let image = new Image();// 解决跨域 Canvas 污染问题image.setAttribute("crossOrigin", "anonymous");image.onload = function() {let canvas = document.createElement("canvas");canvas.width = image.width;canvas.height = image.height;let context = canvas.getContext("2d");context.drawImage(image, 0, 0, image.width, image.height);let url = canvas.toDataURL("image/png"); //得到图片的base64编码数据let a = document.createElement("a"); // 生成一个a元素let event = new MouseEvent("click"); // 创建一个单击事件a.download = name || "photo"; // 设置图片名称a.href = url; // 将生成的URL设置为a.href属性a.dispatchEvent(event); // 触发a的单击事件};image.src = url;
}

但是这种方法,好像也有限制,如果不行就没办法了,这里列出只做记录参考。而且txt格式的文件依然无法下载

7、跨域文件下载解决方案-前端(有效)

因为即使是跨域文件,将该url输入在浏览器地址栏回车,是可以查看的,打开控制台,可以看到这里是get图片资源显示出来:
在这里插入图片描述
所以,我们也可以直接以该文件的url发送一个get请求,不通过后端协议,而是直接向文件服务器请求资源。

理论上如果url可以直接查看到文件,那这个get请求就应该也能成功。get请求仍需设置请求头responseType

这个get请求自然直接返回该文件流,我们用上面【二进制式下载】的方法处理返回结果,就能成功下载文件。这也就是变相使用二进制式下载

import axios from 'axios'
/*** 下载文件* @param url 文件url* @param fileName*/
function downloadByURL(url,fileName) {axios.get(url, {responseType: 'blob'}).then((response) => {downloadByFile(response.data,fileName)});
}/*** 下载文件* @param data  二进制文件流数据* @param filename*/
const downloadByFile= function (data, filename) {if (!data) returnlet url = window.URL.createObjectURL(new Blob([data]))let link = document.createElement('a')link.style.display = 'none'link.href = urllink.setAttribute('download', filename)document.body.appendChild(link)link.click()document.body.removeChild(link);
}

这个方法亲测有效,暂时没有遇到什么问题。

用axios的项目此时注意,这个get请求应是不需要token之类的,如果底层封装过的axios实例里拦截器各种加东西判断处理,这里就不用和其他的普通请求共用一个封装过的axios实例,使用最原始的axios实例即可,避免拦截器里的处理对它造成影响。


http://chatgpt.dhexx.cn/article/cTfSpMBW.shtml

相关文章

a标签下载pdf文件

通过a标签的download属性可以实现下载pdf文件&#xff0c;不过有一个弊端&#xff1a;网站和pdf文件必须在同一域名下才可行&#xff0c;不然就是先打开一个新标签预览&#xff0c;然后点击下载按钮进行下载。

a标签实现文件下载功能

文件下载原理&#xff1a; java后台只能做到返回二进制流或文件给前端&#xff0c;最终在前端页面创建一个a&#xff0c;然后触发a的点击事件实现点击下载效果。 1.无需token的 2.请求头需要token 接口&#xff1a; 点击事件:

前端-基于a标签实现下载功能

最近在一个项目中需要实现下载功能&#xff0c;在前期与后端多次联调尝试使用接口下载文件无果后&#xff0c;最后抱着试一试的心态使用了A标签下载&#xff0c;方法是有效的&#xff0c;但是有部分局限性&#xff01;&#xff01;&#xff01; 使用a标签实现下载的步骤如下&am…

a标签的download属性(荐)

在html 中 a 链接有 download 这样一个属性 它有什么用呢&#xff1f;&#xff01; 我们在页面中提供下载的时候&#xff0c;都需要去配置一些服务端的东西&#xff0c;比如指定 zip 文件就通知浏览器下载这个文件。 但是&#xff0c;比如 .jpg 这样的图片文件&#xff0c;如…

html a标签下载文件

<a href"/user/test/xxxx.txt" download"文件名.txt">点击下载</a>

前端a标签实现文件下载

a标签实现文件下载 如果想通过纯前端技术实现文件下载&#xff0c; 下载的静态文件放项目路径下&#xff0c;A标签下载&#xff0c;herf指定项目路径&#xff0c;加上download属性。a链接默认的是在同一页面打开&#xff0c;如果我们需要打开新的页面&#xff0c;就需要添加t…

html利用a标签实现下载本地的文件

在写html页面的时候&#xff0c;需要在网页上提供一个下载按钮可以下载我自己电脑中的文件。我已经知道了该文件的路径&#xff0c;但是之前看了很多文章都没找到正确办法&#xff0c;一直不知道如何能够下载本地的文件&#xff0c;经过不断实验发现&#xff0c;可以利用a标签中…

一、<a>标签如何实现下载

实习期间负责的第一个项目&#xff1a;广东互联网协会官网。其中有一个很常见的功能----点击下载。 页面截图 在此之前&#xff0c;我所认识的<a>标签只是用于页面跳转的&#xff0c;实现文件下载是如何做到的呢&#xff1f; 答案是&#xff1a;使用href与download属性 …

面试官:如何用a标签实现文件下载?(一文带你手撕知识点)

前言 大家好&#xff0c;今天给大家带来前端小知识&#xff1a;前端利用a标签实现文件&#xff08;图片&#xff09;下载&#xff0c;也就是教大家利用a标签或者是 window.open() 来实现下载功能。 文章目录 前言常用方式方法分析代码实现 常用方式 <a href"url"…

Jquery之遍历元素

J q u e r y Jquery Jquery之遍历元素 使用 e a c h ( ) each() each()方法传入函数两个参数分别为 i n d e x , d o m index,dom index,dom对象。 <body><div>1</div><div>2</div><div>3</div> </body> <script src&quo…

jQuery 遍历数据

在jQuery 中&#xff0c; $.each( )方法主要用于遍历数据&#xff0c;通过该方法&#xff0c;我们可以遍历任何一个对象&#xff0c;比如数组和对象。 语法格式&#xff1a; $.each(object,function(index,ele))示例&#xff1a; &#xff08;1&#xff09;遍历数组的数据 …

jQuery - 元素遍历

前言&#xff1a; 一提到“遍历”,大家一般都能联想到 each() 或者 for()等语法&#xff0c;但是在jQuery中究竟什么是遍历&#xff1f; 什么是遍历&#xff1a; jQuery 遍历&#xff0c;意为"移动"&#xff0c;用于根据其相对于其他元素的关系来"查找&qu…

Jquery-节点遍历4种方法

节点遍历 遍历子元素 遍历同辈元素 遍历前辈元素 其他遍历方法 遍历子元素 children()方法可以用来获取元素的所有子元素 $(selector).children([expr]); 获取<section>的子元素&#xff0c;但不包含子元素的子元素 var $section $("section").childr…

jq遍历元素

jq遍历元素 通过jq遍历元素、并控制一些元素的属性&#xff08;显示/隐藏、value、src等等&#xff09;&#xff0c;是我们在开发之中比较常见的操作&#xff0c;也为我们的业务功能扩宽了方向&#xff0c;接下来我将结合近段时间在工作上的际遇粗略介绍一下一些稍稍复杂的jq遍…

JS 遍历

目录 1. for 数组遍历2. for ... of3. forEach( )4. some( )5. every( )6. filter( )7. map( )8. find( )9. findIndex( )10. reduce( )11. reduceRight( ) 对象遍历1. for ... in2. Object.keys( )3. Object.values( )4. Object.entries( )5. Object.getOwnPropertyNames( ) 1…

数据库——数据字典

数据库——数据字典是什么&#xff1f; 一.数据字典以及使用场景&#xff1a; User表&#xff0c;User主体有很多属性&#xff0c;比如证件&#xff08;身份证、居住证、港澳通行证…&#xff09;地区&#xff08;河北、河南、北京…&#xff09;等&#xff0c;然后表建好了&…

数据库设计--数据字典

数据字典定义 数据字典&#xff08;data dictionary&#xff09;是对于数据模型中的数据对象或者项目的描述的集合&#xff0c;这样做有利于程序员和其他需要参考的人。分析一个用户交换的对象系统的第一步就是去辨别每一个对象&#xff0c;以及它与其他对象之间的关系。这个过…

解决Error: ENOENT: no such file or directory, scandir ‘xxx\node-sass\vendor‘

解决Error: ENOENT: no such file or directory, scandir xxx\node-sass\vendor 前端项目持续部署打包中出现一个奇怪的问题&#xff0c;记录一下。 cnpm install 安装依赖成功 cnpm run build 构建时失败了&#xff0c;错误表示没有 D:\andex\stofrontend\node_modules\node…

Syntax Error: Error: ENOENT: no such file or directory, scandir ‘D:

在使用npm安装node-sass的时候&#xff0c;可能会出现如下的报错&#xff1a; Syntax Error: Error: ENOENT: no such file or directory, scandir D:\work\ 解决方案是执行以下方法&#xff1a; npm rebuild node-sass

扫描dir目录函数之scandir()

scandir: 读取特定的目录数据头文件: dirent.h 函数定义: int scandir(const char *dir, struct dirent ***namelist, int (*select)(const struct dirent *), int (*compar)(const struct dirent**, const struct dirent**)); 说明: scandir()会扫描参数dir指定的目录文件, …