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

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

一、背景

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

  return 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 = fileName
      elink.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.pathname
    const 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 方法。

四、结果

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

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值