Downloads

downloadButton() and downloadHandler()

Try it first: choose a dataset, click Select dataset, then use the two download buttons.

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [viewer]
#| viewerHeight: 750
## file: ui.R
library(shiny)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      selectInput("dataset", "Choose a dataset:",
                  choices = c("rock", "pressure", "cars")),
      actionButton("load", "Select dataset")
    ),
    mainPanel(
      plotOutput("graph"),
      downloadButton("downloadPlot", "Download plot"),
      verbatimTextOutput("summary"),
      downloadButton("downloadSummary", "Download summary")
    )
  )
)

## file: server.R
library(shiny)

server <- function(input, output, session) {

  dataInput <- eventReactive(input$load, {
    switch(input$dataset,
           rock = rock,
           pressure = pressure,
           cars = cars)
  })

  # descriptive statistics of the first two columns
  ddata <- reactive({
    df <- dataInput()[, 1:2]
    data.frame(variable = names(df),
               mean = sapply(df, mean),
               sd = sapply(df, sd),
               row.names = NULL)
  })

  output$graph <- renderPlot({
    plot(dataInput()[, 1:2])
  })

  output$summary <- renderPrint({
    ddata()
  })

  # downloadHandler = the "render" function of downloadButton()
  output$downloadSummary <- downloadHandler(
    filename = function() {
      paste0("descriptive-", input$dataset, ".csv")
    },
    content = function(file) {
      write.csv(ddata(), file, row.names = FALSE)
    }
  )

  output$downloadPlot <- downloadHandler(
    filename = function() {
      paste0("plot-", input$dataset, ".png")
    },
    content = function(file) {
      png(file, width = 800, height = 600)
      plot(dataInput()[, 1:2])
      dev.off()
    }
  )
}

How it works

Downloads follow the usual pair:

  • in ui.R, downloadButton("id", "Label")
  • in server.R, output$id <- downloadHandler(...)
output$downloadSummary <- downloadHandler(
1  filename = function() {
    paste0("descriptive-", input$dataset, ".csv")
  },
2  content = function(file) {
    write.csv(ddata(), file, row.names = FALSE)
  }
)
1
A function that returns the name of the file the user will get
2
A function that writes the content into file (a temporary path chosen by Shiny)

For a plot, content opens a graphic device, draws the plot and closes the device:

content = function(file) {
  png(file, width = 800, height = 600)
  plot(dataInput()[, 1:2])
  dev.off()
}

The code

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 750
## file: ui.R
library(shiny)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      selectInput("dataset", "Choose a dataset:",
                  choices = c("rock", "pressure", "cars")),
      actionButton("load", "Select dataset")
    ),
    mainPanel(
      plotOutput("graph"),
      downloadButton("downloadPlot", "Download plot"),
      verbatimTextOutput("summary"),
      downloadButton("downloadSummary", "Download summary")
    )
  )
)

## file: server.R
library(shiny)

server <- function(input, output, session) {

  dataInput <- eventReactive(input$load, {
    switch(input$dataset,
           rock = rock,
           pressure = pressure,
           cars = cars)
  })

  # descriptive statistics of the first two columns
  ddata <- reactive({
    df <- dataInput()[, 1:2]
    data.frame(variable = names(df),
               mean = sapply(df, mean),
               sd = sapply(df, sd),
               row.names = NULL)
  })

  output$graph <- renderPlot({
    plot(dataInput()[, 1:2])
  })

  output$summary <- renderPrint({
    ddata()
  })

  # downloadHandler = the "render" function of downloadButton()
  output$downloadSummary <- downloadHandler(
    filename = function() {
      paste0("descriptive-", input$dataset, ".csv")
    },
    content = function(file) {
      write.csv(ddata(), file, row.names = FALSE)
    }
  )

  output$downloadPlot <- downloadHandler(
    filename = function() {
      paste0("plot-", input$dataset, ".png")
    },
    content = function(file) {
      png(file, width = 800, height = 600)
      plot(dataInput()[, 1:2])
      dev.off()
    }
  )
}
Exercise

Add a download button to your explore-a-dataset app that saves newdata() as a CSV file.