在FFmpeg中分片加载大文件以解决Safari移动端崩溃问题的方法
在FFmpeg中分片加载大文件以解决Safari移动端崩溃问题的方法
我之前做移动端视频处理项目时,正好踩过Safari这个坑!一次性把大文件全塞进内存交给FFmpeg,Safari那严格的内存限制分分钟让应用崩给你看。下面给你两个亲测有效的解决思路,都是我当时试过能搞定的:
一、分片读取+分块写入FFmpeg虚拟文件系统
这个思路就是把大文件切成小块,逐块读取后写入FFmpeg,彻底避免一次性占用过多内存:
- 先确定合适的分块大小,建议从10MB左右开始测试(可以根据文件类型调整,比如视频文件块大一点也没问题,文本类可以小一些)
- 用
Blob.slice()方法把原始文件切成多个小Blob块 - 循环读取每个块,逐块写入FFmpeg的虚拟文件,每写完一块再处理下一块,别并行操作,不然内存还是会飙
给你个参考代码片段:
const chunkSize = 10 * 1024 * 1024; // 10MB 每块 const file = 你的原始文件对象; const inputFileName = "input.mp4"; // FFmpeg虚拟文件系统里的文件名 // 先创建空文件(部分FFmpeg版本支持直接追加,这步可省) await ffmpeg.writeFile(inputFileName, new Uint8Array(0)); for (let start = 0; start < file.size; start += chunkSize) { const end = Math.min(start + chunkSize, file.size); const chunk = file.slice(start, end); // 把当前块读取为Uint8Array const chunkBuffer = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(new Uint8Array(reader.result)); reader.onerror = reject; reader.readAsArrayBuffer(chunk); }); // 追加写入到FFmpeg的文件中 await ffmpeg.appendFile(inputFileName, chunkBuffer); }
注意:如果用的是旧版FFmpeg.wasm,可能没有appendFile方法,这时候可以先读出现有文件内容,和新块合并后再写回去,但这种方式内存占用会稍高,优先用支持appendFile的新版本。
二、使用FFmpeg的流式输入能力(推荐)
如果你的FFmpeg版本(比如新版ffmpeg.wasm)支持流式处理,那直接用流式输入会更优雅,完全不用手动管分块:
- 把原始文件转换成可读流
- 直接把流作为FFmpeg的输入源,FFmpeg会按需读取数据,绝不会一次性加载整个文件
参考代码:
const file = 你的原始文件对象; const readableStream = file.stream(); // 获取文件的可读流 // 直接将流作为FFmpeg的输入 await ffmpeg.exec([ "-i", "pipe:0", // 用pipe:0表示从标准输入流读取 // 这里放你的其他处理参数,比如转码、裁剪、加水印等 "output.mp4" ], { stdin: readableStream // 传入可读流作为标准输入 });
这种方式对内存最友好,数据是按需流动的,Safari根本没机会把整个文件塞进内存,强烈推荐优先试这个!
额外小提示
- 多试几种分块大小,找到你场景下的最优值,太大还是会爆内存,太小会增加IO次数拖慢速度
- 一定要捕获块读取和写入时的错误,比如本地文件读取失败、远程文件网络中断的情况
- 如果处理的是远程大文件,别先把整个文件下载到本地再处理,直接对远程响应流做分块或流式处理,能进一步省内存
备注:内容来源于stack exchange,提问作者developer1
相关产品推荐
相关产品推荐

