如何在Shiny中使用reactivePoll监控并处理文件变更?
问题描述
我想在Shiny中实现以下功能:
- 使用自定义读取程序读取源文件
- 处理数据框并进行渲染
- 通过自定义文件变更检查器监控源文件,当文件发生变更时重复前两步
我尝试用reactivePoll实现,概念上它是最合适的,但实际操作中遇到了checkFunc未找到、输入参数不被识别等问题。另外我还搞不清reactive、observe、observeEvent这些响应式变量创建方式的区别,以及在这个场景里该怎么用。
下面是我简化后的示例代码:
library(shiny) library(tidyverse) #---------生成测试数据----------- # 创建tibble id=c('p1','p2','p3') val=c(2,4,6) my_data=tibble(id,val) # 写入csv file_csv='test.csv' write_csv(my_data,file_csv) # 自定义文件检查函数(带参数) myFileCheck = function(input_path) { if (file.exists(input_path)) file.info(input_path)$mtime[1] else ""} # 测试函数 tmp_time = myFileCheck(file_csv) print(tmp_time) # 自定义文件读取函数(带参数) myReadRoutine = function(input_path){ df = read_csv(input_path, id = 'my_source') } # 测试函数 tmp_df=myReadRoutine(file_csv) print(tmp_df) #----------Shiny 应用--------- # UI部分 ui = fluidPage(tableOutput('my_data')) # Server部分 server = function(input, output, session) { #-----这段能正常运行--- # 启动时读取数据 df_shiny=myReadRoutine(file_csv) # 数据处理用于展示 df_shiny_display = reactive({df_shiny %>% mutate(val2=val*10)}) # 渲染表格 output$my_data = renderTable(df_shiny_display()) #-----这段无法正常运行---- # 尝试用自定义文件检查函数监控文件变更,变更时重新读取 df_shiny = reactivePoll(1000, session, checkFunc = myFileCheck(file_csv), myReadRoutine(file_csv)) } shinyApp(ui = ui, server = server)
解决方案
你的核心问题是对reactivePoll的参数理解有误:checkFunc和valueFunc需要传入无参函数对象,而不是直接传入函数调用的结果。修正后的完整代码如下:
library(shiny) library(tidyverse) #---------生成测试数据----------- id=c('p1','p2','p3') val=c(2,4,6) my_data=tibble(id,val) file_csv='test.csv' write_csv(my_data,file_csv) # 自定义文件检查函数 myFileCheck = function(input_path) { if (file.exists(input_path)) file.info(input_path)$mtime[1] else "" } # 自定义文件读取函数 myReadRoutine = function(input_path){ read_csv(input_path, id = 'my_source') } #----------Shiny 应用--------- ui = fluidPage(tableOutput('my_data')) server = function(input, output, session) { # 使用reactivePoll创建响应式数据对象 df_shiny = reactivePoll( intervalMillis = 1000, # 检查间隔(毫秒) session = session, checkFunc = function() { # 封装检查逻辑,返回标识文件状态的值(比如修改时间) myFileCheck(file_csv) }, valueFunc = function() { # 文件状态变化时执行读取逻辑 myReadRoutine(file_csv) } ) # 响应式数据处理:依赖df_shiny,数据更新时自动重新计算 df_shiny_display = reactive({ df_shiny() %>% mutate(val2 = val * 10) }) # 渲染表格:依赖df_shiny_display,数据处理完成后自动更新 output$my_data = renderTable(df_shiny_display()) } shinyApp(ui = ui, server = server)
关键说明
reactivePoll的正确用法:
checkFunc:无参函数,每隔指定间隔执行一次,返回能标识文件状态的值(如修改时间)。返回值变化时,触发valueFunc执行。valueFunc:无参函数,文件状态变化时执行,返回最新读取的数据。
响应式逻辑对应场景:
reactive():用于创建有返回值的响应式对象,比如处理后的数据df_shiny_display,依赖的源数据更新时会自动重新计算。observe/observeEvent:用于无返回值的副作用操作(如弹窗、写入文件),你的场景里不需要用到,仅用reactive()和输出渲染函数即可。
变量覆盖问题:
原代码中先给df_shiny赋值为静态数据,后又用reactivePoll重新赋值,会导致逻辑混乱。修正后直接用reactivePoll创建响应式的df_shiny,启动时会自动执行一次读取逻辑获取初始数据。
内容的提问来源于stack exchange,提问作者mrroy
相关产品推荐
相关产品推荐

