前端经WebSocket传音频至Django后端无声问题求助
问题分析与解决
你遇到的后端保存音频无声问题,核心有三个原因:
1. 音频格式不匹配
前端用MediaRecorder录制的是WebM格式(audio/webm),但后端直接保存成.wav后缀的文件。WebM是容器格式,和WAV的编码、结构完全不同,直接改后缀无法让播放器正确解析,导致无声。
2. 文件写入方式错误
receive方法每次收到音频片段时,都用mode='bx'(二进制写入)打开文件,这会覆盖之前的内容,最终保存的只是最后一段音频片段,而WebM需要完整的分段拼接才能正常播放,所以即使格式正确,这种写法也会导致文件损坏。
3. 断开连接时的代码错误
disconnect方法里存在变量名错误(roomGroupName应该是self.room_group_name),且group_discard的参数顺序颠倒,这会导致断开时无法正确退出群组,可能影响后续连接,但不是音频无声的直接原因,不过也需要修正。
修复步骤
步骤1:修正音频格式处理
有两种选择:
- 选项A:后端保存为WebM格式,直接播放
把保存的文件名改成myfile.webm,播放器支持WebM的话就能正常播放。 - 选项B:前端录制WAV格式(部分浏览器支持)
修改MediaRecorder的mimeType为audio/wav,但要注意兼容性,先检查支持性:if (!MediaRecorder.isTypeSupported('audio/wav')) return alert('浏览器不支持WAV格式录制') const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/wav', })
步骤2:修正文件写入逻辑
将文件打开模式改为追加写入(mode='ab'),确保每次收到的音频片段都拼接到文件末尾:
async def receive(self, bytes_data): # 用追加模式打开,避免覆盖之前的内容 with open('myfile.webm', mode='ab') as f: f.write(bytes_data)
步骤3:修复disconnect方法的错误
修正变量名和参数顺序:
async def disconnect(self, close_code): await self.channel_layer.group_discard( self.channel_name, self.room_group_name ) print("Disconnected")
步骤4:验证前端发送的数据
在前端dataavailable事件里,先把event.data保存到本地,确认音频正常:
mediaRecorder.addEventListener('dataavailable', (event) => { if (event.data.size > 0) { // 前端本地保存测试,确认录制的音频正常 const url = URL.createObjectURL(event.data); const a = document.createElement('a'); a.href = url; a.download = 'test.webm'; a.click(); // 再发送到后端 if (socket.readyState == 1) { socket.send(event.data); } } })
完整修正后的代码
Consumer.py
import json from channels.generic.websocket import AsyncWebsocketConsumer class ChatConsumer(AsyncWebsocketConsumer): async def connect(self): self.room_name = self.scope["url_route"]["kwargs"]["username"] self.room_group_name = "realtime_%s" % self.room_name await self.channel_layer.group_add( self.room_group_name, self.channel_name) await self.accept() print("Connected") async def disconnect(self, close_code): await self.channel_layer.group_discard( self.channel_name, self.room_group_name ) print("Disconnected") async def receive(self, bytes_data): # 追加写入WebM文件 with open('myfile.webm', mode='ab') as f: f.write(bytes_data)
audio.js
<script> navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => { if (!MediaRecorder.isTypeSupported('audio/webm')) return alert('浏览器不支持WebM音频录制') const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm', }) const socket = new WebSocket('ws://localhost:8000/audio') socket.onopen = () => { document.querySelector('#status').textContent = '已连接' console.log({ event: 'onopen' }) mediaRecorder.addEventListener('dataavailable', (event) => { if (event.data.size > 0 && socket.readyState == 1) { socket.send(event.data) } }) mediaRecorder.start(250) } socket.onmessage = (message) => { const received = message.data if (received) { console.log(received) } } socket.onclose = () => { console.log({ event: 'onclose' }) } socket.onerror = (error) => { console.log({ event: 'onerror', error }) } }) </script>
内容的提问来源于stack exchange,提问作者Om Pachauli
相关产品推荐
相关产品推荐

