讲师中心 微信公众号
AI工具推荐 视频效率加速

修复MediaRecorder实时录音至PHP保存文件损坏问题

陌枫大大_9040

陌枫大大_9040

发布时间:2025-12-09 12:57:22

|

322人浏览过

|

来源于php中文网

原创

修复mediarecorder实时录音至php保存文件损坏问题

本文旨在解决使用JavaScript MediaRecorder进行实时录音,并通过Base64编码传输至PHP服务器保存为`.ogg`文件时,文件损坏无法播放的问题。核心问题在于`MediaRecorder`的媒体类型配置不当,以及服务器端对音频数据块的处理方式错误(覆盖而非追加)。教程将详细阐述正确的客户端配置和服务器端文件追加策略,并提供完整的代码示例。

1. 理解MediaRecorder与音频数据流

MediaRecorder API允许我们录制用户的音频和视频流。它通过ondataavailable事件周期性地提供媒体数据块(e.data),这些数据块通常是媒体流的一部分,而非完整的、可独立播放的文件。为了将这些数据块组合成一个可播放的媒体文件,我们需要在客户端或服务器端进行适当的处理。

在将数据发送到服务器进行保存时,常见的流程是:

  1. MediaRecorder捕获音频数据。
  2. ondataavailable事件触发,提供一个数据块。
  3. 数据块被编码(例如Base64),并通过HTTP请求发送到服务器。
  4. 服务器接收数据,解码,并保存到文件。

然而,在这个过程中,有两个关键环节容易导致最终文件损坏。

立即学习“PHP免费学习笔记(深入)”;

2. 客户端配置问题:错误的媒体类型指定

原始代码中,开发者尝试在创建Blob对象时指定媒体类型:

const blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });

这种做法是错误的。MediaRecorder在开始录制时,就需要知道它应该以何种格式和编码器来处理媒体流。Blob构造函数中的type参数仅用于标识Blob的MIME类型,并不会改变其内部数据的实际编码格式。

正确做法是,在MediaRecorder的构造函数中指定媒体类型和编码器。这样,MediaRecorder才会按照指定的格式生成e.data数据块。

dmap-jsapi-skill
dmap-jsapi-skill

DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。

下载
// ...
navigator.mediaDevices.getUserMedia ({ audio: true })
    .then(function(stream) {
        // 在这里定义 MediaRecorder 的选项
        const mrOptions = { mimeType: 'audio/ogg; codecs=opus' };
        mediaRecorder = new MediaRecorder(stream, mrOptions); // 将选项传递给构造函数
        mediaRecorder.start(2000); // 每2秒触发一次 ondataavailable 事件

        mediaRecorder.ondataavailable = function(e) {
            chunks.push(e.data);
            // 创建 Blob 时,可以引用 MediaRecorder 实际使用的 mimeType
            const blob = new Blob(chunks, { type : mediaRecorder.mimeType });
            chunks = []; // 清空 chunks,准备接收下一个数据块
            // ... 后续处理
        };
    })
// ...

通过在MediaRecorder构造函数中设置mimeType,我们确保了e.data数据块本身就是以audio/ogg; codecs=opus格式编码的。

3. 服务器端处理问题:文件覆盖而非追加

原始PHP代码使用file_put_contents("r.ogg", base64_decode($_POST["data"]));来保存数据。file_put_contents函数在默认情况下会覆盖目标文件的全部内容。由于MediaRecorder会周期性地发送数据块,每次服务器收到数据时都会覆盖之前的内容,导致最终文件只包含最后一个数据块,这显然不是一个完整的、可播放的音频文件。

要解决这个问题,我们需要将每次收到的数据追加到文件中,而不是覆盖。

<?php
if(isset($_POST["data"]))
{
    // 使用 FILE_APPEND 标志,将数据追加到文件末尾
    file_put_contents("r.ogg", base64_decode($_POST["data"]), FILE_APPEND);
    exit;
}
?>

重要注意事项: 虽然使用FILE_APPEND可以解决文件覆盖问题,但简单地将原始的Ogg Opus数据块追加到文件中,不一定能保证生成一个完全符合Ogg容器规范的、可播放的.ogg文件。Ogg文件格式包含复杂的页结构、头部信息和数据流管理。MediaRecorder生成的e.data块可能只是原始的Opus编码数据,或者是不完整的Ogg页。直接拼接这些块可能会导致文件结构损坏,或者播放器无法正确解析。

对于更健壮的实时流媒体保存方案,通常需要:

  • 客户端一次性发送完整Blob: 在录制结束时,将所有e.data块聚合成一个大的Blob,然后一次性发送到服务器。这是最简单且可靠的方法,因为MediaRecorder能够确保最终的Blob是一个有效的媒体文件。
  • 服务器端流媒体处理: 使用专门的媒体处理库或服务来接收并正确地拼接或封装这些数据块,以构建一个有效的Ogg容器。这超出了简单的file_put_contents功能。

然而,对于本教程的目标——修复用户现有代码中的直接问题,将file_put_contents改为追加操作是首要且必要的步骤。如果即使追加后文件仍然无法完美播放,则需要考虑上述更复杂的流媒体处理方案。

4. 完整代码示例

结合上述两点修正,以下是优化后的客户端JavaScript和服务器端PHP代码:

客户端 JavaScript (JS)

<script>
var mediaRecorder = null;
let chunks = [];

if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
   console.log('getUserMedia supported.');
   navigator.mediaDevices.getUserMedia (
      {
         audio: true
      })
      .then(function(stream) {
        // 1. 在 MediaRecorder 构造函数中指定媒体类型和编码器
        const mrOptions = { mimeType: 'audio/ogg; codecs=opus' };
        mediaRecorder = new MediaRecorder(stream, mrOptions);

        // 每2秒触发一次 ondataavailable 事件,发送数据块
        mediaRecorder.start(2000); 

        mediaRecorder.ondataavailable = function(e) {
            chunks.push(e.data);
            // 2. 创建 Blob 时,使用 MediaRecorder 实际的 mimeType
            const blob = new Blob(chunks, { type : mediaRecorder.mimeType });
            chunks = []; // 清空 chunks,准备接收下一个数据块

            var reader = new FileReader();
            reader.readAsDataURL(blob); 
            reader.onloadend = function() {
                var data = reader.result.split(";base64,")[1]; 
                requestp2("a.php", "data="+encodeURIComponent(data));
            }
        };

        // 可以添加停止录音的逻辑,例如:
        // setTimeout(() => {
        //     mediaRecorder.stop();
        //     console.log('Recording stopped.');
        // }, 10000); // 录制10秒后停止
      })
      .catch(function(err) {
         console.log('The following getUserMedia error occurred: ' + err);
      });
} else {
   console.log('getUserMedia not supported on your browser!');
}

function requestp2(path, data)
{
    var http = new XMLHttpRequest();
    http.open('POST', path, true);
    http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    http.send(data);
}
</script>

服务器端 PHP

将此代码保存为 a.php (或您在 requestp2 函数中指定的任何文件名)。

<?php
if(isset($_POST["data"]))
{
    // 1. 解码 Base64 数据
    $decodedData = base64_decode($_POST["data"]);

    // 2. 将数据追加到文件末尾,而不是覆盖
    // 注意:首次写入时,如果文件不存在,file_put_contents 会创建它。
    // 每次后续写入都会追加。
    if ($decodedData !== false) {
        file_put_contents("r.ogg", $decodedData, FILE_APPEND);
    } else {
        error_log("Failed to decode base64 data.");
    }
    exit;   
}
?>

5. 最佳实践与注意事项

  1. 文件完整性: 尽管上述修正解决了mimeType配置和文件覆盖问题,但对于Ogg Opus这类复杂容器格式,直接追加原始数据块可能仍无法保证生成一个100%完美的、所有播放器都能兼容的文件。最可靠的方法是在录制结束后,一次性将所有数据块合并成一个完整的Blob发送到服务器。
  2. 错误处理: 在实际应用中,应加强错误处理,例如检查getUserMedia是否成功、base64_decode是否失败、file_put_contents是否成功等。
  3. MIME类型兼容性: 并非所有浏览器都支持所有mimeType和codecs组合。在选择格式时,应考虑目标用户的浏览器兼容性。audio/ogg; codecs=opus通常有较好的支持。
  4. 服务器负载: 频繁发送小数据块可能会增加服务器的I/O和网络负载。根据应用需求,可以调整mediaRecorder.start()的间隔时间,或者在客户端积累更多数据后再发送。
  5. 安全性: 从客户端接收并保存文件到服务器时,务必进行输入验证和清理,防止潜在的安全漏洞(例如路径遍历攻击)。本教程中的示例代码未包含此类安全措施,仅用于演示核心功能。

总结

修复MediaRecorder实时录音至PHP保存文件损坏的问题,关键在于两点:首先,确保在MediaRecorder构造函数中正确指定媒体类型和编码器,使其生成符合预期格式的数据块;其次,在服务器端使用FILE_APPEND模式将接收到的数据块追加到文件中,而非覆盖。虽然直接追加可能对某些复杂媒体格式的完整性有局限,但它解决了文件损坏的核心原因,为进一步的媒体处理奠定了基础。

相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

Seko
Seko Hot

一款AI视频创作工具,主要用于商汤科技推出的创编一体的AI短视频创作Agent,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

相关专题

更多
js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

4276

2023.06.20

js获取当前时间
js获取当前时间

JS全称JavaScript,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言;它是一种属于网络的高级脚本语言,主要用于Web,常用来为网页添加各式各样的动态功能。js怎么获取当前时间呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1315

2023.07.28

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

1718

2023.08.03

js是什么意思
js是什么意思

JS是JavaScript的缩写,它是一种广泛应用于网页开发的脚本语言。JavaScript是一种解释性的、基于对象和事件驱动的编程语言,通常用于为网页增加交互性和动态性。它可以在网页上实现复杂的功能和效果,如表单验证、页面元素操作、动画效果、数据交互等。

9823

2023.08.17

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

960

2023.09.01

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2624

2023.09.04

Js中concat和push的区别
Js中concat和push的区别

Js中concat和push的区别:1、concat用于将两个或多个数组合并成一个新数组,并返回这个新数组,而push用于向数组的末尾添加一个或多个元素,并返回修改后的数组的新长度;2、concat不会修改原始数组,是创建新的数组,而push会修改原数组,将新元素添加到原数组的末尾等等。本专题为大家提供concat和push相关的文章、下载、课程内容,供大家免费下载体验。

1549

2023.09.14

js截取字符串的方法介绍
js截取字符串的方法介绍

JavaScript字符串截取方法,包括substring、slice、substr、charAt和split方法。这些方法可以根据具体需求,灵活地截取字符串的不同部分。在实际开发中,根据具体情况选择合适的方法进行字符串截取,能够提高代码的效率和可读性 。

4509

2023.09.21

OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

0

2026.10.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
墨刀帮助中心
墨刀帮助中心

共0课时 | 0人学习

MyEclipse学习中心
MyEclipse学习中心

共0课时 | 0人学习

Apache Subversion 官方手册
Apache Subversion 官方手册

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn