Mastering file download in shiny

[This article was first published on Rtask, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

You can read the original post in its original format on Rtask website by ThinkR here: Mastering file download in shiny

But why does downloadHandler now return an empty file!?

Context

When we start working with {shiny}, we often reach a point where it is necessary to offer the user the option to download a document generated by the application.
This document can be a PDF report, a PNG or JPEG image, or anything else.

The appropriate function for this purpose is shiny::downloadHandler(), but beyond the (relatively) simple case shown in the documentation’s example, when it comes to using the function in a more complex scenario, it is not uncommon to encounter difficulties, even for an experienced developer.

Therefore, I would like to share with you an approach that we use at ThinkR, which handles both straightforward and more complex cases that we sometimes encounter.

TL;DR:

We will consistently use file.copy in the downloadHandler

Exemple 1

Let’s look at a simple scenario together by starting with the example from the R documentation and making it more “robust”.

Before :

ui <- fluidPage(
  downloadButton(outputId = "downloadData",label =  "Download")
)
server <- function(input, output) {
  # Our dataset
  data <- mtcars
  output$downloadData <- downloadHandler(
    filename = function() {
      paste("data-", Sys.Date(), ".csv", sep="")
    },
    content = function(file) {
      write.csv(data, file)
    }
  )
}
shinyApp(ui, server)

After :

ui <- fluidPage(
  downloadButton(outputId = "downloadData",label =  "Download")
)
server <- function(input, output) {
  local <- reactiveValues(data = mtcars,
                          export_file = NULL
                          )
  observeEvent(local$data,{
    out <- tempfile(fileext = ".csv")
    write.csv(x = local$data,file = out)
    local$export_file <- out
  })
  output$downloadData <- downloadHandler(
    filename = function() {
      paste("data-", Sys.Date(), ".csv", sep="")
    },
    content = function(file) {
      file.copy(
           from = local$export_file,
           to = file
         )
    }
  )
}
shinyApp(ui, server)

Do you see the difference? We regain control over the file that will be placed behind the button. It is created here within an observeEvent, making it easy to debug and understand its content.

Example 2

This approach makes complex scenarios more readable :

library(tidyverse)
library(shiny)
library(flextable)
ui <- fluidPage(
  downloadButton(outputId = "downloadData",label =  "Download")
)
server <- function(input, output) {
  local <- reactiveValues(data = mtcars,
                          export_file = NULL
  )
  observeEvent(local$data,{
    out <- tempfile(fileext = ".png",pattern = "table_")
    # write.csv(x = local$data,file = out)
    local$data %>% 
    flextable::flextable() %>% 
      flextable::save_as_image(path = out)
    if (file.exists(out)){
    local$export_file <- out
    } else{
    local$export_file <- NULL
    }
  })
  output$downloadData <- downloadHandler(
    filename = function() {
      basename(local$export_file)
    },
    content = function(file) {
      file.copy(
        from = local$export_file,
        to = file
      )
    }
  )
}
shinyApp(ui, server)

It’s your turn

This post is better presented on its original ThinkR website here: Mastering file download in shiny

To leave a comment for the author, please follow the link and comment on their blog: Rtask.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)