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

ops/2025/1/16 13:17:29/

背景:

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

效果展示:

https://i-blog.csdnimg.cn/direct/e3012526cf2544e18bbba019b996c428.png" width="391" />

下载文件的两种方式: 

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

这串完整的下载路径是:

开发预留

https://i-blog.csdnimg.cn/direct/1b76e5d47e4b420ea1feb98fdf147eba.png" width="873" />

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

https://i-blog.csdnimg.cn/direct/4fe08e11e4414fd6bd34185699e685f0.png" width="1200" /> 结果分析:

直接在浏览器就可以直接访问,可见这个文件没有加密,是不安全的。

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

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

实现效果:

https://i-blog.csdnimg.cn/direct/afaa5f55e016462b8ece6d0c06ff6f03.png" width="695" />

https://i-blog.csdnimg.cn/direct/cd7493d984424f0f8a36a4ebecb53563.png" width="935" />

导出接口: 

https://i-blog.csdnimg.cn/direct/f54eea26bd6d4b43912ff014a502b849.png" width="726" />

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

https://i-blog.csdnimg.cn/direct/5a6de4c122e34d78abd101474c70318f.png" width="694" />

备注:控制台输出的可以看到是个正确的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为对应的类型。

https://i-blog.csdnimg.cn/direct/b19ec898d1d44261b543bcaa43da9e67.png" width="771" />

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:

    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: 

/**** @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.dataconst 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类型是什么:点击访问

https://i-blog.csdnimg.cn/direct/84fd5a6d7c364e8690e7b6bfd175f0de.png" width="1200" />

MIME类型有哪些: 点击访问

https://i-blog.csdnimg.cn/direct/5b10506e9cef4c3a878c3d6ecdb54494.png" width="1200" />

常见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


http://www.ppmy.cn/ops/150562.html

相关文章

Vue.js组件开发-如何自定义Element UI组件

在Vue.js项目中,自定义Element UI组件通常意味着你要基于Element UI的基础组件来创建新的、符合项目需求的组件。这可以通过组合、扩展或封装Element UI的现有组件来实现。 以下是一个基本的步骤指南: 一、准备阶段 ‌1.确保Element UI已安装‌&#…

C++并发编程之std::partial_sum的并行版本

在C中,std::partial_sum 是一个用于计算前缀和的算法,它将输入范围中的每个元素替换为其前缀和。为了提高性能,我们可以设计并实现一个并行版本的 std::partial_sum,以便在多核处理器上并行执行前缀和计算。基本思想是将输入范围划…

科技快讯 | 华为余承东2025新年信;教育部拟同意设置福耀科技大学等本科院校;我国成功发射一箭10星

四部门:畅通数据采集、标注、人工智能应用产业 财联社1月13日电,国家发展改革委等四部门发布《关于促进数据标注产业高质量发展的实施意见》。其中提出,畅通数据采集、标注、人工智能应用产业链,推动数据标注产业上下游协同发展。…

VSCode使用纪要

1、常用快捷键 1)注释 ctrl? 单行注释, altshifta 块注释, 个人测试,ctrl? 好像也能块注释 2)开多个项目 可以先开一个新窗口,再新窗口打开另一个项目,这时就是同时打开多个项目了。 打开…

美化IDE之修改IDEA启动界面logo图片

1,关闭运行的IDEA 2,在IDEA安装目录下的lib里找到app.jar或者platform-impl.jar(因为不同版本会有区别)复制出来 3,解压,找到两个图片idea_logo.png和idea_logo2x.png,分辨率一个为640x400 一个是1280x800 4&#xff0…

Spring Boot中如何处理跨域请求(CORS)

在Spring Boot中,处理跨域请求(CORS, Cross-Origin Resource Sharing)通常有几种方法。可以通过全局配置、控制器级别的配置或者方法级别的配置来实现。以下是三种常见的方式: 1. 全局配置 CORS 你可以在全局配置中处理跨域请求…

[Linux]Docker快速上手操作教程

前言 以下命令并不是docker的所有,仅涉及日常使用时最最常用的命令。 目的之一时给入门的朋友熟悉学习,其二时我自己偶尔使用时备忘。 一、概念 简单介绍下docker的相关概念: 镜像:Docker 镜像是一个轻量级、可执行的独立软件…

蓝桥杯_B组_省赛_2022(用作博主自己学习)

题目链接算法11.九进制转十进制 - 蓝桥云课 进制转换 21.顺子日期 - 蓝桥云课 时间与日期 31.刷题统计 - 蓝桥云课 时间与日期 41.修剪灌木 - 蓝桥云课 思维 51.X 进制减法 - 蓝桥云课 贪心 61.统计子矩阵 - 蓝桥云课 二维前缀和 71.积木画 - 蓝桥云课 动态规划 82.扫雷 - 蓝桥…