大文件上传

先介绍文件上传背景

例如:某某Ai产品,需要涉及到用户的自定义模型(1G以上)

可能会遇到的问题:

  • 网络断开之后,之前传的没了
  • 传着传着,网络波动,结果啥都没了
  • 关机了,想接着传,做不到

专业术语:

  • 断点续传
  • 断开重传
  • 切片上传

方案:

  1. 分片上传(Chunk Upload)

    • 将大文件切成多个小块(chunk),分别上传到服务器。

    • 服务端负责将这些小块重新组装成完整的文件。

    • 常用工具:FileReader API、Blob API 和 FormData

    • 示例:

      function uploadFileInChunks(file) {
        const chunkSize = 5 * 1024 * 1024; // 每个块大小为5MB
        let currentChunk = 0;
        const totalChunks = Math.ceil(file.size / chunkSize);
        
        const uploadChunk = () => {
          const start = currentChunk * chunkSize;
          const end = Math.min(file.size, start + chunkSize);
          const chunk = file.slice(start, end);
          const formData = new FormData();
          formData.append('fileChunk', chunk);
          formData.append('chunkIndex', currentChunk);
          
          fetch('/upload', {
            method: 'POST',
            body: formData
          }).then(response => {
            if (response.ok) {
              currentChunk++;
              if (currentChunk < totalChunks) {
                uploadChunk(); // 继续上传下一个块
              } else {
                console.log('文件上传完成');
              }
            }
          });
        };
        
        uploadChunk();
      }
      
  2. 断点续传

    • 如果上传过程中断,可以从上次上传到的位置继续上传,而不需要重新上传整个文件。
    • 需要服务端支持记录已上传的部分。
  3. 后台上传(Background Upload)

    • 使用 Web WorkersService Workers 实现后台上传,用户关闭页面时仍能继续上传。
  4. 利用第三方库

    • axios:可以用 axios 实现分片上传和断点续传。
    • FineUploaderResumable.js:这些库封装了大文件上传的逻辑,支持分片上传、断点续传、失败重试等功能。

使用axios示例:
使用 axios 实现分片上传和断点续传的步骤可以分为以下几个部分:

  1. 分片处理文件
  2. 记录上传进度
  3. 断点续传

实现步骤:

1. 分片处理文件

使用 JavaScript 的 Blob.slice() 方法将大文件分成若干小块。然后使用 axios 发送每个块。

2. 记录上传进度

为每个分片上传记录其进度和状态,服务器可以返回已上传的块号或偏移量,客户端根据此信息决定从哪里开始上传。

3. 断点续传

当网络中断时,可以从断点继续上传。需要与服务器协作,服务器端保存上传状态,客户端通过请求确认哪些块已经上传成功,从而跳过这些块。

示例代码:

import axios from 'axios';

// 分片上传的大小(5MB)
const CHUNK_SIZE = 5 * 1024 * 1024;

// 分片上传函数
async function uploadFileInChunks(file) {
  const totalChunks = Math.ceil(file.size / CHUNK_SIZE);  // 总块数

  // 获取已上传的块信息(用于断点续传)
  const uploadedChunks = await getUploadedChunks(file.name);
  
  for (let currentChunk = 0; currentChunk < totalChunks; currentChunk++) {
    if (uploadedChunks.includes(currentChunk)) {
      // 如果该块已上传,跳过
      console.log(`Chunk ${currentChunk} already uploaded`);
      continue;
    }

    // 切割文件为块
    const start = currentChunk * CHUNK_SIZE;
    const end = Math.min(file.size, start + CHUNK_SIZE);
    const chunk = file.slice(start, end);
    
    // 创建FormData对象,上传分片
    const formData = new FormData();
    formData.append('fileChunk', chunk);
    formData.append('chunkIndex', currentChunk);
    formData.append('fileName', file.name);

    try {
      // 通过 axios 发送POST请求上传分片
      await axios.post('/upload', formData, {
        onUploadProgress: (progressEvent) => {
          const percentCompleted = Math.round(
            (progressEvent.loaded * 100) / progressEvent.total
          );
          console.log(`Uploading chunk ${currentChunk}: ${percentCompleted}%`);
        }
      });
      console.log(`Chunk ${currentChunk} uploaded successfully`);
    } catch (error) {
      console.error(`Failed to upload chunk ${currentChunk}`, error);
      break;
    }
  }

  console.log('File upload complete');
}

// 获取已上传块信息(用于断点续传)
async function getUploadedChunks(fileName) {
  try {
    const response = await axios.get(`/upload/status?fileName=${fileName}`);
    return response.data.uploadedChunks || [];
  } catch (error) {
    console.error('Error fetching uploaded chunks', error);
    return [];
  }
}
  1. 分片处理
    • 使用 Blob.slice() 将文件按块大小切分。
    • 根据文件大小和块大小计算总块数,并逐块上传。
  2. 上传进度
    • axiosonUploadProgress 事件用于监控上传进度,可以显示上传的百分比。
  3. 断点续传
    • 客户端发送请求到服务器,查询已上传的块号,确保只上传未完成的部分。

服务端处理(示例):

服务端需要能接收分片,保存上传状态。以下是一个简单的服务器处理示例:

js复制代码const express = require('express');
const fs = require('fs');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// 上传分片
app.post('/upload', (req, res) => {
  const { fileName, chunkIndex } = req.body;
  const fileChunk = req.files.fileChunk;

  // 保存分片到服务器
  const chunkPath = `./uploads/${fileName}.part${chunkIndex}`;
  fileChunk.mv(chunkPath, (err) => {
    if (err) {
      return res.status(500).send(err);
    }
    res.send({ message: 'Chunk uploaded' });
  });
});

// 获取已上传的块(用于断点续传)
app.get('/upload/status', (req, res) => {
  const { fileName } = req.query;

  // 获取文件上传目录下所有已上传的块
  const uploadedChunks = fs.readdirSync('./uploads')
    .filter(file => file.startsWith(fileName))
    .map(file => parseInt(file.split('.part')[1]));

  res.send({ uploadedChunks });
});

app.listen(3000, () => {
  console.log('Server started on http://localhost:3000');
});
  • 前端使用 Blob.slice() 切割文件,通过 axios 分块上传。
  • 上传进度通过 onUploadProgress 监控。
  • 断点续传通过查询服务器上已上传的块,跳过已上传部分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值