Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shiny app run code to generate a pdf then offer that pdf to user for download

Tags:

r

shiny

In my shiny app I want to be able to click a download button, have it execute a function I have (in a package) that creates a pdf in a /results folder, then offer that created file as a download to the shiny app user. I pasted my current download_portfolio download button code from the server below (so many pieces not sure how I can make it reproducible). Wanted to see if anyone has an idea of whats going wrong, I get the error message below, however the FUNCTION_TO_GENERATE_PDF_IN_/results() function does run and create the PDF but then the app reloads and the user never gets prompted for a download.

Error I receive (but the pdf is still generated correctly from my function, just the app reloads and there is no offer to download the pdf).

   Error in self$downloads$set(name, list(filename = filename, contentType = contentType,  : 
      argument "content" is missing, with no default

app.R code where I am working on the download

observe({
  output$download_portfolio <- downloadHandler({
    FUNCTION_TO_GENERATE_PDF_IN_/results()
    filename = function() { paste(input$pdfname) }
    content = function(file) {
      file.copy(paste("results/",input$pdfname, file, overwrite = TRUE)
    } 
  })
  })
like image 984
Trevor Nederlof Avatar asked Aug 14 '15 14:08

Trevor Nederlof


1 Answers

You are using the downloadHandler incorrectly. You do not need an observe()-function here, as shiny provides "reactivity", meaning you can bind objects to ui-elements which are updated whenever the object changes. Therefore, you only need to assign a downloadHandler to your download-button and tell it how to generate the file you want to provide:

library(shiny)

ui <- fluidPage( # just the download-button and a textInput for the filename
  textInput("pdfname", "Filename", "My.pdf"),
  downloadButton("outputButton", "Download PDF")
  )

server <- function(session, input, output) {
  # No need for an observer! Just assign the downloadHandler to the button.
  output$outputButton <- downloadHandler(input$pdfname, function(theFile) {
    # The first parameter is the name given to the file provided for download to the user.
    # The parameter in the function (theFile) is a placeholder for the name that is later
    # assigned to the download-file. 

    # Now you can call your pdf-generating function...
    makePdf()

    # ... and use file.copy to provide the file "in" the save-button
    file.copy(from = "/results/myGenerated.pdf", to = theFile)
  })
}

# Sample pdf-generating function:
makePdf <- function(){
  pdf(file = "/results/myGenerated.pdf")
  plot(cars)
  dev.off()
}

shinyApp(ui = ui, server = server)

However, you do not need to store your files first, you might want to directly save them "in" the download button:

library(shiny)

ui <- fluidPage( # As above
  textInput("pdfname", "Filename", "My.pdf"),
  downloadButton("outputButton", "Download PDF")
  )

server <- function(input, output) {
  output$outputButton <- downloadHandler(input$pdfname, function(theFile) {
    # Here, your pdf-generator is provided with the "filename" that is used
    # to provide the file for the user.
    makePdf(theFile)
  })
}

# Sample pdf-generating function:
makePdf <- function(filename){
  pdf(file = filename)
  plot(cars)
  dev.off()
}

shinyApp(ui = ui, server = server)
like image 169
bubble Avatar answered Oct 19 '22 19:10

bubble