求助: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
相关产品推荐
相关产品推荐

