• 作者:老汪软件技巧
  • 发表时间:2024-09-12 04:01
  • 浏览量:

前言

在面试中关于如何实现大文件上传是一个非常热门的问题,那么什么是大文件上传呢?大文件上传这是一个非常抽象的概念,抽象在哪里呢?大文件,究竟多大的文件才算是大文件这一点十分的抽象,对于不同的项目,写法等等对于这个大文件的大都不不一致.在本文中将会将诶介绍一种处理大文件上传的方法.

前端代码处理

众所周知,文件的传输是通过数据流的形式传输,我们是否可以将这个数据流分成几份,最后将其拼接在一起实现的数据的传输呢?本文中处理大文件上传就是通过这一个思路来完成的,下面我们来看看具体实现过程.

我们使用input标签设置type='file'属性来进行文件上传,当input的type属性为file时,就可以访问到本地的文件.并且我们需要对input框进行一个change事件的监听,这里我们需要注意的是,我们可以使用两种方式进行事件的监听一种是直接在input框上面绑定事件

但是对于input框绑定change事件是无法触发的,所以这里我们采用通过获取到dom结构进行事件监听这个方式进行事件的监听.

html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Documenttitle>
head>
<body>
    <input type="file" id="input" onchange="">
    <button id="btn">上传button>
    
    <script>
        const input = document.getElementById("input")
        const btn = document.getElementById("btn");
        let fileObj = null
        //事件参数 e
        input.addEventListener("change",handleFileChange);
        btn.addEventListener("click", handleUpdate);  // 点击上传按钮时执行handleUpdate函数
        function handleFileChange(e){
            console.log(e.target.files);
            const [file] = e.target.files 
            fileObj = file
        }
    
    script>
body>
html>

我们看到handleFileChange()函数,默认有一个事件参数对于事件监听一般来说都是会有一个事件参数的,我们看看事件参数打印的是什么内容

事件参数的值是我们上传文件的信息,包含文件上一次修改的时间,文件大小,文件名等等重要信息,然后我们再将我们所需要的信息解构出来赋值给全局声明的fileObj,那么我们所需要的信息又是哪些呢?这里我们对比一下e.target.files和files的值

其实就是把FileList中的数据项给拿出来了,这里我们不用担心数据拿错了,我们这里一次只能上传一个文件,所以我们解构出数据也就会是我们上传的数据.

那么这里我们可以顺利拿到本地文件上传的数据,我们需要一个上传的操作了,所以这里我们使用button按钮来点击上传,那么在这个上传的过程中发生了什么操作呢?我们这就来看看

我们需要知道的是,在上传文件的过程中我们将一个数据流剪切成了多段数据流,分段上传,避免了文件过大影响上传.对于剪切数据这一部分我们需要知道的是,我们拿到的事件参数是一个文件类型的对象,我们都知道对于对象而言它是没有一个叫做剪切的方法,但是文件类型的对象身上就有一个slice()方法用于剪切数据

//上传文件
        function handleUpdate(){
            if(!fileObj){
                return
            }
            const chunkList = createChunk(fileObj)
            console.log(chunkList);
            const chunks = chunkList.map(({file},index)=>{
                return {
                    file,
                    size:file.size,
                    percent:0,
                    chunkName:`${fileObj.name}-${index}`,
                    fileName:fileObj.name,
                    index
                }
            })
           
            uploadChunks(chunks)
        }
       
        function createChunk(file,size = 5 * 1024 * 1024){
            const chunkList = []
            let cur = 0
            while(cursize){
               
                chunkList.push({file:file.slice(cur,cur+size)})
                cur = cur + size;
            }
            return chunkList
        }
        function uploadChunks(chunks){
            
            const formChunks = chunks.map(({file,fileName,index,chunkName})=>{
                
                const formData = new FormData()
                formData.append('file',file)
                formData.append('fileName',fileName)
                formData.append('chunkName',chunkName)
                return {formData,index}
            })
            console.log(formChunks);
            const requestList = formChunks.map(({formData,index})=>{
                return axios.post('http://localhost:3000/upload',formData,()=>{
                   
                    console.log(index);//进度条
                })
            })
            console.log(requestList);
            Promise.all(requestList).then(res=>{
                console.log(res,'所有片段上传成功')
            
            })
            
        }

handleUpdate()函数,我们先判断全局变量fileObj是否有值,也就是是否拿到了事件参数中的值,如果拿到了,就调用函数createChunk()将文件对象切割成默认每一部分为5MB大小的数据流,在createChunk()中我们使用while()循环将数据存入数组 chunkList中,最后返回该数组.然后我们看看该数组长什么样

数组中的元素一个是数据流的大小,一个是数据类型为Blob,然后我们将数据整理一下,我们使用map()遍历数组

最后一步就是发生接口请求了,我们封装一个uploadChunks()函数,用于实现发送接口请求,这里由于后端是读不懂的Blob数据类型,所以我们必须采用前后端都可以读懂的数据类型,也就是表单数据类型,所以这里

const formChunks = chunks.map(({file,fileName,index,chunkName})=>{
                
                const formData = new FormData()
                formData.append('file',file)
                formData.append('fileName',fileName)
                formData.append('chunkName',chunkName)
                return {formData,index}
            })

创建一个表单对象,并将需要发送出去的数据转化成表单类型.最后使用axios发送接口请求,这里使用引入CDN的方式使用axios。

总结

对于大文件上传,其实前端的过程会简单一点,先是读取本地文件,读成一个文件对象,对整个文件对象进行切割,并得到Blob类型的文件对象,再将文件对象转化成前后端都能读懂的表单对象,最后将这些对象一个一个通过发送接口请求发送出去.