0


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

前端下载一般分为两种情况,一种是后端直接给一个文件地址,通过浏览器打开就可以下载,另外一种则需要发送请求,后端返回二进制流数据,前端解析流数据,生成URL,实现下载。

1. a标签:

通过a标签的download属性来实现文件下载,这种方式是最简单的,也是比较常用的方式,先来看示例代码:

<a href="http://www.baidu.com" download="baidu.html">下载</a>

通过上面这个示例,我们发现点击下载,是跳转到了百度的首页,而并没有真的下载文件。

因为a标签下载只能下载同源的文件,如果是跨域的文件,这里包括图片、音视频等媒体文件,都是预览,也无法下载

上面的代码是直接通过书写a标签来实现文件下载,我们也可以通过js来实现,代码如下:

constdownload=(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?){returnaxios({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(newBlob([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 =newXMLHttpRequest();
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 =newBlob([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 =newBlob([],{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 =newBlob([response.data],{ type })

content-type也可能是任意的二进制数据 application/octet-stream,这个时候我们就需要通过file-type来获取文件的type:

import{fileTypeFromStream}from'file-type';const type =awaitfileTypeFromStream(response.body);const blob =newBlob([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 =newBlob([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 类型

  • 图片预览
  1. 同步方式
var img =newImage()
img.src =URL.createObjectURL(file)
document.body.appendChild(img)
  1. 异步方式
var img =newImage()var Reader =newFileReader()
Reader.readAsDataURL(file)
Reader.onload=function(){
    img.src = Reader.result
}
document.body.appendChild(img)
标签: 前端 javascript

本文转载自: https://blog.csdn.net/renlimin1/article/details/127231667
版权归原作者 Rlm* 所有, 如有侵权,请联系我们删除。

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

还没有评论