R Shiny: async downloadHandler - r

I have a shiny app which takes a large amount of time downloading zip files. I am trying to use the futures and promises packages to manage the downloads so that other users can access the app while downloads are in progress.
The app looks as below:
library(shiny)
ui <- fluidPage(
downloadButton("Download", "Download")
)
server <- function(input, output){
output$Download <- downloadHandler(
filename = "Downloads.zip",
content = function(file){
withProgress(message = "Writing Files to Disk. Please wait...", {
temp <- setwd(tempdir())
on.exit(setwd(temp))
files <- c("mtcars.csv", "iris.csv")
write.csv(mtcars, "mtcars.csv")
write.csv(iris, "iris.csv")
zip(zipfile = file, files = files)
})
}
)
}
shinyApp(ui, server)
I've tried wrapping the write.csv inside a future function and setting `and while this does not throw an error, the app is not available for other users during the download.
library(shiny)
library(promises)
library(future)
plan(multiprocess)
ui <- fluidPage(
downloadButton("Download", "Download")
)
server <- function(input, output){
output$Download <- downloadHandler(
filename = "Downloads.zip",
content = function(file){
withProgress(message = "Writing Files to Disk. Please wait...", {
temp <- setwd(tempdir())
on.exit(setwd(temp))
files <- c("mtcars.csv", "iris.csv")
future(write.csv(mtcars, "mtcars.csv"))
future(write.csv(iris, "iris.csv"))
zip(zipfile = file, files = files)
})
}
)
}
shinyApp(ui, server)
I've also tried wrapping the entire downloadHandler function inside the future function, but I get the error:
Error in .subset2(x, "impl")$defineOutput(name, value, label) :
Unexpected MulticoreFuture output for DownloadUnexpected
MultiprocessFuture output for DownloadUnexpected Future output for
DownloadUnexpected environment output for Download
How can I handle the entire downloadHandler asyncronously? I am using the open source version of shiny server.

Don't know if you still need an answer for this, but I think you were very close. I have wrapped both the write.csv and zip in future as below and it works for multiple users on my testing.
library(shiny)
library(promises)
library(future)
plan(multiprocess)
ui <- fluidPage(
downloadButton("Download", "Download")
)
server <- function(input, output){
output$Download <- downloadHandler(
filename = "Downloads.zip",
content = function(file){
withProgress(message = "Writing Files to Disk. Please wait...", {
temp <- setwd(tempdir())
on.exit(setwd(temp))
files <- c("mtcars.csv", "iris.csv")
future({
Sys.sleep(15)
write.csv(mtcars, "mtcars.csv")
write.csv(iris, "iris.csv")
zip(zipfile = file, files = files)})
})
}
)
}
shinyApp(ui, server)

Related

Unable to get data downloaded from Shiny app hosted on shinyapps.io

I have built a Shiny app that allows users to upload a .txt file, filter for key terms and then download the returned dataset. When hosting locally it works. However, when I deployed it to shinyapps.io the download function no longer works. Everything else (e.g the data upload + wrangling step) do work. Nothing comes up as an error in the code log. The file it downloads is called 'downloadData.html' and simply says 'Please wait. Loading'. When I run and download locally it returns a .txt file (which is strange as the function is write.csv). My code is
library(tidyverse)
library(shiny)
library(rsconnect)
ui <- fluidPage(
titlePanel("Download data"),
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose .txt file",
multiple = F,
accept = c(".txt")),
textInput('energy_co', 'Name of energy company'),
textInput('asset', 'name of Asset Manager'),
downloadButton("downloadData", "Download")
),
mainPanel(
tableOutput("table")
)
)
)
options(shiny.maxRequestSize=30*1024^2)
server <- function(input, output, session) {
company_data <- reactive({
req(input$file1,input$asset,input$energy_co)
data <- read_lines(input$file1$datapath)
text_df <- as_data_frame(data)
company_data <- text_df %>%
filter(str_detect(value, input$asset)) %>%
filter(str_detect(value, input$energy_co)) %>%
distinct(.)
company_data
})
output$table <- renderTable({
company_data()
})
output$downloadData <- downloadHandler(
filename = function() {
paste(company_data(), ".csv", sep = "")
},
content = function(file) {
shiny::withProgress(
message = paste0("Downloading", company_data(), " Data"),
value = 0,
{
shiny::incProgress(1/10)
Sys.sleep(1)
shiny::incProgress(5/10)
write.csv(company_data(), file, row.names = FALSE)
}
)
}
)
}
shinyApp(ui, server)
Does anyone have any idea how to get this to work? The code is similar (and works locally) to other related questions on SO, but unable to work out what goes wrong when hosting remotely. Nothing comes up on the app's logs.

R shiny - having trouble with file download

So I want to have a Shiny page which:
A) Allows the user to upload a .xls file;
B) Offers that file back to the user for download as a .csv file;
C) Prints the head of the file in the Shiny app to ensure that it was properly read.
Here is the code I am using:
# Want to read xls files with readxl package
library(readxl)
library(shiny)
## Only run examples in interactive R sessions
if (interactive()) {
ui <- fluidPage(
fileInput("file1", "Choose File", accept = ".xls"),
tags$hr(),
uiOutput("downloader"),
htmlOutput("confirmText", container = tags$h3),
tableOutput("listContents")
)
server <- function(input, output) {
theOutput <- reactiveValues(temp = NULL, df = NULL, msg = NULL, fn = NULL)
observeEvent(input$file1, {
theOutput$fn <- paste('data-', Sys.Date(), '.csv', sep='')
theOutput$temp <- read_xls(input$file1$datapath)
theOutput$msg <- paste("File Contents:")
theOutput$df <- write.csv(theOutput$temp,
file = theOutput$fn,
row.names = FALSE)
})
output$confirmText <- renderText({
theOutput$msg
})
output$listContents <- renderTable({
head(theOutput$temp)
})
output$downloader <- renderUI({
if(!is.null(input$file1)) {
downloadButton("theDownload", label = "Download")
}
})
output$theDownload <- downloadHandler(
filename = theOutput$fn,
content = theOutput$df
)
}
shinyApp(ui, server)
}
The Shiny page renders correctly, it accepts the upload with no problems, it prints out the head of the .csv with no problems, and it creates a properly formatted "data-{today's date}.csv" file in the same directory as the app.R file.
Problem is, when I hit the download button I get the error message:
Warning: Error in download$func: attempt to apply non-function
[No stack trace available]
Can someone tell me what I am doing wrong?
Thanks to the comments above, this is the solution I found (with my comments added, to show where the code changed):
library(readxl)
library(shiny)
if (interactive()) {
ui <- fluidPage(
fileInput("file1", "Choose File", accept = ".xls"),
tags$hr(),
uiOutput("downloader"),
htmlOutput("confirmText", container = tags$h3),
tableOutput("listContents")
)
server <- function(input, output) {
theOutput <- reactiveValues(temp = NULL, msg = NULL)
observeEvent(input$file1, {
# Do not try to automate filename and the write.csv output here!
theOutput$temp <- read_xls(input$file1$datapath)
theOutput$msg <- paste("File Contents:")
})
output$confirmText <- renderText({
theOutput$msg
})
output$listContents <- renderTable({
head(theOutput$temp)
})
output$downloader <- renderUI({
if(!is.null(input$file1)) {
downloadButton("theDownload", label = "Download")
}
})
output$theDownload <- downloadHandler(
# Filename and content need to be defined as functions
# (even if, as with filename here, there are no inputs to those functions)
filename = function() {paste('data-', Sys.Date(), '.csv', sep='')},
content = function(theFile) {write.csv(theOutput$temp, theFile, row.names = FALSE)}
) }
shinyApp(ui, server) }
The fact that content takes an argument (named here "theFile"), which is not called anywhere else, is what was throwing me off.

How to download the edited dataframe in .csv format in rshiny?

I am developing an Rshiny application in which I edited the contents of the dataframe and downloading the edited dataframe in .csv format. But the downloaded file is not in .csv format. Can anyone help me with this issue?
output$downloadData <- downloadHandler(filename = function() {paste(Sys.time(), 'Edited Table.csv', sep='') } ,content = function(file) {write.csv(sample_data(), file, row.names = FALSE)})
This is the code used. Thanks in advance!!
I've tried to replicate your results with an example, but I couldn't reproduce your issue. Here is a self-sufficient RShiny App which downloads a .csv file. Make sure your app follows this template, if the issue still persists please provide a reproducible example of the same.
library(shiny)
ui <- fluidPage(
downloadButton("downloadData", "Download")
)
server <- function(input, output) {
# Your Sample dataset
sample_data <- reactive({mtcars})
output$downloadData <- downloadHandler(
filename = function() {
paste(Sys.time(), ' Edited Table.csv', sep='')
},
content = function(file) {
write.csv(sample_data(), file, row.names = FALSE)
}
)
}
shinyApp(ui, server)

Image Processing Operations inside R shiny server.R

I'm currently working on image processing application using R Shiny It uploads an image using file upload and then I need to read the image to do the image processing operations. server.R file is as follows.
library(shiny)
library(EBImage)
library(imager)
library(jpeg)
function(input, output) {
observe({
file_path <- input$files
if (is.null(file_path))
return(NULL)
file_path$datapath <- gsub("\\\\", "/", file_path$datapath)
img <- readImage(file_path$datapath)
equalized <- equalize(img,range = c(0, 1), levels = 256)
output$text <- renderText({
file_path$datapath
})
output$img <- renderImage({
list(src = file_path$datapath,
contentType = "image/jpg",
width = "50%",
height = "auto",
alt = "This is alternate text")
})
})
}
But this gives me the following error.
Warning: Error in readImage: Please supply at least one filename.
Stack trace (innermost first):
57: readImage
56: observerFunc
I managed to plot an equalized image using raster method. Here are some tips/tricks:
You put everything inside an observer which is a pretty bad idea, so I got rid of that.
Use req() when checking whether a file is uploaded, UI is rendered, etc instead of an if statement. `
if (is.null(file_path)) return(NULL)
There is no need to assign input$files to a variable, you can call input$files$datapath. Also gsub() is not needed in this case.
file_path <- input$files
file_path$datapath <- gsub("\\\\", "/", file_path$datapath)
equalized is calculated, but you don't use it anywhere.
Solution
Checking with req() whether a file is uploaded.
Get the extension of the file (splitting by ., getting the last element)
Plot the equalized image using the display() function with method = "raster".
Print datapath which points to a temp dir/file
See:
library(shiny)
library(EBImage)
library(imager)
library(jpeg)
ui <- fluidPage(
fileInput("files", "Upload a file"),
plotOutput("img"),
textOutput("txt")
)
server <- function(input, output) {
output$img <- renderPlot({
req(input$files)
st <- strsplit(input$files$name, split = "[.]")[[1]]
extension <- st[length(st)]
display(equalize(readImage(input$files$datapath, type = extension), range = c(0, 1), levels = 256), method = "raster")
})
output$txt <- renderText({
input$files$datapath
})
}
shinyApp(ui, server)
Using EBImage we can load an image into Rshiny and use it for further processing. Below code allows the user to upload an image and then same is displayed on shiny screen back.
library(shiny)
library(EBImage)
upload_image <- list()
ui <- fluidPage(
fileInput("file1", "Upload an Image"),
plotOutput("img")
)
server <- function(input, output) {
output$img <- renderPlot({
req(input$file1)
upload_image[[1]] <- readImage(input$file1$datapath)
plot(upload_image[[1]])
})
}
shinyApp(ui , server)

How to specify file and path to save a file with R-shiny and shinyFiles?

I am working with R (shiny) and want to save a dataframe as an excel file.
For this purpose I use the "shinyFiles" package so that the user can specify where the excel file is to be stored:
server.R
library(shiny)
library(shinyFiles)
shinyServer(function(input, output, session) {
## ShinyFiles : get the user favorite directory
volumes=c(home = '~/'),
shinyDirChoose(input, 'dir', roots=volumes, filetypes = c('','xlsx')),
output$dir.res <- renderPrint({parseDirPath(volumes, input$dir)}),
## Button to save the file
observeEvent(input$button.save, {
## A standard file name
A <- "name"
B <- "family
if( input$text == "File name..." ) outFile <- paste( A, "_", B, ".xlsx", sep="" )
## Append the path to the file name
outFile <- paste( parseDirPath(volumes, input$path.out), outFile, sep="/" )
## The data to be saved
x=seq(from=0,to=10,by=1)
d = data.frame( x )
write.xlsx( d, outFile )
}
and the ui.R
library(shiny)
library(shinyFiles)
shinyUI(fluidPage(sidebarLayout(
## Choose the output directory
shinyDirButton("dir", "Choose directory", "Upload"),
## Choose the output file name
textInput("text", label = "", value = "File name..."),
## Save the data
actionButton("button.save", "Save the file"),
## Give the path selected
verbatimTextOutput("dir.res")
)))
Despite all the examples found for similar questions I have been trying around for 2h (shame..) and will be thankful for help
Here is a working example. Again, this assumes that you run the app on your own computer, and users are allowed to access folders on this computer. You can set the root folder where user is allowed to save files (see UserFolder, user will be able to choose any subfolder of this root)
library(shiny)
library(shinyFiles)
library(xlsx)
ui <- shinyUI(fluidPage(
titlePanel("Example"),
shinySaveButton("save", "Save file", "Save file as ...", filetype=list(xlsx="xlsx"))
))
server <- shinyServer(function(input, output, session) {
observe({
volumes <- c("UserFolder"="D:/Data")
shinyFileSave(input, "save", roots=volumes, session=session)
fileinfo <- parseSavePath(volumes, input$save)
data <- data.frame(a=c(1,2))
if (nrow(fileinfo) > 0) {
write.xlsx(data, as.character(fileinfo$datapath))
}
})
})
shinyApp(ui = ui, server = server)

Resources