后端返回一个图片链接,前端如何实现下载功能?

纯原创文章,转载请说明来源。

一、背景

要实现一个下载功能,后端直接返回了一个图片的地址https://xxxxx/pic.jpg。如果我们直接通过window.open(url, '_blank') 的方式去下载这个图片,会发现 Chrome 浏览器会对这个图片进行预览,而不是期望的下载。那要怎么做,才能实现这个下载呢?

二、代码实现

Step1. 封装下载通用的方法:

/*** 判断是否IE浏览器,为IE返回true* @returns {boolean}*/
export const isIE = (): boolean => {const userAgent = navigator.userAgent // 取得浏览器的userAgent字符串const isIE = userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1 // 判断是否IE<11浏览器const isEdge = userAgent.indexOf('Edge') > -1 && !isIE // 判断是否IE的Edge浏览器const isIE11 = userAgent.indexOf('Trident') > -1 && userAgent.indexOf('rv:11.0') > -1return isIE || isEdge || isIE11
}/*** 尝试从Blob中保存文件作为JSON* 先尝试将Blob转换为文本并解析为JSON,如果失败(即不是有效的JSON),则调用另一个函数来处理文件的保存* @param fileName 文件名* @param blob* @param fileMime MIME 类型*/
export const attemptSaveFileFromBlobAsJson = (fileName: string, blob: Blob, fileMime: string) => {const fileReader: any = new FileReader()fileReader.readAsText(blob)fileReader.onloadend = () => {try {// 转换解析结果,转换成功代表后端抛错 { code: 400, msg: 'xxx' }JSON.parse(fileReader.data)} catch (err) {saveFileFromBlobDirectly(fileName, blob, fileMime)}}
}/*** 直接保存Blob为文件* @param fileName 文件名* @param blob* @param fileMime MIME 类型*/function saveFileFromBlobDirectly(fileName: string, blob: Blob, fileMime: string) {try {// 下载文件const fileBlob = new Blob([blob], {type: fileMime})if (!isIE()) {// 非IE下载const elink = document.createElement('a')elink.download = fileNameelink.style.display = 'none'elink.target = '_blank'elink.href = URL.createObjectURL(fileBlob)document.body.appendChild(elink)elink.click()URL.revokeObjectURL(elink.href) // 释放URL 对象document.body.removeChild(elink)} else {// IE10+下载;(navigator as any).msSaveBlob(fileBlob, fileName)}} catch (err) {console.error(err)}
}/*** 通过url获取文件名* @param url* @returns*/
export function getFileNameFromUrl(url: string): string | null {try {const parsedUrl = new URL(url)const path = parsedUrl.pathnameconst lastIndex = path.lastIndexOf('/')return lastIndex === -1 ? null : path.substring(lastIndex + 1)} catch (error) {// 如果URL格式不正确,捕获错误并返回null或抛出错误console.error('Invalid URL:', error)return null}
}

Setp2. 封装发送图片请求的方法

import http from '@/plugins/axios'
/*** 获取图片*/
export const gePicBlob = (url: string) => {return http.get<never, any>(url, { responseType: 'blob' })
}
// axios实例需要做一点点改造
const instance = axios.create({// ... 省略相关配置
})
/*** 返回后置拦截*/
instance.interceptors.response.use(function (response) {// blob 类型 直接返回if (response?.config?.responseType === 'blob') return response.data// 省略其余的业务逻辑}
)

Step3. 使用刚刚封装的方法

/*** 点击下载按钮时触发*/
async function downloadOnClick(downloadUrl?: string) {if (!downloadUrl) {return}try {const fileName = getFileNameFromUrl(downloadUrl) || '下载.jpg'const data = await gePicBlob(downloadUrl)attemptSaveFileFromBlobAsJson(fileName, data, data.type)} catch (ex) {console.error(ex)}
}

三、实现逻辑

  1. 获取图片 Blob
    gePicBlob 函数通过 GET 请求从指定的 URL(后端返回的图片链接)获取图片数据并设置响应类型为 blob。这意味着返回的响应体将直接作为 Blob 对象处理。
    注意事项:发送请求时,如果因为图片资源的域名和当前域名不一致导致跨域,那么我们需要在 nginx 进行配置,通过Access-Control-Allow-Origin配置允许当前的域名访问图片资源。

  2. 处理 Blob 对象
    downloadOnClick 函数中,首先通过 gePicBlob 获取图片的 Blob 对象及其 MIME 类型(通过 data.type 获取)。
    然后,调用 attemptSaveFileFromBlobAsJson 函数,一旦遇到解析错误(即 Blob 不是有效的 JSON),就会直接调用 saveFileFromBlobDirectly 来下载文件。

  3. 直接下载文件
    saveFileFromBlobDirectly 函数是实际执行下载操作的函数。它首先创建一个新的 Blob 对象(为了确保 MIME 类型被正确设置)。
    接着,根据浏览器类型(是否为 IE),使用不同的方法来触发下载。对于非 IE 浏览器,它创建一个临时的 <a> 标签,设置其 href 为 Blob 对象的 URL(通过 URL.createObjectURL 生成),并模拟点击该链接来触发下载。对于 IE 浏览器,则使用 navigator.msSaveBlob 方法。

四、结果

实现下载啦~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/383235.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis 7.x 系列【30】集群管理命令

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2. 集群信息2.1 CLUSTER INFO 3. 节点管理3.1 CLUSTER MYID3.2 CLUSTER NODES3…

扫雷-C语言

一、前言&#xff1a; 众所周知&#xff0c;扫雷是一款大众类的益智小游戏&#xff0c;它的游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子&#xff0c;同时避免踩雷&#xff0c;踩到一个雷即全盘皆输。 今天&#xff0c;我们的目的就是通过C语言来实现一个简…

SpringBoot源码(1)ApplicationContext和BeanFactory

1、调用getBean方法 SpringBootApplication public class SpringBootDemoApplication {public static void main(String[] args) {ConfigurableApplicationContext applicationContext SpringApplication.run(SpringBootDemoApplication.class, args);applicationContext.get…

关于使用宝兰德bes中间件进行windows部署遇到的问题——license不存在

报错信息 日志文件中是这么报错的 遇到的具体情况&#xff1a; 实例按照**的文档手册正常步骤下去节点部署的时候没有报错&#xff0c;成功启动&#xff0c;但是日志里会有报错信息&#xff0c;也是license不存在实例创建的时候失败了&#xff0c;报错信息如下所示 解决方法…

基于jeecgboot-vue3的Flowable流程-自定义业务表单流程历史信息显示

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、对于自定义业务表单的流程历史记录信息做了调整&#xff0c;增加显示自定义业务表单 <el-tab-pane label"表单信息" name"form"><div v-if"customF…

Fine-BI学习笔记

官方学习文档&#xff1a;快速入门指南- FineBI帮助文档 FineBI帮助文档 (fanruan.com) 1.零基础入门 1.1 功能简介 完成四个流程&#xff1a;新建分析主题、添加数据、分析数据、分享协作。 示例数据获取&#xff1a;5分钟上手FineBI - FineBI帮助文档 (fanruan.com) 1.2 …

Pyqt5新手教程

PyQt界面开发的两种方式&#xff1a;可视化UI 编程式UI &#xff08;1&#xff09;可视化UI&#xff1a;基于Qt Designer可视化编辑工具进行组件拖放、属性设置、布局管理等操作创建界面。 一是将其保存为.ui文件&#xff0c;然后在PyQt应用程序中加载和使用.ui文件。 二是使用…

mac OS matplotlib missing from font(s) DejaVu Sans

如果能搜索到这篇文章&#xff0c;我猜你遇到了和我一样的问题&#xff1a;matplotlib绘图中文乱码。如下&#xff1a; 出现这个问题的原因是&#xff1a;matplotlib使用的字体列表中默认没有中文字体。 这里说一种解决方案&#xff1a;我们可以在文件中手动指定matplotlib使用…

记一次Mycat分库分表实践

接了个活,又搞分库分表。 一、分库分表 在系统的研发过程中,随着数据量的不断增长,单库单表已无法满足数据的存储需求,此时就需要对数据库进行分库分表操作。 分库分表是随着业务的不断发展,单库单表无法承载整体的数据存储时,采取的一种将整体数据分散存储到不同服务…

bool数组的理解和应用[C++]

文章目录 bool数组的用法bool数组的定义声明bool数组的初始化访问和修改数组元素遍历数组 运用bool数组简单代码 在今天做题中发现了bool类不仅能用于函数类型还能用于数组类型&#xff0c;好奇查了查发现bool还有很多用处&#xff1a;基本变量&#xff0c;在枚举类型中会用到&…

Java二十三种设计模式-装饰器模式(7/23)

装饰器模式&#xff1a;动态扩展功能的灵活之选 引言 装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;用于在不修改对象自身的基础上&#xff0c;通过添加额外的职责来扩展对象的功能。 基础知识&#xff0c;java设计模式总体来说设计…

大语言模型-GPT2-Generative Pre-Training2

一、背景信息&#xff1a; GPT2是2019年由OpenAI 提出的预训练语言模型。 GPT2提出语言模型式无监督的多任务学习 。旨在通过无监督学习也能达到和finetune一样的效果&#xff0c;并且拥有更强的泛化能能力。 即提出利用语言模型做下游任务时&#xff0c;不需要下游任务的任何…

Python Flask入门到精通:详细教程和实战案例

前言 Flask是一个轻量级的Web框架&#xff0c;用于快速开发Web应用程序。它的设计理念是简洁、灵活和易于扩展&#xff0c;非常适合于从简单的单页应用到复杂的大型项目。通过Flask&#xff0c;可以创建各种Web应用程序&#xff0c;比如博客、电子商务网站、RESTful API等。 …

Unity3d打包到Android

本文参考&#xff1a; Unity3D新手教程&#xff1a;如何打包发布到Android_哔哩哔哩_bilibili 一、Unity 打包Android的环境搭建 1、工具安装 Unity Hub已经集成了Android的环境搭建。 选择Add modules 然后安装Android Build Support下的所有工具。 如果各个工具都安装成功…

H3CNE(路由基础、直连路由与静态路由)

目录 6.1 直连路由 6.2 静态路由理解性实验 6.2.1 配置直连路由 6.2.2 配置静态路由 6.3 路由表的参数与比较 6.3.1 优先级的比较 6.3.2 开销的比较 6.4 路由器中的等价路由、浮动路由、默认路由 6.4.1 等价路由 6.4.2 浮动路由 6.4.3 默认路由(缺省路由) 6.1 直连路…

银河麒麟服务器V10 SP3 安装人大金仓V009R001C001B0030

原文链接&#xff1a;银河麒麟服务器V10 SP3 安装人大金仓V009R001C001B0030 Hello&#xff0c;大家好啊&#xff0c;今天给大家带来一篇关于在银河麒麟服务器V10 SP3上安装人大金仓V009R001C001B0030的文章。人大金仓是国内知名的数据库管理系统&#xff0c;它在高性能、高可靠…

【MySQL进阶之路 | 高级篇】范式概述与第一范式

1. 范式简介 在关系型数据库中&#xff0c;关于数据表的设计的基本原则&#xff0c;规则就称为范式。可以理解为&#xff0c;一张数据表的设计结果需要满足的某种设计标准的级别。要想设计一个结构合理的关系型数据库&#xff0c;必须满足一定的范式。 范式的英文名是Normal …

C++树形结构(1 基础)

目录 一.基础&#xff1a; 1.概念&#xff1a; 2.定义&#xff1a; Ⅰ.树的相关基础术语&#xff1a; Ⅱ.树的层次&#xff1a; 3.树的性质&#xff1a; 二.存储思路&#xff1a; 1.结构体存储&#xff1a; 2.数组存储&#xff1a; 三.树的遍历模板&#xff1a; 四.信…

【SQL语句大全(MySQL)】

SQL语法 添加删除修改查询基本查询条件查询分组函数/聚合函数分组查询排序分页查询&#xff08;限制查询&#xff09;多表查询连接查询根据年代分类连接查询根据连接方式分类1、内连接2、左外连接3、右外连接 多张表连接的语法格式 嵌套查询 SQL语句书写顺序 添加 INSERT INTO…

深入浅出WebRTC—ULPFEC

FEC 通过在发送端添加额外的冗余信息&#xff0c;使接收端即使在部分数据包丢失的情况下也能恢复原始数据&#xff0c;从而减轻网络丢包的影响。在 WebRTC 中&#xff0c;FEC 主要有两种实现方式&#xff1a;ULPFEC 和 FlexFEC&#xff0c;FlexFEC 是 ULPFEC 的扩展和升级&…