Odin
Odin

Reputation: 715

R shiny restrict fileInput to filename pattern and not just file type

I have a Shiny app that use a fileInput to get some files client-side (I cannot use shinyFiles package that manages files server-side).

I want the user to be only able to upload files matching a specific pattern (e.g. helloWorld.txt) not only matching a file type (e.g. text, csv, etc.).

fileInput has an accept argument where you can provide accepted file types. From the doc:

accept  A character vector of MIME types; gives the browser a hint of 
        what kind of files the server is expecting.

I do not just want to specify accepted file types, which is not restrictive enough for my app. Is there a way to do this?

Here is a MWE to accept only text files:

library(shiny)

ui <- fluidPage(
    fileInput(
        "file_choice",
        label = "Choose a files", 
        multiple = TRUE,
        accept = c(
            ".txt"
        )
    )
)

server <- function(input, output, session) {}

shinyApp(ui, server)

If I use:

accept = c(
    "helloWorld.txt"
)

It does not work because it is not a MIME type.

This page Shiny fileInput parameter "accept" issue proposes to handle the selected file afterward server-side, which is what I will end up doing, but I would prefer a restriction a priori and not a posteriori (to avoid the server-side file checking and feedback to user).

Upvotes: 1

Views: 1212

Answers (1)

r2evans
r2evans

Reputation: 160607

One method is to interject some javascript as an onchange event trigger that checks the filename and, if it doesn't match, interrupt the upload process. This method uses an alert, I know many consider this method to be a bit invasive and not great aesthetics, I'm sure others can make better suggestions.

I should start with a simple caveat: the conditional here is strictly "the filename begins with the literal hello". Your example might require a little more finesse, instead requiring the filename sans-extension to match. In that case, regular expressions might be in order, reference something like https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions for more info. What this answer provides is a framework for you to fill in the holes.

(While modifying this code, you might find it useful to add alert(FileName) and/or alert(FileBase) to see what javascript is comparing against your pattern. It will popup with every attempt. In my case here, it helped me discover that, not surprisingly, the windows path was present, meaning it was using backslashes instead of forward-slashes, necessitating the split(/[\\/]/), further escaped for R.)

checkjs <- 'function checkFileName(fieldObj) {
    var FileName  = fieldObj.value;
    var FileBase = FileName.split(/[\\\\/]/).pop();
    if (! FileBase.startsWith("hello")) {
        fieldObj.value = "";
        alert("File does not start with hello");
        return false;
    }
    return true;
}'

attrib_replace <- function(x, cond, ...) {
  if (all(names(cond) %in% names(x)) && identical(cond, x[names(cond)])) x <- c(x, list(...))
  if ("attribs" %in% names(x)) x$attribs <- attrib_replace(x$attribs, cond = cond, ...)
  if ("children" %in% names(x)) x$children <- lapply(x$children, function(ch) attrib_replace(ch, cond = cond, ...))
  x
}

A sample app, using this:

library(shiny)
shinyApp(
  ui = fluidPage(
    tags$script(checkjs),
    attrib_replace(fileInput(
        "file_choice",
        label = "Choose a file",
        multiple = TRUE,
        accept = c(".txt")
    ), list(id = "file_choice", type = "file"), onchange = "checkFileName(this);")
  ),
  server = function(input, output, session) {}
)

shiny app showing interrupt file upload

When you select a file that does not start with "hello", it gives an alert and does not upload the file. A proper file uploads just file.

Some other answers I referenced for this:

Upvotes: 2

Related Questions