如何实现大文件上传

问题背景

如果将大文件一次性上传,耗时会非常长,甚至可能传输失败,那么我们怎么解决这个问题呢?既然大文件上传不适合一次性上传,那么我们可以尝试将文件分片散上传。 这样的技术就叫做分片上传。分片上传就是将大文件分成一个个小文件(切片),将切片进行上传,等到后端接收到所有切片,再将切片合并成大文件。通过将大文件拆分成多个小文件进行上传,确实就是解决了大文件上传的问题。因为请求时可以并发执行的,这样的话每个请求时间就会缩短,如果某个请求发送失败,也不需要全部重新发送。

解决方案

前置知识

首先,我们需要理解以下两点:

  1. <input />标签的作用
  2. File对象是什么

<input />标签用于上传文件,当我们通过<input />标签上传文件之后,可以通过input.files获取File对象。比如以下代码:

xml 复制代码
<!DOCTYPE html>
<html>
  <body>
    <input type="file" id="fileInput" onchange="handleFiles()" multiple>
    <script>
      function handleFiles() {
        var input = document.getElementById("fileInput");
        var files = input.files; // FileList对象

        for (var i = 0; i < files.length; i++) {
          var file = files[i]; // File对象
          console.log("文件名:", file.name);
          console.log("文件大小:", file.size);
          console.log("文件类型:", file.type);
          console.log("最后修改时间:", file.lastModifiedDate);
        }
      }
    </script>
  </body>
</html>

上传文件之后,可以看到打印结果如下。因此,我们发现每个File对象包含了有关文件的一些信息,如文件名、大小,类型和最后修改时间等等。

创建切片

通过以上例子看出,File对象就是我们需要上传的文件。在上例中,我们上传的文件大小是574835字节,也就是500多KB。这个文件太小了,于是后面我又上传了一个 8 MB的 文件。我们可以尝试将它分片上传(你自己尝试的时候,最好找个大文件比如几十兆的)

arduino 复制代码
// 创建切片
function createChunk(file, size) {
	//file是大文件,size是切片的大小(也就是小文件的大小)
    const chunkList = []
    let cur = 0
    while (cur < file.size) {
        chunkList.push({
                file: file.slice(cur, cur + size)//使用slice()进行切片
        })
        cur += size
    }
    return chunkList;
}

以上函数用来创建切片,它的核心思想是:创建一个空的切片列表数组chunkList。将大文件分成若干个小切片,每个小切片的大小是size(size的大小你可以自定义)。

值得注意的是:这里的 slice 并非 Array 的原型方法,而是 File 对象从 Blob 对象继承而来,它的作用就是将一个大的 File 对象切割成若干个小的 File 对象。所以此处slice和数组没啥关系,不要搞混了。

接下来,我们可以调用上述函数,看看打印结果是什么。在调用函数的时候,第一个参数是 File 对象,在前置知识里已经讲了它的作用了。第二个参数是切片大小,2 * 1024 * 1024 个字节,也就是每个切片的大小是 2MB。最终,我们可以将一个 8MB 的文件 切成 4个小切片,

ini 复制代码
chunkList = createChunk(files, 2 * 1024 * 1024)
console.log(chunkList);

上传切片

首先进行数据处理。从上文可以知道,我们的每个小切片都是一个 File 对象。但是网络传输的时候,我们传输的是二进制文件。因此我们需要借助 FormData 来进行数据处理。FormData 对象会将数据编译成键值对,可用于发送带键数据。通过调用它的 append() 方法来添加字段。当我们调用 FormData.append(文件名,file对象) 的时候,file 对象会被转化为二进制。

xml 复制代码
<!DOCTYPE html>
<html lang="en">
<body>
  <div class="container">
    <h1>大文件上传</h1>
    <input type="file" id="fileInput" accept="image/*">
    <button id="uploadButton">切片上传</button>
    <br>
  </div>
  <script>
   //chunk就是一个切片,也就是小文件
    async function uploadChunk(chunk) {
      const formData = new FormData();
      formData.append('file', chunk);

      //这里的地址可以替换为你的后端地址
      const response = await fetch('https://file.io', {
        method: 'POST',
        body: formData
      });

      const result = await response.json();
      return result;
    }
    
 document.getElementById('uploadButton').addEventListener('click', async function() {
      const fileInput = document.getElementById('fileInput');
      const file = fileInput.files[0];
      const chunkSize = 100 * 1024; // 100KB
      const totalChunks = Math.ceil(file.size / chunkSize);

      for (let i = 0; i < totalChunks; i++) {
        const start = i * chunkSize;
        const end = Math.min(start + chunkSize, file.size);
        const chunk = file.slice(start, end);
        //上传一个切片
        const result = await uploadChunk(chunk);
        
      }
    });
  </script>
</body>
</html>
相关推荐
恋猫de小郭9 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅15 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606116 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了16 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅16 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅17 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅17 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment17 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅17 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端