前端预览或者下载pdf

对于一个给定链接的文件,如果浏览器可以解析,则会直接预览,否则会触发下载。但实际开发中有可能需要不按照浏览器默认行为处理。比如 pdf,可能需要浏览器直接下载。接下来就以 pdf 为例,记录一下浏览器预览和下载的方法。

预览pdf

对于可以直接访问的链接,a 标签打开就行。

<a><a href="./1182.pdf">download pdf</a>

或者也可以通过请求拿到数据后再触发 a 标签打开。

const btn = document.querySelector('#btn')
btn.addEventListener('click', () => {
  fetch('./1182.pdf.bak')
    .then(res => {
      res.blob()
        .then(res => {
          const blob = new Blob([res], {type: "application/pdf"})
          url = URL.createObjectURL(blob)
          window.open(url)
          URL.revokeObjectURL(url)
        })
    })
})

因为是通过请求获取的,所以需要鉴权的话也可以方便地把这些信息加上。

此外,还可以通过 iframe、embed、object 等标签以及 pdfjs 这个包进行预览。

下载 pdf

为了让浏览器不默认打开,可以直接在 a 标签上添加 download 属性。

<a href="./1182.pdf" download>download pdf</a>

download 可以赋值为文件名。我这里就不写了。

同样的,需要鉴权的也可以通过请求拿到数据后创建 a 标签下载。

const btn = document.querySelector('#btn')
btn.addEventListener('click', () => {
  fetch('./1182.pdf')
    .then(res => {
      res.blob()
        .then(res => {
          const url = window.URL.createObjectURL(new Blob([res]))
          const link = document.createElement('a')
          link.style.display = 'none'
          link.href = url
          link.setAttribute('download', 'test')
          document.body.appendChild(link)
          link.click()
          document.body.removeChild(link)
          URL.revokeObjectURL(url)
        })
    })
})

返回数据类型判断

以上预览和下载 pdf 都是在明确知道链接会返回 pdf 的情况。如果链接/接口在没有 pdf 的情况下返回 json 数据就不好用上面的方法了。这时需要通过请求获取并且对返回值类型进行判断。比如下面 axios 拦截器的处理。

service.interceptors.response.use(
  response => {
    console.log('request response: ', response)
    const res = response.data
    // 非 json 格式的数据,直接返回。比如 pdf 之类的
    if (response?.headers?.['content-type'] !== 'application/json') {
      return res
    }
    const { error, errno } = res
    // 以下各种 errno 的判断省略
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

fetch 也可以进行差不多的判断处理,但代码我找不到了,这里就不贴了。