Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`downloadHandler` : how to exit gracefully when download will fail (no data)?

Tags:

r

shiny

I have a downloadButton with a downloadHandler, when we want to download the data I have to run a procedure to figure out if there is data to download.

I cannot find a way to Cancel the download handler, the app below prompts us to save some garbage HTML file.

How can we have the downloadHandler exit cleanly?

library(shiny)

ui <- fluidPage(

  downloadButton("btn", "Download")
)

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

  output$btn <- downloadHandler(
    filename = function(){
      "out.txt"
    },
    content = function(file){

# this example always fails.
      if(TRUE){
        print("Sorry, data not written.")
        return(NULL)
      } else {
        writeLines(mtcars, "out.txt")
      }

    }

  )

}

shinyApp(ui, server)
like image 544
Remko Duursma Avatar asked Feb 02 '26 02:02

Remko Duursma


2 Answers

AFAIK, it's not possible to cancel the download once you pressed the button.

Here is a way to download a file without using downloadHandler. It consists in creating an a element, and once you press a button, the file you want to download is converted to base64 encoding, and with the help of shinyjs::runjs the base64 string is given to the href attribute of the a element and the click on the a element is triggered.

library(shiny)
library(shinyjs)

ui <- fluidPage(
  useShinyjs(),
  checkboxInput("data", "Data"),
  actionButton("btn", "Download"),
  tags$a(id = "dwnld")
)

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

  observeEvent(input$btn, {
    if(input$data){
      tmpFile <- tempfile()
      write.table(iris, tmpFile)
      b64 <- base64enc::dataURI(file=tmpFile, mime="text/plain")
      runjs(sprintf('$("#dwnld").attr("href", "%s").attr("download", "out.txt")', b64))
      runjs("$('#dwnld')[0].click();")
    }else{
      runjs("alert('No data!');")
    }
  })

}

shinyApp(ui, server)
like image 109
Stéphane Laurent Avatar answered Feb 04 '26 15:02

Stéphane Laurent


I wanted to have something similar but in a module. The trick is to have a hidden downloadbutton that is clicked using JS (using shinyjs).

A MWE that implements this using modules looks like this

library(shiny)

download_ui <- function(id) {
  ns <- NS(id)
  tagList(
    shinyjs::useShinyjs(),
    actionButton(ns("download"), "Download"),
    div(downloadButton(ns("actual_download"), ""), style = "visibility: hidden") # hide this
  )
}

download_server <- function(id) {
  moduleServer(
    id,
    function(input, output, session) {
      
      observeEvent(input$download, {
        print("Performing some checks...")
        check <- TRUE
        
        if (!check) {
          showNotification("Abort Download", type = "error")
        } else {
          showNotification("Start Download", type = "message")
          
          shinyjs::runjs(paste0("document.getElementById('",
                                session$ns("actual_download"), "').click();"))
        }
        
        print("Done Download")
      })
      
      output$actual_download <- downloadHandler(
        filename = function() "test-file.csv",
        content = function(file) {
          print("Actually downloading the file")
          write.csv(mtcars[1:4, 1:5], file)
        }
      )
    }
  )
}


# Include the module in the main ui/server components
ui <- fluidPage(
  download_ui("down")
)
server <- function(input, output, session) {
  download_server("down")
}

shinyApp(ui, server)
like image 28
David Avatar answered Feb 04 '26 15:02

David



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!