自定义 hook —— useDownload

基础功能

  1. 根据单个链接下载文件到本地
  2. 根据多个链接同时下载文件到本地
  3. 下载中的状态 state

具体实现代码

import { useState } from 'react'
import { message, Modal } from 'antd'

// urlPrefix 下载链接的前缀,如 window.location.host 等
const useDownLoad = (urlPrefix = '') => {
  // 批量下载按钮用到的 loading 状态
  const [downloadLoading, setDownloadLoading] = useState(false)

  // 使用 iframe 的方法下载,window.open 或 a 标签 download 属性会拦截多次下载
  const createIFrame = (
    url,
    triggerDelay,  
    removeDelay,
    isEnd,
    isBatchDownload
  ) => {
    // 动态添加 iframe ,设置 src ,然后删除
    setTimeout(() => {
      var frame = document.createElement('iframe')
      frame.src = url  // 去加载,才会下载
      frame.style.display = 'none'  // 用户无感的关键,不展示
      document.body.appendChild(frame)  // 直接加到 body 底部
      // 关闭 loading 状态和 iframe 的延迟回调
      setTimeout(function() {
        if (isEnd && isBatchDownload) {  // 结束后关闭批量的 loading 状态
          setDownloadLoading(false)
        }
        frame.remove()
      }, removeDelay)
    }, triggerDelay)
  }
}

  // 统一封装的下载函数,voiceUrlArray 为下载的 url 数组,isBatchDownload 为是否批量下载
  const download = (voiceUrlArray, isBatchDownload) => {
    if (voiceUrlArray?.length === 0) {
      message.error('下载失败')
      return
    }
    if (isBatchDownload) {
      setDownloadLoading(true)
    }
    let triggerDelay = 100  // 切换下载下一个文件的间隔时间
    let removeDelay = 2000  // 打开 iframe 到关闭 iframe 的间隔时间
    // 遍历 url 数组,开始下载
    voiceUrlArray.forEach((item, index) => {
      createIFrame(
        urlPrefix + item,  // 下载 url
        index * triggerDelay,  // 按顺序排队队打开,所以要用到 index
        removeDelay,  // 打开 iframe 到关闭 iframe 的间隔时间
        index === voiceUrlArray.length - 1,  // 是否是最后一个
        isBatchDownload  // 是否批量下载,如果还已经是最后一个要关闭 loading 状态的
      )
    })
  }

  // 会 return 出去的下载单个文件的方法
  const handleDownload = voiceUrl => {
    if (!voiceUrl) {
      message.error('下载失败')
      return
    }
    // 包装成数组,复用 download 方法
    download([voiceUrl], false)
  }

  // 会 return 出去的批量下载多个文件的方法
  const handleBatchDownload = (selectedRows, voiceUrlKey = '') => {
    if (selectedRows.length <= 0) {
      Modal.info({
        title: '提示',
        content: '请先选择文件',
        onOk() {}
      })
      return
    }
    let arr = []
    selectedRows.forEach(item => {
      // selectedRows 可能为对象数组,也可能为字符串数组
      arr = [...arr, ...([voiceUrlKey ? item[voiceUrlKey] : item] || [])]
    })
    download(arr)
  }

 return {
    downloadLoading,
    handleDownload,
    handleBatchDownload
  }
}

export default useDownLoad

你可能感兴趣的:(自定义 hook —— useDownload)