I am using Shiny to produce a data.frame which the users can download by clicking on a button:
server.R
output$downloadresults <- downloadHandler(
filename= function() {
paste(country, " - Provision report.csv", sep = "")
},
content=function(file) {
write.csv(sheet1_report,file)
}
)
When the user clicks the download button, I (simultaneously) want to copy a second file from my server to the same location to where they are downloading the data.frame.
Importantly, I am trying to avoid a secondary button.
So the idea is to add the line file.copy("C:/temp/asdf.csv", file):
output$downloadresults <- downloadHandler(
filename= function() {
paste("User_chosen_filename", sep = "")
},
content=function(file) {
write.csv(sheet1_report,file)
file.copy("C:/temp/asdf.csv", file) #NEW LINE
}
)
This is not working, R only executes the first write.csv line and ignores the file.copy.
I have tested the file.copy in isolation and it works.
What am I doing wrong?
Related
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)
}
)
}
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)
}
)
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=" ")))
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 ;).
I'm developing a shiny application where the user can upload a file and select X parameters, after that he press a button and it generates 5 plots (ggplot2 and barplot) and also a dynamic data table (DT). Also, I want to put my shiny app into a linux server.
I'm using tempfiles() for each file I'm using to create the plots and also the DT.
After that, my question is:
When the user closes the shiny app (close the window), do the tempfiles autodelete?
If not, what can I do to delete the tempfiles?
My tries:
session$onSessionEnded(function() {
if (!is.null(x1)) {
file.remove(x1)
}
if (!is.null(x2)) {
file.remove(x2)
}
if (!is.null(x3)) {
file.remove(x3)
}
if (!is.null(x4)) {
file.remove(x4)
}
if (!is.null(xx)) {
file.remove(xx)
}
})
Or:
session$onSessionEnded(function() {
files <- list.files(tempdir(), full.names = T, pattern = "^file")
file.remove(files)
})
With that code I delete the tempfiles when user presses the button once, and if the user presses the button more than 1 time then the window closes, and it will only delete the last generated files. The second part deletes all files at the temp dir but that affects to the other users?(I think yes so that's why I need another solution).
The .png tempfiles generated by ggplot and barplot doesn't autodelete.
My worry is that if the tempfiles won't autodelete and the linux server will collapse because of a lot of tempfiles.
Hope you can solve my doubts. Att Joan.
You can use the deleteFile=TRUE parameter if you want a render function to automatically delete your temporary files:
shinyServer(function(input, output, clientData) {
output$myImage <- renderImage({
# A temp file to save the output.
# This file will be removed later by renderImage
outfile <- tempfile(fileext='.png')
# Generate the PNG
png(outfile, width=400, height=300)
hist(rnorm(input$obs), main="Generated in renderImage()")
dev.off()
# Return a list containing the filename
list(src = outfile,
contentType = 'image/png',
width = 400,
height = 300,
alt = "This is alternate text")
}, deleteFile = TRUE)
})
A temp file is created to save the output, and that file is later automatically removed because of the deleteFile=TRUE argument.
The default Shiny (shiny.R) also has a built-in mechanism that clear file upload directories if that's your concern. The following code remove the upload directory when the session ends:
registerSessionEndCallbacks = function() {
# This is to be called from the initialization. It registers functions
# that are called when a session ends.
# Clear file upload directories, if present
self$onSessionEnded(private$fileUploadContext$rmUploadDirs)
}
Another point regarding manually deleting your temp files (as what you were attempting): the plot will have to render every time the user switched to another tab or resize his / her browser window, so if you're manually deleting the file, it may be inefficient since it needs to be re-rendered again. The onSessionEnded solution is nicer as it confirms that the session has ended.
session$onSessionEnded(function() {
if (!is.null(input$file1)) {
file.remove(input$file1$datapath)
}
})