大文件分片上传-前端部分(index.html)

File 对象是特殊类型的 Blob,可以直接调用Blob的方法,比如slice, 前端做数据分割就是使用这个方法

我遇到的问题:

1. 使用fetch API 上传formdata时,不能指定content-type multipart/form-data
    + 浏览器会自动为formdata加上 content-type, 同时还会指定Boundary, 比如: 
    + multipart/form-data; boundary=----WebKitFormBoundaryFRNYvdz9BKzQztWq
    + 自己加就没有Boundary了

如何跑起来

1. 将index.html 和 server.js放在同一个文件夹,在cmd使用 `http-server` 挂在前端index.html
2. 使用cnpm下载koa, koa-body然后使用node(version=10+)运行server.js
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Upload Test</title>
</head>
<body>
    <input type="file" id="fileUpload" multiple>
    <button id="submitBtn">upload</button>
</body>
    <script>
        let btn = document.getElementById('submitBtn')
        btn.onclick = function(e) {
            let files = document.getElementById('fileUpload').files
            for (let i = 0; i < files.length; i++) {
                uploadFile(files[i])
            }
        }

        function uploadFile(file) {
             let uuid = Math.random().toString().substring(2)
             let chunks = fileSlice(file)
             uploadSlice(chunks, file, uuid)
        }

        const sliceLenght = 1024*1024
        // 将文件切割放在数组里
        function fileSlice(file) {
            let fileSize = file.size
            let start  = 0, end = 0
            let chunkArr = []
            while(end < fileSize) {
                end += sliceLenght
                let chunk = file.slice(start, end)
                chunkArr.push(chunk)
                start += sliceLenght
            }
            return chunkArr
        }

        const maxUpload = 5 // 最大http上传数量,如果文件过大,分割成数百个文件,也不能一下子全发起请求
        function uploadSlice(chunks, file, uuid) {
            let len = chunks.length
            let curRun = 0, succCnt = 0
            function upload(index) {
                if(index >= len) return
                let fd = new FormData()
                fd.append(`file`, chunks[index])
                fd.append(`fileName`, file.name)
                fd.append(`index`, index)
                fd.append(`uuid`, uuid)
                fetch('http://localhost:8081/upload', {
                    method: 'post',
                    body: fd
                    // 注意此处不要多此一举加 content-type multipart/form-data, 如果加了,boundary就搞没了,后端无法解析
                }).then(r => {
                    console.log(r)
                    upload(curRun++)
                    succCnt++
                    if(succCnt >= len) merge()
                })
            }
            for(let i = 0; i < maxUpload; i++) upload(curRun++)

            // 上传完成后在请求后端合并
            function merge() {
                let fd = new FormData()
                fd.append(`uuid`, uuid)
                fd.append(`count`, len)
                fd.append(`fileName`, file.name)
                fetch('http://localhost:8081/merge', {
                    method: 'post',
                    body: fd
                }).then(r => {
                    console.log(r)
                    console.log('merge success')
                })
            }
        }
    </script>
</html>
Html
JSRUN前端笔记, 是针对前端工程师开放的一个笔记分享平台,是前端工程师记录重点、分享经验的一个笔记本。JSRUN前端采用的 MarkDown 语法 (极客专用语法), 这里属于IT工程师。