大文件上传断点续传的实现方法

2025/3/8
本文详细介绍了大文件上传断点续传的实现,包括文件分片、记录上传进度、上传分片、服务器端处理、合并分片、断点续传、错误处理与重试机制以及前端框架集成等关键步骤和技术细节,能有效提高大文件上传稳定性和用户体验。
大文件上传断点续传流程图,代码示例展示图片,前端框架集成示例图片

大文件上传断点续传的实现是现代Web应用中常见的需求,尤其是在处理大文件上传时,网络不稳定或用户中断操作的情况下,能够从中断处继续上传,而不是重新开始。以下是实现大文件上传断点续传的关键步骤和技术细节:

1. 文件分片

首先,将大文件分割成多个小块(chunks),每个小块的大小可以根据实际情况调整,通常为1MB到5MB。分片的目的是为了减少单次上传的数据量,提高上传的稳定性和效率。

function sliceFile(file, chunkSize) {
  const chunks = [];
  let start = 0;
  while (start < file.size) {
    const chunk = file.slice(start, start + chunkSize);
    chunks.push(chunk);
    start += chunkSize;
  }
  return chunks;
}

2. 记录上传进度

为了实现断点续传,需要记录每个分片的上传状态。可以使用localStorageIndexedDB或服务器端存储来保存上传进度。

function saveProgress(fileName, chunkIndex) {
  localStorage.setItem(`upload_${fileName}`, chunkIndex);
}

function getProgress(fileName) {
  return parseInt(localStorage.getItem(`upload_${fileName}`)) || 0;
}

3. 上传分片

使用XMLHttpRequestfetch API将每个分片上传到服务器。在上传之前,检查该分片是否已经上传过,避免重复上传。

async function uploadChunk(fileName, chunk, chunkIndex) {
  const formData = new FormData();
  formData.append('file', chunk);
  formData.append('chunkIndex', chunkIndex);
  formData.append('fileName', fileName);

  const response = await fetch('/upload', {
    method: 'POST',
    body: formData,
  });

  if (response.ok) {
    saveProgress(fileName, chunkIndex + 1);
  } else {
    throw new Error('Upload failed');
  }
}

4. 服务器端处理

服务器端需要接收分片并保存到临时目录中。同时,服务器需要记录每个文件的上传进度,以便在客户端请求时返回已上传的分片信息。

// 伪代码示例
app.post('/upload', (req, res) => {
  const { fileName, chunkIndex } = req.body;
  const chunk = req.files.file;
  const tempDir = `./temp/${fileName}`;

  if (!fs.existsSync(tempDir)) {
    fs.mkdirSync(tempDir, { recursive: true });
  }

  fs.writeFileSync(`${tempDir}/${chunkIndex}`, chunk.data);
  res.sendStatus(200);
});

5. 合并分片

当所有分片上传完成后,服务器端需要将这些分片合并成完整的文件。

// 伪代码示例
app.post('/merge', (req, res) => {
  const { fileName } = req.body;
  const tempDir = `./temp/${fileName}`;
  const chunks = fs.readdirSync(tempDir).sort((a, b) => a - b);

  const output = fs.createWriteStream(`./uploads/${fileName}`);
  chunks.forEach(chunk => {
    const chunkPath = `${tempDir}/${chunk}`;
    const chunkData = fs.readFileSync(chunkPath);
    output.write(chunkData);
    fs.unlinkSync(chunkPath);
  });

  output.end();
  res.sendStatus(200);
});

6. 断点续传

在上传开始时,客户端首先向服务器查询已上传的分片信息,然后从断点处继续上传。

async function startUpload(file) {
  const fileName = file.name;
  const chunks = sliceFile(file, 1024 * 1024); // 1MB chunks
  const startChunk = getProgress(fileName);

  for (let i = startChunk; i < chunks.length; i++) {
    try {
      await uploadChunk(fileName, chunks[i], i);
    } catch (error) {
      console.error('Upload failed:', error);
      break;
    }
  }

  if (getProgress(fileName) === chunks.length) {
    await fetch('/merge', {
      method: 'POST',
      body: JSON.stringify({ fileName }),
      headers: { 'Content-Type': 'application/json' },
    });
    localStorage.removeItem(`upload_${fileName}`);
  }
}

7. 错误处理与重试机制

在上传过程中,可能会遇到网络错误或其他异常情况。为了提高上传的可靠性,可以实现重试机制,当上传失败时自动重试几次。

async function uploadWithRetry(fileName, chunk, chunkIndex, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      await uploadChunk(fileName, chunk, chunkIndex);
      return;
    } catch (error) {
      if (i === retries - 1) throw error;
    }
  }
}

8. 前端框架集成

如果使用React、Vue等前端框架,可以将上述逻辑封装成可复用的组件或Hook,方便在多个项目中复用。

// React Hook 示例
function useFileUpload() {
  const [progress, setProgress] = useState(0);

  const uploadFile = async (file) => {
    const chunks = sliceFile(file, 1024 * 1024);
    const startChunk = getProgress(file.name);

    for (let i = startChunk; i < chunks.length; i++) {
      await uploadWithRetry(file.name, chunks[i], i);
      setProgress(((i + 1) / chunks.length) * 100);
    }

    await fetch('/merge', {
      method: 'POST',
      body: JSON.stringify({ fileName: file.name }),
      headers: { 'Content-Type': 'application/json' },
    });

    localStorage.removeItem(`upload_${file.name}`);
  };

  return { uploadFile, progress };
}

总结

大文件上传断点续传的实现涉及文件分片、上传进度记录、分片上传、服务器端处理、分片合并等多个步骤。通过合理的设计和实现,可以有效提高大文件上传的稳定性和用户体验。

上次更新:

相关文章

npx完全指南:前端开发必备工具详解 | 20年架构师深度解析

本文由20年前端架构师深入解析npx工具,涵盖其核心功能、优势、高级用法、最佳实践及与npm/yarn的区别比较,帮助开发者掌握这一现代前端开发利器。

·前端开发

<处理关联数据的最佳实践:Article 与 Tags 的关系 | 开发指南>

<本文详细介绍了在开发中处理关联数据(如 Article 和 Tags 的多对多关系)的最佳实践,包括拆分业务逻辑、使用事务保证数据一致性、合理设计关联表结构、批量操作、幂等性和乐观锁等关键要点,并提供了基于 mysql2 和 Sequelize 的代码示例。>

·后端开发

Astro 静态站点生成器:构建高性能网站的最佳选择

Astro 是一个专注于构建快速、轻量级网站的静态站点生成器,支持多种前端框架,采用岛屿架构减少 JavaScript 加载,提升性能。

·前端开发

MySQL外键约束详解:维护数据一致性与完整性

本文详细介绍了MySQL中的外键约束(Foreign Key Constraint),包括其基本概念、创建方法、作用、级联操作、限制、修改与删除方法、查看方式以及最佳实践。通过合理使用外键约束,可以有效管理数据库中的数据关系,确保数据的准确性和可靠性。

·后端开发

MySQL JSON数据类型支持与使用指南 | 详细解析与示例

本文详细解析了MySQL从5.7版本开始支持的JSON数据类型,包括版本支持、创建JSON字段、插入与查询JSON数据、修改JSON数据、生成JSON、索引优化、性能与应用场景、注意事项及示例全流程。

·后端开发

SQL JOIN、LEFT JOIN 和 RIGHT JOIN 的区别与应用场景详解

本文详细介绍了 SQL 中 JOIN、LEFT JOIN 和 RIGHT JOIN 的区别,包括它们的作用、语法、示例以及实际应用场景,帮助读者更好地理解和使用这些连接方式。

·后端开发

Weex 跨平台移动开发框架:核心特性与使用指南

Weex 是由阿里巴巴开源的跨平台移动开发框架,支持使用 Vue.js 或 Rax 构建高性能的 iOS、Android 和 Web 应用。本文详细解析了 Weex 的核心特性、架构、工作流程、组件和模块、开发工具、优缺点、应用场景及未来发展。

·前端开发

ECharts 与 DataV 数据可视化工具对比分析 | 选择指南

本文详细对比了 ECharts 和 DataV 两个常用的数据可视化工具,包括它们的设计目标、优缺点、使用场景和技术栈,帮助读者根据具体需求选择合适的工具。

·前端开发

前端部署后通知用户刷新页面的常见方案 | 单页应用更新提示

本文介绍了在前端部署后通知用户刷新页面的几种常见方案,包括WebSocket实时通知、轮询检查版本、Service Worker版本控制、版本号对比、自动刷新、使用框架内置功能以及第三方库。每种方案的优缺点和示例代码均有详细说明。

·前端开发

TypeScript 映射类型常见问题与解决方案 | 提升代码维护性

本文探讨了在使用 TypeScript 时,映射类型的不当使用可能导致的问题,如代码难以维护、类型推断不准确或性能问题,并提供了相应的解决方案和最佳实践。

·编程语言