javascript之分片上传,断点续传的方法

发布时间:2022-05-07 10:52:47 作者:zzz
来源:亿速云 阅读:624
# JavaScript之分片上传,断点续传的方法

## 一、背景与需求分析

在现代Web应用中,大文件上传是一个常见需求。传统单次上传方式存在以下问题:
- 网络波动导致失败需重新上传
- 大文件占用服务器内存过高
- 无法实时展示上传进度

分片上传(Chunked Upload)和断点续传(Resumable Upload)通过将文件切割为多个片段分别上传,有效解决了这些问题。

## 二、核心实现原理

### 1. 分片上传流程
1. 前端将文件切割为固定大小(如5MB)的片段
2. 为每个片段生成唯一标识
3. 并行/串行上传分片
4. 服务端接收并存储分片
5. 所有分片上传完成后触发合并操作

### 2. 断点续传实现
- 基于文件hash建立唯一标识
- 服务端记录已上传分片信息
- 中断后重新上传时先查询缺失分片

## 三、前端实现代码示例

### 1. 文件分片处理
```javascript
// 生成文件分片
function createFileChunks(file, chunkSize = 5 * 1024 * 1024) {
  const chunks = []
  let cur = 0
  while (cur < file.size) {
    chunks.push({
      chunk: file.slice(cur, cur + chunkSize),
      filename: `${file.name}-${cur}`
    })
    cur += chunkSize
  }
  return chunks
}

// 生成文件hash(使用SparkMD5库)
async function calculateHash(chunks) {
  return new Promise(resolve => {
    const spark = new SparkMD5.ArrayBuffer()
    let count = 0
    
    const loadNext = index => {
      const reader = new FileReader()
      reader.readAsArrayBuffer(chunks[index].chunk)
      reader.onload = e => {
        spark.append(e.target.result)
        count++
        if (count === chunks.length) {
          resolve(spark.end())
        } else {
          loadNext(count)
        }
      }
    }
    loadNext(0)
  })
}

2. 上传控制逻辑

async function uploadFile(file) {
  const chunks = createFileChunks(file)
  const fileHash = await calculateHash(chunks)
  
  // 检查已上传分片
  const { uploadedList } = await checkServerChunks(fileHash)
  
  const requests = chunks.map((chunk, index) => {
    // 跳过已上传分片
    if (uploadedList.includes(`${fileHash}-${index}`)) {
      return Promise.resolve()
    }
    
    const formData = new FormData()
    formData.append('chunk', chunk.chunk)
    formData.append('hash', `${fileHash}-${index}`)
    formData.append('fileHash', fileHash)
    
    return axios.post('/upload', formData, {
      onUploadProgress: createProgressHandler(chunk)
    })
  })
  
  await Promise.all(requests)
  await mergeFileRequest(fileHash, file.name)
}

四、服务端关键处理

1. Node.js示例(Express)

// 接收分片
app.post('/upload', (req, res) => {
  const { hash, fileHash } = req.body
  const chunk = req.files.chunk
  
  // 存储分片到临时目录
  const chunkDir = path.resolve('temp', fileHash)
  if (!fs.existsSync(chunkDir)) {
    fs.mkdirSync(chunkDir)
  }
  
  fs.writeFileSync(path.resolve(chunkDir, hash), chunk.data)
  res.send({ code: 0 })
})

// 合并分片
app.post('/merge', async (req, res) => {
  const { fileHash, filename } = req.body
  const chunkDir = path.resolve('temp', fileHash)
  const chunks = fs.readdirSync(chunkDir)
  
  // 按序号排序后合并
  chunks.sort((a, b) => a.split('-')[1] - b.split('-')[1])
  await Promise.all(
    chunks.map(chunkPath => 
      fs.promises.appendFile(
        path.resolve('uploads', filename),
        fs.readFileSync(path.resolve(chunkDir, chunkPath))
    )
  )
  
  // 删除临时目录
  fs.rmdirSync(chunkDir, { recursive: true })
  res.send({ code: 0 })
})

五、优化策略

  1. 并发控制:通过限制并行请求数避免浏览器卡顿
async function parallelUpload(tasks, limit = 3) {
  const results = []
  const executing = []
  
  for (const task of tasks) {
    const p = Promise.resolve().then(task)
    results.push(p)
    
    const e = p.then(() => executing.splice(executing.indexOf(e), 1))
    executing.push(e)
    
    if (executing.length >= limit) {
      await Promise.race(executing)
    }
  }
  
  return Promise.all(results)
}
  1. 进度计算优化:基于分片大小计算整体进度
function createProgressHandler(chunk) {
  return progressEvent => {
    const percent = Math.round(
      (progressEvent.loaded / chunk.size) * 100
    )
    // 更新对应分片的进度
    updateChunkProgress(chunk.filename, percent)
    
    // 计算整体进度
    const total = chunks.reduce((sum, c) => {
      return sum + (c.progress || 0) * c.size / file.size
    }, 0)
    console.log(`总进度: ${total.toFixed(2)}%`)
  }
}

六、注意事项

  1. 文件一致性验证:合并完成后需校验文件MD5
  2. 临时文件清理:设置定时任务清理过期未合并的分片
  3. 分片大小选择:通常建议2-10MB,需权衡网络环境和服务器限制
  4. 浏览器兼容性:File API的兼容性处理

七、扩展方案

  1. Web Worker计算文件hash避免界面卡顿
  2. 使用IndexedDB存储分片实现暂停/恢复功能
  3. 结合WebSocket实现实时进度推送

通过上述方法,开发者可以构建健壮的大文件上传系统,显著提升用户体验。实际应用中还需结合具体业务场景进行调整优化。 “`

推荐阅读:
  1. javascript之分片上传,断点续传的实际项目的示例分析
  2. iOS如何实现大文件的分片上传和断点上传

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

javascript

上一篇:python怎么实现登录界面

下一篇:JavaScript中怎么用递归函数解汉诺塔算法

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》