How to Change Filenames of User Uploaded Files Shiny

Uploads and downloads

Transferring files to and from the user is a common feature of apps. Y'all can utilise it to upload data for analysis, or download the results as a dataset or as a report. This affiliate shows the UI and server components that you'll need to transfer files in and out of your app.

Upload

Nosotros'll start by discussing file uploads, showing you the basic UI and server components, and then showing how they fit together in a uncomplicated app.

UI

The UI needed to support file uploads is simple: merely add fileInput() to your UI.

Like well-nigh other UI components, in that location are but two required arguments: id and label. The width, buttonLabel and placeholder arguments allow y'all to tweak the appearance in other ways. I won't talk over them here, but you can read more about them in ?fileInput.

Server

Handling fileInput() on the server is a little more than complicated than other inputs. Most inputs render simple vectors, but fileInput() returns a data frame with four columns:

  • name: the original file name on the user's computer.

  • size: the file size, in bytes. By default, the user can only upload files upward to five MB. You can increase this limit by setting the shiny.maxRequestSize option prior to starting Shiny. For example, to let upward to ten MB run options(shiny.maxRequestSize = 10 * 1024^2).

  • type: the "MIME type"31 of the file. This is a formal specification of the file type that is usually derived from the extension and is rarely needed in Shiny apps.

  • datapath: the path to where the data has been uploaded on the server. Care for this path as ephemeral: if the user uploads more files, this file may be deleted. The information is ever saved to a temporary directory and given a temporary name.

I think the easiest style to empathise this data construction is to make a simple app. Run the following code and upload a few files to go a sense of what data Shiny is providing. Yous can see the results later on I uploaded a couple of puppy photos (from Department 7.3) in Effigy 9.1.

Note my use of the characterization and buttonLabel arguments to mildly customise the advent, and use of multiple = Truthful to let the user to upload multiple files.

Uploading information

If the user is uploading a dataset, at that place are ii details that you need to exist aware of:

  • input$upload is initialised to Naught on page load, so you lot'll demand req(input$upload) to make certain your lawmaking waits until the first file is uploaded.

  • The accept statement allows you to limit the possible inputs. The easiest way is to supply a grapheme vector of file extensions, similar accept = ".csv". But the accept statement is only a suggestion to the browser, and is not always enforced, so it's good practise to also validate information technology (e.g. Department viii.1) yourself. The easiest way to go the file extension in R is tools::file_ext(), just be aware it removes the leading . from the extension.

Putting all these ideas together gives united states of america the following app where y'all tin upload a .csv or .tsv file and see the showtime north rows. See information technology in activeness in https://hadley.shinyapps.io/ms-upload-validate.

                                  ui                  <-                  fluidPage                  (                  fileInput                  (                  "upload",                  Aught, accept                  =                  c                  (                  ".csv",                  ".tsv"                  )                  ),                  numericInput                  (                  "due north",                  "Rows", value                  =                  5, min                  =                  1, step                  =                  1                  ),                  tableOutput                  (                  "head"                  )                  )                  server                  <-                  role                  (                  input,                  output,                  session                  )                  {                  data                  <-                  reactive                  (                  {                  req                  (                  input                  $                  upload                  )                  ext                  <-                  tools                  ::                  file_ext                  (                  input                  $                  upload                  $                  name                  )                  switch                  (                  ext,       csv                  =                  vroom                  ::                  vroom                  (                  input                  $                  upload                  $                  datapath, delim                  =                  ","                  ),       tsv                  =                  vroom                  ::                  vroom                  (                  input                  $                  upload                  $                  datapath, delim                  =                  "\t"                  ),                  validate                  (                  "Invalid file; Please upload a .csv or .tsv file"                  )                  )                  }                  )                  output                  $                  head                  <-                  renderTable                  (                  {                  head                  (                  information                  (                  ),                  input                  $                  due north                  )                  }                  )                  }                              

Note that since multiple = False (the default), input$file will exist a single row data frame, and input$file$name and input$file$datapath volition be a length-one grapheme vector.

Download

Next, nosotros'll expect at file downloads, showing you the basic UI and server components, then demonstrating how you might use them to allow the user to download information or reports.

Basics

Over again, the UI is straightforward: apply either downloadButton(id) or downloadLink(id) to give the user something to click to download a file. The results are shown in Figure 9.2.

A download button and a download link

Figure ix.ii: A download button and a download link

Yous can customise their appearance using the same grade and icon arguments every bit for actionButtons(), every bit described in Section 2.two.7.

Unlike other outputs, downloadButton() is not paired with a return function. Instead, yous employ downloadHandler(), which looks something like this:

downloadHandler() has two arguments, both functions:

  • filename should be a part with no arguments that returns a file name (as a string). The task of this part is to create the name that will exist shown to the user in the download dialog box.

  • content should be a function with i statement, file, which is the path to save the file. The task of this function is to save the file in a place that Shiny knows about, so it can so send it to the user.

This is an unusual interface, but it allows Shiny to control where the file should exist saved (so it can exist placed in a secure location) while you even so control the contents of that file.

Next we'll put these pieces together to evidence how to transfer information files or reports to the user.

Downloading data

The following app shows off the basics of data download by assuasive y'all to download whatsoever dataset in the datasets package as a tab separated file, Figure 9.iii. I recommend using .tsv (tab separated value) instead of .csv (comma separated values) considering many European countries use commas to separate the whole and fractional parts of a number (eastward.g.ane,23 vs 1.23). This means they can't employ commas to separate fields and instead apply semi-colons in and then-chosen "c"sv files! You tin can avoid this complexity past using tab separated files, which work the same manner everywhere.

                                  ui                  <-                  fluidPage                  (                  selectInput                  (                  "dataset",                  "Choice a dataset",                  ls                  (                  "package:datasets"                  )                  ),                  tableOutput                  (                  "preview"                  ),                  downloadButton                  (                  "download",                  "Download .tsv"                  )                  )                  server                  <-                  office                  (                  input,                  output,                  session                  )                  {                  information                  <-                  reactive                  (                  {                  out                  <-                  get                  (                  input                  $                  dataset,                  "packet:datasets"                  )                  if                  (                  !                  is.data.frame                  (                  out                  )                  )                  {                  validate                  (                  paste0                  (                  "'",                  input                  $                  dataset,                  "' is non a data frame"                  )                  )                  }                  out                  }                  )                  output                  $                  preview                  <-                  renderTable                  (                  {                  caput                  (                  data                  (                  )                  )                  }                  )                  output                  $                  download                  <-                  downloadHandler                  (                  filename                  =                  function                  (                  )                  {                  paste0                  (                  input                  $                  dataset,                  ".tsv"                  )                  },     content                  =                  function                  (                  file                  )                  {                  vroom                  ::                  vroom_write                  (                  data                  (                  ),                  file                  )                  }                  )                  }                              

Note the utilise of validate() to only permit the user to download datasets that are data frames. A ameliorate approach would be to pre-filter the listing, but this lets you lot see another application of validate().

Downloading reports

As well as downloading data, y'all may want the users of your app to download a study that summarises the result of interactive exploration in the Shiny app. This is quite a lot of piece of work, because you also need to display the same information in a different format, merely it is very useful for high-stakes apps.

1 powerful way to generate such a report is with a parameterised RMarkdown document. A parameterised RMarkdown file has a params field in the YAML metadata:

                                                      title                    :                                          My Document                                                        output                    :                                          html_document                                                        params                    :                                                                                                year                    :                                                            2018                                                                                                region                    :                                          Europe                                                                                                printcode                    :                                                            TRUE                                                                                                data                    :                                          file.csv                                                

Inside the certificate, you can refer to these values using params$year, params$region etc. The values in the YAML metadata are defaults; you'll generally override them by providing the params argument in a call to rmarkdown::render(). This makes it easy to generate many unlike reports from the same .Rmd.

Here's a simple example adapted from https://shiny.rstudio.com/manufactures/generating-reports.html, which describes this technique in more detail. The cardinal thought is to call rmarkdown::render() from the content statement of downloadHander(). If you want to produce other output formats, merely change the output format in the .Rmd, and make certain to update the extension (e.g. to .pdf). See it in action at https://hadley.shinyapps.io/ms-download-rmd.

                                  ui                  <-                  fluidPage                  (                  sliderInput                  (                  "due north",                  "Number of points",                  1,                  100,                  50                  ),                  downloadButton                  (                  "report",                  "Generate report"                  )                  )                  server                  <-                  function                  (                  input,                  output,                  session                  )                  {                  output                  $                  report                  <-                  downloadHandler                  (                  filename                  =                  "report.html",     content                  =                  function                  (                  file                  )                  {                  params                  <-                  list                  (n                  =                  input                  $                  n                  )                  id                  <-                  showNotification                  (                  "Rendering written report...",          elapsing                  =                  Cypher,          closeButton                  =                  FALSE                  )                  on.get out                  (                  removeNotification                  (                  id                  ), add                  =                  True                  )                  rmarkdown                  ::                  render                  (                  "report.Rmd",          output_file                  =                  file,         params                  =                  params,         envir                  =                  new.env                  (parent                  =                  globalenv                  (                  )                  )                  )                  }                  )                  }                              

It'll generally have at to the lowest degree a few seconds to return a .Rmd, and then this is a skillful place to apply a notification from Section viii.2.

There are a couple of other tricks worth knowing about:

  • RMarkdown works in the current working directory, which volition fail in many deployment scenarios (e.grand. on shinyapps.io). Y'all tin work around this by copying the study to a temporary directory when your app starts (i.e. outside of the server function):

                                              report_path                      <-                      tempfile                      (fileext                      =                      ".Rmd"                      )                      file.copy                      (                      "written report.Rmd",                      report_path, overwrite                      =                      Truthful                      )                                      

    Then replace "study.Rmd" with report_path in the call to rmarkdown::return():

  • Past default, RMarkdown will return the report in the current procedure, which means that it volition inherit many settings from the Shiny app (similar loaded packages, options, etc). For greater robustness, I recommend running render() in a separate R session using the callr package:

                                              render_report                      <-                      role                      (                      input,                      output,                      params                      )                      {                      rmarkdown                      ::                      return                      (                      input,     output_file                      =                      output,     params                      =                      params,     envir                      =                      new.env                      (parent                      =                      globalenv                      (                      )                      )                      )                      }                      server                      <-                      role                      (                      input,                      output                      )                      {                      output                      $                      report                      <-                      downloadHandler                      (                      filename                      =                      "report.html",     content                      =                      function                      (                      file                      )                      {                      params                      <-                      list                      (n                      =                      input                      $                      slider                      )                      callr                      ::                      r                      (                      render_report,                      list                      (input                      =                      report_path, output                      =                      file, params                      =                      params                      )                      )                      }                      )                      }                                      

You tin see all these pieces put together in rmarkdown-report/, found inside the Mastering Shiny GitHub repo.

The shinymeta package solves a related problem: sometimes you need to be able to plough the current state of a Shiny app into a reproducible report that can be re-run in the hereafter. Learn more than about information technology in Joe Cheng's useR! 2022 keynote, "Shiny's holy grail: Interactivity with reproducibility".

Example study

To finish upwardly, we'll piece of work through a small-scale example written report where we upload a file (with user supplied separator), preview it, perform some optional transformations using the janitor package, by Sam Firke, and and then permit the user download it as a .tsv.

To brand it easier to understand how to employ the app, I've used sidebarLayout() to split up the app into three primary steps:

  1. Uploading and parsing the file:

                                          ui_upload                    <-                    sidebarLayout                    (                    sidebarPanel                    (                    fileInput                    (                    "file",                    "Information", buttonLabel                    =                    "Upload..."                    ),                    textInput                    (                    "delim",                    "Delimiter (leave blank to gauge)",                    ""                    ),                    numericInput                    (                    "skip",                    "Rows to skip",                    0, min                    =                    0                    ),                    numericInput                    (                    "rows",                    "Rows to preview",                    ten, min                    =                    one                    )                    ),                    mainPanel                    (                    h3                    (                    "Raw data"                    ),                    tableOutput                    (                    "preview1"                    )                    )                    )                                  
  2. Cleaning the file.

  3. Downloading the file.

which become assembled into a single fluidPage():

                              ui                <-                fluidPage                (                ui_upload,                ui_clean,                ui_download                )                          

This aforementioned organisation makes it easier to understand the app:

                              server                <-                office                (                input,                output,                session                )                {                # Upload ---------------------------------------------------------                raw                <-                reactive                (                {                req                (                input                $                file                )                delim                <-                if                (                input                $                delim                ==                ""                )                NULL                else                input                $                delim                vroom                ::                vroom                (                input                $                file                $                datapath, delim                =                delim, skip                =                input                $                skip                )                }                )                output                $                preview1                <-                renderTable                (                head                (                raw                (                ),                input                $                rows                )                )                # Make clean ----------------------------------------------------------                tidied                <-                reactive                (                {                out                <-                raw                (                )                if                (                input                $                serpent                )                {                names                (                out                )                <-                janitor                ::                make_clean_names                (                names                (                out                )                )                }                if                (                input                $                empty                )                {                out                <-                janitor                ::                remove_empty                (                out,                "cols"                )                }                if                (                input                $                abiding                )                {                out                <-                janitor                ::                remove_constant                (                out                )                }                out                }                )                output                $                preview2                <-                renderTable                (                head                (                tidied                (                ),                input                $                rows                )                )                # Download -------------------------------------------------------                output                $                download                <-                downloadHandler                (                filename                =                function                (                )                {                paste0                (                tools                ::                file_path_sans_ext                (                input                $                file                $                name                ),                ".tsv"                )                },     content                =                part                (                file                )                {                vroom                ::                vroom_write                (                tidied                (                ),                file                )                }                )                }                          

Exercises

  1. Apply the ambient package by Thomas Lin Pedersen to generate worley noise and download a PNG of it.

  2. Create an app that lets yous upload a csv file, select a variable, and and so perform a t.test() on that variable. Subsequently the user has uploaded the csv file, you'll need to use updateSelectInput() to fill in the available variables. See Department 10.ane for details.

  3. Create an app that lets the user upload a csv file, select one variable, describe a histogram, and so download the histogram. For an additional challenge, allow the user to select from .png, .pdf, and .svg output formats.

  4. Write an app that allows the user to create a Lego mosaic from any .png file using Ryan Timpe'due south brickr package. Once you've completed the nuts, add controls to allow the user to select the size of the mosaic (in bricks), and choose whether to use "universal" or "generic" colour palettes.

  5. The final app in Department 9.3 contains this one large reactive:

                                          tidied                    <-                    reactive                    (                    {                    out                    <-                    raw                    (                    )                    if                    (                    input                    $                    snake                    )                    {                    names                    (                    out                    )                    <-                    janitor                    ::                    make_clean_names                    (                    names                    (                    out                    )                    )                    }                    if                    (                    input                    $                    empty                    )                    {                    out                    <-                    janitor                    ::                    remove_empty                    (                    out,                    "cols"                    )                    }                    if                    (                    input                    $                    abiding                    )                    {                    out                    <-                    janitor                    ::                    remove_constant                    (                    out                    )                    }                    out                    }                    )                                  

    Break information technology upward into multiple pieces and then that (due east.yard.) janitor::make_clean_names() is not re-run when input$empty changes.

Summary

In this chapter, you've learned how to transfer files to and from the user using fileInput() and downloadButton(). Most of the challenges arise either treatment the uploaded files or generating the files to download, so I showed y'all how to handle a couple of common cases. If I didn't cover your specific claiming here, you lot'll need to employ your ain unique creativity the problem 😄.

The side by side affiliate will help you handle a common challenge when working with user supplied data: you demand to dynamically adapt the user interface to better fit the data. I'll start with some elementary techniques that are easy to understand and can exist applied in many situations, gradually working our style up to fully a dynamic user-interface generated by code.

yorksarasked.blogspot.com

Source: https://mastering-shiny.org/action-transfer.html

0 Response to "How to Change Filenames of User Uploaded Files Shiny"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel