Shiny 应用程序 (R) 中的交互式目录输入

Posted

技术标签:

【中文标题】Shiny 应用程序 (R) 中的交互式目录输入【英文标题】:Interactive directory input in Shiny app (R) 【发布时间】:2017-01-04 22:09:57 【问题描述】:

我正在构建一个闪亮的应用程序,该应用程序需要用户在本地计算机上选择一个文件夹,其中包含应用程序要处理的文件。

我正在使用here 提出的解决方案。这在本地计算机上工作正常,但如果应用程序部署到 shinyapps 服务器则不起作用。 该解决方案的作者确认它仅设计用于本地 Shiny 应用程序,因为它通过调用 OS shell 来显示目录对话框。

我想知道目录对话框是否有不同的解决方案,它适用于已部署的 Shiny 应用程序(我正在部署到 shinyapps.io)。

已编辑:请注意,我不能使用fileInput 接口有两个原因:

    应用程序的用户不是技术人员,他们不知道应用程序使用文件夹中的哪些文件。 所选文件夹可能包含其他文件夹,其中包含所需文件,因此即使文件输入界面启用了multiple选项,也无法一次选择所有文件。

文件夹/文件结构不是我可以更改的,它是从医疗设备上按原样下载的,因此我唯一能从用户那里得到的就是指定父文件夹,其余的应该在 R 中完成代码。

【问题讨论】:

您是部署到shinyapps.io 还是拥有自己的shiny-server? 无论哪种方式,我认为最好的解决方案不是在本地存储目录文件,而是将文件夹移动到保管箱,然后使用 rdrop2 包调用用户选择的任何文件夹并在文件存储后获取数据下载到应用程序工作环境中 你好,卡尔。目前我正在部署到shinyapps.io。我打算在后端使用 Dropbox 来存储上传的数据。但是,我的应用程序的用户应该能够通过应用程序界面直接从他们的计算机上传他们的文件,而无需 Dropbox。 哦..我明白了,我以为你是从你的目录中采购的。为什么不直接使用闪亮的 fileUpload 函数? fileUpload上传单个文件,但是我需要上传用户指定目录下的所有文件... 【参考方案1】:

这是一个基于使用“webkitdirectory”属性的工作示例。目前该属性已被 Chrome、Opera 和 Safari(移动和桌面)支持,应该会在 9 月发布的 Firefox 49 中得到支持。 更多关于这个here。它也适用于子目录。

需要在ui.R中使用tags关键字。我通过上传三个 csv 文件对其进行了测试,每个文件包含三个由逗号分隔的数字。使用 Chrome 和 Opera 在本地和 shinyapps.io 上测试。这是代码:

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>")
    )

    )          

服务器.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);

    );

如果这有帮助,请告诉我。

【讨论】:

我按原样运行此代码,它一次只允许选择一个文件。我错过了什么? 您使用的是 Chrome 还是 Opera?它在 Firefox 或 IE 中不起作用 我现在在 Chrome 中进行了测试,它可以工作了!之前我只在 RStudio 中进行了测试,没有在浏览器中打开它,它只允许选择单个文件而不是文件夹。奇怪...知道为什么它在 RStudio 中不起作用吗? 顺便问一下,有没有办法改变按钮上的标签?在您的示例中,按钮旁边显示“选择文件”和“未选择文件”。就我而言,我需要说“选择文件夹”...谢谢! 你可以把js文件放到shiny app文件夹里面的www文件夹里面。引导程序在这里Bootstrap【参考方案2】:

您是否尝试过使用 shinyFiles 包? 有一个小部件可让您选择目录。 作为输出,您将获得该目录的路径,然后您可以使用该路径访问文件。 这是一个如何工作的示例。

服务器

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()))
) 

用户界面

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")
  )

))) 

希望这会有所帮助。

【讨论】:

这种方法在本地效果很好,但是当我将它部署到 shinyapps 服务器时,我无法再访问我的本地文件了。该对话框是空的,不允许我在任何地方导航。我试图在roots选项中指定一个特定的目录(例如roots = c(home='C:'),但是它在部署的应用程序中没有任何效果。 shinyFiles 用于服务器端目录选择,而 OP 显然想要的是客户端目录

以上是关于Shiny 应用程序 (R) 中的交互式目录输入的主要内容,如果未能解决你的问题,请参考以下文章

R - Shiny 上的实时图表

构建Shiny应用

如何在R / Shiny中将相机方向固定在交互式3D绘图中

Selectizeinput 输入互斥 R Shiny

通过 R Shiny 中的先前输入限制输入的选项

如何防止 splitLayout、Shiny、R 中的两个输入标签重叠?