基于阿里云 OSS 下载图片跨域问题

问题描述:我们通过 URL 图片连接下载图片至本地,产生图片请求异常:No 'Access-Control-Allow-Origin' header is present on the requested resource。

我们通过 XMLHttpRequest 加载外部图片文件

// 图片下载
download (row) {
  const $that = this
  const imgUrl = row.fileUrl
  let xhr = new XMLHttpRequest()
  xhr.open('get', imgUrl, true)
  // 至关重要
  xhr.responseType = 'blob'
  xhr.onload = function () {
    if (this.status === 200) {
      // 得到一个blob对象
      let blob = this.response
      //  至关重要
      $that.downloadFile(row.fileName, blob)
    }
  }
  xhr.send()
},

将结果返回结果放入 标签中,进行下载。

// 下载
downloadFile (fileName, blob) {
  var dowloadElement = document.createElement('a')
  var href = window.URL.createObjectURL(blob)
  let evt = document.createEvent('HTMLEvents')
  dowloadElement.href = href
  dowloadElement.download = fileName
  document.body.appendChild(dowloadElement)
  evt.initEvent('click', true, true)
  dowloadElement.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }))
  document.body.removeChild(dowloadElement)
  window.URL.revokeObjectURL(href)
},

此时出现图片链接出现 403 禁止访问的问题,但是通过链接,可以在浏览器中查看访问都没有问题。

首先想到的是不再是通过 XMLHttpRequest 进行加载图片,而是通过  标签在浏览器中可以无视同源策略。

标签中,配置 download 的属性时,图片链接直接在浏览器中打开了,因此仅适用于同源的 URL。

 解决方案:先创建一个图片,然后用 canvas 绘制该图片,然后使用 canvas.toDataURL 获得,代码如下:

async download (row) {
   let image = new Image()
   image.setAttribute('crossOrigin', 'anonymous')
   image.src = row.fileUrl
   image.onload = () => {
     let canvas = document.createElement('canvas')  
     canvas.width = image.width   
     canvas.height = image.height   
     let ctx = canvas.getContext('2d')
     ctx.drawImage(image, 0, 0, image.width, image.height)
     let ext = image.src.substring(image.src.lastIndexOf('.')+1).toLowerCase()
     let dataURL = canvas.toDataURL(ext)
     this.downloadFile(dataURL, row.fileName)
   }
},
   

代码中有设置图片的 crossOrigin 属性 image.setAttribute('crossOrigin', 'anonymous')。因为图片跨域,canvas的toDataURL会报错拿不到结果。

将通过 canvas 制作的图片链接,放入到 标签中进行下载。

download (href, name = 'pic') {
  let eleLink = document.createElement('a')
  eleLink.download = name
  eleLink.href = href
  eleLink.click()
  eleLink.remove()
}

此时产生了新的问题:就是有些图片可以下载下来,有些图片同样会产生 403 禁止访问的问题。找了很久这个问题的产生,初步估计是上传图片的时间在当前上传的就可以下载,而非当天上传的图片,下载就产生了403 禁止访问的问题。通过查看 阿里云 OSS 下载文件,通过文件 URL 下载文件,浏览器中使用 signatureUrl 方法生成用于下载的文件 URL,URL的有效期默认为半小时,即 1800s。

调用接口,获取下载文件配置信息,代码如下:

import Aliyun from '../../config/aliyun/aliyunConfig'
async getData (row) {   
  const { data } = await common.getSecurityToken()   
  const { accessKeyId, accessKeySecret, securityToken } = data     
  return new Aliyun().configDownload({   
    AWconfig: {   
        aliyunConfig: {  // AWconfig为阿里云图片地址配置
          url: 'xxx',
          endpoint: 'xxx',
          bucket: 'xxx',
        },
    },
    token: { accessKeyId, accessKeySecret, securityToken },
    name: row.fileName,   
    url: row.fileId
  })   
},

将处理过后的 URL 链接放入 image 中,代码如下:

// 图片下载
async download (row) {
   const result = await this.getData(row)
   image.src = result
   ...
},

那么我们如何配置 Aliyun 类中的 configDownload 方法呢?

export default class Aliyun {
  // 配置下载功能
  configDownload (obj) {
    this.validate(obj)  // 检验参数
    let { accessKeyId, accessKeySecret, securityToken } = this.getParams(obj) // 处理参数
    var aliyunConfig = obj.AWconfig.aliyunConfig
    var client = new OSS.Wrapper({
      accessKeyId,   
      accessKeySecret,   
      stsToken: securityToken,   
      endpoint: aliyunConfig.endpoint,   
      bucket: aliyunConfig.bucket
    })   
    const filename = obj.name // filename为自定义下载后的文件名。
    const response = {   
      'content-disposition': `attachment; filename=${encodeURIComponent(filename)}`
    }
    // object-key表示从OSS下载文件时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。
    return client.signatureUrl(obj.url, { response });
  }
}

这里需要注意的是,传入的 URL 地址并非完整路径;传入的是包含文件后缀在内的完整路径。

你可能感兴趣的:(跨域,阿里云,OSS,下载图片,跨域问题)