download handler for saving plot without repeating code

Hello everyone, I'm wondering if there is a way to save a plot using the downloadhandler without having to repeat the code that generates the plot in the content function. In so far, all the examples I have seen repeat the code that generates the plot. However this could be a problem if, for example, the code that generates the plot takes too much time to run or the code is too long.

I'd appreciate any help.

Thanks in advance

Hi, the plot is generated somewhere else in your app I guess?
In that case you can use a reactive, so the plot code doesn't need to be executed again.

library(shiny)
library(ggplot2)

ui <- fluidPage(
  plotOutput("plot"),
  downloadLink("downloadPlot", "Download Plot")
)

server <- function(input, output) {

  my_plot <- reactive ({
    ggplot(data = mtcars, aes(x=cyl, y=mpg)) + geom_bar(stat="identity")
  })
  
  output$plot <- renderPlot({
    my_plot()
  })
  
  output$downloadPlot <- downloadHandler(
    filename = function() { "output.pdf" },
    content = function(file) {
      pdf(file, paper = "default")
      plot(my_plot())
      dev.off()
    }
  )
}
shinyApp(ui, server)
1 Like

Hi Ginberg! Thank you very much for your help. I see that the code you posted works properly. I tried to change my code as you suggest and create the plot in a reactive which works fine. However, the file saved is empty.

I attach a piece of code so you maybe can figure out what isn't right please.

The function descdist is the one that creates the plot and is from package fitdistrplus.

Thanks in advance!!

  data_tab2 <- eventReactive(input$si, {
    
    datos()
  
    })

  cyf_plot <- reactive({
   
    if(isolate(input$bootstrap==F)){
      descdist(data_tab2()[,isolate(input$serie2)])
    } else {
      descdist(data_tab2()[,isolate(input$serie2)], boot = isolate(input$slider1))
    }
    
  })

output$CyF <- renderPlot({  

      cyf_plot()
})

#Download handler

    output$download1 <-  downloadHandler(
      filename = function() {
        paste("gráfico_CyF", input$radioB1, sep = ".")  
      },
      content = function(file) {
        if(input$radioB1 == "png")
          png(file)
        else pdf(file)
        cyf_plot() 
        dev.off()
      }
    )


hi @calvarez , I am glad you are making progress. Could you share a reproducible example of the app? This makes it easier for us to help you

maybe

 if(input$radioB1 == "png")
          png(file)
        else pdf(file)
        plot(cyf_plot())
        dev.off()

Hi!! Below I attach an example. Thanks for your help. I really appreciate it.

library(shiny)
library(fitdistrplus)


ui <- fluidPage(
  
  sidebarLayout( 
    
    sidebarPanel(
      
      selectInput(inputId = "selectVar", label = "Select variable",
                  choices = c("var1", "var2")),
      
      checkboxInput(inputId = "bootstrap", label = "Perform bootstrap", value = F),
      
      conditionalPanel(condition = "input.bootstrap == true",
                       sliderInput(inputId = "slider1", label = "Select sample size for bootstrap",
                                   min = 1000, max = 50000, value = 5000, step = 1000 )),
      radioButtons(inputId = "radioB1", label = "Select the type of file", choices = list("png", "pdf")),
      actionButton(inputId = "boton1", label = "Make plot")
      
    ),
    
    mainPanel( 
      br(),
      
      tabPanel(title = "tab1",
               
               plotOutput(outputId = "CyF") %>% withSpinner(type = getOption("spinner.type", 
                                                            default = 4), hide.ui = T)),
               downloadButton(outputId = "download1", label = "Download plot")
               #withSpinner(plotOutput(outputId = "CyF"),
               #             type = getOption("spinner.type", default = 4), hide.ui = T))
    )
  )
  )



server <- function(input, output, session) {
  
  data <- eventReactive(input$boton1, { 
    
    var1 <- rnorm(1000, 0, 1)
    
    var2 <- rnorm(1000,10,5)
    
    cbind(var1, var2)
    
  })
  
  
  cyf_plot <- reactive({
    
    
    if(isolate(input$bootstrap==F)){
      descdist(data()[,isolate(input$selectVar)])
    } else {
      descdist(data()[,isolate(input$selectVar)], boot = isolate(input$slider1))
    }
    
  })
  
  output$CyF <- renderPlot({  
    
    cyf_plot()
    
  })
  
  #DownloadHandler para guardar el gráfico
  
  output$download1 <-  downloadHandler(
    filename = function() {
      paste("gráfico_CyF", input$radioB1, sep = ".")  
    },
    content = function(file) {
      if(input$radioB1 == "png")
        png(file)
      else pdf(file)
      cyf_plot()
      dev.off()
    }
  )
  
  
}

shinyApp(ui = ui, server = server)

Hi @nirgrahamuk, I already tried that and didn't work :frowning_face:

Alright, it is a bit involved being base R graphics. One needs to record any plot one wants to play over and over.

llibrary(shiny)
library(fitdistrplus)
ui <- fluidPage(
  plotOutput("myplot"),
  downloadButton("dlbtn",
                 label = "Download")
)


server <- function(input, output, session) {
  
  some_plot <- reactive({
    set.seed(1234)
    x1 <- rnorm(100)
     descdist(x1,boot=500)
    recordPlot()
  })
  
  output$myplot <- renderPlot({
    replayPlot(req(some_plot()))
  })
  output$dlbtn <- downloadHandler( 
    filename = function() {
    paste("new.png")
  },
  content = function(file) {
   png(file)
   replayPlot(some_plot())
  dev.off()
  }
    
  )
}

shinyApp(ui, server)
1 Like

Thanks @nirgrahamuk it works!!

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.