当前位置: 首页 > news >正文

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

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

一、背景

要实现一个下载功能,后端直接返回了一个图片的地址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 方法。

四、结果

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

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 零基础入门:创建一个简单的Python爬虫管理系统
  • 杰发科技AC7840——SENT数据解析及软件Sent发送的实现
  • 【Node.js基础04】包的理解与使用
  • 如何使用 API list 极狐GitLab 容器镜像仓库中的 tag?
  • SVN文件夹没有图标(绿钩子和红感叹号)
  • 【C# WInForm】将TextBox从输入框设置为文本框
  • Nginx笔记(一)
  • 在Mac M1上面使用Dockerfile打x86_64镜像
  • nng协议nni_taskq_sys_init(void) 对nni_taskq_systq 初始化
  • Python从0到100(四十三):数据库与Django ORM 精讲
  • LeetCode 129, 133, 136
  • 鸿蒙界面开发
  • Redis 主从搭建
  • 过滤出List集合的元素是Person对象,过滤出每个元素非null的name字段得到String类型的集合
  • vue侦听器(Watch)精彩案例剖析一
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • 【前端学习】-粗谈选择器
  • ➹使用webpack配置多页面应用(MPA)
  • Fastjson的基本使用方法大全
  • java2019面试题北京
  • js继承的实现方法
  • JS数组方法汇总
  • Node 版本管理
  • Phpstorm怎样批量删除空行?
  • Promise初体验
  • python学习笔记-类对象的信息
  • Yii源码解读-服务定位器(Service Locator)
  • 飞驰在Mesos的涡轮引擎上
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 关于Java中分层中遇到的一些问题
  • 基于游标的分页接口实现
  • 老板让我十分钟上手nx-admin
  • 盘点那些不知名却常用的 Git 操作
  • 时间复杂度与空间复杂度分析
  • 世界编程语言排行榜2008年06月(ActionScript 挺进20强)
  • 线上 python http server profile 实践
  • 写给高年级小学生看的《Bash 指南》
  • 自制字幕遮挡器
  • 【运维趟坑回忆录】vpc迁移 - 吃螃蟹之路
  • C# - 为值类型重定义相等性
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • ​Spring Boot 分片上传文件
  • ​VRRP 虚拟路由冗余协议(华为)
  • ​水经微图Web1.5.0版即将上线
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • $.type 怎么精确判断对象类型的 --(源码学习2)
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第5节(封闭类和Final方法)
  • (第二周)效能测试
  • (分布式缓存)Redis分片集群
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366