Download a zip file and extract a specific file in Shiny App - r

I have a RShiny app where I fetch a zip file from a s3 bucket using aws.s3 library. I have a specific file within this zip archive that users will download upon clicking downloadButton.
Below is a snippet from my server part of the code
rvalues <- reactiveValues(r = file())
observe({
rvalues$r <- tempfile(fileext = paste0(".", tools::file_ext("MyArchive.zip")))
r <- save_object(bucket = MyBucket,
object = "MyArchive.zip",
file = rvalues$r,
key = accesskey,
secret = secretKey,
region = region)
})
output$download <- downloadHandler(
filename = function() {
"Sample.json"
},
content = function(file) {
unzip(rvalues$r,"Sample.json")
}
)
I am creating a temp file and saving the zip from s3 to this temp file. From this temp file, I am unzipping my specific file and passing it to the download handler function. For some reason, this doesn't work. Any help/guidance is much appreciated!

I would try the following code (I have not tried it since you don't provide a reproducible example):
output$download <- downloadHandler(
filename = function() {
"Sample.json"
},
content = function(file) {
filepath <- unzip(rvalues$r,"Sample.json")
file.copy(filepath, file)
}
)

Related

How to create a download link in shiny for a local excel workbook (not dataframe) via downloadHandler

ref: How to download workbook via downloadHandler on Shiny?
** Workbook is already held in the app's directory**
Does anyone have experience with putting in a download link into a shiny app for an excel file held in the app's directory? I have an excel form that I need users to be able to download and use (it is specifically NOT a dataframe).
The following code worked for when I put in a powerpoint file in the downloadHandler function:
output$downloadinflationguidance <- downloadHandler(
filename = function() {
paste("www/inflation-guidance - ", Sys.Date(), '.pptx', sep='')
},
content = function(con) {
pptx <- read_pptx("www/inflation-guidance.pptx")
print(pptx, target = con)
}
)
But when I swap out the powerpoint stuff for the excel form (see below) it doesn't work. When I run the app and click on the download link, the user gets an error saying "file not found". The excel file is held locally on the app in a folder titled "www". Am I missing a trick here?
shinyUI(
downloadLink("dl_excel_calc", label = "Excel Version")
)
shinyServer(function(input, output, session) {
output$dl_excel_calc <- downloadHandler(
filename = function() {
paste0("indexation_tool_excel -", Sys.Date(), ".xlsx", sep='')
},
content = function(con) {
xlsx <- read_excel('indexation_tool_excel.xlsx')
print(xlsx, target = con)
}
)
}

R/Shiny: Download multiple files (zip) from a folder on the server

I would like to create a zip archive (containing several xlsx files) and save it locally. The files are stored in a folder on the server side.
The user selects the files to zip using a checkboxInput.
Here the code for the checkbox:
get.files <- reactive({
list.files("output_file/")
})
obsList <- list()
output$links_list <- renderUI({
lapply(as.list(1:length(get.files())), function(i)
{
btName <- get.files()[i]
# creates an observer only if it doesn't already exists
if (is.null(obsList[[btName]])) {
obsList[[btName]] <<- btName
}
fluidRow(checkboxInput(btName, get.files()[i]) )
})
})
The checkboxes are created dynamically reading the content in the folder ("output_file/"). Near each checkbox there is the name of the file.
The function for the download is:
output$downloadzip<-downloadHandler(
filename = function(){
paste0("Extract.zip")
},
content = function(file){
files <- NULL;
for (i in 1:length(obsList)){
if(input[[obsList[[i]]]])
files <- c(paste("output_file/",obsList[[i]],sep=""),files)
}
#create the zip file
zip(file,files)
},
contentType = "application/zip"
)
The function creates an array of filenames (files) using only the names of files that have been checked.
I have created also a function that allows me to check that only the right files are chosen:
tempText <- eventReactive({input$TempTest},{
l<-c()
for (i in 1:length(obsList)){
if(input[[obsList[[i]]]])
l<-c(l,paste("output_file/",obsList[[i]],sep=""))
}
return(paste(l) )
},
ignoreInit = TRUE)
output$Temp <- renderPrint({ tempText()})
This function renders correctly the strings with the name of the files.
The error that I get when I try to download the zip file is:
sh: : command not found
Can someone help me to fix this?
I have fixed the problem.
The issue is with the zip function that for some reasons doesn't work properly on my server.
The solution is to use directly the system2 function (that is called internally by zip).
Instead of
zip(file,files)
I have to use:
system2("zip", args=(paste(file,files,sep=" ")))

R Shiny DownloadHandler + base64decode

I like to download a file in shiny which is created by base64enc::base64decode.
What I have so far is:
library(base64enc)
library(shiny)
downloadHandler(
filename = function()
"test.txt",
content = function(file) {
base64decode(what = "VGhpcyBpcyBhIHRlc3Qu", output = file)
}
)
and I get Warning: Error in file: argument "file" is missing, with no default
When I use base64decode without shiny, I use:
base_string <- "VGhpcyBpcyBhIHRlc3Qu"
o_file <- file("C:/User/Desktop/test.txt"), "wb")
base64decode(what = base_string, output = o_file)
close(o_file)
and everything works fine.
Is it possible to use the downloadHandler without executing the second statement first? I want to create the file just for the download.
If we look into the documentation of ?downloadHandler we see that the content parameter requires a file path (string) of a nonexistent temp file and writes the content to that file path.
If we look into the code of base64decode:
if (is.character(output)) {
output <- file(output, "wb")
on.exit(close(output))
}
we see that file() is called, so that you would create/open a connection to a file already and the condition of "non-existance" of that file wouldn´t be fulfilled (my understanding).
Maybe, you could use smthg like:
write.csv(base64decode(what = base_string), file)
Full app:
library(base64enc)
library(shiny)
ui <- fluidPage(
downloadLink("downloadData", "Download")
)
server <- function(input, output) {
# Our dataset
data <- mtcars
output$downloadData <- downloadHandler(
filename = function() {
paste("data-", Sys.Date(), ".stackoverflow", sep="")
},
content = function(file) {
base_string <- "VGhpcyBpcyBhIHRlc3Qu"
write.table(base64decode(what = base_string), file)
}
)
}
shinyApp(ui, server)
Edit: Given your question in the comment. You can use write.table() for an arbitrary file type. See the edited example above to write to a file of type .stackoverflow ;).

Downloading a file generated by shiny

I'm trying to download a BSON file that I export to my shiny app from a MongoDB using the mongolite package. This is the code in my download button:
output$downloadTiming <- downloadHandler(
filename = "/keyTiming.bson",
content = function(fileToDownload){
mongolite::mongo(
collection = "keyTiming",
url = "mongodb://<User>:<Pass>#<url>"
)$export(fileToDownload, bson = TRUE)
}
)
When I try to download it, it says "Error: inherits(con, "connection") is not TRUE". I have spent a good amount of time researching and have found nothing, and hope that someone here can be of use.
I figured it out eventually. The final code looks like this
output$downloadTiming <- downloadHandler(
filename <- function(){
return("timingOut.bson")
},
content <- function(file){
outFile = file("timingOut.bson")
mongolite::mongo(
collection = "timings",
url = "mongodb://<user>:<pass>#<database>"
)$export(outFile, bson = TRUE)
file.copy("timingOut.bson", file, overwrite = TRUE)
}
)

How do you find filepath of the downloadHandler in Shiny?

I have a reactive dataframe and I have the download handler working. I would like to access the filepath so I can reference the downloaded data without using the fileInput function. The code below works, however, I am trying to to grab the filepath so I can run a prewritten function on the downloaded csv file.
output$downloadData <- downloadHandler(filename =
function({paste(Sys.time(), ' Shiny File.csv', sep='') },
content = function(file) {write.csv(sample_data(),
file, row.names = FALSE)
})

Resources