Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interactive directory input in Shiny app (R)

I am building a shiny app that requires a user to select a folder on the local machine, which contains the files to be processed by the app.

I am using a solution proposed here. This works fine on a local machine, but does not work if the app is deployed to a shinyapps server. The author of this solution confirmed that it was only designed to work with local Shiny apps, since it makes OS shell calls to display a directory dialog.

I am wondering if there is a different solution for directory dialog, which will work on the deployed Shiny apps (I am deploying to shinyapps.io).

Edited: Notice that I cannot use fileInput interface for two reasons:

  1. The users of the app are not technical people and they do not know which files inside the folder are used by the app.
  2. The selected folder may contain other folders within which the needed files reside, such that it is impossible to select all files at once, even if the fileInput interface has the multiple option enabled.

The folder/files structure is not something I can change, it is downloaded AS IS from a medical device and therefore the only thing I can expect from the users is to specify the parent folder and the rest should be done inside the R code.

like image 608
Sasha Avatar asked Aug 28 '16 23:08

Sasha


2 Answers

This is a working example based on using the "webkitdirectory" attribute. At the moment the attribute is supported by Chrome, Opera and Safari (mobile and desktop) and it should be supported in Firefox 49 to be released in September. More about this here. It work with subdirectories also.

It requires using the tags keyword in ui.R. I have tested it by uploading three csv files each contaning three numbers separeted by a coma. Tested locally and on shinyapps.io with Chrome and Opera. This is the code:

ui.R

    library(shiny)
    library(DT)

    shinyUI(tagList(fluidPage(theme = "bootstrap.css",
                      includeScript("./www/text.js"),
                      titlePanel("Folder content upload"),

                      fluidRow(
                              column(4,
                                     wellPanel(
                                             tags$div(class="form-group shiny-input-container", 
                                                      tags$div(tags$label("File input")),
                                                      tags$div(tags$label("Choose folder", class="btn btn-primary",
                                                                          tags$input(id = "fileIn", webkitdirectory = TRUE, type = "file", style="display: none;", onchange="pressed()"))),
                                                      tags$label("No folder choosen", id = "noFile"),
                                                      tags$div(id="fileIn_progress", class="progress progress-striped active shiny-file-input-progress",
                                                               tags$div(class="progress-bar")
                                                      )     
                                             ),
                                             verbatimTextOutput("results")
                                     )
                              ),
                              column(8,
                                     tabsetPanel(
                                             tabPanel("Files table", dataTableOutput("tbl")),
                                             tabPanel("Files list", dataTableOutput("tbl2"))
                                     )
                              )
                      )
    ),
    HTML("<script type='text/javascript' src='getFolders.js'></script>")
    )

    )          

server.R

    library(shiny)
    library(ggplot2)
    library(DT)

    shinyServer(function(input, output, session) {
            df <- reactive({
                    inFiles <- input$fileIn
                    df <- data.frame()
                    if (is.null(inFiles))
                            return(NULL)
                    for (i in seq_along(inFiles$datapath)) {
                            tmp <- read.csv(inFiles$datapath[i], header = FALSE)  
                            df <- rbind(df, tmp)
                    }
                    df

            })
            output$tbl <- DT::renderDataTable(
                    df()
            )
            output$tbl2 <- DT::renderDataTable(
                    input$fileIn
            )
            output$results = renderPrint({
                    input$mydata
            })

    })

text.js

window.pressed = function(){
        var a = document.getElementById('fileIn');
        if(a.value === "")
        {
            noFile.innerHTML = "No folder choosen";
        }
        else
        {
            noFile.innerHTML = "";
        }
    };

getFolders.js

     document.getElementById("fileIn").addEventListener("change", function(e) {

            let files = e.target.files;
            var arr = new Array(files.length*2);
            for (let i=0; i<files.length; i++) {

            //console.log(files[i].webkitRelativePath);
            //console.log(files[i].name);
            arr[i] = files[i].webkitRelativePath;
            arr[i+files.length] = files[i].name;


            }

            Shiny.onInputChange("mydata", arr);

    });

Let me know if this helps.

like image 151
Valter Beaković Avatar answered Oct 14 '22 08:10

Valter Beaković


Have you tried around with the shinyFiles package? There is a widget which lets you chose a directory. As output you get the path of that directory which you can in turn use to access the files. Here is an example how it works.

server

library(shiny)
library(shinyFiles)

shinyServer(function(input, output, session) {

  # dir
  shinyDirChoose(input, 'dir', roots = c(home = '~'), filetypes = c('', 'txt'))
  dir <- reactive(input$dir)
  output$dir <- renderPrint(dir())

  # path
  path <- reactive({
    home <- normalizePath("~")
    file.path(home, paste(unlist(dir()$path[-1]), collapse = .Platform$file.sep))
  })

  # files
  output$files <- renderPrint(list.files(path()))
}) 

ui

library(shiny)
library(shinyFiles)

shinyUI(fluidPage(sidebarLayout(

  sidebarPanel(
    shinyDirButton("dir", "Chose directory", "Upload")
  ),

  mainPanel(
    h4("output$dir"),
    verbatimTextOutput("dir"), br(),
    h4("Files in that dir"),
    verbatimTextOutput("files")
  )

))) 

Hope this helps.

like image 45
mRcSchwering Avatar answered Oct 14 '22 09:10

mRcSchwering