大文件分片上传

38 阅读1分钟

核心步骤

  1. 读取文件
    • 返回一个文件格式的数据
  2. 按照指定大小将文件进行截取分割成多个内容
    • file.slice(cur, cur + size) 这里处理后的内容是一个 Blob数据内容
  3. 将分割后的数据使用 formData 表单进行上传,使用Promise.all进行监控所有请求返回
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>大文件分片上传</title>
</head>
<body>
    <input type="file" id="input" />
    <button id="upload">上传</button>
</body>
</html>

<script>
    let input = document.getElementById("input");
    let upload = document.getElementById("upload");
    // 创建一个文件对象
    let files = {};
    const chunkList = []
    const url = ''

    // 读取文件
    input.addEventListener("change", (e) => {
        files = e.target.files[0];
        console.log('test files', files)
        // 返回
        // {
        //     lastModified: 1725077894216
        //     lastModifiedDate: Sat Aug 31 2024 12:18:14 GMT+0800 (中国标准时间) {}
        //     name: "截屏2024-08-31 12.18.08.png"
        //     size: 71291
        //     type: "image/png"
        //     webkitRelativePath: ""
        // }
        console.log('test files typeof', Object.prototype.toString.call(files))
        // 返回: [object File]
        
        
        createChunk(files)
        console.log('test chunkList', chunkList)
        // 返回
        // [
        //     { file: {  // Prototype Blob
        //         size: 71291,
        //         type: ''
        //     } }
        // ]
    });

    // 大文件分片
    function createChunk(file, size = 1 * 1024 * 1024) {
        let cur = 0;
        while (cur < file.size) {
            chunkList.push({
                file: file.slice(cur, cur + size) // 将文件信息按指定大小分割成数组
            })
            return cur + size
        }
    }

    async function uploadFile(list) {
        const requestList = list
            .map(({ file, fileName, index, chunkName }) => {
                // 只能使用这个进行表单格式提交body
                const formData = new FormData()
                formData.append('file', file) // 是一个二进制文件 binary
                formData.append('fileName', fileName)
                formData.append('chunkName', chunkName)
                return { formData, index }
                
                // 如果这样传 body里面是一个 [object Object] 普通对象,传参格式不对
                // return {
                //     formData: { file, fileName, chunkName},
                //     index,
                // }
            })
            .map(({ formData, index }) => {
                fetch(url, {
                    method: 'POST',
                    body: formData
                }).then((res) => {
                    console.log(res);
                })
            })
        await Promise.all(requestList)
    }

    // 上传
    upload.addEventListener('click', () => {
        const uploadList = chunkList.map(({ file }, index) => ({
            file,
            fileName: files.name,
            index,
            chunkName: `${files.name}-${index}`,
            size: file.size,
        }));
        uploadFile(uploadList);
    })
</script>