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.
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)
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)
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.
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
.
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.
We then retrieve and print the result of the upload with the
get_response
method.
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")
})
observeEvent(s$get_response(), {
print(s$get_response())
})
}
shinyApp(ui, server)
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")
})
observeEvent(s$get_response(), {
print(s$get_response())
})
# download a file
output$downloadUI <- renderUI({
f$req_sign_in()
actionButton("download", "Download Image")
})
observeEvent(input$download, {
s$download_file("dl")
})
observeEvent(s$get_response("dl"), {
print(s$get_response("dl"))
})
}
shinyApp(ui, server)
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("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")
})
observeEvent(s$get_response(), {
print(s$get_response())
})
# download a file
output$downloadUI <- renderUI({
f$req_sign_in()
actionButton("download", "Download Image")
})
observeEvent(input$download, {
s$download_file("dl")
})
observeEvent(s$get_response("dl"), {
print(s$get_response("dl"))
})
# delete file
output$deleteUI <- renderUI({
f$req_sign_in()
actionButton("delete", "Delete Image")
})
observeEvent(input$delete, {
s$delete_file("del")
})
observeEvent(s$get_response("del"), {
print(s$get_response("del"))
})
}
shinyApp(ui, server)