fetch 下载文件 + 显示进度

2,442 阅读1分钟

不多BB,直接上代码:

export type FileRequestType = {
  url: string
  way?: ResponseType
  option?: Object
  onfail?: (err: any) => void
  onsuccess?: (data: any) => void
}

// 下载文件
export const FileRequestDownLoad = async ({
  url = '',
  onsuccess,
  onfail,
  option = {
    headers: {
      Authorization: `Bearer ${getToken()}`,
      'Content-Type': 'application/pdf',
      responseType: 'arraybuffer',
    },
  },
}: FileRequestType) => {
  const res = await fetch(url, option)
  const result = await res.arrayBuffer()
  if (res.ok && !!onsuccess) {
    onsuccess(result)
  } else if (!res.ok && !!onfail) {
    onfail(result)
  } else {
    return result
  }
}
传递的option类型自定义,signal同时可以取消当前请求 类似xhr.abort()
   option = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json;charset=utf-8',
      },
      body: JSON.stringify(''), //传递的对象
      signal: controller.signal,
      responseType: way,
  },
下载文件放法2,添加下载进度的信息
由于第一次请求回的fetch的res只能读取一次

(1. 要么使用流读取器,要么使用 reponse 方法来获取结果。)

// 下载进度
type DownloadPercent = {
  url: string
  option?: Object
  onprocess: (now: number, all: number) => void
  onsuccess: (data: any) => void
}
export const RequestPercent = async ({
  url = '',
  option = {
    headers: {
      Authorization: `Bearer ${getToken()}`,
      'Content-Type': 'application/pdf',
      responseType: 'arraybuffer',
    },
  },
  onsuccess,
  onprocess,
}: DownloadPercent) => {
  const response = (await fetch(url, option)) as any

  const reader = response?.body.getReader()

  // 文件总长度
  const contentLength = +response.headers.get('content-length')

  let receivedLength = 0
  const chunks = []
  // eslint-disable-next-line no-constant-condition
  while (true) {
    const { done, value } = await reader.read()

    if (done) {
      break
    }

    chunks.push(value)
    receivedLength += value.length
    onprocess(receivedLength, contentLength)
  }
// 这里的chunksAll 已经是ArrayBuffer的数据类型了,可以直接返回,也可以转为blob处理
  const chunksAll = new Uint8Array(receivedLength)
  let position = 0
  for (const chunk of chunks) {
    chunksAll.set(chunk, position)
    position += chunk.length
  }

  onsuccess(chunksAll)

  return chunksAll
}

总结:
ArrayBuffer和Blob一样,都是二进制数据的容器,而ArrayBuffer相比更为底层。ArrayBuffer的数据,是可以按照字节去操作的,而Blob的只能作为一个整的对象去处理。

常规数据类型转换:

1.  `TextEncoder` => `ArrayBuffer`
//uint8Array 
const res = new TextEncoder().encode('data')
const arr = res.buffer

2.  `blob` => `ArrayBuffer`
//借助blob
const blob = new Blib([data], { type: 'application/pdf' })
const utf8decoder = new TextDecoder()
blob.arrayBuffer().then(buffer=>{
  console.log(buffer)
  const text = utf8decoder.decode(buffer)
  // text =>arraybuffer
  console.log(text)
})