I'm trying to get fileInput()
and reactiveFileReader()
to work together. The goal is to reread the file and replot every 5 seconds (via reactiveFileReader) or when the user picks a file (fileInput). Both parts work fine on their own, but when I combine them, the file is not reread when new data is added. Here's a reprex. The background function writes a new line of the mtcars dataset to a file every 4 seconds. The table in the shiny app updates when the filename is hard coded in reactiveFileReader, but does not update when using the filename passed from fileInput.
library(callr)
# create dummy data -------------------------------------------------------
bgprocess <- r_bg(func = function(){
if(!file.exists("test.csv")){
write.csv(mtcars[1,], "test.csv")
}
for (i in 2:NROW(mtcars)){
Sys.sleep(4)
print("Updating file")
write.table(mtcars[i,], "test.csv", sep = ",",
append = TRUE,
col.names = FALSE)
if(i == NROW(mtcars)){
print("Finished updating file")
}
}
})
# bgprocess$is_alive()
print(paste("bgprocess pid:", bgprocess$get_pid()))
# shiny app ---------------------------------------------------------------
ui <- fluidPage(
fileInput("file", "Filename"),
tableOutput("table")
)
server <- function(input, output, session) {
file_data <- reactive({
req(input$file)
input$file
})
file_reader <- reactive({
reactiveFileReader(5000, session, "test.csv", read.csv)
#reactiveFileReader(5000, session, file_data()$datapath, read.csv)
})
output$table <- renderTable(file_reader()())
}
# Run the application
shinyApp(ui = ui, server = server)
shinyApp(ui, server, onStart = function() {
onStop(function() {
try(bgprocess$kill())
})
})```
without running anything, my guess is that the input file being loaded is sent to a temp directory. That isn't changing after being uploaded since your background job is writing to the user's working directory in your example. If you change the reactiveFileReader call to below, it should work (though you probably want to fix the actual path):
reactiveFileReader(5000, session, file_data()$name, read.csv)
Thank you! This is exactly it. datapath
contains the path to the temp file uploaded by fileInput()
and is therefore static. Using name
works for this example, but will be problematic if the user is picking something outside of a local directory.
After a small rabbit hole, the shinyFiles
package looks like the right way to handle this. Here's the example using shinyFiles
tools to pick the file.
library(shiny)
library(callr)
library(shinyFiles)
# create dummy data -------------------------------------------------------
bgprocess <- r_bg(func = function(){
if(!file.exists("test.csv")){
write.csv(mtcars[1,], "test.csv")
}
for (i in 2:NROW(mtcars)){
Sys.sleep(4)
print("Updating file")
write.table(mtcars[i,], "test.csv", sep = ",",
append = TRUE,
col.names = FALSE)
if(i == NROW(mtcars)){
print("Finished updating file")
}
}
})
# bgprocess$is_alive()
print(paste("bgprocess pid:", bgprocess$get_pid()))
# shiny app ---------------------------------------------------------------
ui <- fluidPage(
shinyFilesButton('file', label='File select', title='Please select a file', multiple=FALSE),
tableOutput("table")
)
server <- function(input, output, session) {
roots = c(root='.')
file_data <- reactive({
req(input$file)
shinyFileChoose(input, 'file', root=roots, filetypes=c('', 'csv'))
parseFilePaths(root=roots, input$file)
})
file_reader <- reactive({
req(file_data()$datapath)
reactiveFileReader(5000, session, file_data()$datapath[[1]], read.csv)
})
output$table <- renderTable(file_reader()())
}
# Run the application
shinyApp(ui = ui, server = server)
shinyApp(ui, server, onStart = function() {
onStop(function() {
try(bgprocess$kill())
})
})