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

求助:Shiny应用集成浏览器端录音功能的方案问题

解决Shiny应用客户端录音集成问题

一、适配shinyearr包的服务器端部署

  • 安装依赖工具:服务器必须安装ffmpeg以支持音频处理。Linux服务器执行sudo apt-get install ffmpeg,Windows服务器需手动安装并配置环境变量。
  • 配置文件权限:确保Shiny进程拥有临时目录(或指定的音频存储目录)的读写权限,避免录音文件上传失败。
  • 调整代码逻辑:在server.R中明确指定录音文件的存储路径,示例代码:
server <- function(input, output, session) {
  # 初始化录音,指定服务器存储目录
  recorded_audio <- shinyearr::record(
    session = session,
    output_dir = tempdir(),
    filename = "user_recording.wav"
  )
  
  # 监听保存按钮,处理录音文件
  observeEvent(input$save_recording, {
    if (!is.null(recorded_audio())) {
      file.copy(recorded_audio(), "/var/shiny-server/recordings/recording.wav")
    }
  })
}
  • 检查CORS设置:若使用Shiny Server或Posit Connect,确保服务器配置未阻止客户端音频上传请求,必要时添加CORS允许规则。

二、集成Simple Web Audio Recorder到Shiny

1. 项目结构准备

将Simple Web Audio Recorder的recorder.js、自定义样式文件style.css以及前端逻辑文件app.js放入Shiny应用的www子目录中。

2. Shiny UI代码

ui <- fluidPage(
  tags$head(
    tags$link(rel = "stylesheet", type = "text/css", href = "style.css"),
    tags$script(src = "recorder.js"),
    tags$script(src = "app.js")
  ),
  
  # 录音控制组件
  tags$div(
    id = "recorder-box",
    tags$button(id = "start-rec", "开始录音"),
    tags$button(id = "stop-rec", "停止录音", style = "display: none;"),
    tags$p(id = "rec-status", "当前状态:未录音")
  ),
  
  # 隐藏输入,用于传递音频数据到后端
  shiny::hidden(shiny::textInput("audio_payload", label = NULL)),
  
  # 显示上传结果
  verbatimTextOutput("upload_result")
)

3. Shiny Server代码

server <- function(input, output, session) {
  # 监听前端传递的音频数据
  observeEvent(input$audio_payload, {
    if (nchar(input$audio_payload) > 0) {
      # 解码base64音频数据
      audio_bytes <- jsonlite::base64_dec(input$audio_payload)
      # 保存到服务器指定路径
      writeBin(audio_bytes, "/path/to/your/recordings/user_audio.wav")
      
      output$upload_result <- renderPrint({
        "录音已成功保存到服务器"
      })
    }
  })
}

4. 前端逻辑(www/app.js)

document.addEventListener('DOMContentLoaded', () => {
  const startBtn = document.getElementById('start-rec');
  const stopBtn = document.getElementById('stop-rec');
  const statusText = document.getElementById('rec-status');
  const audioInput = document.getElementById('audio_payload');
  
  let audioRecorder;
  let audioCtx;

  startBtn.addEventListener('click', () => {
    navigator.mediaDevices.getUserMedia({ audio: true })
      .then(stream => {
        audioCtx = new AudioContext();
        const source = audioCtx.createMediaStreamSource(stream);
        audioRecorder = new Recorder(source, { numChannels: 1 });
        
        audioRecorder.record();
        startBtn.style.display = 'none';
        stopBtn.style.display = 'inline-block';
        statusText.textContent = '当前状态:正在录音';
      })
      .catch(err => {
        alert('麦克风访问失败:' + err.message);
      });
  });

  stopBtn.addEventListener('click', () => {
    audioRecorder.stop();
    startBtn.style.display = 'inline-block';
    stopBtn.style.display = 'none';
    statusText.textContent = '当前状态:正在处理录音';
    
    audioRecorder.exportWAV(blob => {
      const reader = new FileReader();
      reader.onloadend = () => {
        // 提取base64数据(移除前缀)
        const base64 = reader.result.split(',')[1];
        // 传递给Shiny后端
        Shiny.setInputValue('audio_payload', base64);
        statusText.textContent = '当前状态:录音已上传';
      };
      reader.readAsDataURL(blob);
      
      // 释放资源
      audioRecorder.clear();
      audioCtx.close();
    });
  });
});

注意事项

  • 用户首次访问应用时需在浏览器中授权麦克风权限。
  • 确保服务器存储目录拥有足够的磁盘空间,且Shiny进程具备读写权限。

内容的提问来源于stack exchange,提问作者tannaz hasanzadeh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 20:40:30