I have a shiny app to generate a .txt file to download.
In addition, I would like to keep a copy of the file that users generate in my shiny server.
the server function looks like :
server <- function(input, output, session){
data_gen <- reactive({
d1= data.frame(...)
d2= data.frame(...)
result <- list(d1=d1, d2=d2)
return(result)
})
create_file <- reactive({
sink("/srv/shiny-server/S3/file.txt",append = TRUE)
print(data_gen()$d1)
print(data_gen()$d2)
sink()
})
output$downloadData <- downloadHandler(
filename = function() {"input.txt"},
content = function(file) {
sink(file,append = TRUE)
print(data_gen()$d1)
print(data_gen()$d2)
sink()
}
)
}
I'm able to download the data but the app does not react to the create_file function and it does not write a copy into shiny server.
Any Idea how could I fix this ?
Your create_file function is a reactive. Reactive functions only evaluate when 1) their output is required, and 2) their inputs have changed. Neither appears to apply here.
What you could do is move the contents of create_file inside your downloadhandler. content must receive a function that returns a file, but the function can do other things first. So try the following:
server <- function(input, output, session){
data_gen <- reactive({
d1= data.frame(...)
d2= data.frame(...)
result <- list(d1=d1, d2=d2)
return(result)
})
output$downloadData <- downloadHandler(
filename = function() {"input.txt"},
content = function(file) {
# save non-user copy
sink("/srv/shiny-server/S3/file.txt",append = TRUE)
print(data_gen()$d1)
print(data_gen()$d2)
sink()
# copy to be returned for user
sink(file,append = TRUE)
print(data_gen()$d1)
print(data_gen()$d2)
sink()
})
}
Related
I want to read a json file continuously, e.g. every 1000 ms.
One option my be reactiveFileReader
reactiveFileReader(intervalMillis, session, filePath, readFunc, ...)
described here.
This function seems only working with csv files and not for json files:
file_data <- reactiveFileReader(intervalMillis = 1000, NULL, filePath = json_path, readFunc = read.json)
observe({
View(file_data())
})
Error in View : object read.json not found
With reactivePoll like here:
getJsonData <- reactivePoll(1000, session,
checkFunc = function() {
if (file.exists(path))
file.info(path)$mtime[1]
else
""
},
valueFunc = function() {
read_json(path)
}
I get nearly what I want, but this function is not working in my context. How do I force the program to read the file every second and not only when the content of the file is changing?
Are there other possibilities I not have thought about yet?
In your first way, you wrote read.json instead of read_json.
With your second way, you could replace file.info(path)$mtime[1] with runif(1, 0, 1e6). You would be very unlucky if runif returns the same number two consecutive times.
Finally, a third way could be:
server <- function(input, output, session){
autoInvalidate <- reactiveTimer(1000)
getJsonData <- reactive({
autoInvalidate()
read_json("path/to/file.json")
})
}
Here is a reprex on how to use reactiveFileReader with a json file.
I used a future to detach the writing process from the shiny session - you can simply replace this with your json input.
library(shiny)
library(jsonlite)
library(datasets)
library(promises)
library(future)
plan(multisession(workers = 2))
ui <- fluidPage(
uiOutput("printResult")
)
server <- function(input, output, session) {
json_path <- tempfile(fileext = ".json")
write_json(NULL, json_path)
# async file writing process
future({
for(i in seq_len(nrow(iris))){
Sys.sleep(1)
write_json(iris[i,], json_path)
}
})
file_data <- reactiveFileReader(intervalMillis = 1000, NULL, filePath = json_path, readFunc = read_json)
output$printResult <- renderUI({
req(file_data())
})
}
shinyApp(ui, server)
I've been using fileInput in my shiny app to upload a file and then create a reactive object that allow me to read the data.frame and do another stuff like subsetting or filtering. However, I need to get the absolute path of the file for another calculations, and it seems that fileUpload only stores a temporal path.
Here is the server part that works
server = function(input, output) {
options(shiny.maxRequestSize=100*1024^2)
contents <- reactive({
inputFile <- input$fileUpload
if (is.null(inputFile))
return()
read.delim(inputFile$datapath, header = TRUE)
})
# Return filename as another object
file_name <- reactive({
inFile <- input$fileUpload
if (is.null(inFile))
return()
else { print(inFile$name); return(tools::file_path_sans_ext(inFile$name))}
})
output$tabla <- DT::renderDataTable({
if(is.null(contents()))
return()
DT::datatable(contents(),
filter = 'top')
})
However, I would like to use the shinyFiles option because it stores the real path, and I will need that path to load more files
I have tries this part of code in the server to mimic the same behaviour that fileUpload but it does not work
server = function(input, output, session) {
volumes = getVolumes()
volumes <- c(Home = fs::path_home(), "R Installation" = R.home(), getVolumes()())
file_selected <- reactive({
shinyFileChoose(input, "file", roots = volumes, session = session)
return(parseFilePaths(volumes, input$file))
})
contents <- reactive({
if (is.null(file_selected()))
return()
read.delim(file_selected(), header = TRUE)
})
# Reactive function creating the DT output object
output$tabla <- DT::renderDataTable({
if(is.null(contents()))
return()
DT::datatable(contents(),
filter = 'top')
})
I got an error about file must be a character string or connection and if I use read.delim(as.character(file_selected()), header = TRUE) then the error is about invalid "description" argument
There are different possible causes of the error. Try the following steps:
Use req(input$file) before parsing the file paths
Check if input$file is NULL before parsing the file paths
Return $datapath of the parseFilePaths function.
According to this, your code should look like the following:
file_selected <- reactive({
shinyFileChoose(input, "file", roots = volumes, session = session)
req(input$file)
if (is.null(input$file))
return(NULL)
return(parseFilePaths(volumes, input$file)$datapath)
})
Hope it works for you.
I'm new to shiny.
I have a very basic question but I can't find a solution here on stackoverflow.
I am using directory Input function created by wleepang (https://github.com/wleepang/shiny-directory-input).
I wrote a function read_files that rbind all files in the directory selected.
I can display this table with renderTable, this works perfectly. But I do not manage to save this table to work with later (check for missing data, add columns, draw ggplots..) and to download is with write.xlsx
ui <- fluidPage(
directoryInput('directory', label = 'select a directory'),
actionButton("upload", label="Hochladen"),
downloadButton("download", label="Runterladen")
)
server <- function(input, output, session) {
#this part is to set the directory
observeEvent(
ignoreNULL = TRUE,
eventExpr = {
input$directory
},
handlerExpr = {
if (input$directory > 0) {
path = choose.dir(default = readDirectoryInput(session, 'directory'))
updateDirectoryInput(session, 'directory', value = path)
}})
#now comes the actual code
observeEvent(input$upload,{
df <- read_files(readDirectoryInput(session, 'directory'))
})
How can I access this df later?
output$downloadData <- downloadHandler(
filename = function() {
paste('tabelle', '.csv', sep="") },
content = function(file) {
write.xlsx(df, file)
}
)
}
And my second question how can I download it as a xlsx file in the set directory?
my global.r with the read_files function
source('directoryInput.R')
read_files = function(inDir, pat="*.csv", readMe=read.csv2){
files = list.files(inDir, pattern=pat)
files = lapply(files, function(x) file.path(inDir, x))
df = do.call(rbind, lapply(files, readMe))
return(df)
}
I have saved the element using a reactive function
upload_data <- eventReactive(input$upload, {
read_files(readDirectoryInput(session, 'directory')) })
and can access it through upload_data() this works for me
In my below example of a simple shiny app i recently created, im currently trying to include also the possibility of downloading a data frame that is created from the results. I mention here part of the server.R script in order to not make a huge post:
shinyServer(function(input, output) {
table_options<- list(lengthMenu = list(c(5,10,15,20),
c('5','10', '15', '20')), pageLength = 15, ordering=TRUE,
class = 'cell-border stripe',dom ='t',scrollX = TRUE,
fixedColumns = list(leftColumns = 2, rightColumns = 1))
inTable <- reactive({# upload a tsv file
inFile <- input$file1
if (is.null(inFile))
return(NULL)
read.table(inFile$datapath,header=input$header,
sep="\t",stringsAsFactors = FALSE)
}) #END REACTIVE
rv <- reactiveValues()
rv$data <- NULL # to further use it into the observeEvent below
observeEvent(input$goButton, {
df <- inTable()
# some data manipulation with df...
if(input$repo_option=="mimic"){
# some functions here that result to a data frame named final dat
rv$data <- final.dat
rv$data
}
else if(input$repo_option=="reverse"){
# similar procedure...
rv$data <- final.dat
rv$data
}
})
output$contents <- DT::renderDataTable({
expr=DT::datatable(rv$data, options=table_options,
extensions ='FixedColumns',selection="none")
})
output$downloadData <- downloadHandler(
filename = function() { paste("input$file1", ".csv", sep=",") },
content = function(file) {
write.csv(rv$data,file)
}
)
})
My main issue is that, although the output$contents works fine in the app, when i press the download button from the ui.R server (not posted here for simplicity), the download "pop-up" window appears, but the saving does not work. Thus, i suspect that is something wrong with the code in the downloadHandler function, but any ideas or help ?
I want to split my app into smaller peaces for better handling.
server.R
library(shiny)
source("onLoad.R", local = TRUE)
shinyServer(function(input, output, session) {
sourceRecursive("/.../")
})
sourceRecursive
#check folder and all subfolders for .R files
#source() them!
sourceRecursive <- function(path) {
dirs <- list.dirs()
files <- dir(pattern = "^.*[Rr]$", include.dirs = FALSE)
for (f in files)
source(f)
for (d in dirs)
sourceRecursive(d)
}
example file I try to source. file.R
output$myChoices <- renderUI({
selectInput(inputId = 'x',
label = 'y',
choices = levels(myDataSet$df$z),
multiple = T
)
})
Bounces back with:
Error in output$myChoices <- renderUI({ :
object 'output' not found
Obviously the problem is that within the file.R the variable output is not defined since this is a variable which is used in the shiny context. How would I tell R (or shiny) to treat all the variables as shiny defined variables (such as output$whatever, input$something, reactive etc). That seems crucial to me in order to break up the programme into smaller peaces.
I'm using both source(local=TRUE) and sys.source to load the file into the proper environment, it seems to work:
library(shiny)
shinyServer(function(input, output, session) {
# From http://shiny.rstudio.com/articles/scoping.html
output$text <- renderText({
source('each_call.R', local=TRUE)
})
# Source in the file.R from the example in the question
sys.source('file.R', envir=environment())
})
I didn't test it, but you might be able to use:
sourceRecursive <- function(path, env) {
files <- list.files(path = path, pattern = "^.*[Rr]$", recursive = TRUE)
for (f in files) sys.source(f, env)
}
shinyServer(function(input, output, session) {
session.env <- environment()
sourceRecursive(path = ".", env = session.env)
})
What if you use local=TRUE in your call to source provided that sourceRecursive is in the right scope (maybe put it in server.R). See this documentation here