Created
December 27, 2018 05:56
-
-
Save dkulp2/b77ec1dc0031f2838f9dae08436efd35 to your computer and use it in GitHub Desktop.
Example shiny dynamic download
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Author: dkulp2 | |
# | |
# Shiny example for dynamically downloading a file related to a specific row. | |
# | |
# A DT datatable is created with options that adds a 'download-control' class to a column with a download icon | |
# and a callback to set up the click event on the 'download-control' cells. | |
# The javascript click event determines which page of the datatable is being displayed and which row the click event occurred. | |
# This approach fails if rows are reordered or the table is filtered, so both are disabled. An alternative is to place the | |
# row ID in a "data-" attribute, which would allow reordering and filtering. | |
# It then sends a Shiny event, 'download-row', with the index of the row in the table. | |
# The shiny event then creates the data file based on the row number and sends the raw data to javascript via the 'download-data' message. | |
# In this example I create a data frame with the row index in it and send it as an RDS. | |
# The javascript message handler for 'download-data' decodes the raw data and triggers the client to download the data as a file. | |
# This approach will not perform well for large data due to latency and memory. An alternative is to store the file | |
# dynamically in the www/ folder and send the URL to the client. | |
library(shiny) | |
library(DT) | |
JS.row.dl <- htmlwidgets::JS(" | |
table.on('click', 'td.download-control', | |
function() { | |
start=table.page.info().start | |
i=$(this).closest('tr')[0].rowIndex; | |
Shiny.setInputValue('download_row', {row:start+i}, {priority:'event'}) | |
});") | |
ui <- fluidPage( | |
div(style="display: none", icon("")), # hack to trigger inclusion of icon library | |
dataTableOutput("myTable"), | |
includeScript("download.js") | |
) | |
server <- function(input, output, session) { | |
output$myTable <- renderDataTable(DT::datatable(data.frame(' '=as.character(icon('download')), 'alphabet'=LETTERS), | |
escape=FALSE, | |
selection='single', | |
options=list(ordering=FALSE, | |
dom="ltip", # disable filtering | |
columnDefs=list(list(className='download-control',targets=1))), | |
callback=JS.row.dl)) | |
observeEvent(input$download_row, { | |
row.i <- input$download_row$row | |
message("clicked row: ",row.i) | |
rc <- rawConnection(raw(0), "r+") | |
saveRDS(data.frame(x=row.i), rc) | |
data <- rawConnectionValue(rc) | |
close(rc) | |
session$sendCustomMessage("download-data", | |
list(row=row.i, data=data, filename='df.RDS')) | |
}, ignoreInit=TRUE) | |
} | |
shinyApp(ui = ui, server = server) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://stackoverflow.com/a/20151856 | |
function base64toBlob(base64Data, contentType) { | |
contentType = contentType || ''; | |
var sliceSize = 1024; | |
var byteCharacters = atob(base64Data); | |
var bytesLength = byteCharacters.length; | |
var slicesCount = Math.ceil(bytesLength / sliceSize); | |
var byteArrays = new Array(slicesCount); | |
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) { | |
var begin = sliceIndex * sliceSize; | |
var end = Math.min(begin + sliceSize, bytesLength); | |
var bytes = new Array(end - begin); | |
for (var offset = begin, i = 0; offset < end; ++i, ++offset) { | |
bytes[i] = byteCharacters[offset].charCodeAt(0); | |
} | |
byteArrays[sliceIndex] = new Uint8Array(bytes); | |
} | |
return new Blob(byteArrays, { type: contentType }); | |
} | |
function saveData(blob, fileName) // does the same as FileSaver.js - https://stackoverflow.com/a/33664602 | |
{ | |
var a = document.createElement("a"); | |
document.body.appendChild(a); | |
a.style = "display: none"; | |
var url = window.URL.createObjectURL(blob); | |
a.href = url; | |
a.download = fileName; | |
a.click(); | |
window.URL.revokeObjectURL(url); | |
} | |
Shiny.addCustomMessageHandler('download-data', function(args) { | |
console.log("Downloading table for row " + args.row); | |
saveData(base64toBlob(args.data), args.filename); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
wow such long code! :)