I am creating a Shiny app for a R package of mine.
In my package, I read in a FASTA sequence alignment file using the ape package via
seqs <- ape::read.dna(file = file.choose(), format = "fasta")
This works fine within my app locally since file.choose() is interactive. However, the above code fails on shiny apps.io.
It was suggested to me via Stack Overflow to write a new file.choose() function that incorporates Shiny's syntax, but I haven't a clue where to begin with this, since I am a Shiny novice. See: Popup window not appearing in R Shiny app - Stack Overflow for more details
Does anyone know where to start with coding up such a function? Perhaps someone has already implemented a working version and is willing to share?
Note: I don't want to have to use shiny::fileInput() as this would require me to change my package code, which runs in the background of my app,
Perhaps if Shiny guru Dean Attali is on the forum, he can weigh in?
Just as it was done in the SO post you linked, I would suggest using shinyFiles if you want your users to choose files from (part of) the server file system.
Another option could be to map those file paths into selectInput choices. Here is an example:
library(shiny)
my_files <- vector(mode = "character", length = 10)
for (i in seq_along(my_files)){
my_files[i] <- tempfile(fileext = ".txt")
}
names(my_files) <- basename(my_files)
ui <- fluidPage(
selectizeInput("choose_file", "Please choose a file:", choices = my_files),
textOutput("selected_file")
)
server <- function(input, output, session) {
output$selected_file <- renderText(paste("Selected file:", input$choose_file))
}
shinyApp(ui, server)
It seems that your second solution merely allows a user to select a file from a dropdown menu. Running shinyFilesExample() instead lets a user press a button that opens a popup window for file selection. To me, these are two different tasks.
I don't explicitly know what a users file path would be, nor what is contained there. Thus, I believe shinyFiles is the correct approach.
Absolutely right – both approaches are aiming at a different task. Unfortunately, you didn’t provide us with the needed details regarding your use case. Otherwise, I would not have created the selectizeInput example.
When using library(shinyFiles) know it only allows the user to browse the file system of the shiny host. fileInput is more flexible.
The last point you raise regarding shinyFiles is exactly the issue I'm wanting to get around.
The end user would ideally like to analyze their own data available on their own file system, not Shiny's. This functionality is provided by fileInput, but the issue is that my app basically runs a package through a server.
So, I'm thinking my problem could be solved (not ideally, however) using part of the other solution you provide. The user would need to ensure their file is contained within a temporary directory and then be able to access it to be read into the app. Something like
Running a package though the Shiny server should not be an issue in general, nor should fileInputbe problematic.
I think It's just that the solution in my case would entail altering my R package code.
The app runs via the CRAN version of my package, which contains the line
seqs <- ape::read.dna(file = file.choose(), format = "fasta").
The above line of course does not work on the server, but does work locally.
I do intend to include a launchApp() function through my package for users to run the app locally. However, a fully online version is also nice to have.
I could have a shiny = FALSE argument within my package's main function and then simply set this to TRUE for the app. Something like
if (shiny == TRUE) {
...
} else {
seqs <- ape::read.dna(file = file.choose(), format = "fasta")
}
I think that's the right way to go. However, I'd check for an interactive R session via interactive() because this is the required condition for file.choose() to work:
if (interactive()) {
seqs <- ape::read.dna(file = file.choose(), format = "fasta")
} else {
seqs <- ape::read.dna(file = some_path, format = "fasta")
}