You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Web开发:视频分片上传后合并失败的解决方案求助

MP4分片上传后合并失败的问题排查与解决方案

问题背景

我在Web开发项目中遇到MP4视频分片上传后的合并难题。最初实现了按固定字节大小切割MP4的服务,但上传后分片无法成功合并。已检查分片流程并尝试多种方案,仍未解决,也了解到视频分片存在格式层面的复杂度。

客户端分片代码

export class Chunks {
  chunkSize: number
  private file: File

  constructor({chunkSize}) {
    this.chunkSize = chunkSize * 1024
  }

  get totalChunks () {
    return Math.ceil(this.file.size / this.chunkSize);
  }

  setFile(file: File) {
    this.file = file
  }

  // 读取文件片段
  readChunk(start: number, end: number) {
    const file = this.file
    return file.slice(start, end)
  }

  async getChunks(i: number) {
    i--
    if(i < this.totalChunks) {
      const start = i * this.chunkSize;
      const end = Math.min(start + this.chunkSize, this.file.size);
      const chunk = await this.readChunk(start, end);
      return chunk
    }
  }
}

// 分片上传逻辑
const chunks = new Chunks({chunkSize: 1024})
chunks.setFile(File) // 注:此处应为实际的File对象,示例中为笔误

for (let index = 1;  index <= chunks.totalChunks; index++) {
  const blob = await chunks.getChunks(index)
  const blobFile = new File([blob], "test.mp4", { type: blob.type });
  const formData = new FormData();

  formData.append("blobFile", blobFile);
  formData.append("length", chunks.totalChunks);
  formData.append("index", index);
  return this.http.post(`${geturl}`, formData, options)
}

服务器端情况

服务器端合并逻辑在concatMedia函数中出错,但未捕获到异常。核心逻辑为接收分片后尝试合并媒体文件。

测试结果

  • 第一个分片可正常播放,其余分片无法被识别为视频
  • 后端使用在线工具生成的分片上传后可成功合并
  • 完整视频上传后concatMedia函数无报错

问题根源与解决方案

核心问题

MP4是结构化的媒体容器,包含moov(元数据块)、mdat(媒体数据块)等关键部分。直接用File.slice()按固定字节切割会破坏文件结构:第一个分片可能包含完整的moov块所以能播放,后续分片只有部分mdat数据,缺失元数据,无法被识别为有效视频,服务器自然无法合并。

具体解决方案

1. 客户端按MP4结构分片(替换字节切割)

不要直接切割原始字节,使用媒体处理库在客户端生成每个都能独立播放的MP4分片:
推荐用ffmpeg.wasm(浏览器端FFmpeg)实现按关键帧切割:

import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';

async function splitVideo(file) {
  const ffmpeg = createFFmpeg({ log: true });
  await ffmpeg.load();

  // 写入原文件到FFmpeg文件系统
  ffmpeg.FS('writeFile', 'input.mp4', await fetchFile(file));
  
  // 按关键帧切割,每个分片10秒(可自定义时长)
  await ffmpeg.run(
    '-i', 'input.mp4',
    '-c:v', 'libx264',
    '-crf', '23',
    '-f', 'segment',
    '-segment_time', '10',
    '-reset_timestamps', '1',
    'output_%03d.mp4'
  );

  // 读取所有分片
  const chunks = [];
  let i = 0;
  while (true) {
    try {
      const chunkData = ffmpeg.FS('readFile', `output_${i.toString().padStart(3, '0')}.mp4`);
      chunks.push(new File([chunkData], `chunk_${i}.mp4`, { type: 'video/mp4' }));
      i++;
    } catch (err) {
      break;
    }
  }
  return chunks;
}

2. 服务器端用媒体工具合并

服务器端不能简单拼接字节,需用FFmpeg等工具合并完整的MP4分片:

  • 生成分片列表文件list.txt:
    file 'chunk_0.mp4'
    file 'chunk_1.mp4'
    file 'chunk_2.mp4'
    
  • 执行合并命令:
    ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4
    

如果用Node.js,可使用fluent-ffmpeg库调用FFmpeg:

const ffmpeg = require('fluent-ffmpeg');

ffmpeg()
  .input('list.txt')
  .inputOptions(['-f concat', '-safe 0'])
  .outputOptions('-c copy')
  .save('output.mp4')
  .on('end', () => console.log('合并完成'))
  .on('error', (err) => console.error('合并失败:', err));

3. 直接使用成熟分片上传库

避免手动实现的复杂度,推荐使用现成库:

  • tus-js-client:支持断点续传、分片上传,服务器端可搭配tus服务端实现完整流程
  • resumable.js:专注文件分片上传,自定义性强,可配合后端合并逻辑

验证步骤

  1. 检查客户端生成的每个分片是否能独立播放,若不能则客户端切割逻辑有误
  2. 用FFmpeg手动合并客户端分片,验证是否能生成完整视频,排查是客户端还是服务器问题

内容的提问来源于stack exchange,提问作者BackdoorTech

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 14:30:05