前端实现文件下载的方法;以及后端返回 blob 文件流,进行展示。

这篇具有很好参考价值的文章主要介绍了前端实现文件下载的方法;以及后端返回 blob 文件流,进行展示。。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前端下载一般分为两种情况,一种是后端直接给一个文件地址,通过浏览器打开就可以下载,另外一种则需要发送请求,后端返回二进制流数据,前端解析流数据,生成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);
  });
});
  1. 上面通过原生fetch请求,动态生成一个a标签实现文件下载。

  2. res.blob() 该方法是Fetch API的response对象方法,该方法将后端返回的文件流转换为返回blob的Promise;blob(Binary Large Object)是一个二进制类型的对象,记录了原始数据信息。

  3. 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 方案总结

  1. 非跨域情况下 给a标签加上 download 属性,如 < a href=“url” download=“xxx.png”>< /a >
    download 里写文件名 注意后缀 (值非必填)

  2. 所有情况通用的方式: 后端设置下载请求的响应头 Content-Disposition: attachment; filename=“filename.jpg”

    • attachment 表示让浏览器强制下载
    • filename 用于设置下载弹出框里预填的文件名
  3. 通过请求解决跨域问题 动态创建a标签通过blob形式下载 具体看下面解析

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

  1. 通过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. 注意

  1. 当用方案一二实现时,下载文件名优先取的是Content-Disposition的filename而不是download,而通过blob的形式,文件名优先取的download属性,如果都没有设置则取的url最后一节。

  2. 当通过接口的形式fetch(‘/upload/downloadfile’)访问文件,又想保留浏览器的预览效果时,可以仅设置Content-Disposition的filename以指定预览时下载的文件名,否则浏览器会默认取url最后一节,即downloadfile为文件名,导致下载的文件无后缀无法打开。

  3. window.open() 和 a标签 执行的是打开链接的操作,类似于将地址直接输入到浏览器中,相当于从一个域跳到另一个域,因此window.open(‘http://xxx’)可以访问而不会报跨域错误;而fetch/xhr 仅是从当前域发送请求,因此fetch(‘http://xxx’)会报跨域错误。

  4. 浏览器取下载时文件名的优先级是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’

其他格式可以参考:菜鸟教程 MIME 类型文章来源地址https://www.toymoban.com/news/detail-736454.html

  • 图片预览
  1. 同步方式
var img = new Image()
img.src = URL.createObjectURL(file)
document.body.appendChild(img)
  1. 异步方式
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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包