Skip to content

Storage

Firebase comes with many features, it is not limited to authentication. This document covers Firebase Storage, a storage system akin to AWS S3 though more flexible.

Firebase Storage stores files, you can add files, download files, delete files, and list the files. These files can be in buckets, or directories.

This requires the authentication to work, which has the advantage of easily ensuring that only those who may access certain files can.

Permissions

Note the firebase implements everything from the front-end: users of your shiny application will access the storage bucket via their own firebase credentials.

Let’s start from a basic application that features authentication. The app below uses the pre-built UI, probably the simplest app that can be made using Firebase.

library(shiny)
library(firebase)

ui <- fluidPage(
  useFirebase(), # import dependencies
  firebaseUIContainer(),
  reqSignin(h4("Logged in!")) # hide from UI
)

server <- function(input, output){
  f <- FirebaseUI$
    new()$ # instantiate
    set_providers( # define providers
      email = TRUE, 
      google = TRUE
    )$
    launch() # launch
}

shinyApp(ui, server)

Initialise

To use the storage we need to Initialise it client-side, it’s very simple.

library(shiny)
library(firebase)

ui <- fluidPage(
  useFirebase(), # import dependencies
  firebaseUIContainer(),
  reqSignin(h4("Logged in!")) # hide from UI
)

server <- function(input, output){
  f <- FirebaseUI$
    new()$ # instantiate
    set_providers( # define providers
      email = TRUE, 
      google = TRUE
    )$
    launch() # launch

  s <- Storage$new() # initialise
}

shinyApp(ui, server)

Reference

A crucial concept with the Storage is the idea of the reference. The reference is the path, directory, or bucket one is currently working with.

This is defined by a method on its own, methods used subsequently will then use this as reference.

For instance one can reference the file test.png even if it does not exist, then calling the method to upload a file will upload the file as test.png. Make sure you keep track of this.

Response

Much of JavaScript works asynchronously, it’s certainly one of its strong points. This means that when a function is called we do not receive the result from said function straight away, we get a promise back. Exactly like the R {promises} package.

Hence when triggering a sign in with other classes of the package the response is not returned by the same function. This is because the results of the authentication are returned later, and we cannot know when.

The same applies to the storage: when we upload a file we cannot know whether the upload was successful at the time of the upload, only later, and so on for every action we take on the storage.

Therefore, upon performing such actions (upload, delete, etc.) we (optionally) specify a response. This response identifier can then be used with the get_response method to retrieve the results of said operation: get_response acts exactly like any other shiny input.

Upload

We can then upload a file with the upload_file method.

Note that below we use a file on disk but you can use uploaded files: the method accepts the path to a file which can be obtained from a file upload in Shiny.

We also set the reference (ref) to test.png, we’ll be working with this file; it does not exist yet, we’ll create it, download it, delete it, etc.

The button to upload is only rendered if the user is logged in. The button triggers to upload_file method that uploads a file from the disk. To this method we also pass the name of the input we want to set to capture the response.

Below we set the response of the upload to up so we can pick up the response with input$up.

Note that you can set response to FALSE if you do not want to retrieve the results.

library(shiny)
library(firebase)

ui <- fluidPage(
  useFirebase(), # import dependencies
  firebaseUIContainer(),
  reqSignin(h4("Logged in!")), # hide from UI
  uiOutput("uploadUI")
)

server <- function(input, output){
  f <- FirebaseUI$
    new()$ # instantiate
    set_providers( # define providers
      email = TRUE, 
      google = TRUE
    )$
    launch() # launch

  s <- Storage$new()$
    ref("test.png")

  # upload a file
  output$uploadUI <- renderUI({
    f$req_sign_in()

    actionButton("upload", "Upload Image")
  })

  observeEvent(input$upload, {
    s$upload_file("/path/to/file.png", "up")
  })

  observeEvent(input$up, {
    print(input$up)
  })
}

shinyApp(ui, server)

Download

Once the file uploaded we can add a button to download the file. It does not truly download the file but will retrieve a valid URL to it. You may then do what you want with said link, download the file with {httr} (or {httr2}), use it as src atribute for a an <img/> tag, etc.

library(shiny)
library(firebase)

ui <- fluidPage(
  useFirebase(), # import dependencies
  firebaseUIContainer(),
  reqSignin(h4("Logged in!")), # hide from UI
  uiOutput("uploadUI"),
  uiOutput("downloadUI")
)

server <- function(input, output){
  f <- FirebaseUI$
    new()$ # instantiate
    set_providers( # define providers
      email = TRUE, 
      google = TRUE
    )$
    launch() # launch

  s <- Storage$new()$
    ref("test.png")

  # upload a file
  output$uploadUI <- renderUI({
    f$req_sign_in()

    actionButton("upload", "Upload Image")
  })

  observeEvent(input$upload, {
    s$upload_file("/path/to/file.png", "up")
  })

  observeEvent(input$up, {
    print(input$up)
  })

  # download a file
  output$downloadUI <- renderUI({
    f$req_sign_in()

    actionButton("download", "Download Image")
  })

  observeEvent(input$download, {
    s$download_file("dl")
  })

  observeEvent(input$dl, {
    print(input$dl)
  })
}

shinyApp(ui, server)

Delete

To delete a file simply call the delete_file method.

library(shiny)
library(firebase)

ui <- fluidPage(
  useFirebase(), # import dependencies
  firebaseUIContainer(),
  reqSignin(h4("Logged in!")), # hide from UI
  uiOutput("uploadUI"),
  uiOutput("downloadUI"),
  uiOutput("deleteUI")
)

server <- function(input, output){
  f <- FirebaseUI$
    new()$ # instantiate
    set_providers( # define providers
      email = TRUE, 
      google = TRUE
    )$
    launch() # launch

  s <- Storage$new()$
    ref("logo.png")

  # upload a file
  output$uploadUI <- renderUI({
    f$req_sign_in()

    actionButton("upload", "Upload Image")
  })

  observeEvent(input$upload, {
    s$upload_file("docs/logo.png", "up")
  })

  observeEvent(input$up, {
    print(input$up)
  })

  # download a file
  output$downloadUI <- renderUI({
    f$req_sign_in()

    actionButton("download", "Download Image")
  })

  observeEvent(input$download, {
    s$download_file("dl")
  })

  observeEvent(input$dl, {
    print(input$dl)
  })

  # delete file
  output$deleteUI <- renderUI({
    f$req_sign_in()

    actionButton("delete", "Delete Image")
  })

  observeEvent(input$delete, {
    s$delete_file("del")
  })

  observeEvent(input$del, {
    print(input$del)
  })
}

shinyApp(ui, server)

Other Methods

There are other methods to list files, and retrieve metadata but they work exactly like all others so we’ll live it at that