当前位置: 首页 > news >正文

前端大文件分片上传与断点续传方案

## 一、背景与需求

在处理大文件上传时(如视频、设计稿等),传统单次上传方式存在以下问题:

- 网络波动导致上传失败需重新上传

- 服务器限制单次请求大小

- 无法暂停/恢复上传

- 上传进度难以跟踪

分片上传与断点续传方案能有效解决这些问题,提升用户体验和上传可靠性。

---

## 二、核心概念

### 1. 分片上传(Chunked Upload)

将大文件切割为多个小分片(如2MB/片),逐个上传分片,最后在服务端合并。

### 2. 断点续传(Resume Upload)

记录已上传分片信息,当上传中断后,再次上传时只需上传未完成的分片。

### 3. 秒传(Instant Upload)

通过文件哈希校验,若服务器已存在相同文件,则直接返回成功。

---

## 三、技术方案

### 前端实现流程

```mermaid

graph TD

    A[选择文件] --> B(计算文件哈希)

    B --> C{查询服务器状态}

    C -->|已存在| D[秒传完成]

    C -->|未存在| E[分片切割]

    E --> F[并发上传分片]

    F --> G[全部分片完成?]

    G -->|否| F

    G -->|是| H[通知合并]

    H --> I[上传完成]

```

### 1. 文件分片处理

```javascript

// 计算文件哈希(使用SparkMD5库)

async function calculateFileHash(file) {

  return new Promise((resolve) => {

    const chunkSize = 2 * 1024 * 1024 // 2MB

    const chunks = Math.ceil(file.size / chunkSize)

    const spark = new SparkMD5.ArrayBuffer()

    let currentChunk = 0

    const loadNext = () => {

      const start = currentChunk * chunkSize

      const end = start + chunkSize >= file.size ? file.size : start + chunkSize

      const fileReader = new FileReader()

     

      fileReader.onload = e => {

        spark.append(e.target.result)

        currentChunk++

       

        if (currentChunk < chunks) {

          loadNext()

        } else {

          resolve(spark.end())

        }

      }

     

      fileReader.readAsArrayBuffer(file.slice(start, end))

    }

   

    loadNext()

  })

}

// 文件分片

function createFileChunks(file, chunkSize = 2 * 1024 * 1024) {

  const chunks = []

  let start = 0

  while (start < file.size) {

    const end = Math.min(start + chunkSize, file.size)

    chunks.push({

      index: chunks.length,

      file: file.slice(start, end)

    })

    start = end

  }

  return chunks

}

```

### 2. 断点续传实现

```javascript

class Uploader {

  constructor(file) {

    this.file = file

    this.chunkSize = 2 * 1024 * 1024 // 2MB

    this.chunks = []

    this.hash = ''

    this.uploadedChunks = new Set() // 已上传分片索引

  }

  async init() {

    this.hash = await calculateFileHash(this.file)

    this.chunks = createFileChunks(this.file, this.chunkSize)

   

    // 从localStorage恢复进度

    const savedProgress = localStorage.getItem(this.hash)

    if (savedProgress) {

      this.uploadedChunks = new Set(JSON.parse(savedProgress))

    }

  }

  async upload() {

    // 查询服务器上传状态

    const { uploaded } = await checkUploadStatus(this.hash)

    if (uploaded) return // 秒传

   

    // 过滤未上传分片

    const pendingChunks = this.chunks.filter(

      chunk => !this.uploadedChunks.has(chunk.index)

    )

    // 并发控制(最大5个并行)

    const pool = new ConcurrentPool(5)

    await pool.addTasks(pendingChunks.map(chunk =>

      () => this.uploadChunk(chunk)

    ))

   

    // 通知合并

    await mergeChunks(this.hash, this.file.name)

  }

  async uploadChunk(chunk) {

    const formData = new FormData()

    formData.append('hash', this.hash)

    formData.append('index', chunk.index)

    formData.append('file', chunk.file)

   

    await axios.post('/api/upload', formData)

   

    // 更新进度

    this.uploadedChunks.add(chunk.index)

    localStorage.setItem(this.hash, JSON.stringify([...this.uploadedChunks]))

  }

}

```

### 3. 服务端接口设计

| 接口                | 方法 | 参数                      | 功能                     |

|---------------------|------|--------------------------|-------------------------|

| /api/check          | GET  | hash                     | 检查文件上传状态         |

| /api/upload         | POST | hash, index, file        | 上传分片                 |

| /api/merge          | POST | hash, filename           | 合并分片                 |

---

## 四、关键优化点

1. **分片大小选择**

   - 2-5MB 平衡网络效率和分片数量

   - 根据网络质量动态调整

2. **哈希计算优化**

   - Web Worker 中计算避免阻塞主线程

   - 抽样计算哈希(首尾各取2MB + 中间随机取3段)

3. **并发控制**

   - 限制并行上传数(通常3-5个)

   - 失败自动重试(最多3次)

4. **进度保存**

   - 使用localStorage/IndexedDB存储进度

   - 服务端记录已上传分片

5. **错误处理**

   - 网络中断自动重连

   - 分片MD5校验防止损坏

   - 服务端定时清理未完成的分片

---

## 五、服务端示例(Node.js)

```javascript

// 检查上传状态

router.get('/check', async (ctx) => {

  const { hash } = ctx.query

  const chunkDir = path.join(UPLOAD_DIR, hash)

  // 1. 检查文件是否已存在

  if (fs.existsSync(path.join(UPLOAD_DIR, hash + '.file'))) {

    return ctx.body = { code: 200, uploaded: true }

  }

  // 2. 返回已上传分片

  const uploaded = fs.existsSync(chunkDir)

    ? fs.readdirSync(chunkDir).map(Number)

    : []

   

  ctx.body = { code: 200, uploaded }

})

// 分片上传

router.post('/upload', async (ctx) => {

  const { hash, index } = ctx.request.body

  const file = ctx.request.files.file

  const chunkDir = path.join(UPLOAD_DIR, hash)

  if (!fs.existsSync(chunkDir)) {

    fs.mkdirSync(chunkDir)

  }

  await fs.promises.rename(

    file.path,

    path.join(chunkDir, index)

  )

  ctx.body = { code: 200 }

})

// 合并分片

router.post('/merge', async (ctx) => {

  const { hash, filename } = ctx.request.body

  const chunkDir = path.join(UPLOAD_DIR, hash)

  const chunks = fs.readdirSync(chunkDir)

    .sort((a, b) => a - b)

  await Promise.all(

    chunks.map((chunkPath, index) =>

      fs.promises.appendFile(

        path.join(UPLOAD_DIR, hash + '.file'),

        fs.readFileSync(path.join(chunkDir, chunkPath))

      )

    )

  )

  // 清理分片目录

  fs.rmdirSync(chunkDir, { recursive: true })

  ctx.body = { code: 200 }

})

```

---

## 六、注意事项

1. **安全性**

   - 限制文件类型和大小

   - 服务端校验分片MD5

   - 防止目录遍历攻击

2. **性能优化**

   - 使用CDN加速分片上传

   - 服务端分片合并使用流式操作

3. **浏览器兼容性**

   - File API 兼容到IE10+

   - 使用axios等库处理上传

---

## 七、总结

该方案通过分片上传解决大文件传输问题,结合断点续传提升可靠性,主要优势包括:

- 支持任意大小文件上传

- 网络中断后可恢复

- 实时进度反馈

- 减少服务器压力

实际部署时需要根据业务需求调整分片策略和并发控制参数。

```

http://www.xdnf.cn/news/662149.html

相关文章:

  • 边缘AI:在物联网设备上实现智能处理
  • 深浅拷贝?
  • 【数据集】基于ubESTARFM法的100m 地温LST数据集(澳大利亚)
  • 自动化测试工具:Selenium详解
  • Python基础语法(十三):命名空间与作用域
  • 新质生产力引擎:营销枢纽智能体贯通全链路,AI赋能企业数字化运营高效升级!
  • 了解哈希表
  • Haproxy编译安装
  • 【MogDB】测试 ubuntu server 22.04 LTS 安装mogdb 5.0.11
  • ceph osd 无法启动
  • 安装conda
  • 如何查看 GitLab 内置的 PostgreSQL 版本?
  • 记录一个有用的tcpdump命令
  • Veeam Backup Replication Console 13 beta 备份 VMware esxi
  • Redis 中跳表
  • 从“无我”到“无生法忍”:解构执着的终极智慧
  • (vue)vue3+vite+ts项目router路由添加
  • 项目管理进阶:详解项目管理办公室(PMO)实用手册【附全文阅读】
  • Vuex Actions: 异步操作
  • LVGL显示其他大小的中文
  • AE THYRO-AX 功率控制器 THYRISTOR-LEISTUNGSSTELLER THYRISTOR POWER CONTROLLER
  • NumPy 2.x 完全指南【十九】广播机制
  • Windows 拓展Path环境变量
  • uniapp 搭配uviwe u-picker 实现地区联栋
  • ETL 工具与数据中台的关系与区别
  • 1.6 如何使用命令行执行 TypeScript 文件
  • Transformer,多头注意力机制 隐式学习子空间划分
  • JAVA Zip导入导出实现
  • 20250526给荣品PRO-RK3566的Android13单独编译boot.img
  • Python程序中字符串与JSON转换的最佳实践详解