下载文件,浏览器阻止不安全下载

背景:

在项目开发中,遇到需要下载文件的情况,文件类型可能是图片、excell表、pdf、zip等文件类型,但浏览器会阻止不安全的下载链接。

效果展示:

下载文件的两种方式:

一、根据接口的相对url,拼接成完整路径下载

这串完整的下载路径是:

开发预留

在浏览器访问,图片如下:

结果分析:

直接在浏览器就可以直接访问,可见这个文件没有加密,是不安全的。
还有一个原因是实际情况,根据接口的url直接下载的。另外一种导出下载,是发起网络请求的,接收后台传给前端的二进制流之前需要先设置responseType为blob,否则默认会以json获取,下载下来的文件打开会提示文件已损坏。是发起网络请求的,并且后端接口返回的response头的content-type也是对应的类型,我的这里是application/vnd.ms-excel;charset=UTF-8。

二、网络接口,导出excell表格

实现效果:

导出接口:

这个接口返回的数据在控制台打印:

备注:控制台输出的可以看到是个正确的Blob对象,这就说明我们的配置是对的。

实现思路【重点】:

导出接口传参给后端,后端对请求到的数据经过后端拼接,然后输出二进制流文件,然后给前端返回,前端直接下载。

需要注意几点:

1.前端请求需要携带请求体,config里面要带上responseType: 'blob'。举例:

//导出文件【渡船管理】

exportCrewInfoFile(params) {

return request.Get("/data/ferryShip/download?", params, {

headers: {

"Content-Type": "application/json",

},

responseType: 'blob',

});

},

所以我们接收后台传给前端的二进制流之前需要先设置responseType为blob,否则默认会以json获取,下载下来的文件打开会提示文件已损坏。

2.后端最好也要配置response头的content-type为对应的类型。

3.需要给这个Blob对象设置一个type,这个type表明改Blob对象所包含数据的MIME类型。如果类型未知,则该值为空字符串。例如:type: "application/vnd.ms-excel",

/**

*

* @param {*} res 接口返回的文件流

*/

export const dowloadFileUrl = (res) => {

console.log(res)

const fileNames = res.headers['content-disposition']

if (fileNames) {

//解码

const fileName = decodeURIComponent(fileNames.match(/=(.*)$/)[1])

// 处理返回的文件流

const content = res.data

const blob = new Blob([content], {

// type: res.data.type||"application/vnd.ms-excel",

type: res.data.type||"application/octet-stream; charset=utf-8"

});

if ('download' in document.createElement('a')) {

//非IE下载

const a = document.createElement('a') //创建一个a标签

a.download = fileName //指定文件名称

a.style.display = 'none' //页面隐藏

a.href = URL.createObjectURL(blob) // href用于下载地址

document.body.appendChild(a) //插到页面上

a.click() //通过点击触发

URL.revokeObjectURL(a.href) //释放URL 对象

document.body.removeChild(a) //删掉a标签

} else {

//IE10 + 下载

navigator.msSaveBlob(blob, fileName)

}

}

}

三、下载文件的两种方式的对比

实现代码:

代码1:

javascript 复制代码
    if (!data.file) {
      ElMessage.error("文件不存在!");
      return;
    }
    const url = BASEUrl + "/file/" + data.file;//拼接下载地址
    const a = document.createElement("a"); //创建一个a标签
    a.download = data.name; //指定文件名称
    a.style.display = "none"; //页面隐藏
    a.href = url; // href用于下载地址
    document.body.appendChild(a); //插到页面上
    a.click(); //通过点击触发
    URL.revokeObjectURL(a.href); //释放URL 对象
    document.body.removeChild(a); //删掉a标签

代码2:

javascript 复制代码
/**
 *
 * @param {*} fileContent 文件本体
 * @param {*} _fileName 自定义文件名
 */
export const exportFileUtil = (fileContent, _fileName) => {
  const content = fileContent;
  const blob = new Blob([content], {
    type: fileContent.type || "application/octet-stream; charset=utf-8",
  });
  const fileName = _fileName;
  if ("download" in document.createElement("a")) {
    //非IE下载
    const a = document.createElement("a"); //创建一个a标签
    a.download = fileName; //指定文件名称
    a.style.display = "none"; //页面隐藏
    a.href = URL.createObjectURL(blob); // href用于下载地址
    document.body.appendChild(a); //插到页面上
    a.click(); //通过点击触发
    URL.revokeObjectURL(a.href); //释放URL 对象
    document.body.removeChild(a); //删掉a标签
  } else {
    //IE10 + 下载
    navigator.msSaveBlob(blob, fileName);
  }
};
/**
 * 
 * @param {*} res 接口返回的文件流
 */
export const dowloadFileUrl = (res) => {
  console.log(res)
  const fileNames = res.headers['content-disposition']
  if (fileNames) {
      //解码
      const fileName = decodeURIComponent(fileNames.match(/=(.*)$/)[1])
      // 处理返回的文件流
      const content = res.data
      const blob = new Blob([content], {
          // type: res.data.type||"application/vnd.ms-excel",
          type: res.data.type||"application/octet-stream; charset=utf-8"
      });
      if ('download' in document.createElement('a')) {
          //非IE下载
          const a = document.createElement('a') //创建一个a标签
          a.download = fileName //指定文件名称
          a.style.display = 'none' //页面隐藏
          a.href = URL.createObjectURL(blob) // href用于下载地址
          document.body.appendChild(a) //插到页面上
          a.click() //通过点击触发
          URL.revokeObjectURL(a.href) //释放URL 对象
          document.body.removeChild(a) //删掉a标签
      } else {
          //IE10 + 下载
          navigator.msSaveBlob(blob, fileName)
      }
  }
}

总结:

直接拼接url为下载路径,创建一个a标签触发下载;

导出接口通过接口返回的二进制流,经过出来二进制流为Blob且type类型与接口一致。

三、补充理论知识

MIME类型是什么:点击访问

MIME类型有哪些: 点击访问

常见MIME【媒体类型】 ,如下:

扩展名----------MIME类型

.csv--------------text/csv

.jpeg/.jpg-------image/jpeg

.png-------------image/png

.rar--------------application/x-rar-compressed

.doc-------------application/msword

.docx-----------application/vnd.openxmlformats-officedocument.wordprocessingml.document

.xls--------------application/vnd.ms-excel

.xlsx------------application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

.zip--------------application/zip

相关推荐
TE-茶叶蛋6 小时前
Vue Fragment vs React Fragment
javascript·vue.js·react.js
Angindem6 小时前
从零搭建uniapp项目
前端·vue.js·uni-app
玩转4G物联网7 小时前
零基础玩转物联网-串口转以太网模块如何快速实现与TCP服务器通信
服务器·网络·物联网·网络协议·tcp/ip·http·fs100p
hie988948 小时前
HTTP常见的请求方法、响应状态码、接口规范介绍
http
前端小白从0开始8 小时前
Vue3项目实现WPS文件预览和内容回填功能
前端·javascript·vue.js·html5·wps·文档回填·文档在线预览
難釋懷9 小时前
Vue解决开发环境 Ajax 跨域问题
前端·vue.js·ajax
挑战者66688810 小时前
vue入门环境搭建及demo运行
前端·javascript·vue.js
一曝十寒11 小时前
那些常见的 HTTP 状态码
前端·http
程序猿ZhangSir12 小时前
Vue3 项目的基本架构解读
前端·javascript·vue.js
亲亲小宝宝鸭12 小时前
写了两个小需求,终于搞清楚了表格合并
前端·vue.js