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

前端经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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 20:05:17