How to download multiple files from R/Shiny app? - r

There are a number of different Q/A's regarding this topic on SO, but none that I have been able to find that fit my use-case. I am also very surprised that RStudio / the Shiny developers themselves have not come out with some documentation on how to do this. Regardless, take this example application:
library(shiny)
library(glue)
library(tidyverse)
# Define UI for application
ui <- fluidPage(
# Application title
titlePanel("Test Multi-File Download"),
p("I hope this works!"),
downloadButton(
outputId = "download_btn",
label = "Download",
icon = icon("file-download")
)
)
# Define server logic
server <- function(input, output) {
#datasets stored in reactiveValues list
to_download <- reactiveValues(dataset1 = iris, dataset2 = airquality, dataset3 = mtcars, dataset4 = NULL)
blahblah <- iris
output$download_btn <- downloadHandler(
filename = function(){
paste("my_data_", Sys.Date(), ".csv", sep = "")
},
content = function(file){
#works
#readr::write_csv(blahblah, file)
#Attempt 1
# #create some temp directory
# temp_directory <- tempdir()
# browser()
# reactiveValuesToList(to_download) %>%
# #x is data, y is name
# imap(function(x,y){
# #browser()
# #check if data is not null
# if(!is.null(x)){
# #create file name based on name of dataset
# file_name <- glue("{y}_data.csv")
# #write file to temp directory
# readr::write_csv(x, file_name)
# }
# })
# zip::zip(
# zipfile = file,
# files = ls(temp_directory),
# root = temp_directory
# )
}
)
}
# Run the application
shinyApp(ui = ui, server = server)
I have some datasets that are stored in a reactiveValues list, and I would like the user to be able to download them all. Ideally, I'd like for them just to be able to download multiple files all at once, rather than having to zip them up, and then download a .zip file. Another option I would be okay with is to add each dataset to an Excel sheet, then download the multi-sheet Excel file.
My general thought process (on the former) is as follows:
Download button gets pressed
create some temporary directory
write (the not NULL) datasets contained in to_download reactiveValues list to this directory
zip the temp directory and download
I feel like I am very close, however I have not been able to successfully get this work yet. Any ideas?
Edit 1: I am aware of the proposed answer here, but would like to avoid using setwd() because I believe it is bad practice to mess with working directories from within a Shiny application.

A few things edited and it's working:
using dir instead of ls inside the zip::zip call to show the contents of the temp directory (ls lists R environment rather than directory contents)
as a further suggestion: making a new, unique folder inside tempdir() to ensure only relevant files are added.
library(shiny)
library(glue)
library(tidyverse)
# Define UI for application
ui <- fluidPage(
# Application title
titlePanel("Test Multi-File Download"),
p("I hope this works!"),
downloadButton(
outputId = "download_btn",
label = "Download",
icon = icon("file-download")
)
)
# Define server logic
server <- function(input, output) {
#datasets stored in reactiveValues list
to_download <- reactiveValues(dataset1 = iris, dataset2 = airquality, dataset3 = mtcars, dataset4 = NULL)
blahblah <- iris
output$download_btn <- downloadHandler(
filename = function(){
paste("my_data_", Sys.Date(), ".zip", sep = "")
},
content = function(file){
temp_directory <- file.path(tempdir(), as.integer(Sys.time()))
dir.create(temp_directory)
reactiveValuesToList(to_download) %>%
imap(function(x,y){
if(!is.null(x)){
file_name <- glue("{y}_data.csv")
readr::write_csv(x, file.path(temp_directory, file_name))
}
})
zip::zip(
zipfile = file,
files = dir(temp_directory),
root = temp_directory
)
},
contentType = "application/zip"
)
}
shinyApp(ui = ui, server = server)
In my own Shiny app I had used a multi-worksheet approach as you suggested above. An alternative setup which works to produce a multi-sheet xlsx workbook using openxlsx could be:
...
output$download_btn <- downloadHandler(
filename = function(){
paste("my_data_", Sys.Date(), ".xlsx", sep = "")
},
content = function(file){
wb <- createWorkbook()
reactiveValuesToList(to_download) %>%
imap(function(x,y){
if(!is.null(x)){
addWorksheet(wb, sheetName = y)
writeData(wb, x, sheet = y)
}
})
saveWorkbook(wb, file = file)
},
contentType = "file/xlsx"
)
...
Created on 2021-12-16 by the reprex package (v2.0.1)

Related

Use R shiny to add a sheet to a pre-existing excel file with action button

I have an excel file called testfile.xlsx. the first sheet of this file is called sheet1.
I have written appended a new sheet called New_Sheet using xlsx package as follows
library(xlsx)
setwd()##set the file path to where testfile.xlsx is located
write.xlsx('new_data', "testfile.xlsx", sheetName="New_Sheet", append=TRUE)
This adds the required sheet.
I have created the following shiny app to write the sheet to the file
library(shiny)
library(xlsx)
library(openxlsx)
library(readxl)
ui <- fluidPage(
titlePanel("Writer App"),
sidebarLayout(sidebarPanel(fileInput(inputId = "file", label = "Read File Here", accept =
c(".xlsx")),actionButton(inputId = "Run", label = "Write Data to Table")),
mainPanel(dataTableOutput(outputId = "table1"))))
server <- function(input, output) {
datasetInput <- reactive({
infile<- input$file
if (is.null(infile))
return(NULL)
#READ .XLSX AND .CSV FILES
if(grepl(infile, pattern = ".xlsx" )==T){
data=read_excel(infile$datapath)
} else if(grepl(infile , pattern = ".csv" )==T)
{data=read.csv(infile$datapath )}
#RENAME DATAFRAME WITH UNDERSCORES
names(data)<-gsub(pattern = " ", replacement = "_", x = names(data))
return(data) })
output$table1 <- renderDataTable({
datasetInput()})
observeEvent(input$Run,{
infile<-input$file
testfile<-(infile[1])
filepath<-(input$file)
filepath<-gsub(pattern = "0.xlsx", replacement ="" , x = filepath)
# print(infile$datapath[1])
print(filepath)
print(testfile)
setwd(dir = filepath)
write.xlsx('new_data', testfile, sheetName="New_Sheet3", append=TRUE)})
}
shinyApp(ui = ui, server = server)
The app renders the data in the excel sheet as a table without any problems
.When we push the run app button, the print commands generate the name of the file and the filepath. The write excel function doesnt work. Is there a way to insert the new_data sheet using the action button. I request someone to guide me here.
I recommend using downloadHandler instead. See here for an example.

Allow user to upload a shapefile in shiny

I am making a shiny app that allows the user to upload a shapefile using the sf package. When I select the .shp file via the Browse window, I get an error. How can I allow the user to upload a shapefile, that then get it read by st_read' or readOGR. And, I don't know why st_read is going to C:\Users\Ed\AppData... as this is not location of the shapefile.
library(shiny)
library(shinydahsboard)
library(sf)
UI
ui = navbarPage("Project Eddy", theme = shinytheme("sandstone"),
tabPanel("Location",
sidebarLayout(sidebarPanel(fileInput("shp", "Please choose a Shapefile",
multiple = F,
".shp")),
mainPanel(plotlyOutput(outputId = "Area")))))
Server
server = function(input, output, session) {
myshp.df = reactive({
# input$shp will be NULL initially. After the user selects
# and uploads a file, head of that data file by default,
# or all rows if selected, will be shown.
req(input$shp)
df = st_read(dsn = input$shp$datapath,
quite = T)
if(input$disp == "head") {
return(head(df))
}
else {
return(df)
}
})
output$Area = renderPlotly({
req(myshp.df())
a = myshp.df
c = leaflet(a) %>%
addPolygons(stroke = FALSE, fillOpacity = 0.5, smoothFactor = 0.5) %>%
addProviderTiles('Esri.WorldImagery')
})
})
Error
Warning in CPL_read_ogr(dsn, layer, query, as.character(options), quiet, :
GDAL Error 4: Unable to open C:\Users\Ed\AppData\Local\Temp\RtmpioUU3m\b0cd5b1eb5c4fe4219e6c114\0.shx or C:\Users\Ed\AppData\Local\Temp\RtmpioUU3m\b0cd5b1eb5c4fe4219e6c114\0.SHX. Set SHAPE_RESTORE_SHX config option to YES to restore or create it.
Warning: Error in : Cannot open "C:\Users\Ed\AppData\Local\Temp\RtmpioUU3m\b0cd5b1eb5c4fe4219e6c114\0.shp"; The source could be corrupt or not supported. See `st_drivers()` for a list of supported formats.
128:
ESRI shapefiles are known troublemakers, as they live over multiple files - the single *.shp file is not enough for your shiny app to work with.
Consider a solution proposed by user fiorepalombina on RStudio Community forum: https://community.rstudio.com/t/shinyfiles-and-shapefiles/89099
To read-in a shapefile, the user must submit at minimum the mandatory files (.shp, .shx and .dbf). Once files are uploaded, you can access the location and name via $datapath and $name.
By default, shiny names file inputs like this:
C:\Users\DWISME~1\AppData\Local\Temp\17\RtmpiOjVGv/6903ae29a41daccceee4b8a5/0.dbf
C:\Users\DWISME~1\AppData\Local\Temp\17\RtmpiOjVGv/6903ae29a41daccceee4b8a5/1.prj
C:\Users\DWISME~1\AppData\Local\Temp\17\RtmpiOjVGv/6903ae29a41daccceee4b8a5/2.sbn
C:\Users\DWISME~1\AppData\Local\Temp\17\RtmpiOjVGv/6903ae29a41daccceee4b8a5/3.sbx
C:\Users\DWISME~1\AppData\Local\Temp\17\RtmpiOjVGv/6903ae29a41daccceee4b8a5/4.shp
C:\Users\DWISME~1\AppData\Local\Temp\17\RtmpiOjVGv/6903ae29a41daccceee4b8a5/5.shx
My approach is to create a function that accesses the file input location and change the directory:
library(shiny)
library(sf)
library(purrr)
ui <- fluidPage(
br(),
fluidRow(column(6, offset = 3,
fileInput("shp", label = "Input Shapfile (.shp,.dbf,.sbn,.sbx,.shx,.prj)",
width = "100%",
accept = c(".shp",".dbf",".sbn",".sbx",".shx",".prj"), multiple=TRUE))),
br(),
fluidRow(column(8, offset = 2,
p("input$shp$datapath" , style = "font-weight: bold"),
verbatimTextOutput("shp_location", placeholder = T))),
br(),
fluidRow(column(8, offset = 2,
p("input$shp$name" , style = "font-weight: bold"),
verbatimTextOutput("shp_name", placeholder = T)))
)
server <- function(input, output, session) {
# Read-in shapefile function
Read_Shapefile <- function(shp_path) {
infiles <- shp_path$datapath # get the location of files
dir <- unique(dirname(infiles)) # get the directory
outfiles <- file.path(dir, shp_path$name) # create new path name
name <- strsplit(shp_path$name[1], "\\.")[[1]][1] # strip name
purrr::walk2(infiles, outfiles, ~file.rename(.x, .y)) # rename files
x <- read_sf(file.path(dir, paste0(name, ".shp"))) # read-in shapefile
return(x)
}
# Read-shapefile once user submits files
observeEvent(input$shp, {
user_shp <- Read_Shapefile(input$shp)
plot(user_shp) # plot to R console
# Print original file path location and file name to UI
output$shp_location <- renderPrint({
full_path <- strsplit(input$shp$datapath," ")
purrr::walk(full_path, ~cat(.x, "\n"))
})
output$shp_name <- renderPrint({
name_split <- strsplit(input$shp$name," ")
purrr::walk(name_split, ~cat(.x, "\n"))
})
})
}
shinyApp(ui, server)

Downloadhandler not working on published server

I am creating an app to allow user to upload two excel files and carry over the comments one to the other one, then to download the merged file. The downloadhandler is not working when I tried to run it on the published server, however it running properly locally in rstudio. Any thoughts/suggestions?
library(plyr)
library(dplyr)
library(tidyr)
library(readxl)
library(xlsx)
library(openxlsx)
ui <- fluidPage(
br(),
titlePanel("Excel File Merging Tool"),
br(),
br(),
sidebarLayout(
sidebarPanel(
fileInput("file1", label = h3("Upload New File"), multiple = FALSE, buttonLabel = "Browse", placeholder = "No file selected"),
fileInput("file2", label = h3("Upload Old File"), multiple = FALSE, buttonLabel = "Browse", placeholder = "No file selected"),
actionButton("actionMerge", label = "Merge Uploaded Files"),
hr(),
downloadButton('downloadData', 'Download Merged File')
),
mainPanel(
)
)
)
#Defined Funtions
read_excel_allsheets <- function(filename, tibble = FALSE) {
sheets <- readxl::excel_sheets(filename)
x <- lapply(sheets, function(X) readxl::read_excel(filename, sheet = X))
if(!tibble) x <- lapply(x, as.data.frame)
names(x) <- sheets
x
}
server <- function(input, output) {
getData <- eventReactive(input$actionMerge, {
inFile1 <- input$file1
if (is.null(inFile1)){
return(NULL)
} else {
mydata1= read_excel_allsheets(inFile1$datapath)}
inFile2 <- input$file2
if (is.null(inFile2)){
return(NULL)
} else {
mydata2= read_excel_allsheets(inFile2$datapath)}
wb <- createWorkbook()
#find tabs not in old file
newSheets <- (names(mydata1))[which(!(names(mydata1)) %in% (names(mydata2)))]
if (length(newSheets) > 0){
for (n in newSheets)
{
mydata6 <- bind_rows(mydata1[n])
addWorksheet(wb, sheetName = names(mydata1[n]))
writeData(wb, names(mydata1[n]), mydata6)
}}
for (i in names(mydata1)){
for (j in names(mydata2)){
if (i == j ){
if ((nrow(as.data.frame(mydata1[i]))) == 0 | (nrow(as.data.frame(mydata2[j]))) == 0 )
{
mydata6 <- bind_rows(mydata1[i])
addWorksheet(wb, sheetName = names(mydata1[i]))
writeData(wb, names(mydata1[i]), mydata6)
}
else {
if (ncol(bind_rows(mydata1[i])) == ncol(bind_rows(mydata2[j])) )
{
mydata6 <- bind_rows(mydata1[i])
addWorksheet(wb, sheetName = names(mydata1[i]))
writeData(wb, names(mydata1[i]), mydata6)
}
else {
# validate(
# column_mismatch(mydata1[i], mydata2[j])
# )
drop_in_key <- c("Earliest data creation time", "Latest data update time", "Timestamp of last save in clinical views", "Date time value from the source file name",
"Lowest Date of Rec, Pg, Inst or Subj", "Record Minimum Created Datetime Stamp", "Record Maximum Updated Datetime Stamp", "Accessible to Jreview Timestamp")
mydatax0 = bind_rows(mydata1[i])
mydatax = bind_rows(mydata1[i])[,!(names(bind_rows(mydata1[i])) %in% drop_in_key)]
mydatanew <- mydatax %>% unite(col="Key", 1:(ncol(mydatax)-1), sep=";", remove=FALSE)
mydatanew$Newflag <- "New"
mydatanew0 = mydatanew %>% select(Key, Newflag)
mydatanew1 = bind_cols(mydatanew0,mydatax0)
mydatay0 = bind_rows(mydata2[j])
mydatay = bind_rows(mydata2[j])[,!(names(bind_rows(mydata2[j])) %in% drop_in_key)]
mydataold <- mydatay %>% unite(col="Key", 1:(ncol(mydatay)-1), sep=";", remove=FALSE)
mydataold$Oldflag <- "Old"
mydataold0 <- mydataold %>% select(Oldflag, Key)
mydataold1 <- bind_cols(mydataold0,mydatay0)
mydataold2 = select(mydataold1, Key, Oldflag, (ncol(bind_rows(mydata1[i]))+3):((ncol(mydataold1))))
mydata3 <- merge(x=mydatanew0, y=mydataold2, by="Key", all=TRUE)
mydata4 <- subset(mydata3, Newflag == "New")
mydata5 <- merge(x=mydatanew1, y=mydata4, by="Key", all.y=TRUE)
drop <- c("Key", "Newflag.x", "Oldflag", "Newflag.y")
mydata6 = mydata5[,!(names(mydata5) %in% drop)]
addWorksheet(wb, sheetName = names(mydata1[i]))
writeData(wb, names(mydata1[i]), mydata6)
}}}
else
NULL
}
}
saveWorkbook(wb, file = "aaa.xlsx" , overwrite = TRUE)
})
output$downloadData <- downloadHandler(
filename = function() {
paste0(input$file2, ".xlsx")
},
content = function(file) {
file.copy("aaa.xlsx", file)
})
}
shinyApp(ui = ui, server = server)```
Here's a toy shiny app that provides a solution that is safe for concurrent users. All operations are done on either (a) temporary files that shiny controls, or (b) in the directory of one of these temp files, using tempfile to create the new filename. Both of those assure new-file uniqueness, so no filename collisions. (I believe shiny's method is temporary directories under a temp-directory, at least that's what I'm seeing in my dev env here. So ... seemingly robust.)
The some_magic_function function is mostly because I didn't want to generate an example with openxlsx and sample datas and such, mostly my laziness. For your code, remove all of the if (runif... within the tryCatch and replace with whatever you need, ensuring your code ends by returning the filename with the new data (or updated) data.
... but keep the tryCatch! It will ensure that the function always returns "something". If all code succeeds, then the function will return the filename with new/updated data. If something goes wrong, it returns a class "error" string that can be used to communicate to the user (or otherwise react/recover).
Last thing, though it's just icing on my cupcake here: I use the shinyjs package to disable the 'merge' and 'download' buttons until there is valid data. Frankly, once the two file-selection inputs have something set, the "merge" button will likely never be disabled. However, if there's ever a problem during the merge/update, then the download button will be disabled (until a merge/update happens without error).
library(shiny)
library(shinyjs)
# a naive function that just concatenates the files, first removing
# the header row from the second file
some_magic_function <- function(f1, f2) {
# put the output file in the same directory as 'f2'
d <- dirname(f2)
if (!length(d)) d <- "."
output_file <- tempfile(tmpdir = d, fileext = paste0(".", tools::file_ext(f2)))
tryCatch({
if (runif(1) < 0.2) {
# purely for StackOverflow demonstration
stop("Something went wrong")
} else {
# add your stuff here (and remove the runif if/else)
writeLines(c(readLines(f1), readLines(f2)[-1]), output_file)
output_file # you must return this filename
}
}, error = function(e) e)
# implicitly returning the output_file or an error (text with class 'error')
}
shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(),
titlePanel("Tool"),
sidebarLayout(
sidebarPanel(
fileInput("file1", label = "File #1", multiple = FALSE, placeholder = "No file selected"),
fileInput("file2", label = "File #2", multiple = FALSE, placeholder = "No file selected"),
actionButton("btn", label = "Merge uploaded files"),
hr(),
downloadButton("dnld", "Download merged file")
),
mainPanel(
tableOutput("tbl"),
hr(),
verbatimTextOutput("bigtext")
)
)
),
server = function(input, output, session) {
# start with neither button enabled
for (el in c("btn", "dnld")) shinyjs::disable(el)
# disable the 'merge' button until both files are set
observeEvent({
input$file1
input$file2
}, {
req(input$file1, input$file2)
shinyjs::toggleState("btn", isTRUE(file.exists(input$file1$datapath) && file.exists(input$file2$datapath)))
})
# this is the "workhorse" of the shiny app
newfilename <- eventReactive(input$btn, {
req(input$file1, input$file2)
some_magic_function(input$file1$datapath, input$file2$datapath)
})
# prevent the download handler from being used if the new file does not exist
observeEvent(newfilename(), {
cond <- !is.null(newfilename()) &&
!inherits(newfilename(), "error") &&
file.exists(newfilename())
shinyjs::toggleState("dnld", cond)
})
output$dnld <- downloadHandler(
filename = function() paste0("merged_", input$file2),
content = function(f) {
file.copy(newfilename(), f)
}
)
# some sample output, for fun
output$tbl <- renderTable({
req(newfilename(),
!inherits(newfilename(), "error"),
file.exists(newfilename()))
read.csv(newfilename(), nrows = 10, stringsAsFactors = FALSE)
})
output$bigtext <- renderText({
if (inherits(newfilename(), "error")) {
# if we get here then there was a problem
as.character(newfilename())
} else "(No problem)"
})
}
)
Notes:
shiny::req is supposed to ensure the data has something useful and "truthy" in it (see shiny::isTruthy). Normally it is good with detecting nulls, NA, empty variables, etc ... but it "passes" something that has class "error", perhaps counter-intuitive. That's why I had to be a little more explicit with conditions in some of the reactive blocks.
One impetus for having the merge/update functionality within an external not-shiny-requiring function (some_magic_function here) is that it facilitates testing of the merge functionality before adding the shiny scaffolding. It's difficult to test basic functionality when one is required to interact with a browser for every debugging step of basic functionality.

Use multiple reactive datasets to write to an excel workbook under downloadHandler shiny

Good afternoon, thank you in advance for taking the time to read my question. In my Shiny app, I am trying to create a list of the reactive objects to write to a single excel workbook for the user to download. I was able to use parts of responses from other posts to replicate my issue and I get pretty close to a resolution. However, while the example below uses a list of dataframes, such as mtcars, iris, etc, I am trying to use reactive datasets, such as datasetInput1(), datasetInput2(), etc.
shinyApp(
ui = fluidPage(
downloadButton("downloadExcelSheet", "Download Excel Workbook with Multiple Sheets")
),
server = function(input, output) {
#### Write an Excel workbook with one sheet per dataframe ####
output$downloadExcelSheet <- downloadHandler(
filename = function() {
"excelWorkbook.xlsx"
},
content = function(file) {
# write workbook and first sheet
write.xlsx(mtcars, file, sheetName = "mtcars", append = FALSE)
# add other sheets for each dataframe
listOtherFiles <- list(iris = iris,
airquality = airquality,
sleep = sleep)
for(i in 1:length(listOtherFiles)) {
write.xlsx(listOtherFiles[i], file,
sheetName = names(listOtherFiles)[i], append = TRUE)
}
}
)
When I try to use these reactive objects in the example below, I am able to successfully download the data when there is just one dataset in the list. For example, the below works, but once I start adding more to the list listOtherFiles such as listOtherFiles <- list(datasetInput2(), datasetInput3()), I get an error.
shinyApp(
ui = fluidPage(
downloadButton("downloadExcelSheet", "Download Excel Workbook with Multiple Sheets")
),
server = function(input, output) {
datasetInput1 <- reactive({
data %>%
filter(sub_date == input$date, app_type == input$type)
})
datasetInput2 <- reactive({
data2 %>%
filter(sub_date == input$date, app_type == input$type)
})
output$downloadExcelSheet <- downloadHandler(
filename = function() {
"datasetOutput.xlsx"
},
content = function(file) {
# write workbook and first sheet
write.xlsx(datasetInput1(), file, sheetName = "dataset1", append = FALSE)
# add other sheets for each dataframe
listOtherFiles <- list(datasetInput2())
for(i in 1:length(listOtherFiles)) {
write.xlsx(listOtherFiles[i], file,
sheetName = names(listOtherFiles)[i], append = TRUE)
}
}
)
I'm not sure I can reproduce the problem. Here is my example below. This seems to work and uses two reactive expressions. Does it work for you?
If not, please edit your question and describe further. Perhaps include example data and ui with inputs to reproduce. What was your error?
library(xlsx)
library(shiny)
library(tidyverse)
shinyApp(
ui = fluidPage(
downloadButton("downloadExcelSheet", "Download Excel Workbook with Multiple Sheets")
),
server = function(input, output) {
datasetInput1 <- reactive({
iris %>%
filter(Species == "setosa")
})
datasetInput2 <- reactive({
iris %>%
filter(Species == "versicolor")
})
#### Write an Excel workbook with one sheet per dataframe ####
output$downloadExcelSheet <- downloadHandler(
filename = function() {
"excelWorkbook.xlsx"
},
content = function(file) {
# write workbook and first sheet
write.xlsx(mtcars, file, sheetName = "mtcars", append = FALSE)
# add other sheets for each dataframe
listOtherFiles <- list(setosa = datasetInput1(), versicolor = datasetInput2())
for(i in 1:length(listOtherFiles)) {
write.xlsx(listOtherFiles[[i]], file,
sheetName = names(listOtherFiles)[i], append = TRUE)
}
}
)
}
)
datasetInput1() is a reactive value not defined within the server logic. This needs to be assigned a value first or a function created to update values.
The following are some useful articles to understand shiny's reactive elements:
https://shiny.rstudio.com/articles/understanding-reactivity.html
https://shiny.rstudio.com/articles/reactivity-overview.html

Download multiple csv files with one button (downloadhandler) with R Shiny

*Hi, I'm trying to download multiple csv file from a unique excel file. I want to download (using only one downloadbutton) the differents sheets from the excel file.
I don't understand why a for() loop doesn't work, and I can't see how can I do?
If anyone knows..
The point is to download differents csv files, which are in the "wb" list (wb[1],wb[2]...)
Thanks.
Here is my code who works with the third sheet for instance (and sorry for my bad english) :
ui :
library(readxl)
library(shiny)
library(XLConnect)
fluidPage(
titlePanel("Export onglets en CSV"),
sidebarLayout(
sidebarPanel(
fileInput('fichier1','Choisissez votre fichier excel :',
accept = ".xlsx"),
fluidPage(
fluidRow(
column(width = 12,
numericInput("sheet","Indiquez l'onglet à afficher :",min = 1, value = 1),
tags$hr(),
textInput('text',"Indiquez le nom des fichiers :"),
tags$hr(),
h4("Pour télécharger les fichiers .csv :"),
downloadButton("download","Télécharger")
)
)
)),
mainPanel(
tabsetPanel(
tabPanel('Importation',
h4("Fichier de base:"),
dataTableOutput("contents"))
)
)
)
)
Server :
function(input,output){
#Création data :
data <- reactive({
inFile<- input$fichier1
if (is.null(inFile)){
return(NULL)
}else{
file.rename(inFile$datapath,
paste(inFile$datapath,".xlsx", sep =""))
wb = loadWorkbook(paste(inFile$datapath,".xlsx",sep=""))
lst = readWorksheet(wb,sheet = getSheets(wb))
list(wb = wb, lst = lst)
}
})
#Sortie de la table :
output$contents <- renderDataTable({
data()$wb[input$sheet]
},options = list(pageLength = 10))
#Téléchargement :
output$download <- downloadHandler(
#for (i in 1:input$sheet){
filename = function(){
paste(input$text,"_0",3,".csv",sep = "")
},
content = function(file){
write.table(data()$wb[3],file,
sep = ';', row.names = F, col.names = T)
}
#}
)
}
As #BigDataScientist pointed out, you could zip all of your csv file and download the zipped file. Your downloadHandler could look like:
output$download <- downloadHandler(
filename = function(){
paste0(input$text,".zip")
},
content = function(file){
#go to a temp dir to avoid permission issues
owd <- setwd(tempdir())
on.exit(setwd(owd))
files <- NULL;
#loop through the sheets
for (i in 1:input$sheet){
#write each sheet to a csv file, save the name
fileName <- paste(input$text,"_0",i,".csv",sep = "")
write.table(data()$wb[i],fileName,sep = ';', row.names = F, col.names = T)
files <- c(fileName,files)
}
#create the zip file
zip(file,files)
}
)
This does not download all the sheets from the excel file but the sheets ranging from 1 to whatever the user has as input in input$sheet.
You could also disable the download button if the user has not added an excel file/name.
Hope you've solved this MBnn, but in case anyone else is having similar problems, this case is down to RTools not being installed correctly on windows.
Currently you need to play close attention while running through the install process, and make sure to hit the checkbox to edit the system path.
Based on your code, this is likely to be the same issue preventing you from saving XLSX workbooks too.
I know this is an old thread but I had the same issue and the top answer did not work for me. However a simple tweak and using the archive package worked.
Reproductible example below:
library(shiny)
library(archive)
shinyApp(
# ui
ui = fluidPage(downloadButton("dl")),
# server
server = function(input, output, session) {
# download handler
output$dl <- downloadHandler(
filename = function() {"myzipfile.zip"},
# content: iris and mtcars
content = function(file) {
# definition of content to download
to_dl <- list(
# names to use in file names
names = list(a = "iris",
b = "mtcars"),
# data
data = list(a = iris,
b = mtcars)
)
# temp dir for the csv's as we can only create
# an archive from existent files and not data from R
twd <- setwd(tempdir())
on.exit(setwd(twd))
files <- NULL
# loop on data to download and write individual csv's
for (i in c("a", "b")) {
fileName <- paste0(to_dl[["names"]][[i]], ".csv") # csv file name
write.csv(to_dl[["data"]][[i]], fileName) # write csv in temp dir
files <- c(files, fileName) # store written file name
}
# create archive from written files
archive_write_files(file, files)
}
)
}
)
This will create the zip file myzipfile.zip which will contain iris.csv and mtcars.csv.

Resources