React实现大文件分片上传——详解基础版(用hooks进行封装)

本文详细介绍了如何在React中使用hooks实现大文件分片上传,包括理解分片原因、操作步骤(获取文件信息、分割文件、并发上传、合并请求)以及创建一个useUploadHook。后续将涉及hash值生成和更高级功能的实现。
摘要由CSDN通过智能技术生成

1.了解为什么大文件需要分片(面试可能会问)

  1. 一般来说我们会设置请求时长,如果文件太大可能会超出请求的时长,导致请求超时
  2. 一般来说在服务端会对客户端请求数据的大小进行限制,文件过大会超出限制,导致上传失败。服务端做出这个限制的原因:是因为防止DDOS攻击,占用你服务器的大量性能导致服务器崩溃
  3. 将文件分割成多段,进行并发上传,可以极大的提高上传速度,而且当传输出现问题的时候,我们只需要将传错的分片进行上传,不需要重新全部上传

2.如何实现大文件分片上传

1. 思考分片上传需要哪些操作

  1. 获取上传文件的信息
  2. 对文件信息进行分割
  3. 并发的进行上传请求
  4. 当请求结束的时候,给服务器发送一个合并的请求,告诉服务器你这次上传结束可以进行对切片的合并

2.实现自己的思考

接下来我们根据上述思考一步一步实现,最后拼成一个 useUpload.js

  1. 获取文件上传信息
const [files, setFiles] = useState()
  const handleFileChange = (e) => {
    const [file] = e.target.files
    setFiles(file)
  }
  1. 对文件信息进行分割
  // 生成文件切片
  const createFileChunk = (file, size = SIZE) => {
  	// 临时存储文件的切片的数组,里面是一个一个的切片数据
    const fileChunkList = []
    // 表示从0的位置开展切片,每一个片的大小和你的SIZE设置有关系
    let cur = 0
    while (cur < file.size) {
      // 每进行一次循环,都会把切片放进临时数组
      fileChunkList.push({ file: file.slice(cur, cur + size) })
      // 每次循环都会更新起始位置,最终会等于文件的大小,然后退出循环
      cur += size
    }
    return fileChunkList
  }
  1. 并发的进行上传请求, 当请求结束的时候,给服务器发送一个合并的请求,告诉服务器你这次上传结束可以进行对切片的合并
  // 上传切片
  const uploadChunks = async (newFileChunkList) => {
    // 对文件进行循环,提取出我们需要的值,chunk,hash, files.name
    // hash的作用是后续服务端需要根据hash值来拼接
    // 将所有请求放在requestList中,后续通过 await Promise.all(requestList)并发请求
    const requestList = newFileChunkList
      .map(({ chunk, hash }) => {
        const formData = new FormData()
        formData.append('chunk', chunk)
        formData.append('hash', hash)
        formData.append('filename', files.name)
        return { formData }
      })
      .map(({ formData }) => {
        return request.post('api/upload', formData)
      })
    // 并发请求
    await Promise.all(requestList)
    // 等待并发请求结束 发送合并切片请求
    await mergeRequest()
  }
    // 合并请求
  const mergeRequest = async () => {
    const data = { size: SIZE, filename: files.name }
    await request.post('api/merge', data)
  }
  1. 将这些方法防止上传事件中
  const handleUpload = async () => {
    if (!files) return
    const fileChunkList = createFileChunk(files)
    const newFileChunkList = fileChunkList.map(({ file }, index) => {
      return { chunk: file, hash: files.name + '-' + index }
    })
    await uploadChunks(newFileChunkList)
  }

3.将这些方法拼接,变成一个hooks

其中 request.ts是封装的axios请求
SIZE 是定义的一个常量
如果需要封装的request.ts可以私信我

import { useState } from 'react'
import request from '@/utils/request.ts'
// 如果不想这么麻烦导入,可以直接 const SIZE = 1* 1024 * 1024
import { SIZE } from '@/common/config'
export default function useUpload() {
  const [files, setFiles] = useState()
  const handleFileChange = (e) => {
    const [file] = e.target.files
    setFiles(file)
  }
  // 生成文件切片
  const createFileChunk = (file, size = SIZE) => {
    // 临时存储文件的切片的数组,里面是一个一个的切片数据
    const fileChunkList = []
    // 表示从0的位置开展切片,每一个片的大小和你的SIZE设置有关系
    let cur = 0
    while (cur < file.size) {
      // 每进行一次循环,都会把切片放进临时数组
      fileChunkList.push({ file: file.slice(cur, cur + size) })
      // 每次循环都会更新起始位置,最终会等于文件的大小,然后退出循环
      cur += size
    }
    return fileChunkList
  }
  // 合并请求
  const mergeRequest = async () => {
    console.log(2)
    const data = { size: SIZE, filename: files.name }
    await request.post('api/merge', data)
  }

  // 上传切片
  const uploadChunks = async (newFileChunkList) => {
    // 对文件进行循环,提取出我们需要的值,chunk,hash, files.name
    // hash的作用是后续服务端需要根据hash值来拼接
    const requestList = newFileChunkList
      .map(({ chunk, hash }) => {
        console.log(1)
        const formData = new FormData()
        formData.append('chunk', chunk)
        formData.append('hash', hash)
        formData.append('filename', files.name)
        return { formData }
      })
      .map(({ formData }) => {
        return request.post('api/upload', formData)
      })
    // 并发请求
    await Promise.all(requestList)
    // 合并切片请求
    await mergeRequest()
  }
  const handleUpload = async () => {
    if (!files) return
    const fileChunkList = createFileChunk(files)
    const newFileChunkList = fileChunkList.map(({ file }, index) => {
      return { chunk: file, hash: files.name + '-' + index }
    })
    await uploadChunks(newFileChunkList)
  }

  return {
    handleFileChange,
    handleUpload,
  }
}

总结

这样一个简单的分片上传就做好了,当然也有很多不完善,后续需要将hash值利用spark-md5这个库进行生成,并且也需要对request进行配置可以实现暂停上传恢复上传等等,会在后续的文章进行讲解,这样子分开讲解希望各位读者能理解更快,有不足的地方可以私信我

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值