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

【实用案例】录音分片上传的核心逻辑和实现案例【文章附有代码】

前言

最近接到一个业务需求:录音文件上传时因为文件过大(超过100M)有报错,

(服务器端对单次文件的上传,有大小限制)。

经过讨论,最后决定使用‘分片’解决问题。

我做了一个小案例(非业务代码),用HTML代码实现前端页面用express实现后端服务

一、分片上传的核心原理​

分片上传是将大文件分割成多个小片段(分片),逐个上传到服务器,最后在服务器端合并所有分片形成完整文件的技术。这种方式的优势在于:​

  • 避免单次上传大文件导致的超时问题​
  • 支持断点续传,某分片失败只需重传该分片​
  • 减轻服务器单次处理压力,提高系统稳定性​

对于录音文件来说,由于可能包含长时间的音频数据(尤其是高清录音),文件体积往往较大,非常适合采用分片上传方案。

二、前端实现核心逻辑​

前端主要负责录音、文件分片和上传控制三个部分。

1. 录音功能实现​

使用浏览器原生的MediaRecorderAPI 实现录音功能:

// 开始录音
startRecordBtn.addEventListener('click', async () => {const stream = await navigator.mediaDevices.getUserMedia({ audio: true });mediaRecorder = new MediaRecorder(stream);audioChunks = [];mediaRecorder.ondataavailable = (event) => {audioChunks.push(event.data);};mediaRecorder.start();// 更新UI状态...
});// 停止录音
stopRecordBtn.addEventListener('click', () => {mediaRecorder.stop();mediaRecorder.stream.getTracks().forEach(track => track.stop());mediaRecorder.onstop = () => {audioBlob = new Blob(audioChunks, { type: 'audio/webm' });audioUrl = URL.createObjectURL(audioBlob);// 显示上传区域...};
});

2. 分片上传核心实现​

创建AudioChunkUploader类处理分片逻辑:

class AudioChunkUploader {constructor(options) {this.options = {chunkSize: 5 * 1024 * 1024, // 5MB每片uploadUrl: 'http://localhost:3000/upload/chunk',mergeUrl: 'http://localhost:3000/upload/merge',...options};this.state = {totalChunks: 0,uploadedChunks: 0,fileMd5: '', // 文件唯一标识isPaused: false};}// 计算文件MD5(用于标识同一文件)async _calculateFileMd5() {return new Promise((resolve) => {const fileReader = new FileReader();const spark = new SparkMD5.ArrayBuffer();fileReader.onload = (e) => {spark.append(e.target.result);this.state.fileMd5 = spark.end();resolve();};fileReader.readAsArrayBuffer(this.options.file);});}// 上传单个分片async _uploadSingleChunk(chunkIndex) {const start = chunkIndex * this.options.chunkSize;const end = Math.min(start + this.options.chunkSize, this.state.fileSize);const chunk = this.options.file.slice(start, end);const formData = new FormData();formData.append('chunk', chunkIndex);formData.append('chunks', this.state.totalChunks);formData.append('fileMd5', this.state.fileMd5);formData.append('file', chunk);return fetch(this.options.uploadUrl, {method: 'POST',body: formData}).then(response => response.json());}// 上传所有分片async _uploadChunks() {for (let i = 0; i < this.state.totalChunks; i++) {if (this.state.isPaused) {// 等待恢复上传await new Promise(resolve => {const check = setInterval(() => {if (!this.state.isPaused) {clearInterval(check);resolve();}}, 100);});}await this._uploadSingleChunk(i);this.state.uploadedChunks = i + 1;// 触发进度更新...}}// 通知服务器合并分片async _mergeChunks() {return fetch(this.options.mergeUrl, {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({fileMd5: this.state.fileMd5,fileName: this.options.fileName,chunks: this.state.totalChunks})}).then(response => response.json());}
}

3. 播放控制功能​

实现带暂停 / 继续功能的音频播放:

let audioPlayer = null;
let lastPlayTime = 0;// 播放录音
playRecordBtn.addEventListener('click', () => {if (!audioPlayer) {audioPlayer = new Audio(audioUrl);if (lastPlayTime > 0) {audioPlayer.currentTime = lastPlayTime;}} else {audioPlayer.currentTime = lastPlayTime;}audioPlayer.play();// 更新按钮状态...
});// 暂停播放
stopPlayBtn.addEventListener('click', () => {lastPlayTime = audioPlayer.currentTime;audioPlayer.pause();// 更新按钮状态...
});

三、后端实现核心逻辑​

Express 后端负责接收分片、临时存储和合并文件:​

1. 分片接收接口

const multer = require('multer');
const upload = multer({ storage: multer.diskStorage({destination: (req, file, cb) => {// 为每个文件创建独立的临时目录const chunkDir = path.join(tempDir, req.body.fileMd5);if (!fs.existsSync(chunkDir)) {fs.mkdirSync(chunkDir, { recursive: true });}cb(null, chunkDir);},filename: (req, file, cb) => {// 用分片索引作为文件名cb(null, req.body.chunk);}})
});// 处理分片上传
app.post('/upload/chunk', upload.single('file'), (req, res) => {res.json({success: true,message: `分片 ${req.body.chunk} 上传成功`});
});

2. 分片合并接口

app.post('/upload/merge', (req, res) => {const { fileMd5, fileName, chunks } = req.body;const chunkDir = path.join(tempDir, fileMd5);const destPath = path.join(uploadDir, fileName);// 检查所有分片是否上传完成if (fs.readdirSync(chunkDir).length !== parseInt(chunks)) {return res.status(400).json({success: false,message: '分片不完整'});}// 合并分片const writeStream = fs.createWriteStream(destPath);let chunkIndex = 0;const mergeNextChunk = () => {const chunkPath = path.join(chunkDir, chunkIndex.toString());if (fs.existsSync(chunkPath)) {const readStream = fs.createReadStream(chunkPath);readStream.pipe(writeStream, { end: false });readStream.on('end', () => {fs.unlinkSync(chunkPath); // 删除已合并的分片chunkIndex++;mergeNextChunk();});} else {writeStream.end(); // 所有分片合并完成}};mergeNextChunk();writeStream.on('finish', () => {fs.rmdirSync(chunkDir, { recursive: true }); // 清理临时目录res.json({success: true,fileName,filePath: destPath});});
});

四、效果展示

可以在设定好存放上传文件的文件夹下看到上传的录音文件

五、总结与拓展​

录音文件分片上传方案的核心在于:​

  • 前端将录音文件分割成固定大小的分片,计算文件唯一标识​
  • 逐个上传分片,支持暂停 / 继续功能​
  • 后端接收分片并临时存储​
  • 所有分片上传完成后,后端按顺序合并成完整文件​

方案可以进一步优化:​

  • 添加断点续传功能,上传前先查询已上传的分片​
  • 实现分片上传的并发控制,提高上传速度​
  • 增加文件校验机制,确保上传文件的完整性​
  • 对大文件 MD5 计算进行优化,避免页面卡顿​

分片上传技术不仅适用于录音文件,也可推广到视频、文档等各种大文件上传场景,是 Web 开发中处理大文件的重要方案。

代码获取,后台联系。

http://www.dtcms.com/a/325384.html

相关文章:

  • 智能双行框!百度全量上线AI搜索,是革新浪潮还是昙花一现?
  • 场外个股期权交易系统全球解决方案:监管协同与流动性创新——基于香港LEAP框架与多级清算体系的实践验证
  • 腾讯 iOA 测评 | 横向移动检测、病毒查杀、外设管控、部署性能
  • 智能合约执行引擎在Hyperchain中的作用
  • 飞算 JavaAI 智能进阶:从技术工具到金融科技开发范式的革新
  • 能力评估:如何系统评估你的技能和经验
  • “人工智能 +”新政即将出台,哪些领域将迎来发展风口?
  • 论文学习22:UNETR: Transformers for 3D Medical Image Segmentation
  • IDE认知革命:JetBrains AI Assistant插件深度调教手册(终极实战指南)
  • @ContextConfiguration
  • Java基础结课题-统计双色球中奖数
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘lightgbm’问题
  • yolo目标检测技术之yolo1到yolo5(二)
  • Profile.vue组件详细解析
  • 缓存的三大问题分析与解决
  • 【运维进阶】WEB 服务器
  • Linux epoll:高并发网络编程的终极武器
  • Android Coil3视频封面抽取封面帧存Disk缓存,Kotlin
  • 自动化UI测试工具TestComplete的多语言引擎与内置实践
  • LabVIEW声波测井信号处理系统
  • 【前沿技术动态】【AI总结】时隔六年!OpenAI 8 月 5 日「开放权重」回归,GPT-OSS 双模型能否重塑开源格局?
  • 小项目方的“活跃术”:市值管理 + 批量交易 + 新地址买入指南
  • [4.2-1] NCCL新版本的register如何实现的?
  • ESP32将DHT11温湿度传感器采集的数据上传到XAMPP的MySQL数据库
  • 【JavaEE】(12) 创建一个 Sring Boot 项目
  • 如何在直播APP中集成美颜SDK?美白滤镜功能开发全流程解析
  • Python笔记之`getattr`和`hasattr`用法详解
  • Vibe Coding 自然语言驱动 AI 编程方式
  • 5G NR NTN 在 PHY 层和 MAC 层实现 OAI
  • 第9节 大模型分布式推理核心挑战与解决方案