前端下载一般分为两种情况,一种是后端直接给一个文件地址,通过浏览器打开就可以下载,另外一种则需要发送请求,后端返回二进制流数据,前端解析流数据,生成URL,实现下载。
1. a标签:
通过a标签的download属性来实现文件下载,这种方式是最简单的,也是比较常用的方式,先来看示例代码:
<a href="http://www.baidu.com" download="baidu.html">下载</a>
通过上面这个示例,我们发现点击下载,是跳转到了百度的首页,而并没有真的下载文件。
因为a标签下载只能下载同源的文件,如果是跨域的文件,这里包括图片、音视频等媒体文件,都是预览,也无法下载。
上面的代码是直接通过书写a标签来实现文件下载,我们也可以通过js来实现,代码如下:
const download = (filename, url) => {
let a = document.createElement('a');
a.style = 'display: none'; // 创建一个隐藏的a标签
a.download = filename;
a.href = url;
document.body.appendChild(a);
a.click(); // 触发a标签的click事件
document.body.removeChild(a);
}
效果和上面的一样,都是跳转到百度的首页,没有下载文件。
这里的重点是a标签的download属性,这个属性是HTML5新增的。
它的作用是指定下载的文件名,如果不指定,那么下载的文件名就会根据请求内容的Content-Disposition来确定,如果没有Content-Disposition,那么就会使用请求的URL的最后一部分作为文件名。
- Content-Disposition是什么?
Content-Disposition是指在HTTP响应头部中用来控制客户端保存文件的行为的一个参数。这个参数可以设置为“inline”或者“attachment”,前者表示直接在浏览器中打开文件,后者表示让浏览器下载文件。
例如,Content-Disposition:attachment;filename=”example.mp4″表示强制浏览器下载当前HTTP响应中的example.mp4文件。而Content-Disposition:inline;filename=”example.pdf”则会在浏览器中直接打开当前HTTP响应中的example.pdf文件。
- Content-Disposition的作用和用法
Content-Disposition主要用于告诉浏览器如何处理服务器返回的文件。当服务器返回一个文件的时候,浏览器默认会根据文件的MIME类型来决定如何处理。但是,如果服务器希望浏览器采取特定的行为,比如下载文件,就可以通过Content-Disposition来告诉浏览器采取什么行动。
在实际应用中,Content-Disposition通常是在服务器端设置的,而不是在客户端设置。例如,在Spring框架中,可以通过添加以下代码来为响应设置Content-Disposition。
<response-headername="Content-Disposition"expression="attachment;filename=myfile.pdf"/>
上述代码表示响应的Content-Disposition为“attachment”,并指定下载的文件名为“myfile.pdf”。
2. window.open
使用a标签的也可以通过window.open来实现,效果是一样的,代码如下:
window.open('http://www.baidu.com', '_blank', 'download=baidu.html')
_blank,就是在新的页面打开,不指定的话,默认在当前页打开;a标签的download属性也是可以使用。同样也不能下载跨域的文件。
3. 后端提供接口,通过文件流下载
3.1 方案一 a标签+download属性
当url是同源(同域名、同协议、同端口号)时,这种情况用 a标签加download属性的方式即可,download属性指示浏览器该下载而不是打开该文件,同时该属性值即下载时的文件名;
3.2 方案二 后端设置下载请求的响应头 Content-Disposition 强制下载
这是最通用的一种方式 不受跨域和请求方式的影响
Content-Disposition: attachment; filename=“filename.jpg”
想使用window.open实现强制下载的可以用这种方式
在常规的 HTTP 应答中,该响应头的值表示对响应内容的展现形式
- inline 表示将响应内容作为页面的一部分进行展示
- attachment 表示将响应内容作为附件下载,大多数浏览器会呈现一个“保存为”的对话框
- filename(可选) 指定为保存框中预填的文件名
3.3 方案三 通过接口跨域请求,动态创建a标签,以blob形式下载
当接口请求的跨域问题已经解决时(如Nginx方式),可以直接通过请求的方式拿到文件流,将文件流转为blob格式,再通过a标签的download属性下载
3.3.1 fetch发送请求
fetch('/upload/user.png').then((res) => {
res.blob().then((blob) => {
const blobUrl = window.URL.createObjectURL(blob);
// 这里的文件名根据实际情况从响应头或者url里获取
const filename = 'user.jpg';
const a = document.createElement('a');
a.href = blobUrl;
a.download = filename;;
a.click();
window.URL.revokeObjectURL(blobUrl);
});
});
-
上面通过原生fetch请求,动态生成一个a标签实现文件下载。
-
res.blob() 该方法是Fetch API的response对象方法,该方法将后端返回的文件流转换为返回blob的Promise;blob(Binary Large Object)是一个二进制类型的对象,记录了原始数据信息。
-
URL.createObjectURL(blob) 该方法的返回值可以理解为一个 指向传入参数对象的url 可以通过该url访问 参数传入的对象。
- 该方法需要注意的是,即便传入同一个对象作为参数,每次返回的url对象都是不同的。
- 该url对象保存在内存中,只有在当前文档(document)被卸载时才会被清除,因此为了更好的性能,需要通过URL.revokeObjectURL(blobUrl)主动释放。
3.3.2 axios发送请求
download(url: string, body: any, fileName: string, method?) {
return axios({
method: method ||'post',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
url,
responseType: 'blob',
headers: { //如果需要权限下载的话,加在这里
Authorization: '123456'
}
data: JSON.stringify(params),
timeout: 60 * 1000
}).then((res: any) => {
if (!res) {
message.error('下载失败')
return
}
console.log('res:', res)
let url = window.URL.createObjectURL(new Blob([res], { type: 'application/vnd.ms-excel' }))
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
if (!fileName || typeof fileName != "string") {
fileName = "下载文件"
}
link.setAttribute('download', fileName + '.xlsx')
document.body.appendChild(link)
link.click()
document.body.removeChild(link); //下载完成移除元素
window.URL.revokeObjectURL(url); //释放掉blob对象
})
}
3.3.3 XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open('GET', '/upload/user.png', true);
xhr.responseType = 'blob';
xhr.setRequestHeader('satoken', JSON.parse(sessionStorage.getItem('id_token'))); //设置请求头
xhr.onload = function() {
if (this.status === 200) {
const fileName = 'test.jpg';
const blob = new Blob([this.response]);
const blobUrl = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = blobUrl;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(blobUrl);
}
};
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
// 成功后 做一些操作
}
};
3.4 方案总结
-
非跨域情况下 给a标签加上 download 属性,如 < a href=“url” download=“xxx.png”>< /a >
download 里写文件名 注意后缀 (值非必填) -
所有情况通用的方式: 后端设置下载请求的响应头 Content-Disposition: attachment; filename=“filename.jpg”
- attachment 表示让浏览器强制下载
- filename 用于设置下载弹出框里预填的文件名
-
通过请求解决跨域问题 动态创建a标签通过blob形式下载 具体看下面解析
浏览器通过请求头Content-Type中的MIME类型(媒体类型,通常称为 Multipurpose Internet Mail Extensions 或 MIME 类型,如 :image/jpeg application/pdf)识别数据类型,对相应的数据做出相应处理,对于图像文本等浏览器可以直接打开的文件,默认处理方式就是打开,为了避免浏览器直接打开文件我们需要做一些处理;
- 通过fetch、axios、XMLHttpRequest 请求,这里主要的逻辑是当我们的请求成功后,我们会拿到响应体的response,这个response就是我们要下载的内容,然后我们把它转换成blob对象,然后通过URL.createObjectURL来创建一个url,然后通过a标签的download属性来实现文件下载。
- blob
Blob对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。
Blob表示的不一定是JavaScript原生格式的数据。File 接口基于 Blob,继承了blob的功能并将其扩展以支持用户系统上的文件。
blob对象是html5新增的对象,它的作用是用来存储二进制数据的,比如图片、视频、音频等,它的使用方法如下:
/**
* @param {Array} array 二进制数据,是一个由ArrayBuffer, ArrayBufferView, Blob, DOMString 等对象构成的 Array ,或者其他类似对象的混合体,它将会被放进 Blob。DOMStrings 会被编码为 UTF-8。
* @param {Object} options 配置项,是一个可选的BlobPropertyBag字典,它可能会指定如下两个属性:
* @param {String} options.type 文件类型,它代表了将会被放入到 blob 中的数组内容的 MIME 类型。
* @param {String} options.endings 用于指定包含行结束符\n的字符串如何被写入。默认为transparent,表示不会修改行结束符。还可以指定为native,表示会将\n转换为\r\n。
*/
const blob = new Blob([], { type: '' })
MIME知识点:MIME
常用 MIME 类型:
{ “.xls”, “application/vnd.ms-excel” },
{ “.xlsx”, “application/vnd.openxmlformats-officedocument.spreadsheetml.sheet” },
{ “.doc”, “application/msword” },
{ “.docx”, “application/vnd.openxmlformats-officedocument.wordprocessingml.document” },
{ “.zip”, “application/zip” },
{ “.json”, “application/json” },
{ “.jpeg”, “image/jpeg” },
{ “.jpg”, “image/jpeg” },
{ “.png”, “image/png” },
{ “.ppt”, “application/vnd.ms-powerpoint” },
{ “.pptx”, “application/vnd.openxmlformats-officedocument.presentationml.presentation” },
{ “.mp3”, “audio/mpeg” },
{ “.mp4”, “video/mp4” },
这里主要关注的是type属性,默认情况下,blob对象是没有type属性的,那么这个Blob就是一个无类型的Blob,文件不会损毁,但是无法被正常识别。
- URL.createObjectURL
URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的 URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的 URL 对象表示指定的 File 对象或 Blob 对象。
这个方法是用来创建一个url的,它的作用是把一个blob对象转换成一个url,这个url可以用来下载文件,也可以用来预览文件,代码如下:
const url = URL.createObjectURL(blob)
这里需要注意的是,这个url的生命周期和创建它的窗口中的document绑定,也就是说,当我们的document被销毁后,这个url就会失效,所以我们需要在合适的时机销毁它,代码如下:
URL.revokeObjectURL(url)
回到我们刚才下载的问题,我们是通过blob对象来解决,如果这个接口就是下载文件的接口,文件可能是各种类型的,我们应该怎么处理?
通过response的header来获取文件的type:
const type = response.headers['content-type']
const blob = new Blob([response.data], { type })
content-type也可能是任意的二进制数据 application/octet-stream,这个时候我们就需要通过file-type来获取文件的type:
import {fileTypeFromStream} from 'file-type';
const type = await fileTypeFromStream(response.body);
const blob = new Blob([response.data], { type })
4. 注意
-
当用方案一二实现时,下载文件名优先取的是Content-Disposition的filename而不是download,而通过blob的形式,文件名优先取的download属性,如果都没有设置则取的url最后一节。
-
当通过接口的形式fetch(‘/upload/downloadfile’)访问文件,又想保留浏览器的预览效果时,可以仅设置Content-Disposition的filename以指定预览时下载的文件名,否则浏览器会默认取url最后一节,即downloadfile为文件名,导致下载的文件无后缀无法打开。
-
window.open() 和 a标签 执行的是打开链接的操作,类似于将地址直接输入到浏览器中,相当于从一个域跳到另一个域,因此window.open(‘http://xxx’)可以访问而不会报跨域错误;而fetch/xhr 仅是从当前域发送请求,因此fetch(‘http://xxx’)会报跨域错误。
-
浏览器取下载时文件名的优先级是Content-Disposition: filename=“文件名.png” 优先于< a download=“文件名.png”>< / a > 优先于 url最后一节 http://localhost:8087/upload/文件名.png。
5. bolb 文件流展示
- 展示pdf
var bl = new Blob([blob], {
type: 'application/pdf;charset=UTF-8'
})
// var link = document.createElement('a');
// link.href = window.URL.createObjectURL(bl);
// link.target = '_blank';
// link.click();
var urrl = window.URL.createObjectURL(bl);
window.open(urrl)
预览jpg图片把上方 new Blob 里面的type 改成:‘image/jpg;charset=UTF-8’文章来源:https://www.toymoban.com/news/detail-736454.html
其他格式可以参考:菜鸟教程 MIME 类型文章来源地址https://www.toymoban.com/news/detail-736454.html
- 图片预览
- 同步方式
var img = new Image()
img.src = URL.createObjectURL(file)
document.body.appendChild(img)
- 异步方式
var img = new Image()
var Reader = new FileReader()
Reader.readAsDataURL(file)
Reader.onload = function() {
img.src = Reader.result
}
document.body.appendChild(img)
到了这里,关于前端实现文件下载的方法;以及后端返回 blob 文件流,进行展示。的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!