前端實現文件下載

以前寫了一篇《前端實現圖片下載》大部分場景下,文件下載均可以按照這個思路來實現。javascript

可是,最近遇到了一個新的需求——POST 下載。服務端只支持 POST 請求,而上一篇文章中涉及的大部分場景都是 GET 請求。html

服務端實現

以 Node + Koa2 實現爲例,服務端返回 excel 文件流前端

const fs = require('fs')
const path = require('path')

module.exports = ctx => {
  ctx.set('Content-Type', 'application/vnd.ms-excel')
  ctx.set('Content-Disposition', 'attachment; filename=download.xlsx')
  ctx.body = fs.createReadStream(path.resolve(__dirname, './download.xlsx'))
}

兼容性好的老方案

經典的、兼容性好的方案能夠經過構建 Form 表單來實現java

let uuidIndex = 0

export default (url, params, method = 'post') => {
  const uuid = `TMP_FRAME_NAME__${uuidIndex++}`
  const iframe = document.createElement('iframe')
  iframe.name = uuid
  iframe.style = 'display:none'
  // 不管響應成功失敗,都會調用 onload
  // iframe.onload = success
  // iframe.onerror = fail
  document.body.appendChild(iframe)

  const form = document.createElement('form')
  form.action = url
  form.method = method
  form.target = uuid
  form.style = 'display:none'
  form.enctype = 'application/x-www-form-urlencoded'
  document.body.appendChild(form)

  if (params) {
    Object.keys(params).forEach(key => {
      const v = params[key]
      if (v !== undefined) {
        const input = document.createElement('input')
        input.type = 'hidden'
        input.name = key
        input.value = v
        form.appendChild(input)
      }
    })
  }

  form.submit()
  document.body.removeChild(form)
  document.body.removeChild(iframe)
}

尋找新方案

產品提了一個需求,下載成功要提示,下載失敗也要提示。那麼問題來了,上面的老方案,不太好監聽此次操做是正常仍是異常。(其實能夠和後臺約定返回內容,前端經過監聽 iframe 的內容實現監聽。)jquery

插曲

有個 jQuery 插件 jquery.form.js API 中提供了對成功和失敗的回調。縱觀源碼,主要實現 form 上傳,可借鑑用於下載的方案並無發現對請求的狀態進行監聽。ios

正題

在經歷了以上插曲後,找到一種新的方案。git

在新方案中,使用了一些 HTML5 的 API,例如 Blob。因此,兼容性須要 IE 10+ 。github

function download(url, name) {
  const aLink = document.createElement('a')
  aLink.download = name
  aLink.href = url
  aLink.dispatchEvent(new MouseEvent('click', {}))
}

export default (data, name, type) => {
  const blob = new Blob([data], { type })
  const url = URL.createObjectURL(blob)
  download(url, name)
}

demo 一枚

<button id="button">下載</button>
import axios from 'axios'
// 上面的新方案
import download from './download' 

document.getElementById('button').addEventListener('click', () => {
  axios.post('/download', null, {
    // 記得設置爲成以 buffer 格式讀取
    responseType: 'arraybuffer'
  })
  .then((res) => {
    // 從響應頭裏面讀取名字,固然,能夠自定義
    const name = res.headers['content-disposition'].replace(/.*filename=/, '')
    download(res.data, name, 'application/vnd.ms-excel')
  })
  .catch((error) => {
    console.log(error)
  })
})

吐槽

segmentfault 網站編輯器編輯 markdown 對 js 代碼塊的解析有 bug,帶了箭頭函數 // 註釋就失效了。axios

相關文章
相關標籤/搜索